summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormsweet <msweet@a1ca3aef-8c08-0410-bb20-df032aa958be>2013-05-10 18:56:23 +0000
committermsweet <msweet@a1ca3aef-8c08-0410-bb20-df032aa958be>2013-05-10 18:56:23 +0000
commit641f3d83c2b5a4c402cdc8bc3ed1baa5336d404d (patch)
treec3115ba719feb9bc45c28f2e71a52ecb044bd1aa
downloadcups-release-1.0b7.tar.gz
Import cups.org releasesrelease-1.0b7
git-svn-id: svn+ssh://src.apple.com/svn/cups/cups.org/tags/release-1.0b7@4306 a1ca3aef-8c08-0410-bb20-df032aa958be
-rw-r--r--LICENSE.html386
-rw-r--r--Makedefs.in138
-rw-r--r--Makefile76
-rw-r--r--README.txt195
-rw-r--r--backend/Makefile119
-rw-r--r--backend/ipp.c399
-rw-r--r--backend/lpd.c393
-rw-r--r--backend/parallel.c179
-rw-r--r--backend/serial.c297
-rwxr-xr-xbackend/smb.sh88
-rw-r--r--backend/socket.c232
-rw-r--r--berkeley/Makefile95
-rw-r--r--berkeley/lpc.c458
-rw-r--r--berkeley/lpq.c369
-rw-r--r--berkeley/lpr.c252
-rw-r--r--berkeley/lprm.c205
-rw-r--r--cgi-bin/Makefile83
-rw-r--r--cgi-bin/classes.c484
-rw-r--r--cgi-bin/jobs.c584
-rw-r--r--cgi-bin/printers.c486
-rw-r--r--conf/Makefile68
-rw-r--r--conf/classes.conf72
-rw-r--r--conf/cupsd.conf369
-rw-r--r--conf/cupsd.conf-personal250
-rw-r--r--conf/cupsd.conf-professional313
-rw-r--r--conf/mime.convs62
-rw-r--r--conf/mime.types122
-rw-r--r--conf/printers.conf89
-rw-r--r--config.h.in101
-rw-r--r--configure.in316
-rw-r--r--cups.dsw113
-rwxr-xr-xcups.sh112
-rw-r--r--cups/Makefile150
-rw-r--r--cups/cups.dsp176
-rw-r--r--cups/cups.h143
-rw-r--r--cups/cups_C.h123
-rw-r--r--cups/debug.h57
-rw-r--r--cups/emit.c301
-rw-r--r--cups/filter.c297
-rw-r--r--cups/http.c1446
-rw-r--r--cups/http.h291
-rw-r--r--cups/ipp.c1459
-rw-r--r--cups/ipp.h343
-rw-r--r--cups/language.c373
-rw-r--r--cups/language.h195
-rw-r--r--cups/mark.c412
-rw-r--r--cups/mime.c617
-rw-r--r--cups/mime.h137
-rw-r--r--cups/options.c378
-rw-r--r--cups/page.c189
-rw-r--r--cups/ppd.c1769
-rw-r--r--cups/ppd.h239
-rw-r--r--cups/raster.c252
-rw-r--r--cups/raster.h233
-rw-r--r--cups/string.c125
-rw-r--r--cups/string.h66
-rw-r--r--cups/testhttp.c109
-rw-r--r--cups/testmime.c199
-rw-r--r--cups/testmime.dsp102
-rw-r--r--cups/testppd.c183
-rw-r--r--cups/testppd.dsp102
-rw-r--r--cups/type.c1011
-rw-r--r--cups/usersys.c175
-rw-r--r--cups/util.c986
-rw-r--r--data/Makefile56
-rw-r--r--doc/Makefile109
-rw-r--r--doc/cmp.html651
-rw-r--r--doc/cmp.pdf963
-rw-r--r--doc/cmp.shtml717
-rw-r--r--doc/cups.css4
-rw-r--r--doc/cupsdoc.css9
-rw-r--r--doc/documentation.html70
-rw-r--r--doc/figures.scbin0 -> 51020 bytes
-rw-r--r--doc/idd.html746
-rw-r--r--doc/idd.pdf1577
-rw-r--r--doc/idd.shtml1219
-rw-r--r--doc/images/classes.gifbin0 -> 591 bytes
-rw-r--r--doc/images/cups-bar.gifbin0 -> 1242 bytes
-rw-r--r--doc/images/cups-block-diagram.gifbin0 -> 3509 bytes
-rw-r--r--doc/images/cups-large.gifbin0 -> 7457 bytes
-rw-r--r--doc/images/cups-medium.gifbin0 -> 3163 bytes
-rw-r--r--doc/images/cups-small.gifbin0 -> 1266 bytes
-rw-r--r--doc/images/draft.gifbin0 -> 926 bytes
-rw-r--r--doc/images/logo.gifbin0 -> 1958 bytes
-rw-r--r--doc/images/navbar.gifbin0 -> 2251 bytes
-rw-r--r--doc/images/navbar.xcf.gzbin0 -> 4253 bytes
-rw-r--r--doc/images/printer-idle.gifbin0 -> 706 bytes
-rw-r--r--doc/images/printer-processing.gifbin0 -> 805 bytes
-rw-r--r--doc/images/printer-stopped.gifbin0 -> 794 bytes
-rw-r--r--doc/index.html34
-rw-r--r--doc/overview.html287
-rw-r--r--doc/overview.pdfbin0 -> 18985 bytes
-rw-r--r--doc/sam.html873
-rw-r--r--doc/sam.pdf1114
-rw-r--r--doc/sam.shtml1038
-rw-r--r--doc/sdd.html473
-rw-r--r--doc/sdd.pdf989
-rw-r--r--doc/sdd.shtml567
-rw-r--r--doc/ssr.html221
-rw-r--r--doc/ssr.pdf446
-rw-r--r--doc/ssr.shtml252
-rw-r--r--doc/stp.html145
-rw-r--r--doc/stp.pdfbin0 -> 22085 bytes
-rw-r--r--doc/stp.shtml169
-rw-r--r--doc/sum.html511
-rw-r--r--doc/sum.pdf949
-rw-r--r--doc/sum.shtml562
-rw-r--r--doc/svd.html149
-rw-r--r--doc/svd.pdfbin0 -> 21933 bytes
-rw-r--r--doc/svd.shtml167
-rw-r--r--filter/Makefile150
-rw-r--r--filter/common.c252
-rw-r--r--filter/common.h67
-rw-r--r--filter/hpgl-attr.c405
-rw-r--r--filter/hpgl-char.c433
-rw-r--r--filter/hpgl-config.c473
-rw-r--r--filter/hpgl-input.c232
-rw-r--r--filter/hpgl-main.c255
-rw-r--r--filter/hpgl-polygon.c380
-rw-r--r--filter/hpgl-prolog.c192
-rw-r--r--filter/hpgl-vector.c704
-rw-r--r--filter/hpgltops.h196
-rw-r--r--filter/image-colorspace.c910
-rw-r--r--filter/image-gif.c644
-rw-r--r--filter/image-jpeg.c190
-rw-r--r--filter/image-photocd.c319
-rw-r--r--filter/image-png.c205
-rw-r--r--filter/image-pnm.c288
-rw-r--r--filter/image-sgi.c267
-rw-r--r--filter/image-sgi.h94
-rw-r--r--filter/image-sgilib.c857
-rw-r--r--filter/image-sun.c376
-rw-r--r--filter/image-tiff.c1622
-rw-r--r--filter/image-zoom.c310
-rw-r--r--filter/image.c743
-rw-r--r--filter/image.h223
-rw-r--r--filter/imagetops.c494
-rw-r--r--filter/imagetoraster.c3839
-rw-r--r--filter/pstops.c804
-rw-r--r--filter/rastertohp.c493
-rw-r--r--filter/textcommon.c745
-rw-r--r--filter/textcommon.h89
-rw-r--r--filter/texttops.c568
-rw-r--r--fonts/AvantGarde-Bookbin0 -> 25994 bytes
-rw-r--r--fonts/AvantGarde-BookObliquebin0 -> 26882 bytes
-rw-r--r--fonts/AvantGarde-Demibin0 -> 26980 bytes
-rw-r--r--fonts/AvantGarde-DemiObliquebin0 -> 27530 bytes
-rw-r--r--fonts/Bookman-Demibin0 -> 35453 bytes
-rw-r--r--fonts/Bookman-DemiItalicbin0 -> 35218 bytes
-rw-r--r--fonts/Bookman-Lightbin0 -> 35522 bytes
-rw-r--r--fonts/Bookman-LightItalicbin0 -> 34617 bytes
-rw-r--r--fonts/Courierbin0 -> 35719 bytes
-rw-r--r--fonts/Courier-Boldbin0 -> 38284 bytes
-rw-r--r--fonts/Courier-BoldObliquebin0 -> 40686 bytes
-rw-r--r--fonts/Courier-Obliquebin0 -> 34677 bytes
-rw-r--r--fonts/Helveticabin0 -> 27505 bytes
-rw-r--r--fonts/Helvetica-Boldbin0 -> 26762 bytes
-rw-r--r--fonts/Helvetica-BoldObliquebin0 -> 28971 bytes
-rw-r--r--fonts/Helvetica-Narrowbin0 -> 27406 bytes
-rw-r--r--fonts/Helvetica-Narrow-Boldbin0 -> 27960 bytes
-rw-r--r--fonts/Helvetica-Narrow-BoldObliquebin0 -> 29384 bytes
-rw-r--r--fonts/Helvetica-Narrow-Obliquebin0 -> 28298 bytes
-rw-r--r--fonts/Helvetica-Obliquebin0 -> 28397 bytes
-rw-r--r--fonts/Makefile68
-rw-r--r--fonts/NewCenturySchlbk-Boldbin0 -> 38763 bytes
-rw-r--r--fonts/NewCenturySchlbk-BoldItalicbin0 -> 38742 bytes
-rw-r--r--fonts/NewCenturySchlbk-Italicbin0 -> 38413 bytes
-rw-r--r--fonts/NewCenturySchlbk-Romanbin0 -> 37561 bytes
-rw-r--r--fonts/Palatino-Boldbin0 -> 41591 bytes
-rw-r--r--fonts/Palatino-BoldItalicbin0 -> 40387 bytes
-rw-r--r--fonts/Palatino-Italicbin0 -> 39917 bytes
-rw-r--r--fonts/Palatino-Romanbin0 -> 42328 bytes
-rw-r--r--fonts/Symbolbin0 -> 32213 bytes
-rw-r--r--fonts/Times-Boldbin0 -> 34996 bytes
-rw-r--r--fonts/Times-BoldItalicbin0 -> 36325 bytes
-rw-r--r--fonts/Times-Italicbin0 -> 37139 bytes
-rw-r--r--fonts/Times-Romanbin0 -> 35161 bytes
-rw-r--r--fonts/Utopia-Boldbin0 -> 36580 bytes
-rw-r--r--fonts/Utopia-BoldItalicbin0 -> 37836 bytes
-rw-r--r--fonts/Utopia-Italicbin0 -> 37599 bytes
-rw-r--r--fonts/Utopia-Regularbin0 -> 36350 bytes
-rw-r--r--fonts/ZapfChancery-MediumItalicbin0 -> 38422 bytes
-rw-r--r--fonts/ZapfDingbatsbin0 -> 44537 bytes
-rw-r--r--locale/C/cups_C123
-rw-r--r--locale/Makefile73
-rw-r--r--locale/de/cups_de123
-rw-r--r--locale/en/cups_en123
-rw-r--r--locale/es/cups_es123
-rw-r--r--locale/fr/cups_fr123
-rw-r--r--locale/it/cups_it123
-rw-r--r--locale/locale.txt32
-rw-r--r--locale/translate.c259
-rw-r--r--man/Makefile78
-rw-r--r--man/accept.857
-rw-r--r--man/accept.zbin0 -> 905 bytes
-rw-r--r--man/backend.188
-rw-r--r--man/backend.zbin0 -> 1387 bytes
-rw-r--r--man/classes.conf.536
-rw-r--r--man/classes.conf.zbin0 -> 433 bytes
-rw-r--r--man/cupsd.847
-rw-r--r--man/cupsd.conf.536
-rw-r--r--man/cupsd.conf.zbin0 -> 432 bytes
-rw-r--r--man/cupsd.zbin0 -> 883 bytes
-rw-r--r--man/enable.864
-rw-r--r--man/enable.zbin0 -> 940 bytes
-rw-r--r--man/filter.195
-rw-r--r--man/filter.zbin0 -> 1683 bytes
-rw-r--r--man/lp.171
-rw-r--r--man/lp.zbin0 -> 836 bytes
-rw-r--r--man/lpadmin.8124
-rw-r--r--man/lpadmin.zbin0 -> 2514 bytes
-rw-r--r--man/lpc.879
-rw-r--r--man/lpc.zbin0 -> 1264 bytes
-rw-r--r--man/lpr.196
-rw-r--r--man/lpr.zbin0 -> 1451 bytes
-rw-r--r--man/lprm.140
-rw-r--r--man/lprm.zbin0 -> 414 bytes
-rw-r--r--man/lpstat.1115
-rw-r--r--man/lpstat.zbin0 -> 1983 bytes
-rw-r--r--man/mime.convs.536
-rw-r--r--man/mime.convs.zbin0 -> 432 bytes
-rw-r--r--man/mime.types.536
-rw-r--r--man/mime.types.zbin0 -> 432 bytes
-rw-r--r--man/printers.conf.536
-rw-r--r--man/printers.conf.zbin0 -> 434 bytes
-rw-r--r--ppd/Makefile55
-rw-r--r--ppd/deskjet.ppd186
-rw-r--r--ppd/laserjet.ppd172
-rw-r--r--pstoraster/Fontmap98
-rw-r--r--pstoraster/Makefile167
-rw-r--r--pstoraster/bfont.h54
-rw-r--r--pstoraster/bseq.h66
-rw-r--r--pstoraster/btoken.h65
-rw-r--r--pstoraster/ctype_.h31
-rw-r--r--pstoraster/dirent_.h50
-rw-r--r--pstoraster/dstack.h307
-rw-r--r--pstoraster/errno_.h35
-rw-r--r--pstoraster/errors.h179
-rw-r--r--pstoraster/estack.h137
-rw-r--r--pstoraster/files.h142
-rw-r--r--pstoraster/fname.h38
-rw-r--r--pstoraster/gconf.c126
-rw-r--r--pstoraster/gconfig.h196
-rw-r--r--pstoraster/gconfigv.h3
-rw-r--r--pstoraster/gdebug.h117
-rw-r--r--pstoraster/gdevabuf.c355
-rw-r--r--pstoraster/gdevcups.c2459
-rw-r--r--pstoraster/gdevddrw.c470
-rw-r--r--pstoraster/gdevdflt.c878
-rw-r--r--pstoraster/gdevemap.c64
-rw-r--r--pstoraster/gdevht.h47
-rw-r--r--pstoraster/gdevm1.c689
-rw-r--r--pstoraster/gdevm16.c154
-rw-r--r--pstoraster/gdevm2.c244
-rw-r--r--pstoraster/gdevm24.c484
-rw-r--r--pstoraster/gdevm32.c233
-rw-r--r--pstoraster/gdevm4.c207
-rw-r--r--pstoraster/gdevm8.c225
-rw-r--r--pstoraster/gdevmem.c363
-rw-r--r--pstoraster/gdevmem.h217
-rw-r--r--pstoraster/gdevmpla.c182
-rw-r--r--pstoraster/gdevmrop.c1013
-rw-r--r--pstoraster/gdevmrop.h65
-rw-r--r--pstoraster/gdevnfwd.c561
-rw-r--r--pstoraster/gdevpipe.c74
-rw-r--r--pstoraster/gdevprn.c757
-rw-r--r--pstoraster/gdevprn.h418
-rw-r--r--pstoraster/genarch.c131
-rw-r--r--pstoraster/ghost.h27
-rw-r--r--pstoraster/gp.h201
-rw-r--r--pstoraster/gp_nofb.c54
-rw-r--r--pstoraster/gp_unifn.c58
-rw-r--r--pstoraster/gp_unifs.c424
-rw-r--r--pstoraster/gp_unix.c174
-rw-r--r--pstoraster/gpcheck.h58
-rw-r--r--pstoraster/gs_btokn.ps287
-rw-r--r--pstoraster/gs_ccfnt.ps98
-rw-r--r--pstoraster/gs_cidfn.ps127
-rw-r--r--pstoraster/gs_cmap.ps235
-rw-r--r--pstoraster/gs_cmdl.ps186
-rw-r--r--pstoraster/gs_dbt_e.ps65
-rw-r--r--pstoraster/gs_diskf.ps230
-rw-r--r--pstoraster/gs_dps1.ps307
-rw-r--r--pstoraster/gs_fform.ps115
-rw-r--r--pstoraster/gs_fonts.ps797
-rw-r--r--pstoraster/gs_init.ps1271
-rw-r--r--pstoraster/gs_iso_e.ps72
-rw-r--r--pstoraster/gs_kanji.ps164
-rw-r--r--pstoraster/gs_ksb_e.ps70
-rw-r--r--pstoraster/gs_l2img.ps191
-rw-r--r--pstoraster/gs_lev2.ps332
-rw-r--r--pstoraster/gs_mex_e.ps70
-rw-r--r--pstoraster/gs_mro_e.ps63
-rw-r--r--pstoraster/gs_pdf.ps575
-rw-r--r--pstoraster/gs_pdf_e.ps48
-rw-r--r--pstoraster/gs_pdfwr.ps288
-rw-r--r--pstoraster/gs_pfile.ps184
-rw-r--r--pstoraster/gs_res.ps555
-rw-r--r--pstoraster/gs_setpd.ps644
-rw-r--r--pstoraster/gs_statd.ps275
-rw-r--r--pstoraster/gs_std_e.ps79
-rw-r--r--pstoraster/gs_sym_e.ps89
-rw-r--r--pstoraster/gs_ttf.ps447
-rw-r--r--pstoraster/gs_typ42.ps47
-rw-r--r--pstoraster/gs_type1.ps132
-rw-r--r--pstoraster/gs_wan_e.ps48
-rw-r--r--pstoraster/gs_wl1_e.ps72
-rw-r--r--pstoraster/gs_wl2_e.ps72
-rw-r--r--pstoraster/gs_wl5_e.ps72
-rw-r--r--pstoraster/gsalloc.c1010
-rw-r--r--pstoraster/gsalloc.h58
-rw-r--r--pstoraster/gsbitops.c605
-rw-r--r--pstoraster/gsbitops.h178
-rw-r--r--pstoraster/gsbittab.c135
-rw-r--r--pstoraster/gsbittab.h55
-rw-r--r--pstoraster/gsccode.h58
-rw-r--r--pstoraster/gsccolor.h51
-rw-r--r--pstoraster/gscdef.c83
-rw-r--r--pstoraster/gscdefs.h55
-rw-r--r--pstoraster/gschar.c1353
-rw-r--r--pstoraster/gschar.h120
-rw-r--r--pstoraster/gschar0.c342
-rw-r--r--pstoraster/gscie.c1206
-rw-r--r--pstoraster/gscie.h485
-rw-r--r--pstoraster/gscolor.c361
-rw-r--r--pstoraster/gscolor.h44
-rw-r--r--pstoraster/gscolor1.c231
-rw-r--r--pstoraster/gscolor1.h49
-rw-r--r--pstoraster/gscolor2.c205
-rw-r--r--pstoraster/gscolor2.h82
-rw-r--r--pstoraster/gscoord.c463
-rw-r--r--pstoraster/gscoord.h55
-rw-r--r--pstoraster/gscpm.h38
-rw-r--r--pstoraster/gscrypt1.h44
-rw-r--r--pstoraster/gscsepr.c161
-rw-r--r--pstoraster/gscspace.h194
-rw-r--r--pstoraster/gsdcolor.h287
-rw-r--r--pstoraster/gsdevice.c416
-rw-r--r--pstoraster/gsdevice.h86
-rw-r--r--pstoraster/gsdevmem.c221
-rw-r--r--pstoraster/gsdparam.c603
-rw-r--r--pstoraster/gsdps1.c169
-rw-r--r--pstoraster/gserror.h33
-rw-r--r--pstoraster/gserrors.h48
-rw-r--r--pstoraster/gsexit.h39
-rw-r--r--pstoraster/gsfont.c486
-rw-r--r--pstoraster/gsfont.h70
-rw-r--r--pstoraster/gsfont0.c115
-rw-r--r--pstoraster/gshsb.c138
-rw-r--r--pstoraster/gshsb.h28
-rw-r--r--pstoraster/gsht.c427
-rw-r--r--pstoraster/gsht.h81
-rw-r--r--pstoraster/gsht1.c346
-rw-r--r--pstoraster/gsht1.h42
-rw-r--r--pstoraster/gshtscr.c503
-rw-r--r--pstoraster/gsimage.c312
-rw-r--r--pstoraster/gsimage.h50
-rw-r--r--pstoraster/gsimpath.c188
-rw-r--r--pstoraster/gsinit.c74
-rw-r--r--pstoraster/gsio.h65
-rw-r--r--pstoraster/gsiodev.c270
-rw-r--r--pstoraster/gsiparam.h238
-rw-r--r--pstoraster/gslib.h39
-rw-r--r--pstoraster/gsline.c234
-rw-r--r--pstoraster/gsline.h60
-rw-r--r--pstoraster/gslparam.h47
-rw-r--r--pstoraster/gsmatrix.c418
-rw-r--r--pstoraster/gsmatrix.h75
-rw-r--r--pstoraster/gsmdebug.h50
-rw-r--r--pstoraster/gsmemory.c392
-rw-r--r--pstoraster/gsmemory.h355
-rw-r--r--pstoraster/gsmisc.c649
-rw-r--r--pstoraster/gspaint.c292
-rw-r--r--pstoraster/gspaint.h36
-rw-r--r--pstoraster/gsparam.c374
-rw-r--r--pstoraster/gsparam.h349
-rw-r--r--pstoraster/gspath.c259
-rw-r--r--pstoraster/gspath.h77
-rw-r--r--pstoraster/gspath1.c371
-rw-r--r--pstoraster/gspath2.h35
-rw-r--r--pstoraster/gspcolor.c743
-rw-r--r--pstoraster/gspenum.h39
-rw-r--r--pstoraster/gsrefct.h137
-rw-r--r--pstoraster/gsrop.c91
-rw-r--r--pstoraster/gsrop.h44
-rw-r--r--pstoraster/gsropt.h180
-rw-r--r--pstoraster/gsroptab.c342
-rw-r--r--pstoraster/gsstate.c786
-rw-r--r--pstoraster/gsstate.h68
-rw-r--r--pstoraster/gsstruct.h602
-rw-r--r--pstoraster/gstype1.c966
-rw-r--r--pstoraster/gstype1.h174
-rw-r--r--pstoraster/gstype42.c431
-rw-r--r--pstoraster/gstypes.h84
-rw-r--r--pstoraster/gsuid.h72
-rw-r--r--pstoraster/gsutil.c222
-rw-r--r--pstoraster/gsutil.h66
-rw-r--r--pstoraster/gsxfont.h43
-rw-r--r--pstoraster/gx.h43
-rw-r--r--pstoraster/gxacpath.c424
-rw-r--r--pstoraster/gxalloc.h351
-rw-r--r--pstoraster/gxarith.h83
-rw-r--r--pstoraster/gxbcache.c147
-rw-r--r--pstoraster/gxbcache.h121
-rw-r--r--pstoraster/gxbitmap.h133
-rw-r--r--pstoraster/gxccache.c437
-rw-r--r--pstoraster/gxccman.c768
-rw-r--r--pstoraster/gxchar.h158
-rw-r--r--pstoraster/gxcht.c525
-rw-r--r--pstoraster/gxcindex.h101
-rw-r--r--pstoraster/gxclbits.c593
-rw-r--r--pstoraster/gxcldev.h484
-rw-r--r--pstoraster/gxclfile.c117
-rw-r--r--pstoraster/gxclimag.c626
-rw-r--r--pstoraster/gxclio.h70
-rw-r--r--pstoraster/gxclip2.c362
-rw-r--r--pstoraster/gxclip2.h60
-rw-r--r--pstoraster/gxclist.c1138
-rw-r--r--pstoraster/gxclist.h187
-rw-r--r--pstoraster/gxclpath.c959
-rw-r--r--pstoraster/gxclpath.h155
-rw-r--r--pstoraster/gxclread.c1523
-rw-r--r--pstoraster/gxcmap.c626
-rw-r--r--pstoraster/gxcmap.h84
-rw-r--r--pstoraster/gxcolor2.h72
-rw-r--r--pstoraster/gxcoord.h38
-rw-r--r--pstoraster/gxcpath.c802
-rw-r--r--pstoraster/gxcpath.h123
-rw-r--r--pstoraster/gxcspace.h168
-rw-r--r--pstoraster/gxctable.c145
-rw-r--r--pstoraster/gxctable.h63
-rw-r--r--pstoraster/gxcvalue.h46
-rw-r--r--pstoraster/gxdcconv.c151
-rw-r--r--pstoraster/gxdcconv.h36
-rw-r--r--pstoraster/gxdcolor.c89
-rw-r--r--pstoraster/gxdcolor.h144
-rw-r--r--pstoraster/gxdda.h118
-rw-r--r--pstoraster/gxdevice.h950
-rw-r--r--pstoraster/gxdevmem.h148
-rw-r--r--pstoraster/gxdevrop.h36
-rw-r--r--pstoraster/gxdht.h172
-rw-r--r--pstoraster/gxdither.c483
-rw-r--r--pstoraster/gxdither.h55
-rw-r--r--pstoraster/gxfarith.h134
-rw-r--r--pstoraster/gxfcache.h248
-rw-r--r--pstoraster/gxfill.c1495
-rw-r--r--pstoraster/gxfixed.h218
-rw-r--r--pstoraster/gxfmap.h93
-rw-r--r--pstoraster/gxfont.h216
-rw-r--r--pstoraster/gxfont0.h65
-rw-r--r--pstoraster/gxfont1.h90
-rw-r--r--pstoraster/gxfont42.h59
-rw-r--r--pstoraster/gxfrac.h96
-rw-r--r--pstoraster/gxhint1.c253
-rw-r--r--pstoraster/gxhint2.c308
-rw-r--r--pstoraster/gxhint3.c459
-rw-r--r--pstoraster/gxht.c439
-rw-r--r--pstoraster/gxht.h141
-rw-r--r--pstoraster/gxhttile.h52
-rw-r--r--pstoraster/gximage.c820
-rw-r--r--pstoraster/gximage.h252
-rw-r--r--pstoraster/gximage0.c283
-rw-r--r--pstoraster/gximage1.c437
-rw-r--r--pstoraster/gximage2.c324
-rw-r--r--pstoraster/gximage3.c302
-rw-r--r--pstoraster/gximage4.c282
-rw-r--r--pstoraster/gximage5.c168
-rw-r--r--pstoraster/gxiodev.h177
-rw-r--r--pstoraster/gxistate.h94
-rw-r--r--pstoraster/gxline.h68
-rw-r--r--pstoraster/gxlum.h44
-rw-r--r--pstoraster/gxmatrix.h85
-rw-r--r--pstoraster/gxobj.h214
-rw-r--r--pstoraster/gxop1.h43
-rw-r--r--pstoraster/gxpaint.c93
-rw-r--r--pstoraster/gxpaint.h123
-rw-r--r--pstoraster/gxpath.c543
-rw-r--r--pstoraster/gxpath.h149
-rw-r--r--pstoraster/gxpath2.c407
-rw-r--r--pstoraster/gxpcmap.c511
-rw-r--r--pstoraster/gxpcolor.h130
-rw-r--r--pstoraster/gxpcopy.c998
-rw-r--r--pstoraster/gxpdash.c143
-rw-r--r--pstoraster/gxstate.h63
-rw-r--r--pstoraster/gxstroke.c1060
-rw-r--r--pstoraster/gxtmap.h42
-rw-r--r--pstoraster/gxtype1.h222
-rw-r--r--pstoraster/gxxfont.h172
-rw-r--r--pstoraster/gzacpath.h44
-rw-r--r--pstoraster/gzcpath.h52
-rw-r--r--pstoraster/gzht.h147
-rw-r--r--pstoraster/gzline.h33
-rw-r--r--pstoraster/gzpath.h221
-rw-r--r--pstoraster/gzstate.h154
-rw-r--r--pstoraster/ialloc.c261
-rw-r--r--pstoraster/ialloc.h122
-rw-r--r--pstoraster/iastate.h29
-rw-r--r--pstoraster/iastruct.h27
-rw-r--r--pstoraster/ibnum.c213
-rw-r--r--pstoraster/ibnum.h64
-rw-r--r--pstoraster/iccinit0.c30
-rw-r--r--pstoraster/ichar.h65
-rw-r--r--pstoraster/icharout.h53
-rw-r--r--pstoraster/icie.h77
-rw-r--r--pstoraster/icolor.h54
-rw-r--r--pstoraster/iconf.c73
-rw-r--r--pstoraster/icsmap.h39
-rw-r--r--pstoraster/idebug.c262
-rw-r--r--pstoraster/idebug.h41
-rw-r--r--pstoraster/idict.c974
-rw-r--r--pstoraster/idict.h183
-rw-r--r--pstoraster/idparam.c347
-rw-r--r--pstoraster/idparam.h80
-rw-r--r--pstoraster/ifilter.h77
-rw-r--r--pstoraster/ifont.h83
-rw-r--r--pstoraster/igc.c1092
-rw-r--r--pstoraster/igc.h82
-rw-r--r--pstoraster/igcref.c562
-rw-r--r--pstoraster/igcstr.c353
-rw-r--r--pstoraster/igcstr.h35
-rw-r--r--pstoraster/igstate.h161
-rw-r--r--pstoraster/iimage.h35
-rw-r--r--pstoraster/iinit.c476
-rw-r--r--pstoraster/ilevel.h29
-rw-r--r--pstoraster/ilocate.c309
-rw-r--r--pstoraster/imain.c569
-rw-r--r--pstoraster/imain.h271
-rw-r--r--pstoraster/imemory.h105
-rw-r--r--pstoraster/iminst.h80
-rw-r--r--pstoraster/iname.c575
-rw-r--r--pstoraster/iname.h105
-rw-r--r--pstoraster/inamedef.h212
-rw-r--r--pstoraster/interp.c1419
-rw-r--r--pstoraster/interp.h66
-rw-r--r--pstoraster/ipacked.h128
-rw-r--r--pstoraster/iparam.c822
-rw-r--r--pstoraster/iparam.h103
-rw-r--r--pstoraster/iparray.h36
-rw-r--r--pstoraster/ireclaim.c206
-rw-r--r--pstoraster/iref.h367
-rw-r--r--pstoraster/isave.c989
-rw-r--r--pstoraster/isave.h111
-rw-r--r--pstoraster/iscan.c1034
-rw-r--r--pstoraster/iscan.h148
-rw-r--r--pstoraster/iscanbin.c581
-rw-r--r--pstoraster/iscannum.c357
-rw-r--r--pstoraster/iscannum.h30
-rw-r--r--pstoraster/iscantab.c114
-rw-r--r--pstoraster/isstate.h40
-rw-r--r--pstoraster/istack.c490
-rw-r--r--pstoraster/istack.h235
-rw-r--r--pstoraster/istream.h37
-rw-r--r--pstoraster/istruct.h60
-rw-r--r--pstoraster/iutil.c536
-rw-r--r--pstoraster/iutil.h106
-rw-r--r--pstoraster/iutil2.c100
-rw-r--r--pstoraster/iutil2.h46
-rw-r--r--pstoraster/ivmspace.h128
-rw-r--r--pstoraster/main.h93
-rw-r--r--pstoraster/malloc_.h51
-rw-r--r--pstoraster/math_.h86
-rw-r--r--pstoraster/memory_.h86
-rw-r--r--pstoraster/opcheck.h72
-rw-r--r--pstoraster/opdef.h143
-rw-r--r--pstoraster/oper.h100
-rw-r--r--pstoraster/opextern.h108
-rw-r--r--pstoraster/ostack.h93
-rw-r--r--pstoraster/pdf_2ps.ps270
-rw-r--r--pstoraster/pdf_base.ps392
-rw-r--r--pstoraster/pdf_draw.ps320
-rw-r--r--pstoraster/pdf_font.ps374
-rw-r--r--pstoraster/pdf_main.ps478
-rw-r--r--pstoraster/pdf_sec.ps58
-rw-r--r--pstoraster/pfbtogs.ps117
-rw-r--r--pstoraster/pstoraster.c196
-rw-r--r--pstoraster/sa85x.h45
-rw-r--r--pstoraster/sbcp.c237
-rw-r--r--pstoraster/sbhc.c274
-rw-r--r--pstoraster/sbhc.h90
-rw-r--r--pstoraster/sbtx.h39
-rw-r--r--pstoraster/sbwbs.c476
-rw-r--r--pstoraster/sbwbs.h71
-rw-r--r--pstoraster/scanchar.h69
-rw-r--r--pstoraster/scf.h173
-rw-r--r--pstoraster/scfd.c771
-rw-r--r--pstoraster/scfdtab.c938
-rw-r--r--pstoraster/scfe.c490
-rw-r--r--pstoraster/scfetab.c161
-rw-r--r--pstoraster/scfx.h122
-rw-r--r--pstoraster/scommon.h157
-rw-r--r--pstoraster/sdct.h97
-rw-r--r--pstoraster/sdctc.c41
-rw-r--r--pstoraster/sdctd.c282
-rw-r--r--pstoraster/sdcte.c173
-rw-r--r--pstoraster/seexec.c175
-rw-r--r--pstoraster/sfile.c246
-rw-r--r--pstoraster/sfilter.h129
-rw-r--r--pstoraster/sfilter1.c292
-rw-r--r--pstoraster/sfilter2.c284
-rw-r--r--pstoraster/shc.c74
-rw-r--r--pstoraster/shc.h248
-rw-r--r--pstoraster/shcgen.c467
-rw-r--r--pstoraster/shcgen.h55
-rw-r--r--pstoraster/siscale.c486
-rw-r--r--pstoraster/siscale.h136
-rw-r--r--pstoraster/sjpeg.h72
-rw-r--r--pstoraster/sjpegc.c274
-rw-r--r--pstoraster/sjpegd.c91
-rw-r--r--pstoraster/sjpege.c107
-rw-r--r--pstoraster/sjpegerr.c97
-rw-r--r--pstoraster/slzwc.c47
-rw-r--r--pstoraster/slzwce.c160
-rw-r--r--pstoraster/slzwd.c373
-rw-r--r--pstoraster/slzwx.h71
-rw-r--r--pstoraster/smtf.c171
-rw-r--r--pstoraster/smtf.h43
-rw-r--r--pstoraster/spcxd.c77
-rw-r--r--pstoraster/spcxx.h30
-rw-r--r--pstoraster/spdiff.c302
-rw-r--r--pstoraster/spdiffx.h49
-rw-r--r--pstoraster/spngp.c338
-rw-r--r--pstoraster/spngpx.h55
-rw-r--r--pstoraster/srld.c100
-rw-r--r--pstoraster/srle.c120
-rw-r--r--pstoraster/srlx.h64
-rw-r--r--pstoraster/sstring.c434
-rw-r--r--pstoraster/sstring.h66
-rw-r--r--pstoraster/stat_.h51
-rw-r--r--pstoraster/std.h225
-rw-r--r--pstoraster/stdio_.h53
-rw-r--r--pstoraster/stdpre.h336
-rw-r--r--pstoraster/store.h241
-rw-r--r--pstoraster/stream.c792
-rw-r--r--pstoraster/stream.h314
-rw-r--r--pstoraster/strimpl.h146
-rw-r--r--pstoraster/string_.h53
-rw-r--r--pstoraster/szlibc.c48
-rw-r--r--pstoraster/szlibd.c90
-rw-r--r--pstoraster/szlibe.c90
-rw-r--r--pstoraster/szlibx.h47
-rw-r--r--pstoraster/time_.h65
-rw-r--r--pstoraster/vmsmath.h42
-rw-r--r--pstoraster/zarith.c296
-rw-r--r--pstoraster/zarray.c125
-rw-r--r--pstoraster/zbseq.c271
-rw-r--r--pstoraster/zchar.c608
-rw-r--r--pstoraster/zchar1.c559
-rw-r--r--pstoraster/zchar2.c276
-rw-r--r--pstoraster/zchar42.c171
-rw-r--r--pstoraster/zcharout.c239
-rw-r--r--pstoraster/zcie.c632
-rw-r--r--pstoraster/zcolor.c248
-rw-r--r--pstoraster/zcolor1.c217
-rw-r--r--pstoraster/zcolor2.c197
-rw-r--r--pstoraster/zcontrol.c639
-rw-r--r--pstoraster/zcrd.c316
-rw-r--r--pstoraster/zcsindex.c225
-rw-r--r--pstoraster/zcssepr.c154
-rw-r--r--pstoraster/zdevcal.c75
-rw-r--r--pstoraster/zdevice.c326
-rw-r--r--pstoraster/zdevice2.c333
-rw-r--r--pstoraster/zdict.c490
-rw-r--r--pstoraster/zdps1.c450
-rw-r--r--pstoraster/zfbcp.c91
-rw-r--r--pstoraster/zfdctc.c372
-rw-r--r--pstoraster/zfdctd.c103
-rw-r--r--pstoraster/zfdcte.c270
-rw-r--r--pstoraster/zfdecode.c350
-rw-r--r--pstoraster/zfile.c887
-rw-r--r--pstoraster/zfileio.c778
-rw-r--r--pstoraster/zfilter.c422
-rw-r--r--pstoraster/zfilter2.c154
-rw-r--r--pstoraster/zfilterx.c319
-rw-r--r--pstoraster/zfname.c111
-rw-r--r--pstoraster/zfont.c405
-rw-r--r--pstoraster/zfont0.c271
-rw-r--r--pstoraster/zfont1.c192
-rw-r--r--pstoraster/zfont2.c493
-rw-r--r--pstoraster/zfont42.c108
-rw-r--r--pstoraster/zfproc.c337
-rw-r--r--pstoraster/zfzlib.c101
-rw-r--r--pstoraster/zgeneric.c495
-rw-r--r--pstoraster/zgstate.c329
-rw-r--r--pstoraster/zhsb.c62
-rw-r--r--pstoraster/zht.c238
-rw-r--r--pstoraster/zht1.c143
-rw-r--r--pstoraster/zht2.c328
-rw-r--r--pstoraster/zimage2.c166
-rw-r--r--pstoraster/ziodev.c401
-rw-r--r--pstoraster/ziodev2.c133
-rw-r--r--pstoraster/zmath.c247
-rw-r--r--pstoraster/zmatrix.c325
-rw-r--r--pstoraster/zmedia2.c442
-rw-r--r--pstoraster/zmisc.c313
-rw-r--r--pstoraster/zmisc1.c127
-rw-r--r--pstoraster/zmisc2.c244
-rw-r--r--pstoraster/zpacked.c237
-rw-r--r--pstoraster/zpaint.c484
-rw-r--r--pstoraster/zpath.c179
-rw-r--r--pstoraster/zpath1.c244
-rw-r--r--pstoraster/zpcolor.c253
-rw-r--r--pstoraster/zrelbit.c314
-rw-r--r--pstoraster/zstack.c267
-rw-r--r--pstoraster/zstring.c161
-rw-r--r--pstoraster/zsysvm.c146
-rw-r--r--pstoraster/ztoken.c232
-rw-r--r--pstoraster/ztype.c459
-rw-r--r--pstoraster/zupath.c526
-rw-r--r--pstoraster/zusparam.c503
-rw-r--r--pstoraster/zvmem.c336
-rw-r--r--pstoraster/zvmem2.c156
-rw-r--r--scheduler/Makefile82
-rw-r--r--scheduler/auth.c602
-rw-r--r--scheduler/auth.h108
-rw-r--r--scheduler/classes.c522
-rw-r--r--scheduler/classes.h43
-rw-r--r--scheduler/client.c1507
-rw-r--r--scheduler/client.h94
-rw-r--r--scheduler/conf.c1189
-rw-r--r--scheduler/conf.h112
-rw-r--r--scheduler/cupsd.h161
-rw-r--r--scheduler/dirsvc.c544
-rw-r--r--scheduler/dirsvc.h58
-rw-r--r--scheduler/ipp.c2421
-rw-r--r--scheduler/job.c971
-rw-r--r--scheduler/job.h75
-rw-r--r--scheduler/listen.c142
-rw-r--r--scheduler/main.c422
-rw-r--r--scheduler/printers.c1035
-rw-r--r--scheduler/printers.h81
-rw-r--r--scheduler/testspeed.c126
-rw-r--r--systemv/Makefile114
-rw-r--r--systemv/accept.c218
-rw-r--r--systemv/cancel.c220
-rw-r--r--systemv/lp.c246
-rw-r--r--systemv/lpadmin.c1147
-rw-r--r--systemv/lpstat.c1268
737 files changed, 193960 insertions, 0 deletions
diff --git a/LICENSE.html b/LICENSE.html
new file mode 100644
index 000000000..7a25722a6
--- /dev/null
+++ b/LICENSE.html
@@ -0,0 +1,386 @@
+<HTML>
+<HEAD>
+ <TITLE>Software License Agreement - Common UNIX Printing System</TITLE>
+</HEAD>
+
+<BODY BGCOLOR=#ffffff>
+
+<H2 ALIGN="CENTER">Common UNIX Printing System License Agreement</H2>
+
+<P ALIGN="CENTER">Copyright 1997-1999 by Easy Software Products<BR>
+44141 AIRPORT VIEW DR STE 204<BR>
+HOLLYWOOD, MARYLAND 20636-3111 USA<BR>
+<BR>
+Voice: +1.301.373.9603<BR>
+Email: cups-info@cups.org<BR>
+WWW: http://www.cups.org
+
+<H3>Introduction</H3>
+
+<P>The Common UNIX Printing System<SUP>TM</SUP>, or CUPS<SUP>TM</SUP>,
+is provided under the GNU General Public License, Version 2. A copy of
+this license follows this introduction.
+
+<P>For those not familiar with the GNU General Public License, the license
+basically allows you to:
+
+<UL>
+
+ <LI>Use the CUPS software at no charge.
+
+ <LI>Distribute verbatim copies of the software in source or
+ binary form.
+
+ <LI>Sell verbatim copies of the software for a media fee, or
+ sell support for the software.
+
+ <LI>Distribute or sell printer drivers and filters that use the
+ CUPS API so long as source code is made available under the GPL.
+
+</UL>
+
+<P>What this license <B>does not</B> allow you to do is make changes or
+add features to CUPS and then sell a binary distribution without source
+code. You have to provide source for any new drivers, changes, or
+additions to the software, and all code must be provided under the GPL.
+
+<P>Also, since we have trademarked the Common UNIX Printing System, CUPS,
+and CUPS logo, you may not release a derivative product using those names
+without permission from Easy Software Products.
+
+<H3>Binary Distribution Rights</H3>
+
+<P>Easy Software Products also sells rights to the CUPS source code
+under a binary distribution license for vendors that are unable to
+release source code for their drivers or additions and modifications to
+CUPS under the GPL. For pricing information please contact us at the
+address shown above.
+
+<P>The Common UNIX Printing System utilizes GNU GhostScript 4.03 to
+convert PostScript files into a stream of raster images. For binary
+distribution licensing of this software, please contact:
+
+<BLOCKQUOTE>Miles Jones<BR>
+Director of Marketing<BR>
+Artifex Software Inc.<BR>
+454 Las Gallinas Ave., Suite 108<BR>
+San Rafael, CA 94903 USA<BR>
+Voice: +1.415.492.9861<BR>
+Fax: +1.415.492.9862<BR>
+EMail: info@arsoft.com
+</BLOCKQUOTE>
+
+<H3>Support</H3>
+
+<P>Easy Software Products sells software support for distributors and
+resellers of CUPS. Support for users of CUPS is available from Easy
+Software Products through our ESP Print software.
+
+<H3>Trademarks</H3>
+
+<P>The Common UNIX Printing System, CUPS, and the CUPS logo are the
+trademark property of Easy Software Products. Any derivative of this
+software may not use any of these trademarks without the expressed
+written consent of Easy Software Products.
+
+<H2 ALIGN="CENTER">GNU General Public License</H2>
+
+<P ALIGN="CENTER">Version 2, June 1991<BR>
+<BR>
+Copyright 1989, 1991 Free Software Foundation, Inc.<BR>
+59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+<P ALIGN="CENTER">Everyone is permitted to copy and distribute verbatim
+copies of this license document, but changing it is not allowed.
+
+<H3>Preamble</H3>
+
+<P>The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+<P>When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+<P>To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+<P>For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+<P>We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+<P>Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+<P>Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+<P>The precise terms and conditions for copying, distribution and
+modification follow.
+
+<H3>GNU GENERAL PUBLIC LICENSE<BR>
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION</H3>
+
+<OL START="0">
+
+<LI>This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+<P>Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+<LI>You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+<P>You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+<LI>You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+<OL TYPE="a">
+
+<LI>You must cause the modified files to carry prominent notices
+stating that you changed the files and the date of any change.
+
+<LI>You must cause any work that you distribute or publish, that in
+whole or in part contains or is derived from the Program or any
+part thereof, to be licensed as a whole at no charge to all third
+parties under the terms of this License.
+
+<LI>if the modified program normally reads commands interactively
+when run, you must cause it, when started running for such
+interactive use in the most ordinary way, to print or display an
+announcement including an appropriate copyright notice and a
+notice that there is no warranty (or else, saying that you provide
+a warranty) and that users may redistribute the program under
+these conditions, and telling the user how to view a copy of this
+License. (Exception: if the Program itself is interactive but
+does not normally print such an announcement, your work based on
+the Program is not required to print an announcement.)
+
+</OL>
+
+<P>These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+<P>Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+<P>In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+<LI>You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+<OL TYPE="a">
+
+<LI>Accompany it with the complete corresponding machine-readable
+source code, which must be distributed under the terms of Sections
+1 and 2 above on a medium customarily used for software interchange; or,
+
+<LI>Accompany it with a written offer, valid for at least three
+years, to give any third party, for a charge no more than your
+cost of physically performing source distribution, a complete
+machine-readable copy of the corresponding source code, to be
+distributed under the terms of Sections 1 and 2 above on a medium
+customarily used for software interchange; or,
+
+<LI>Accompany it with the information you received as to the offer
+to distribute corresponding source code. (This alternative is
+allowed only for noncommercial distribution and only if you
+received the program in object code or executable form with such
+an offer, in accord with Subsection b above.)
+
+</OL>
+
+<P>The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+<P>If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+<LI>You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+<LI>You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+<LI>Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+<LI>If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+<P>If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+<P>It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+<P>This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+<LI>If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+<LI>The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+<P>Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+<LI>If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+</OL>
+
+<H3>NO WARRANTY</H3>
+
+<OL START="11">
+
+<LI>BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+<LI>IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+</OL>
+
+<H3>END OF TERMS AND CONDITIONS</H3>
+
+</BODY>
+</HTML>
diff --git a/Makedefs.in b/Makedefs.in
new file mode 100644
index 000000000..19b96ea12
--- /dev/null
+++ b/Makedefs.in
@@ -0,0 +1,138 @@
+#
+# "$Id$"
+#
+# Common makefile definitions for the Common UNIX Printing System (CUPS).
+#
+# @configure_input@
+#
+# Copyright 1997-1999 by Easy Software Products, all rights reserved.
+#
+# These coded instructions, statements, and computer programs are the
+# property of Easy Software Products and are protected by Federal
+# copyright law. Distribution and use rights are outlined in the file
+# "LICENSE.txt" which should have been included with this file. If this
+# file is missing or damaged please contact Easy Software Products
+# at:
+#
+# Attn: CUPS Licensing Information
+# Easy Software Products
+# 44141 Airport View Drive, Suite 204
+# Hollywood, Maryland 20636-3111 USA
+#
+# Voice: (301) 373-9603
+# EMail: cups-info@cups.org
+# WWW: http://www.cups.org
+#
+
+#
+# Programs...
+#
+
+AR = @AR@
+AWK = @AWK@
+CC = @CC@
+CHMOD = @CHMOD@
+CP = @CP@
+DSO = @DSO@
+HTMLDOC = @HTMLDOC@
+LN = /bin/ln -sf
+MKDIR = @MKDIR@ -p
+NROFF = @NROFF@
+PACK = @PACK@
+RANLIB = @RANLIB@
+RM = @RM@ -f
+SED = @SED@
+SHELL = /bin/sh
+SMBCLIENT = @SMBCLIENT@
+
+#
+# Libraries...
+#
+
+LIBCUPS = @LIBCUPS@
+LIBCUPSIMAGE = @LIBCUPSIMAGE@
+LIBJPEG = @LIBJPEG@
+LIBPNG = @LIBPNG@
+LIBTIFF = @LIBTIFF@
+LIBZ = @LIBZ@
+
+#
+# Program options...
+#
+# OPTIM defines the common compiler optimization/debugging options.
+# OPTIONS defines other compile-time options (currently only -dDEBUG for
+# extra debug info)
+#
+
+ARFLAGS = crvs
+CFLAGS = @CFLAGS@ $(OPTIM) -I.. $(OPTIONS)
+DSOLIBS = @DSOLIBS@
+IMGLIBS = @IMGLIBS@ -lm
+LDFLAGS = @LDFLAGS@ $(OPTIM)
+LIBS = -L../cups -lcups $(NETLIBS) @LIBS@
+NETLIBS = @NETLIBS@
+OPTIM = @OPTIM@
+OPTIONS =
+
+#
+# Formatted man page extension...
+#
+
+CAT = @CAT@
+
+#
+# Directories...
+#
+# The first section uses the GNU names (which are *extremely*
+# difficult to find in a makefile because they are lowercase...)
+# We have to define these first because autoconf uses ${prefix}
+# and ${exec_prefix} for most of the other directories...
+#
+# This is immediately followed by definition in ALL CAPS for the
+# needed directories...
+#
+
+bindir = @bindir@
+datadir = @datadir@
+exec_prefix = @exec_prefix@
+includedir = @includedir@
+infodir = @infodir@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+oldincludedir = @oldincludedir@
+prefix = @prefix@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+top_srcdir = @top_srcdir@
+
+BINDIR = @bindir@
+DATADIR = @CUPS_DATADIR@
+INCLUDEDIR = $(includedir)
+LIBDIR = $(libdir)
+LOCALEDIR = @CUPS_LOCALEDIR@
+MANDIR = @mandir@
+SBINDIR = @sbindir@
+SERVERROOT = @CUPS_SERVERROOT@
+
+#
+# Rules...
+#
+
+.SILENT:
+.SUFFIXES: .a .c .gz .h .o .z .1 .5 .8
+.c.o:
+ echo Compiling $<...
+ $(CC) $(CFLAGS) -c $<
+.1.z .5.z .8.z .1.gz .5.gz .8.gz:
+ echo Formatting $<...
+ $(NROFF) -man $< >t
+ $(PACK) t
+ -mv t.$(CAT) $@
+
+#
+# End of "$Id$"
+#
diff --git a/Makefile b/Makefile
new file mode 100644
index 000000000..934bf137c
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,76 @@
+#
+# "$Id$"
+#
+# Top-level Makefile for the Common UNIX Printing System (CUPS).
+#
+# Copyright 1997-1999 by Easy Software Products, all rights reserved.
+#
+# These coded instructions, statements, and computer programs are the
+# property of Easy Software Products and are protected by Federal
+# copyright law. Distribution and use rights are outlined in the file
+# "LICENSE.txt" which should have been included with this file. If this
+# file is missing or damaged please contact Easy Software Products
+# at:
+#
+# Attn: CUPS Licensing Information
+# Easy Software Products
+# 44141 Airport View Drive, Suite 204
+# Hollywood, Maryland 20636-3111 USA
+#
+# Voice: (301) 373-9603
+# EMail: cups-info@cups.org
+# WWW: http://www.cups.org
+#
+
+include Makedefs
+
+#
+# Directories to make...
+#
+
+DIRS = cups backend berkeley cgi-bin filter man pstoraster \
+ scheduler systemv
+
+#
+# Make all targets...
+#
+
+all:
+ for dir in $(DIRS); do\
+ echo Making all in $$dir... ;\
+ (cd $$dir; make);\
+ done
+
+#
+# Remove object and target files...
+#
+
+clean:
+ for dir in $(DIRS); do\
+ echo Cleaning in $$dir... ;\
+ (cd $$dir; make clean);\
+ done
+
+#
+# Install object and target files...
+#
+
+install:
+ for dir in $(DIRS); do\
+ echo Installing in $$dir... ;\
+ (cd $$dir; make install);\
+ done
+ echo Installing in conf...
+ (cd conf; make install)
+ echo Installing in data...
+ (cd data; make install)
+ echo Installing in doc...
+ (cd doc; make install)
+ echo Installing in fonts...
+ (cd fonts; make install)
+ echo Installing in ppd...
+ (cd ppd; make install)
+
+#
+# End of "$Id$".
+#
diff --git a/README.txt b/README.txt
new file mode 100644
index 000000000..b12e30a94
--- /dev/null
+++ b/README.txt
@@ -0,0 +1,195 @@
+README - CUPS v1.0b6 - 07/30/1999
+---------------------------------
+
+BETA SOFTWARE BETA SOFTWARE BETA SOFTWARE BETA SOFTWARE BETA SOFTWARE
+
+ WARNING - This is a BETA release of CUPS, which means that it may
+ contain "bugs" that could prevent you from printing. If
+ you are concerned that this may cause you lost time or
+ money, please STOP and do not install this software!
+
+BETA SOFTWARE BETA SOFTWARE BETA SOFTWARE BETA SOFTWARE BETA SOFTWARE
+
+
+INTRODUCTION
+
+The Common UNIX Printing System provides a portable printing layer for
+UNIX® operating systems. It has been developed by Easy Software
+Products to promote a standard printing solution for all UNIX vendors
+and users. CUPS provides the System V and Berkeley command-line
+interfaces.
+
+CUPS uses the Internet Printing Protocol (IETF-IPP) as the basis for
+managing print jobs and queues. The Line Printer Daemon (LPD,
+RFC1179), Server Message Block (SMB), and AppSocket protocols are also
+supported with reduced functionality.
+
+CUPS adds network printer browsing and PostScript Printer Description
+("PPD")-based printing options to support real world applications under
+UNIX.
+
+CUPS also includes a customized version of GNU GhostScript (currently
+based off GNU GhostScript 4.03) and an image file RIP that can be used
+to support non-PostScript printers.
+
+CUPS is Copyright 1993-1999 by Easy Software Products, All Rights
+Reserved. CUPS is currently licensed under the terms of the GNU
+General Public License. Please see the license file for details.
+
+
+SYSTEM REQUIREMENTS
+
+Binary distributions require a minimum of 10MB of free disk space. We
+do not recommend using CUPS on a workstation with less than 32MB of RAM
+or a PC with less than 16MB of RAM.
+
+If you are installing from source you'll need an ANSI C compiler and
+optionally one or more image file support libraries. Complete source
+installation instructions can be found in the CUPS System
+Administrator's Manual in the files "doc/sam.html" or "doc/sam.pdf".
+
+
+SOFTWARE REQUIREMENTS
+
+The following operating system software is required to install one of
+the binary distributions from Easy Software Products:
+
+ - Digital UNIX (aka OSF1 aka Compaq Tru64 UNIX) 4.0 or higher
+ - HP-UX 10.20 or higher
+ - IRIX 5.3 or higher
+ - Linux 2.0.36 with glibc2 or higher (tested with RedHat 5.2)
+ - Solaris 2.5 or higher (SPARC or Intel)
+
+
+INSTALLING CUPS
+
+We are currently distributing CUPS binary distributions in TAR format
+with installation and removal scripts generated by our ESP Package
+Manager (EPM) software, which is also included with the source
+distribution.
+
+WARNING: Installing CUPS will overwrite your existing printing system.
+Backup files are made by the installation script and restored by the
+removal script, so if you experience problems you should be able to
+remove the CUPS software to restore your previous configuration.
+However, Easy Software Products makes no warranty for this and will not
+be liable for any lost revenues, etc.
+
+To install the CUPS software you will need to be logged in as root
+(doing an "su" is good enough). Once you are the root user, run the
+installation script with:
+
+ ./cups.install ENTER
+
+After asking you a few yes/no questions the CUPS software will be
+installed and the scheduler will be started automatically.
+
+
+SETTING UP PRINTER QUEUES
+
+CUPS works best with PPD (PostScript Printer Description) files. In a
+pinch you can also use System V style printer interface scripts.
+
+Two sample PPD files are provided with this distribution that utilize
+the PostScript and image file RIPs and the sample HP printer driver.
+To add the sample DeskJet driver to the system for a printer connected
+to the parallel port, use one of the following commands:
+
+ Digital UNIX:
+
+ /usr/lib/lpadmin -p DeskJet -m deskjet.ppd -v parallel:/dev/lp0 -E
+
+ HP-UX:
+
+ /usr/lib/lpadmin -p DeskJet -m deskjet.ppd -v parallel:/dev/c2t0d0_lp -E
+
+ IRIX:
+
+ /usr/lib/lpadmin -p DeskJet -m deskjet.ppd -v parallel:/dev/plp -E
+
+ Linux:
+
+ /usr/lib/lpadmin -p DeskJet -m deskjet.ppd -v parallel:/dev/par0 -E
+ /usr/lib/lpadmin -p DeskJet -m deskjet.ppd -v parallel:/dev/par1 -E
+ /usr/lib/lpadmin -p DeskJet -m deskjet.ppd -v parallel:/dev/par2 -E
+
+ Solaris:
+
+ /usr/lib/lpadmin -p DeskJet -m deskjet.ppd -v parallel:/dev/bpp0 -E
+ /usr/lib/lpadmin -p DeskJet -m deskjet.ppd -v parallel:/dev/ecpp0 -E
+
+Similarly, for the sample LaserJet driver you can use "LaserJet" and
+"laserjet.ppd".
+
+For other printers and interfaces see the CUPS System Administator's
+Manual included with this software.
+
+
+PRINTING FILES
+
+CUPS provides both the System V "lp" and Berkeley "lpr" commands for
+printing:
+
+ lp filename
+ lpr filename
+
+Both the "lp" and "lpr" commands support printing options for the
+driver:
+
+ lp -omedia=A4 -oresolution=600dpi filename
+ lpr -omedia=A4 -oresolution=600dpi filename
+
+CUPS recognizes many types of images files as well as PostScript, HP-GL/2,
+and text files, so you can print those files directly rather than through
+an application.
+
+If you have an application that generates output specifically for your
+printer then you need to use the "-oraw" or "-l" options:
+
+ lp -oraw filename
+ lpr -l filename
+
+This will prevent the filters from misinterpreting your print file.
+
+
+REPORTING PROBLEMS
+
+If you have problems, please send an email to cups-support@cups.org.
+Include your operating system and version, compiler and version, and
+any errors or problems you've run into.
+
+
+OTHER RESOURCES
+
+See the CUPS web site at "http://www.cups.org" for other site links.
+
+You can subscribe to the CUPS mailing list by sending a message
+containing "subscribe cups" to majordomo@cups.org. This list is
+provided to discuss problems, questions, and improvements to the CUPS
+software. New releases of CUPS are announced to this list as well.
+
+
+LEGAL STUFF
+
+CUPS is Copyright 1993-1999 by Easy Software Products. CUPS, the CUPS
+logo, and the Common UNIX Printing System are the trademark property of
+Easy Software Products.
+
+CUPS is provided under the terms of the GNU General Public License
+which is located in the files "LICENSE.html" and "LICENSE.txt" (or the
+file "cups.license" for a binary distribution.) For commercial
+licensing information, please contact:
+
+ Attn: CUPS Licensing Information
+ Easy Software Products
+ 44141 Airport View Drive, Suite 204
+ Hollywood, Maryland 20636-3111 USA
+
+ Voice: +1.301.373.9603
+ Email: cups-info@cups.org
+ WWW: http://www.cups.org
+
+If you're interested in a complete, commercial printing solution for
+UNIX, check out our ESP Print Pro software at:
+
+ http://www.easysw.com/printpro
diff --git a/backend/Makefile b/backend/Makefile
new file mode 100644
index 000000000..b4915aa5c
--- /dev/null
+++ b/backend/Makefile
@@ -0,0 +1,119 @@
+#
+# "$Id$"
+#
+# Backend makefile for the Common UNIX Printing System (CUPS).
+#
+# Copyright 1997-1999 by Easy Software Products, all rights reserved.
+#
+# These coded instructions, statements, and computer programs are the
+# property of Easy Software Products and are protected by Federal
+# copyright law. Distribution and use rights are outlined in the file
+# "LICENSE.txt" which should have been included with this file. If this
+# file is missing or damaged please contact Easy Software Products
+# at:
+#
+# Attn: CUPS Licensing Information
+# Easy Software Products
+# 44141 Airport View Drive, Suite 204
+# Hollywood, Maryland 20636-3111 USA
+#
+# Voice: (301) 373-9603
+# EMail: cups-info@cups.org
+# WWW: http://www.cups.org
+#
+
+include ../Makedefs
+
+TARGETS = ipp lpd parallel serial smb socket
+OBJS = ipp.o lpd.o parallel.o serial.o socket.o
+
+#
+# Make all targets...
+#
+
+all: $(TARGETS)
+
+#
+# Clean all object files...
+#
+
+clean:
+ rm -f $(OBJS) $(TARGETS)
+
+#
+# Install all targets...
+#
+
+install:
+ -$(MKDIR) $(SERVERROOT)/backend
+ $(CP) $(TARGETS) $(SERVERROOT)/backend
+ -$(LN) ipp $(SERVERROOT)/backend/http
+ $(CHMOD) u+s $(SERVERROOT)/backend/lpd
+
+#
+# ipp
+#
+
+ipp: ipp.o ../cups/$(LIBCUPS)
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o ipp ipp.o $(LIBS)
+ -$(LN) ipp http
+
+ipp.o: ../cups/cups.h ../Makedefs
+
+#
+# lpd
+#
+
+lpd: lpd.o ../cups/$(LIBCUPS)
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o lpd lpd.o $(LIBS)
+
+lpd.o: ../cups/cups.h ../Makedefs
+
+#
+# parallel
+#
+
+parallel: parallel.o ../cups/$(LIBCUPS)
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o parallel parallel.o $(LIBS)
+
+parallel.o: ../cups/cups.h ../Makedefs
+
+#
+# serial
+#
+
+serial: serial.o ../cups/$(LIBCUPS)
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o serial serial.o $(LIBS)
+
+serial.o: ../cups/cups.h ../Makedefs
+
+#
+# smb
+#
+# Note: reading through these commands is a good way to get a headache... :)
+#
+
+smb: smb.sh ../Makedefs
+ echo Generating $@...
+ $(RM) smb
+ sedcmd="1,\$$s/^SMBCLIENT=.\*/SMBCLIENT=`echo $(SMBCLIENT) | sed -e '1,$$s/\\//\\\\\\//g'`/" ;\
+ $(SED) -e "$$sedcmd" <smb.sh >smb
+ $(CHMOD) +x smb
+
+#
+# socket
+#
+
+socket: socket.o ../cups/$(LIBCUPS)
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o socket socket.o $(LIBS)
+
+socket.o: ../cups/cups.h ../Makedefs
+
+#
+# End of "$Id$".
+#
diff --git a/backend/ipp.c b/backend/ipp.c
new file mode 100644
index 000000000..297ad393b
--- /dev/null
+++ b/backend/ipp.c
@@ -0,0 +1,399 @@
+/*
+ * "$Id$"
+ *
+ * IPP backend for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-1999 by Easy Software Products, all rights reserved.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * Contents:
+ *
+ * main() - Send a file to the printer or server.
+ */
+
+/*
+ * Include necessary headers.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <cups/cups.h>
+#include <cups/language.h>
+#include <cups/string.h>
+
+
+/*
+ * 'main()' - Send a file to the printer or server.
+ *
+ * Usage:
+ *
+ * printer-uri job-id user title copies options [file]
+ */
+
+int /* O - Exit status */
+main(int argc, /* I - Number of command-line arguments (6 or 7) */
+ char *argv[]) /* I - Command-line arguments */
+{
+ int i; /* Looping var */
+ int n, n2; /* Attribute values */
+ char *option, /* Name of option */
+ *val, /* Pointer to option value */
+ *s; /* Pointer into option value */
+ int num_options; /* Number of printer options */
+ cups_option_t *options; /* Printer options */
+ char method[255], /* Method in URI */
+ hostname[1024], /* Hostname */
+ username[255], /* Username info */
+ resource[1024], /* Resource info (printer name) */
+ filename[1024]; /* File to print */
+ int port; /* Port number (not used) */
+ char password[255], /* Password info */
+ uri[HTTP_MAX_URI];/* Updated URI without user/pass */
+ http_status_t status; /* Status of HTTP job */
+ FILE *fp; /* File to print */
+ http_t *http; /* HTTP connection */
+ ipp_t *request, /* IPP request */
+ *response; /* IPP response */
+ ipp_attribute_t *job_id; /* job-id attribute */
+ cups_lang_t *language; /* Default language */
+ struct stat fileinfo; /* File statistics */
+ size_t nbytes, /* Number of bytes written */
+ tbytes; /* Total bytes written */
+ char buffer[8192]; /* Output buffer */
+
+
+ if (argc < 6 || argc > 7)
+ {
+ fprintf(stderr, "Usage: %s job-id user title copies options [file]\n",
+ argv[0]);
+ return (1);
+ }
+
+ /*
+ * If we have 7 arguments, print the file named on the command-line.
+ * Otherwise, print stdin...
+ */
+
+ if (argc == 6)
+ fp = stdin;
+ else if ((fp = fopen(argv[6], "rb")) == NULL)
+ {
+ perror("ERROR: Unable to open print file");
+ return (1);
+ }
+ else
+ stat(argv[6], &fileinfo);
+
+ /*
+ * Extract the hostname and printer name from the URI...
+ */
+
+ httpSeparate(argv[0], method, username, hostname, &port, resource);
+
+ /*
+ * Try connecting to the remote server...
+ */
+
+ fprintf(stderr, "INFO: Connecting to %s...\n", hostname);
+
+ if ((http = httpConnect(hostname, port)) == NULL)
+ {
+ perror("ERROR: Unable to connect to IPP host");
+
+ if (fp != stdin)
+ fclose(fp);
+ return (1);
+ }
+
+ /*
+ * Build a URI for the printer and fill the standard IPP attributes for
+ * an IPP_PRINT_FILE request. We can't use the URI in argv[0] because it
+ * might contain username:password information...
+ */
+
+ request = ippNew();
+ request->request.op.operation_id = IPP_PRINT_JOB;
+ request->request.op.request_id = 1;
+
+ sprintf(uri, "%s://%s:%d%s", method, hostname, port, resource);
+
+ language = cupsLangDefault();
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, cupsLangEncoding(language));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL,
+ language != NULL ? language->language : "C");
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
+ NULL, uri);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
+ NULL, argv[2]);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL, argv[3]);
+
+ /*
+ * Handle options on the command-line...
+ */
+
+ options = NULL;
+ num_options = cupsParseOptions(argv[5], 0, &options);
+
+ if (cupsGetOption("raw", num_options, options))
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE, "document-format",
+ NULL, "application/vnd.cups-raw");
+ else
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE, "document-format",
+ NULL, "application/octet-stream");
+
+ ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_INTEGER, "copies", atoi(argv[4]));
+
+ for (i = 0; i < num_options; i ++)
+ {
+ /*
+ * Skip the "raw" option - handled above...
+ */
+
+ if (strcmp(options[i].name, "raw") == 0)
+ continue;
+
+ /*
+ * See what the option value is; for compatibility with older interface
+ * scripts, we have to support single-argument options as well as
+ * option=value, option=low-high, and option=MxN.
+ */
+
+ option = options[i].name;
+ val = options[i].value;
+
+ if (*val == '\0')
+ val = NULL;
+
+ if (val != NULL)
+ {
+ if (strcasecmp(val, "true") == 0 ||
+ strcasecmp(val, "on") == 0 ||
+ strcasecmp(val, "yes") == 0)
+ {
+ /*
+ * Boolean value - true...
+ */
+
+ n = 1;
+ val = "";
+ }
+ else if (strcasecmp(val, "false") == 0 ||
+ strcasecmp(val, "off") == 0 ||
+ strcasecmp(val, "no") == 0)
+ {
+ /*
+ * Boolean value - false...
+ */
+
+ n = 0;
+ val = "";
+ }
+
+ n = strtol(val, &s, 0);
+ }
+ else
+ {
+ if (strncmp(option, "no", 2) == 0)
+ {
+ option += 2;
+ n = 0;
+ }
+ else
+ n = 1;
+
+ s = "";
+ }
+
+ if (*s != '\0' && *s != '-' && (*s != 'x' || s == val))
+ /*
+ * String value(s)...
+ */
+ ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, option, NULL, val);
+ else if (val != NULL)
+ {
+ /*
+ * Numeric value, range, or resolution...
+ */
+
+ if (*s == '-')
+ {
+ n2 = strtol(s + 1, NULL, 0);
+ ippAddRange(request, IPP_TAG_JOB, option, n, n2);
+ }
+ else if (*s == 'x')
+ {
+ n2 = strtol(s + 1, &s, 0);
+
+ if (strcmp(s, "dpc") == 0)
+ ippAddResolution(request, IPP_TAG_JOB, option, IPP_RES_PER_CM, n, n2);
+ else if (strcmp(s, "dpi") == 0)
+ ippAddResolution(request, IPP_TAG_JOB, option, IPP_RES_PER_INCH, n, n2);
+ else
+ ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, option, NULL, val);
+ }
+ else
+ ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_INTEGER, option, n);
+ }
+ else
+ /*
+ * Boolean value...
+ */
+ ippAddBoolean(request, IPP_TAG_JOB, option, (char)n);
+ }
+
+ /*
+ * Now fill in the HTTP request stuff...
+ */
+
+ httpClearFields(http);
+ httpSetField(http, HTTP_FIELD_CONTENT_TYPE, "application/ipp");
+ httpEncode64(password, username);
+ httpSetField(http, HTTP_FIELD_AUTHORIZATION, password);
+
+ if (fp != stdin)
+ {
+ sprintf(buffer, "%u", ippLength(request) + (size_t)fileinfo.st_size);
+ httpSetField(http, HTTP_FIELD_CONTENT_LENGTH, buffer);
+ }
+ else
+ httpSetField(http, HTTP_FIELD_TRANSFER_ENCODING, "chunked");
+
+ /*
+ * Do the request...
+ */
+
+ for (;;)
+ {
+ /*
+ * POST the request, retrying as needed...
+ */
+
+ if (httpPost(http, resource))
+ {
+ fputs("INFO: Unable to POST print request; retrying...\n", stderr);
+ sleep(10);
+ httpReconnect(http);
+ continue;
+ }
+
+ fputs("INFO: POST successful, sending IPP request...\n", stderr);
+
+ /*
+ * Send the IPP request...
+ */
+
+ request->state = IPP_IDLE;
+
+ if (ippWrite(http, request) == IPP_ERROR)
+ {
+ fputs("ERROR: Unable to send IPP request!\n", stderr);
+ status = HTTP_ERROR;
+ break;
+ }
+
+ fputs("INFO: IPP request sent, sending print file...\n", stderr);
+
+ /*
+ * Then send the file...
+ */
+
+ tbytes = 0;
+ while ((nbytes = fread(buffer, 1, sizeof(buffer), fp)) > 0)
+ {
+ tbytes += nbytes;
+ fprintf(stderr, "INFO: Sending print file, %uk...\n", tbytes / 1024);
+
+ if (httpWrite(http, buffer, nbytes) < nbytes)
+ {
+ perror("ERROR: Unable to send print file to printer");
+ status = HTTP_ERROR;
+ break;
+ }
+ }
+
+ fputs("INFO: Print file sent; checking status...\n", stderr);
+
+ /*
+ * Finally, check the status from the HTTP server...
+ */
+
+ while ((status = httpUpdate(http)) == HTTP_CONTINUE);
+
+ if (status == HTTP_OK)
+ {
+ response = ippNew();
+ ippRead(http, response);
+
+ if (response->request.status.status_code > IPP_OK_CONFLICT)
+ fprintf(stderr, "ERROR: Print file was not accepted (%04x)!\n",
+ response->request.status.status_code);
+ else if ((job_id = ippFindAttribute(response, "job-id", IPP_TAG_INTEGER)) == NULL)
+ fputs("INFO: Print file accepted - job ID unknown.\n", stderr);
+ else
+ fprintf(stderr, "INFO: Print file accepted - job ID %d.\n",
+ job_id->values[0].integer);
+ }
+ else
+ {
+ response = NULL;
+ httpFlush(http);
+
+ fprintf(stderr, "ERROR: Print request was not accepted (%d)!\n", status);
+ }
+
+ break;
+ }
+
+ /*
+ * Free memory...
+ */
+
+ httpClose(http);
+ if (request != NULL)
+ ippDelete(request);
+ if (response != NULL)
+ ippDelete(response);
+
+ /*
+ * Close the print file as needed...
+ */
+
+ if (fp != stdin)
+ fclose(fp);
+
+ /*
+ * Return the queue status...
+ */
+
+ return (status != HTTP_OK);
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/backend/lpd.c b/backend/lpd.c
new file mode 100644
index 000000000..3fa417564
--- /dev/null
+++ b/backend/lpd.c
@@ -0,0 +1,393 @@
+/*
+ * "$Id$"
+ *
+ * Line Printer Daemon backend for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-1999 by Easy Software Products, all rights reserved.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * Contents:
+ *
+ * main() - Send a file to the printer or server.
+ * lpd_command() - Send an LPR command sequence and wait for a reply.
+ * lpd_queue() - Queue a file using the Line Printer Daemon protocol.
+ */
+
+/*
+ * Include necessary headers.
+ */
+
+#include <cups/cups.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <cups/string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#if defined(WIN32) || defined(__EMX__)
+# include <winsock.h>
+#else
+# include <sys/socket.h>
+# include <netinet/in.h>
+# include <arpa/inet.h>
+# include <netdb.h>
+#endif /* WIN32 || __EMX__ */
+
+extern int rresvport(int *port); /* Hello? No prototype for this... */
+
+
+/*
+ * Local functions...
+ */
+
+static int lpd_command(int lpd_fd, char *format, ...);
+static int lpd_queue(char *hostname, char *printer, char *filename,
+ char *user, int copies);
+
+
+/*
+ * 'main()' - Send a file to the printer or server.
+ *
+ * Usage:
+ *
+ * printer-uri job-id user title copies options [file]
+ */
+
+int /* O - Exit status */
+main(int argc, /* I - Number of command-line arguments (6 or 7) */
+ char *argv[]) /* I - Command-line arguments */
+{
+ char method[255], /* Method in URI */
+ hostname[1024], /* Hostname */
+ username[255], /* Username info (not used) */
+ resource[1024], /* Resource info (printer name) */
+ filename[1024]; /* File to print */
+ int port; /* Port number (not used) */
+ int status; /* Status of LPD job */
+
+
+ if (argc < 6 || argc > 7)
+ {
+ fprintf(stderr, "Usage: %s job-id user title copies options [file]\n",
+ argv[0]);
+ return (1);
+ }
+
+ /*
+ * If we have 7 arguments, print the file named on the command-line.
+ * Otherwise, copy stdin to a temporary file and print the temporary
+ * file.
+ */
+
+ if (argc == 6)
+ {
+ /*
+ * Copy stdin to a temporary file...
+ */
+
+ FILE *fp; /* Temporary file */
+ char buffer[8192]; /* Buffer for copying */
+ int bytes; /* Number of bytes read */
+
+
+ if ((fp = fopen(cupsTempFile(filename, sizeof(filename)), "w")) == NULL)
+ {
+ perror("ERROR: unable to create temporary file");
+ return (1);
+ }
+
+ while ((bytes = fread(buffer, 1, sizeof(buffer), stdin)) > 0)
+ if (fwrite(buffer, 1, bytes, fp) < bytes)
+ {
+ perror("ERROR: unable to write to temporary file");
+ fclose(fp);
+ unlink(filename);
+ return (1);
+ }
+
+ fclose(fp);
+ }
+ else
+ strcpy(filename, argv[6]);
+
+ /*
+ * Extract the hostname and printer name from the URI...
+ */
+
+ httpSeparate(argv[0], method, username, hostname, &port, resource);
+
+ /*
+ * Queue the job...
+ */
+
+ status = lpd_queue(hostname, resource + 1, filename,
+ argv[2] /* user */, atoi(argv[4]) /* copies */);
+
+ /*
+ * Remove the temporary file if necessary...
+ */
+
+ if (argc < 7)
+ unlink(filename);
+
+ /*
+ * Return the queue status...
+ */
+
+ return (status);
+}
+
+
+/*
+ * 'lpd_command()' - Send an LPR command sequence and wait for a reply.
+ */
+
+static int /* O - Status of command */
+lpd_command(int fd, /* I - Socket connection to LPD host */
+ char *format, /* I - printf()-style format string */
+ ...) /* I - Additional args as necessary */
+{
+ va_list ap; /* Argument pointer */
+ char buf[1024]; /* Output buffer */
+ int bytes; /* Number of bytes to output */
+ char status; /* Status from command */
+
+
+ /*
+ * Format the string...
+ */
+
+ va_start(ap, format);
+ bytes = vsprintf(buf, format, ap);
+ va_end(ap);
+
+ fprintf(stderr, "DEBUG: lpd_command %02.2x %s", buf[0], buf + 1);
+
+ /*
+ * Send the command...
+ */
+
+ if (send(fd, buf, bytes, 0) < bytes)
+ return (-1);
+
+ /*
+ * Read back the status from the command and return it...
+ */
+
+ if (recv(fd, &status, 1, 0) < 1)
+ return (-1);
+
+ fprintf(stderr, "DEBUG: lpd_command returning %d\n", status);
+
+ return (status);
+}
+
+
+/*
+ * 'lpd_queue()' - Queue a file using the Line Printer Daemon protocol.
+ */
+
+static int /* O - Zero on success, non-zero on failure */
+lpd_queue(char *hostname, /* I - Host to connect to */
+ char *printer, /* I - Printer/queue name */
+ char *filename, /* I - File to print */
+ char *user, /* I - Requesting user */
+ int copies) /* I - Number of copies */
+{
+ FILE *fp; /* Job file */
+ char localhost[255]; /* Local host name */
+ int error; /* Error number */
+ struct stat filestats; /* File statistics */
+ int port; /* LPD connection port */
+ int fd; /* LPD socket */
+ char control[10240], /* LPD control 'file' */
+ *cptr; /* Pointer into control file string */
+ char status; /* Status byte from command */
+ struct sockaddr_in addr; /* Socket address */
+ struct hostent *hostaddr; /* Host address */
+ size_t nbytes, /* Number of bytes written */
+ tbytes; /* Total bytes written */
+ char buffer[8192]; /* Output buffer */
+
+
+ /*
+ * First try to reserve a port for this connection...
+ */
+
+ if ((hostaddr = gethostbyname(hostname)) == NULL)
+ {
+ fprintf(stderr, "ERROR: Unable to locate printer \'%s\' - %s",
+ hostname, strerror(errno));
+ return (1);
+ }
+
+ fprintf(stderr, "INFO: Attempting to connect to host %s for printer %s\n",
+ hostname, printer);
+
+ memset(&addr, 0, sizeof(addr));
+ memcpy(&(addr.sin_addr), hostaddr->h_addr, hostaddr->h_length);
+ addr.sin_family = hostaddr->h_addrtype;
+ addr.sin_port = htons(515); /* LPD/printer service */
+
+ for (port = 732;;)
+ {
+ if ((fd = rresvport(&port)) < 0)
+ {
+ perror("ERROR: Unable to connect to printer");
+ return (1);
+ }
+
+ if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
+ {
+ error = errno;
+ close(fd);
+ fd = -1;
+
+ if (error == ECONNREFUSED)
+ {
+ fprintf(stderr, "INFO: Network host \'%s\' is busy; will retry in 30 seconds...",
+ hostname);
+ sleep(30);
+ }
+ else if (error == EADDRINUSE)
+ {
+ port --;
+ if (port < 721)
+ port = 732;
+ }
+ else
+ {
+ perror("ERROR: Unable to connect to printer");
+ return (1);
+ }
+ }
+ else
+ break;
+ }
+
+ /*
+ * Next, open the print file and figure out its size...
+ */
+
+ if (stat(filename, &filestats))
+ {
+ perror("ERROR: unable to stat print file");
+ return (1);
+ }
+
+ if ((fp = fopen(filename, "rb")) == NULL)
+ {
+ perror("ERROR: unable to open print file for reading");
+ return (1);
+ }
+
+ /*
+ * Send a job header to the printer, specifying no banner page and
+ * literal output...
+ */
+
+ lpd_command(fd, "\002%s\n", printer); /* Receive print job(s) */
+
+ gethostname(localhost, sizeof(localhost));
+ localhost[31] = '\0'; /* RFC 1179, Section 7.2 - host name < 32 chars */
+
+ sprintf(control, "H%s\nP%s\n", localhost, user);
+ cptr = control + strlen(control);
+
+ while (copies > 0)
+ {
+ sprintf(cptr, "ldfA%03.3d%s\n", getpid() % 1000, localhost);
+ cptr += strlen(cptr);
+ copies --;
+ }
+
+ sprintf(cptr, "UdfA%03.3d%s\nNdfA%03.3d%s\n",
+ getpid() % 1000, localhost,
+ getpid() % 1000, localhost);
+
+ fprintf(stderr, "DEBUG: Control file is:\n%s", control);
+
+ lpd_command(fd, "\002%d cfA%03.3d%s\n", strlen(control), getpid() % 1000,
+ localhost);
+
+ fprintf(stderr, "INFO: Sending control file (%d bytes)\n", strlen(control));
+
+ if (send(fd, control, strlen(control) + 1, 0) < (strlen(control) + 1))
+ {
+ perror("ERROR: Unable to write control file");
+ status = 1;
+ }
+ else if (read(fd, &status, 1) < 1 || status != 0)
+ fprintf(stderr, "ERROR: Remote host did not accept control file (%d)\n",
+ status);
+ else
+ {
+ /*
+ * Send the print file...
+ */
+
+ fputs("INFO: Control file sent successfully\n", stderr);
+
+ lpd_command(fd, "\003%u dfA%03.3d%s\n", (unsigned)filestats.st_size,
+ getpid() % 1000, localhost);
+
+ fprintf(stderr, "INFO: Sending data file (%u bytes)\n",
+ (unsigned)filestats.st_size);
+
+ tbytes = 0;
+ while ((nbytes = fread(buffer, 1, sizeof(buffer), fp)) > 0)
+ {
+ fprintf(stderr, "INFO: Spooling LPR job, %u%% complete...\n",
+ (unsigned)(100 * tbytes / filestats.st_size));
+
+ if (send(fd, buffer, nbytes, 0) < nbytes)
+ {
+ perror("ERROR: Unable to send print file to printer");
+ break;
+ }
+ else
+ tbytes += nbytes;
+ }
+
+ send(fd, "", 1, 0);
+
+ if (tbytes < filestats.st_size)
+ status = 1;
+ else if (recv(fd, &status, 1, 0) < 1 || status != 0)
+ fprintf(stderr, "ERROR: Remote host did not accept data file (%d)\n",
+ status);
+ else
+ fputs("INFO: Data file sent successfully\n", stderr);
+ }
+
+ /*
+ * Close the socket connection and input file and return...
+ */
+
+ close(fd);
+ fclose(fp);
+
+ return (status);
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/backend/parallel.c b/backend/parallel.c
new file mode 100644
index 000000000..df74637e6
--- /dev/null
+++ b/backend/parallel.c
@@ -0,0 +1,179 @@
+/*
+ * "$Id$"
+ *
+ * Parallel port backend for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-1999 by Easy Software Products, all rights reserved.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * Contents:
+ *
+ * main() - Send a file to the specified parallel port.
+ */
+
+/*
+ * Include necessary headers.
+ */
+
+#include <cups/cups.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <cups/string.h>
+
+#if defined(WIN32) || defined(__EMX__)
+# include <io.h>
+#else
+# include <unistd.h>
+# include <fcntl.h>
+# include <termios.h>
+#endif /* WIN32 || __EMX__ */
+
+
+/*
+ * 'main()' - Send a file to the specified parallel port.
+ *
+ * Usage:
+ *
+ * printer-uri job-id user title copies options [file]
+ */
+
+int /* O - Exit status */
+main(int argc, /* I - Number of command-line arguments (6 or 7) */
+ char *argv[]) /* I - Command-line arguments */
+{
+ char method[255], /* Method in URI */
+ hostname[1024], /* Hostname */
+ username[255], /* Username info (not used) */
+ resource[1024], /* Resource info (device and options) */
+ *options; /* Pointer to options */
+ int port; /* Port number (not used) */
+ FILE *fp; /* Print file */
+ int fd; /* Parallel device */
+ int error; /* Error code (if any) */
+ size_t nbytes, /* Number of bytes written */
+ tbytes; /* Total number of bytes written */
+ char buffer[8192]; /* Output buffer */
+ struct termios opts; /* Parallel port options */
+
+
+ if (argc < 6 || argc > 7)
+ {
+ fputs("Usage: parallel job-id user title copies options [file]\n", stderr);
+ return (1);
+ }
+
+ /*
+ * If we have 7 arguments, print the file named on the command-line.
+ * Otherwise, send stdin instead...
+ */
+
+ if (argc == 6)
+ fp = stdin;
+ else
+ {
+ /*
+ * Try to open the print file...
+ */
+
+ if ((fp = fopen(argv[6], "rb")) == NULL)
+ {
+ perror("ERROR: unable to open print file");
+ return (1);
+ }
+ }
+
+ /*
+ * Extract the device name and options from the URI...
+ */
+
+ httpSeparate(argv[0], method, username, hostname, &port, resource);
+
+ /*
+ * See if there are any options...
+ */
+
+ if ((options = strchr(resource, '?')) != NULL)
+ {
+ /*
+ * Yup, terminate the device name string and move to the first
+ * character of the options...
+ */
+
+ *options++ = '\0';
+ }
+
+ /*
+ * Open the parallel port device...
+ */
+
+ if ((fd = open(resource, O_WRONLY)) == -1)
+ {
+ perror("ERROR: Unable to open parallel port device file");
+ return (1);
+ }
+
+ /*
+ * Set any options provided...
+ */
+
+ tcgetattr(fd, &opts);
+
+ opts.c_lflag &= ~(ICANON | ECHO | ISIG); /* Raw mode */
+
+ /**** No options supported yet ****/
+
+ tcsetattr(fd, TCSANOW, &opts);
+
+ /*
+ * Finally, send the print file...
+ */
+
+ tbytes = 0;
+ while ((nbytes = fread(buffer, 1, sizeof(buffer), fp)) > 0)
+ {
+ /*
+ * Write the print data to the printer...
+ */
+
+ if (write(fd, buffer, nbytes) < nbytes)
+ {
+ perror("ERROR: Unable to send print file to printer");
+ break;
+ }
+ else
+ tbytes += nbytes;
+
+ if (argc > 6)
+ fprintf(stderr, "INFO: Sending print file, %u bytes...\n", tbytes);
+ }
+
+ /*
+ * Close the socket connection and input file and return...
+ */
+
+ close(fd);
+ if (fp != stdin)
+ fclose(fp);
+
+ return (0);
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/backend/serial.c b/backend/serial.c
new file mode 100644
index 000000000..d9edfe2a2
--- /dev/null
+++ b/backend/serial.c
@@ -0,0 +1,297 @@
+/*
+ * "$Id$"
+ *
+ * Serial port backend for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-1999 by Easy Software Products, all rights reserved.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * Contents:
+ *
+ * main() - Send a file to the printer or server.
+ */
+
+/*
+ * Include necessary headers.
+ */
+
+#include <cups/cups.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <cups/string.h>
+
+#if defined(WIN32) || defined(__EMX__)
+# include <io.h>
+#else
+# include <unistd.h>
+# include <fcntl.h>
+# include <termios.h>
+#endif /* WIN32 || __EMX__ */
+
+
+/*
+ * 'main()' - Send a file to the printer or server.
+ *
+ * Usage:
+ *
+ * printer-uri job-id user title copies options [file]
+ */
+
+int /* O - Exit status */
+main(int argc, /* I - Number of command-line arguments (6 or 7) */
+ char *argv[]) /* I - Command-line arguments */
+{
+ char method[255], /* Method in URI */
+ hostname[1024], /* Hostname */
+ username[255], /* Username info (not used) */
+ resource[1024], /* Resource info (device and options) */
+ *options, /* Pointer to options */
+ name[255], /* Name of option */
+ value[255], /* Value of option */
+ *ptr; /* Pointer into name or value */
+ int port; /* Port number (not used) */
+ FILE *fp; /* Print file */
+ int fd; /* Parallel device */
+ int error; /* Error code (if any) */
+ size_t nbytes, /* Number of bytes written */
+ tbytes; /* Total number of bytes written */
+ char buffer[8192]; /* Output buffer */
+ struct termios opts; /* Parallel port options */
+
+
+ if (argc < 6 || argc > 7)
+ {
+ fputs("Usage: serial job-id user title copies options [file]\n", stderr);
+ return (1);
+ }
+
+ /*
+ * If we have 7 arguments, print the file named on the command-line.
+ * Otherwise, send stdin instead...
+ */
+
+ if (argc == 6)
+ fp = stdin;
+ else
+ {
+ /*
+ * Try to open the print file...
+ */
+
+ if ((fp = fopen(argv[6], "rb")) == NULL)
+ {
+ perror("ERROR: unable to open print file");
+ return (1);
+ }
+ }
+
+ /*
+ * Extract the device name and options from the URI...
+ */
+
+ httpSeparate(argv[0], method, username, hostname, &port, resource);
+
+ /*
+ * See if there are any options...
+ */
+
+ if ((options = strchr(resource, '?')) != NULL)
+ {
+ /*
+ * Yup, terminate the device name string and move to the first
+ * character of the options...
+ */
+
+ *options++ = '\0';
+ }
+
+ /*
+ * Open the parallel port device...
+ */
+
+ if ((fd = open(resource, O_WRONLY)) == -1)
+ {
+ perror("ERROR: Unable to open serial port device file");
+ return (1);
+ }
+
+ /*
+ * Set any options provided...
+ */
+
+ tcgetattr(fd, &opts);
+
+ opts.c_lflag &= ~(ICANON | ECHO | ISIG); /* Raw mode */
+
+ if (options != NULL)
+ while (*options)
+ {
+ /*
+ * Get the name...
+ */
+
+ for (ptr = name; *options && *options != '=';)
+ *ptr++ = *options++;
+ *ptr = '\0';
+
+ if (*options == '=')
+ {
+ /*
+ * Get the value...
+ */
+
+ options ++;
+
+ for (ptr = value; *options && *options != '+';)
+ *ptr++ = *options++;
+ *ptr = '\0';
+
+ if (*options == '+')
+ options ++;
+ }
+ else
+ value[0] = '\0';
+
+ /*
+ * Process the option...
+ */
+
+ if (strcasecmp(name, "baud") == 0)
+ {
+ /*
+ * Set the baud rate...
+ */
+
+#if B19200 == 19200
+ cfsetispeed(&opts, atoi(value));
+ cfsetospeed(&opts, atoi(value));
+#else
+ switch (atoi(value))
+ {
+ case 1200 :
+ cfsetispeed(&opts, B1200);
+ cfsetospeed(&opts, B1200);
+ break;
+ case 2400 :
+ cfsetispeed(&opts, B2400);
+ cfsetospeed(&opts, B2400);
+ break;
+ case 4800 :
+ cfsetispeed(&opts, B4800);
+ cfsetospeed(&opts, B4800);
+ break;
+ case 9600 :
+ cfsetispeed(&opts, B9600);
+ cfsetospeed(&opts, B9600);
+ break;
+ case 19200 :
+ cfsetispeed(&opts, B19200);
+ cfsetospeed(&opts, B19200);
+ break;
+ case 38400 :
+ cfsetispeed(&opts, B38400);
+ cfsetospeed(&opts, B38400);
+ break;
+ default :
+ fprintf(stderr, "WARNING: Unsupported baud rate %s!\n", value);
+ break;
+ }
+#endif /* B19200 == 19200 */
+ }
+ else if (strcasecmp(name, "bits") == 0)
+ {
+ /*
+ * Set number of data bits...
+ */
+
+ switch (atoi(value))
+ {
+ case 7 :
+ opts.c_cflag &= ~CSIZE;
+ opts.c_cflag |= CS7;
+ opts.c_cflag |= PARENB;
+ opts.c_cflag &= ~PARODD;
+ break;
+ case 8 :
+ opts.c_cflag &= ~CSIZE;
+ opts.c_cflag |= CS8;
+ opts.c_cflag &= ~PARENB;
+ break;
+ }
+ }
+ else if (strcasecmp(name, "parity") == 0)
+ {
+ /*
+ * Set parity checking...
+ */
+
+ if (strcasecmp(value, "even") == 0)
+ {
+ opts.c_cflag |= PARENB;
+ opts.c_cflag &= ~PARODD;
+ }
+ else if (strcasecmp(value, "odd") == 0)
+ {
+ opts.c_cflag |= PARENB;
+ opts.c_cflag |= PARODD;
+ }
+ else if (strcasecmp(value, "none") == 0)
+ opts.c_cflag &= ~PARENB;
+ }
+ }
+
+ tcsetattr(fd, TCSANOW, &opts);
+
+ /*
+ * Finally, send the print file...
+ */
+
+ tbytes = 0;
+ while ((nbytes = fread(buffer, 1, sizeof(buffer), fp)) > 0)
+ {
+ /*
+ * Write the print data to the printer...
+ */
+
+ if (write(fd, buffer, nbytes) < nbytes)
+ {
+ perror("ERROR: Unable to send print file to printer");
+ break;
+ }
+ else
+ tbytes += nbytes;
+
+ if (argc > 6)
+ fprintf(stderr, "INFO: Sending print file, %u bytes...\n", tbytes);
+ }
+
+ /*
+ * Close the socket connection and input file and return...
+ */
+
+ close(fd);
+ if (fp != stdin)
+ fclose(fp);
+
+ return (0);
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/backend/smb.sh b/backend/smb.sh
new file mode 100755
index 000000000..a0c67aecb
--- /dev/null
+++ b/backend/smb.sh
@@ -0,0 +1,88 @@
+#!/bin/sh
+#
+# "$Id$"
+#
+# SMB printing script for the Common UNIX Printing System (CUPS).
+#
+# Copyright 1993-1999 by Easy Software Products, all rights reserved.
+#
+# These coded instructions, statements, and computer programs are the
+# property of Easy Software Products and are protected by Federal
+# copyright law. Distribution and use rights are outlined in the file
+# "LICENSE.txt" which should have been included with this file. If this
+# file is missing or damaged please contact Easy Software Products
+# at:
+#
+# Attn: CUPS Licensing Information
+# Easy Software Products
+# 44145 Airport View Drive, Suite 204
+# Hollywood, Maryland 20636-3111 USA
+#
+# Voice: (301) 373-9603
+# EMail: cups-info@cups.org
+# WWW: http://www.cups.org
+#
+
+SMBCLIENT=/usr/local/samba/bin/smbclient
+
+#
+# Usage:
+#
+# printer job user title copies options [filename]
+#
+
+if [ $# -lt 5 -o $# -gt 6 ]; then
+ # Too few or too many arguments
+ echo 'Usage: smb job-id user title copies options [file]' >&2
+ exit 1
+fi
+
+#
+# If "filename" is not on the command-line, then we read the print
+# data from stdin and write it to a temporary file.
+#
+
+if [ $# = 5 ]; then
+ # Collect all print data and put it in a temporary file...
+ if [ "$TMPDIR" = "" ]; then
+ TMPDIR=/var/tmp
+ fi
+
+ filename="$TMPDIR/$$.smb"
+ cat >$filename
+else
+ # Use the file on the command-line...
+ filename="$6"
+fi
+
+#
+# Take apart the URI in $0...
+#
+
+uri="$0"
+host=`echo $uri | awk -F/ '{print substr($3, index($3, "@") + 1)}'`
+user=`echo $uri | awk -F/ '{print substr($3, 0, index($3, "@") - 1)}'`
+if [ "$user" != "" ]; then
+ user="-U $user"
+fi
+printer=`echo $uri | awk -F/ '{print $4}'`
+
+#
+# Send the file to the remote system...
+#
+
+$SMBCLIENT //$host/$printer $user -P -N <<EOF
+print $filename
+EOF
+
+#
+# Lastly, remove the temporary file as needed...
+#
+
+if [ $# = 5 ]; then
+ rm -f $filename
+fi
+
+#
+# End of "$Id$".
+#
diff --git a/backend/socket.c b/backend/socket.c
new file mode 100644
index 000000000..3f0e3fc72
--- /dev/null
+++ b/backend/socket.c
@@ -0,0 +1,232 @@
+/*
+ * "$Id$"
+ *
+ * AppSocket backend for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-1999 by Easy Software Products, all rights reserved.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * Contents:
+ *
+ * main() - Send a file to the printer or server.
+ */
+
+/*
+ * Include necessary headers.
+ */
+
+#include <cups/cups.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <cups/string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#if defined(WIN32) || defined(__EMX__)
+# include <winsock.h>
+#else
+# include <sys/socket.h>
+# include <netinet/in.h>
+# include <arpa/inet.h>
+# include <netdb.h>
+#endif /* WIN32 || __EMX__ */
+
+
+/*
+ * 'main()' - Send a file to the printer or server.
+ *
+ * Usage:
+ *
+ * printer-uri job-id user title copies options [file]
+ */
+
+int /* O - Exit status */
+main(int argc, /* I - Number of command-line arguments (6 or 7) */
+ char *argv[]) /* I - Command-line arguments */
+{
+ char method[255], /* Method in URI */
+ hostname[1024], /* Hostname */
+ username[255], /* Username info (not used) */
+ resource[1024]; /* Resource info (not used) */
+ FILE *fp; /* Print file */
+ int port; /* Port number */
+ int fd; /* AppSocket */
+ int error; /* Error code (if any) */
+ struct sockaddr_in addr; /* Socket address */
+ struct hostent *hostaddr; /* Host address */
+ int wbytes; /* Number of bytes written */
+ size_t nbytes, /* Number of bytes read */
+ tbytes; /* Total number of bytes written */
+ char buffer[8192], /* Output buffer */
+ *bufptr; /* Pointer into buffer */
+ struct timeval timeout; /* Timeout for select() */
+ fd_set input; /* Input set for select() */
+
+
+ if (argc < 6 || argc > 7)
+ {
+ fprintf(stderr, "Usage: %s job-id user title copies options [file]\n",
+ argv[0]);
+ return (1);
+ }
+
+ /*
+ * If we have 7 arguments, print the file named on the command-line.
+ * Otherwise, send stdin instead...
+ */
+
+ if (argc == 6)
+ fp = stdin;
+ else
+ {
+ /*
+ * Try to open the print file...
+ */
+
+ if ((fp = fopen(argv[6], "rb")) == NULL)
+ {
+ perror("ERROR: unable to open print file");
+ return (1);
+ }
+ }
+
+ /*
+ * Extract the hostname and port number from the URI...
+ */
+
+ httpSeparate(argv[0], method, username, hostname, &port, resource);
+
+ if (port == 0)
+ port = 9100; /* Default to HP JetDirect/Tektronix PhaserShare */
+
+ /*
+ * Then try to connect to the remote host...
+ */
+
+ if ((hostaddr = gethostbyname(hostname)) == NULL)
+ {
+ fprintf(stderr, "ERROR: Unable to locate printer \'%s\' - %s",
+ hostname, strerror(errno));
+ return (1);
+ }
+
+ fprintf(stderr, "INFO: Attempting to connect to host %s on port %d\n",
+ hostname, port);
+
+ memset(&addr, 0, sizeof(addr));
+ memcpy(&(addr.sin_addr), hostaddr->h_addr, hostaddr->h_length);
+ addr.sin_family = hostaddr->h_addrtype;
+ addr.sin_port = htons(port);
+
+ for (;;)
+ {
+ if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
+ {
+ perror("ERROR: Unable to connect to printer");
+ return (1);
+ }
+
+ if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
+ {
+ error = errno;
+ close(fd);
+ fd = -1;
+
+ if (error == ECONNREFUSED)
+ {
+ fprintf(stderr, "INFO: Network host \'%s\' is busy; will retry in 30 seconds...\n",
+ hostname);
+ sleep(30);
+ }
+ else
+ {
+ perror("ERROR: Unable to connect to printer");
+ return (1);
+ }
+ }
+ else
+ break;
+ }
+
+ /*
+ * Finally, send the print file...
+ */
+
+ fputs("INFO: Connected to host, sending print job...\n", stderr);
+
+ tbytes = 0;
+ while ((nbytes = fread(buffer, 1, sizeof(buffer), fp)) > 0)
+ {
+ /*
+ * Write the print data to the printer...
+ */
+
+ tbytes += nbytes;
+ bufptr = buffer;
+
+ while (nbytes > 0)
+ {
+ if ((wbytes = send(fd, bufptr, nbytes, 0)) < 0)
+ {
+ perror("ERROR: Unable to send print file to printer");
+ break;
+ }
+
+ nbytes -= wbytes;
+ bufptr += wbytes;
+ }
+
+ /*
+ * Check for possible data coming back from the printer...
+ */
+
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 0;
+ FD_ZERO(&input);
+ FD_SET(fd, &input);
+ if (select(fd + 1, &input, NULL, NULL, &timeout) > 0)
+ {
+ /*
+ * Grab the data coming back and spit it out to stderr...
+ */
+
+ if ((nbytes = recv(fd, buffer, sizeof(buffer), 0)) > 0)
+ fprintf(stderr, "INFO: Received %u bytes of back-channel data!\n",
+ nbytes);
+ }
+ else if (argc > 6)
+ fprintf(stderr, "INFO: Sending print file, %u bytes...\n", tbytes);
+ }
+
+ /*
+ * Close the socket connection and input file and return...
+ */
+
+ close(fd);
+ if (fp != stdin)
+ fclose(fp);
+
+ return (0);
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/berkeley/Makefile b/berkeley/Makefile
new file mode 100644
index 000000000..5ffc54ab2
--- /dev/null
+++ b/berkeley/Makefile
@@ -0,0 +1,95 @@
+#
+# "$Id$"
+#
+# Berkeley commands makefile for the Common UNIX Printing System (CUPS).
+#
+# Copyright 1997-1999 by Easy Software Products, all rights reserved.
+#
+# These coded instructions, statements, and computer programs are the
+# property of Easy Software Products and are protected by Federal
+# copyright law. Distribution and use rights are outlined in the file
+# "LICENSE.txt" which should have been included with this file. If this
+# file is missing or damaged please contact Easy Software Products
+# at:
+#
+# Attn: CUPS Licensing Information
+# Easy Software Products
+# 44141 Airport View Drive, Suite 204
+# Hollywood, Maryland 20636-3111 USA
+#
+# Voice: (301) 373-9603
+# EMail: cups-info@cups.org
+# WWW: http://www.cups.org
+#
+
+include ../Makedefs
+
+TARGETS = lpc lpq lpr lprm
+OBJS = lpc.o lpq.o lpr.o lprm.o
+
+#
+# Make all targets...
+#
+
+all: $(TARGETS)
+
+#
+# Clean all object files...
+#
+
+clean:
+ rm -f $(OBJS) $(TARGETS)
+
+#
+# Install all targets...
+#
+
+install:
+ -$(MKDIR) $(BINDIR)
+ -$(MKDIR) $(SBINDIR)
+ $(CP) lpq lpr lprm $(BINDIR)
+ $(CP) lpc $(SBINDIR)
+
+#
+# lpc
+#
+
+lpc: lpc.o ../cups/$(LIBCUPS)
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o lpc lpc.o $(LIBS)
+
+lpc.o: ../cups/cups.h ../Makedefs
+
+#
+# lpq
+#
+
+lpq: lpq.o ../cups/$(LIBCUPS)
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o lpq lpq.o $(LIBS)
+
+lpq.o: ../cups/cups.h ../Makedefs
+
+#
+# lpr
+#
+
+lpr: lpr.o ../cups/$(LIBCUPS)
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o lpr lpr.o $(LIBS)
+
+lpr.o: ../cups/cups.h ../Makedefs
+
+#
+# lprm
+#
+
+lprm: lprm.o ../cups/$(LIBCUPS)
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o lprm lprm.o $(LIBS)
+
+lprm.o: ../cups/cups.h ../Makedefs
+
+#
+# End of "$Id$".
+#
diff --git a/berkeley/lpc.c b/berkeley/lpc.c
new file mode 100644
index 000000000..cf2fc3342
--- /dev/null
+++ b/berkeley/lpc.c
@@ -0,0 +1,458 @@
+/*
+ * "$Id$"
+ *
+ * "lpc" command for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-1999 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * Contents:
+ *
+ * main() - Parse options and commands.
+ * compare_strings() - Compare two command-line strings.
+ * do_command() - Do an lpc command...
+ * show_help() - Show help messages.
+ * show_status() - Show printers.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <cups/cups.h>
+#include <cups/language.h>
+#include <cups/debug.h>
+
+
+/*
+ * Local functions...
+ */
+
+static int compare_strings(char *, char *, int);
+static void do_command(http_t *, char *, char *);
+static void show_help(char *);
+static void show_status(http_t *, char *);
+
+
+/*
+ * 'main()' - Parse options and commands.
+ */
+
+int
+main(int argc, /* I - Number of command-line arguments */
+ char *argv[]) /* I - Command-line arguments */
+{
+ int i; /* Looping var */
+ http_t *http; /* Connection to server */
+ char line[1024], /* Input line from user */
+ *params; /* Pointer to parameters */
+
+
+ /*
+ * Connect to the scheduler...
+ */
+
+ http = httpConnect(cupsServer(), ippPort());
+
+ if (argc > 1)
+ {
+ /*
+ * Process a single command on the command-line...
+ */
+
+ do_command(http, argv[1], argv[2]);
+ }
+ else
+ {
+ /*
+ * Do the command prompt thing...
+ */
+
+ printf("lpc> ");
+ while (fgets(line, sizeof(line), stdin) != NULL)
+ {
+ /*
+ * Strip the trailing newline...
+ */
+
+ line[strlen(line) - 1] = '\0';
+
+ /*
+ * Find any options in the string...
+ */
+
+ while (isspace(line[0]))
+ strcpy(line, line + 1);
+
+ for (params = line; *params != '\0'; params ++)
+ if (isspace(*params))
+ break;
+
+ /*
+ * Remove whitespace between the command and parameters...
+ */
+
+ while (isspace(*params))
+ *params++ = '\0';
+
+ /*
+ * The "quit" and "exit" commands exit; otherwise, process as needed...
+ */
+
+ if (compare_strings(line, "quit", 1) == 0 ||
+ compare_strings(line, "exit", 2) == 0)
+ break;
+
+ if (*params == '\0')
+ do_command(http, line, NULL);
+ else
+ do_command(http, line, params);
+
+ /*
+ * Put another prompt out to the user...
+ */
+
+ printf("lpc> ");
+ }
+ }
+
+ /*
+ * Close the connection to the server and return...
+ */
+
+ httpClose(http);
+
+ return (0);
+}
+
+
+/*
+ * 'compare_strings()' - Compare two command-line strings.
+ */
+
+static int /* O - -1 or 1 = no match, 0 = match */
+compare_strings(char *s, /* I - Command-line string */
+ char *t, /* I - Option string */
+ int tmin) /* I - Minimum number of unique chars in option */
+{
+ int slen; /* Length of command-line string */
+
+
+ slen = strlen(s);
+ if (slen < tmin)
+ return (-1);
+ else
+ return (strncmp(s, t, slen));
+}
+
+
+/*
+ * 'do_command()' - Do an lpc command...
+ */
+
+static void
+do_command(http_t *http, /* I - HTTP connection to server */
+ char *command, /* I - Command string */
+ char *params) /* I - Parameters for command */
+{
+ if (compare_strings(command, "status", 4) == 0)
+ show_status(http, params);
+ else if (compare_strings(command, "help", 1) == 0 ||
+ strcmp(command, "?") == 0)
+ show_help(params);
+ else
+ puts("?Invalid command");
+}
+
+
+/*
+ * 'show_help()' - Show help messages.
+ */
+
+static void
+show_help(char *command) /* I - Command to describe or NULL */
+{
+ if (command == NULL)
+ {
+ puts("Commands may be abbreviated. Commands are:");
+ puts("");
+ puts("exit help quit status ?");
+ }
+ else if (compare_strings(command, "help", 1) == 0 ||
+ strcmp(command, "?") == 0)
+ puts("help\t\tget help on commands");
+ else if (compare_strings(command, "status", 4) == 0)
+ puts("status\t\tshow status of daemon and queue");
+ else
+ puts("?Invalid help command unknown");
+}
+
+
+/*
+ * 'show_status()' - Show printers.
+ */
+
+static void
+show_status(http_t *http, /* I - HTTP connection to server */
+ char *dests) /* I - Destinations */
+{
+ ipp_t *request, /* IPP Request */
+ *response, /* IPP Response */
+ *jobs; /* IPP Get Jobs response */
+ ipp_attribute_t *attr; /* Current attribute */
+ cups_lang_t *language; /* Default language */
+ char *printer, /* Printer name */
+ *device; /* Device URI */
+ ipp_pstate_t pstate; /* Printer state */
+ int accepting; /* Is printer accepting jobs? */
+ int jobcount; /* Count of current jobs */
+ char *dptr, /* Pointer into destination list */
+ *ptr; /* Pointer into printer name */
+ int match; /* Non-zero if this job matches */
+ char printer_uri[HTTP_MAX_URI];
+ /* Printer URI */
+
+
+ DEBUG_printf(("show_status(%08x, %08x)\n", http, dests));
+
+ if (http == NULL)
+ return;
+
+ /*
+ * Build a CUPS_GET_PRINTERS request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = CUPS_GET_PRINTERS;
+ request->request.op.request_id = 1;
+
+ language = cupsLangDefault();
+
+ attr = ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, cupsLangEncoding(language));
+
+ attr = ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ if ((response = cupsDoRequest(http, request, "/printers/")) != NULL)
+ {
+ DEBUG_puts("show_status: request succeeded...");
+
+ /*
+ * Loop through the printers returned in the list and display
+ * their status...
+ */
+
+ for (attr = response->attrs; attr != NULL; attr = attr->next)
+ {
+ /*
+ * Skip leading attributes until we hit a job...
+ */
+
+ while (attr != NULL && attr->group_tag != IPP_TAG_PRINTER)
+ attr = attr->next;
+
+ if (attr == NULL)
+ break;
+
+ /*
+ * Pull the needed attributes from this job...
+ */
+
+ printer = NULL;
+ device = "file:/dev/null";
+ pstate = IPP_PRINTER_IDLE;
+ jobcount = 0;
+ accepting = 1;
+
+ while (attr != NULL && attr->group_tag == IPP_TAG_PRINTER)
+ {
+ if (strcmp(attr->name, "printer-name") == 0 &&
+ attr->value_tag == IPP_TAG_NAME)
+ printer = attr->values[0].string.text;
+
+ if (strcmp(attr->name, "device-uri") == 0 &&
+ attr->value_tag == IPP_TAG_URI)
+ device = attr->values[0].string.text;
+
+ if (strcmp(attr->name, "printer-state") == 0 &&
+ attr->value_tag == IPP_TAG_ENUM)
+ pstate = (ipp_pstate_t)attr->values[0].integer;
+
+ if (strcmp(attr->name, "printer-is-accepting-jobs") == 0 &&
+ attr->value_tag == IPP_TAG_BOOLEAN)
+ accepting = attr->values[0].boolean;
+
+ attr = attr->next;
+ }
+
+ /*
+ * See if we have everything needed...
+ */
+
+ if (printer == NULL)
+ {
+ if (attr == NULL)
+ break;
+ else
+ continue;
+ }
+
+ /*
+ * See if this is a printer we're interested in...
+ */
+
+ match = dests == NULL;
+
+ if (dests != NULL)
+ {
+ for (dptr = dests; *dptr != '\0';)
+ {
+ /*
+ * Skip leading whitespace and commas...
+ */
+
+ while (isspace(*dptr) || *dptr == ',')
+ dptr ++;
+
+ if (*dptr == '\0')
+ break;
+
+ /*
+ * Compare names...
+ */
+
+ for (ptr = printer;
+ *ptr != '\0' && *dptr != '\0' && *ptr == *dptr;
+ ptr ++, dptr ++);
+
+ if (*ptr == '\0' && (*dptr == '\0' || *dptr == ',' || isspace(*dptr)))
+ {
+ match = 1;
+ break;
+ }
+
+ /*
+ * Skip trailing junk...
+ */
+
+ while (!isspace(*dptr) && *dptr != '\0')
+ dptr ++;
+ while (isspace(*dptr) || *dptr == ',')
+ dptr ++;
+
+ if (*dptr == '\0')
+ break;
+ }
+ }
+
+ /*
+ * Display the printer entry if needed...
+ */
+
+ if (match)
+ {
+ /*
+ * If the printer state is "IPP_PRINTER_PROCESSING", then grab the
+ * current job for the printer.
+ */
+
+ if (pstate == IPP_PRINTER_PROCESSING)
+ {
+ /*
+ * Build an IPP_GET_JOBS request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * printer-uri
+ * limit
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = IPP_GET_JOBS;
+ request->request.op.request_id = 1;
+
+ language = cupsLangDefault();
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL,
+ cupsLangEncoding(language));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL,
+ language->language);
+
+ sprintf(printer_uri, "ipp://localhost/printers/%s", printer);
+ attr = ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
+ "printer-uri", NULL, printer_uri);
+
+ if ((jobs = cupsDoRequest(http, request, "/jobs/")) != NULL)
+ {
+ for (attr = jobs->attrs; attr != NULL; attr = attr->next)
+ if (strcmp(attr->name, "job-id") == 0)
+ jobcount ++;
+
+ ippDelete(jobs);
+ }
+ }
+
+ /*
+ * Display it...
+ */
+
+ printf("%s:\n", printer);
+ if (strncmp(device, "file:", 5) == 0)
+ printf("\tprinter is on device \'%s\' speed -1\n", device + 5);
+ else
+ printf("\tprinter is on device \'%s\' speed -1\n", device);
+ printf("\tqueuing is %sabled\n", accepting ? "en" : "dis");
+ printf("\tprinting is %sabled\n",
+ pstate == IPP_PRINTER_STOPPED ? "dis" : "en");
+ if (jobcount == 0)
+ puts("\tno entries");
+ else
+ printf("\t%d entries\n", jobcount);
+ puts("\tdaemon present");
+ }
+
+ if (attr == NULL)
+ break;
+ }
+
+ ippDelete(response);
+ }
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/berkeley/lpq.c b/berkeley/lpq.c
new file mode 100644
index 000000000..55bf7ed6a
--- /dev/null
+++ b/berkeley/lpq.c
@@ -0,0 +1,369 @@
+/*
+ * "$Id$"
+ *
+ * "lpq" command for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-1999 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * Contents:
+ *
+ * main() - Parse options and commands.
+ * show_jobs() - Show jobs.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <cups/cups.h>
+#include <cups/language.h>
+#include <cups/debug.h>
+
+
+/*
+ * Local functions...
+ */
+
+static int show_jobs(http_t *, const char *, const char *, const int,
+ const int);
+
+
+/*
+ * 'main()' - Parse options and commands.
+ */
+
+int
+main(int argc, /* I - Number of command-line arguments */
+ char *argv[]) /* I - Command-line arguments */
+{
+ int i; /* Looping var */
+ http_t *http; /* Connection to server */
+ const char *dest, /* Desired printer */
+ *user; /* Desired user */
+ int id, /* Desired job ID */
+ interval, /* Reporting interval */
+ longstatus; /* Show file details */
+
+ /*
+ * Connect to the scheduler...
+ */
+
+ http = httpConnect(cupsServer(), ippPort());
+
+ /*
+ * Check for command-line options...
+ */
+
+ dest = cupsGetDefault();
+ user = NULL;
+ id = 0;
+ interval = 0;
+ longstatus = 0;
+
+ for (i = 1; i < argc; i ++)
+ if (argv[i][0] == '+')
+ interval = atoi(argv[i] + 1);
+ else if (argv[i][0] == '-')
+ {
+ switch (argv[i][1])
+ {
+ case 'P' : /* Printer */
+ if (argv[i][2])
+ dest = argv[i] + 2;
+ else
+ {
+ i ++;
+ dest = argv[i];
+ }
+ break;
+
+ case 'l' : /* Long status */
+ longstatus = 1;
+ break;
+
+ default :
+ fputs("Usage: lpq [-P dest] [-l] [+interval]\n", stderr);
+ return (1);
+ }
+ }
+ else if (isdigit(argv[i][0]))
+ id = atoi(argv[i]);
+ else
+ user = argv[i];
+
+ /*
+ * Show the status in a loop...
+ */
+
+ for (;;)
+ {
+ i = show_jobs(http, dest, user, id, longstatus);
+
+ if (i && interval)
+ sleep(interval);
+ else
+ break;
+ }
+
+ /*
+ * Close the connection to the server and return...
+ */
+
+ httpClose(http);
+
+ return (0);
+}
+
+
+/*
+ * 'show_jobs()' - Show jobs.
+ */
+
+static int /* O - Number of jobs in queue */
+show_jobs(http_t *http, /* I - HTTP connection to server */
+ const char *dest, /* I - Destination */
+ const char *user, /* I - User */
+ const int id, /* I - Job ID */
+ const int longstatus)/* I - 1 if long report desired */
+{
+ ipp_t *request, /* IPP Request */
+ *response; /* IPP Response */
+ ipp_attribute_t *attr; /* Current attribute */
+ cups_lang_t *language; /* Default language */
+ const char *jobdest, /* Pointer into job-printer-uri */
+ *jobuser, /* Pointer to job-originating-user-name */
+ *jobname; /* Pointer to job-name */
+ ipp_jstate_t jobstate; /* job-state */
+ int jobid, /* job-id */
+ jobsize, /* job-k-octets */
+ jobpriority, /* job-priority */
+ jobcount, /* Number of jobs */
+ rank; /* Rank of job */
+ char resource[1024]; /* Resource string */
+ static const char *ranks[10] =/* Ranking strings */
+ {
+ "th",
+ "st",
+ "nd",
+ "rd",
+ "th",
+ "th",
+ "th",
+ "th",
+ "th",
+ "th"
+ };
+
+
+ DEBUG_printf(("show_jobs(%08x, %08x, %08x, %d, %d)\n", http, dest, user, id,
+ longstatus));
+
+ if (http == NULL)
+ return (0);
+
+ /*
+ * Build an IPP_GET_JOBS or IPP_GET_JOB_ATTRIBUTES request, which requires
+ * the following attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * job-uri or printer-uri
+ * [
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = id ? IPP_GET_JOB_ATTRIBUTES : IPP_GET_JOBS;
+ request->request.op.request_id = 1;
+
+ language = cupsLangDefault();
+
+ attr = ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, cupsLangEncoding(language));
+
+ attr = ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ if (dest == NULL)
+ {
+ if (id)
+ sprintf(resource, "ipp://localhost/jobs/%d", id);
+ else
+ strcpy(resource, "ipp://localhost/jobs");
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri",
+ NULL, resource);
+ }
+ else
+ {
+ sprintf(resource, "ipp://localhost/printers/%s", dest);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri",
+ NULL, resource);
+ }
+
+ if (user)
+ {
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
+ "requesting-user-name", NULL, user);
+ ippAddBoolean(request, IPP_TAG_OPERATION, "my-jobs", 1);
+ }
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ if (!longstatus)
+ puts("Rank\tPri Owner Job Files Total Size");
+
+ jobcount = 0;
+
+ if ((response = cupsDoRequest(http, request, "/jobs/")) != NULL)
+ {
+ rank = 1;
+
+ /*
+ * Loop through the job list and display them...
+ */
+
+ for (attr = response->attrs; attr != NULL; attr = attr->next)
+ {
+ /*
+ * Skip leading attributes until we hit a job...
+ */
+
+ while (attr != NULL && attr->group_tag != IPP_TAG_JOB)
+ attr = attr->next;
+
+ if (attr == NULL)
+ break;
+
+ /*
+ * Pull the needed attributes from this job...
+ */
+
+ jobid = 0;
+ jobsize = 0;
+ jobpriority = 50;
+ jobstate = IPP_JOB_PENDING;
+ jobname = "untitled";
+ jobuser = NULL;
+ jobdest = NULL;
+
+ while (attr != NULL && attr->group_tag == IPP_TAG_JOB)
+ {
+ if (strcmp(attr->name, "job-id") == 0 &&
+ attr->value_tag == IPP_TAG_INTEGER)
+ jobid = attr->values[0].integer;
+
+ if (strcmp(attr->name, "job-k-octets") == 0 &&
+ attr->value_tag == IPP_TAG_INTEGER)
+ jobsize = attr->values[0].integer * 1024;
+
+ if (strcmp(attr->name, "job-priority") == 0 &&
+ attr->value_tag == IPP_TAG_INTEGER)
+ jobpriority = attr->values[0].integer;
+
+ if (strcmp(attr->name, "job-state") == 0 &&
+ attr->value_tag == IPP_TAG_ENUM)
+ jobstate = (ipp_jstate_t)attr->values[0].integer;
+
+ if (strcmp(attr->name, "job-printer-uri") == 0 &&
+ attr->value_tag == IPP_TAG_URI)
+ if ((jobdest = strrchr(attr->values[0].string.text, '/')) != NULL)
+ jobdest ++;
+
+ if (strcmp(attr->name, "job-originating-user-name") == 0 &&
+ attr->value_tag == IPP_TAG_NAME)
+ jobuser = attr->values[0].string.text;
+
+ if (strcmp(attr->name, "job-name") == 0 &&
+ attr->value_tag == IPP_TAG_NAME)
+ jobname = attr->values[0].string.text;
+
+ attr = attr->next;
+ }
+
+ /*
+ * See if we have everything needed...
+ */
+
+ if (jobdest == NULL || jobid == 0)
+ {
+ if (attr == NULL)
+ break;
+ else
+ continue;
+ }
+
+ jobcount ++;
+
+ /*
+ * Display the job...
+ */
+
+ if (longstatus)
+ {
+ puts("");
+
+ if (jobstate == IPP_JOB_PROCESSING)
+ printf("%s: active\t\t\t\t ", jobuser);
+ else
+ {
+ printf("%s: %d%s\t\t\t\t ", jobuser, rank, ranks[rank % 10]);
+ rank ++;
+ }
+
+ printf("[job %03dlocalhost]\n", jobid);
+ printf("\t%-33s%d bytes\n", jobname, jobsize);
+ }
+ else
+ {
+ if (jobstate == IPP_JOB_PROCESSING)
+ printf("active\t");
+ else
+ {
+ printf("%d%s\t", rank, ranks[rank % 10]);
+ rank ++;
+ }
+
+ printf(" %-5d%-7.7s%-7d%-19s%d bytes\n", jobpriority, jobuser, jobid,
+ jobname, jobsize);
+ }
+ if (attr == NULL)
+ break;
+ }
+
+ ippDelete(response);
+ }
+
+ return (jobcount);
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/berkeley/lpr.c b/berkeley/lpr.c
new file mode 100644
index 000000000..f209efee6
--- /dev/null
+++ b/berkeley/lpr.c
@@ -0,0 +1,252 @@
+/*
+ * "$Id$"
+ *
+ * "lpr" command for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-1999 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * Contents:
+ *
+ * main() - Parse options and send files for printing.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <cups/cups.h>
+
+
+/*
+ * 'main()' - Parse options and send files for printing.
+ */
+
+int
+main(int argc, /* I - Number of command-line arguments */
+ char *argv[]) /* I - Command-line arguments */
+{
+ int i; /* Looping var */
+ int job_id; /* Job ID */
+ const char *dest; /* Destination printer */
+ const char *title; /* Job title */
+ int priority; /* Job priority (1-100) */
+ int num_copies; /* Number of copies per file */
+ int num_files; /* Number of files printed */
+ int num_options; /* Number of options */
+ cups_option_t *options; /* Options */
+ int silent, /* Silent or verbose output? */
+ deletefile; /* Delete file after print? */
+ char tempfile[1024]; /* Temporary file for printing from stdin */
+ char buffer[8192]; /* Copy buffer */
+ FILE *temp; /* Temporary file pointer */
+
+
+ silent = 0;
+ deletefile = 0;
+ dest = cupsGetDefault();
+ num_options = 0;
+ options = NULL;
+ num_files = 0;
+ title = NULL;
+
+ for (i = 1; i < argc; i ++)
+ if (argv[i][0] == '-')
+ switch (argv[i][1])
+ {
+ case 'i' : /* indent */
+ case 'w' : /* width */
+ if (argv[i][2] == '\0')
+ i ++;
+ case 'c' : /* CIFPLOT */
+ case 'd' : /* DVI */
+ case 'f' : /* FORTRAN */
+ case 'g' : /* plot */
+ case 'n' : /* Ditroff */
+ case 't' : /* Troff */
+ case 'v' : /* Raster image */
+ fprintf(stderr, "Warning: \'%c\' format modifier not supported - output may not be correct!\n",
+ argv[i][1]);
+ break;
+
+ case 'o' : /* Option */
+ if (argv[i][2] != '\0')
+ num_options = cupsParseOptions(argv[i] + 2, num_options, &options);
+ else
+ {
+ i ++;
+ num_options = cupsParseOptions(argv[i], num_options, &options);
+ }
+ break;
+
+ case 'l' : /* Literal/raw */
+ num_options = cupsParseOptions("raw", num_options, &options);
+ break;
+
+ case 'p' : /* Prettyprint */
+ num_options = cupsParseOptions("prettyprint", num_options, &options);
+ break;
+
+ case 'h' : /* Suppress burst page */
+ case 's' : /* Don't use symlinks */
+ break;
+
+ case 'm' : /* Mail on completion */
+ fputs("Warning: email notification is not supported!\n", stderr);
+ break;
+
+ case 'r' : /* Remove file after printing */
+ deletefile = 1;
+ break;
+
+ case 'P' : /* Destination printer or class */
+ if (argv[i][2] != '\0')
+ dest = argv[i] + 2;
+ else
+ {
+ i ++;
+ dest = argv[i];
+ }
+ break;
+
+ case '#' : /* Number of copies */
+ if (argv[i][2] != '\0')
+ num_copies = atoi(argv[i] + 2);
+ else
+ {
+ i ++;
+ num_copies = atoi(argv[i]);
+ }
+
+ if (num_copies < 1 || num_copies > 100)
+ {
+ fputs("lpr: Number copies must be between 1 and 100.\n", stderr);
+ return (1);
+ }
+
+ sprintf(buffer, "%d", num_copies);
+ num_options = cupsAddOption("copies", buffer, num_options, &options);
+ break;
+
+ case 'C' : /* Class */
+ case 'J' : /* Job name */
+ case 'T' : /* Title */
+ if (argv[i][2] != '\0')
+ title = argv[i] + 2;
+ else
+ {
+ i ++;
+ title = argv[i];
+ }
+ break;
+
+ default :
+ fprintf(stderr, "lpr: Unknown option \'%c\'!\n", argv[i][1]);
+ return (1);
+ }
+ else
+ {
+ /*
+ * Print a file...
+ */
+
+ if (dest == NULL)
+ {
+ fputs("lpr: error - no default destination available.\n", stderr);
+ return (1);
+ }
+
+ num_files ++;
+ if (title)
+ job_id = cupsPrintFile(dest, argv[i], title, num_options, options);
+ else
+ {
+ char *filename;
+
+ if ((filename = strrchr(argv[i], '/')) != NULL)
+ filename ++;
+ else
+ filename = argv[i];
+
+ job_id = cupsPrintFile(dest, argv[i], filename, num_options, options);
+ }
+
+ if (job_id < 1)
+ {
+ fprintf(stderr, "lpr: unable to print file \'%s\'.\n", argv[i]);
+ return (1);
+ }
+ else if (deletefile)
+ unlink(argv[i]);
+ }
+
+ /*
+ * See if we printed anything; if not, print from stdin...
+ */
+
+ if (num_files == 0)
+ {
+ if (dest == NULL)
+ {
+ fputs("lpr: error - no default destination available.\n", stderr);
+ return (1);
+ }
+
+ temp = fopen(cupsTempFile(tempfile, sizeof(tempfile)), "w");
+
+ if (temp == NULL)
+ {
+ fputs("lpr: unable to create temporary file.\n", stderr);
+ return (1);
+ }
+
+ while ((i = fread(buffer, 1, sizeof(buffer), stdin)) > 0)
+ fwrite(buffer, 1, i, temp);
+
+ i = ftell(temp);
+ fclose(temp);
+
+ if (i == 0)
+ {
+ fputs("lpr: standard input is empty, so no job has been sent.\n", stderr);
+ return (1);
+ }
+
+ if (title)
+ job_id = cupsPrintFile(dest, tempfile, title, num_options, options);
+ else
+ job_id = cupsPrintFile(dest, tempfile, "(stdin)", num_options, options);
+
+ unlink(tempfile);
+
+ if (job_id < 1)
+ {
+ fputs("lpr: unable to print standard input.\n", stderr);
+ return (1);
+ }
+ }
+
+ return (0);
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/berkeley/lprm.c b/berkeley/lprm.c
new file mode 100644
index 000000000..2df9530c6
--- /dev/null
+++ b/berkeley/lprm.c
@@ -0,0 +1,205 @@
+/*
+ * "$Id$"
+ *
+ * "lprm" command for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-1999 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * Contents:
+ *
+ * main() - Parse options and cancel jobs.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#include <cups/cups.h>
+#include <cups/language.h>
+
+
+/*
+ * 'main()' - Parse options and cancel jobs.
+ */
+
+int /* O - Exit status */
+main(int argc, /* I - Number of command-line arguments */
+ char *argv[]) /* I - Command-line arguments */
+{
+ http_t *http; /* HTTP connection to server */
+ int i; /* Looping var */
+ int job_id; /* Job ID */
+ const char *dest; /* Destination printer */
+ char uri[1024]; /* Printer or job URI */
+ ipp_t *request; /* IPP request */
+ ipp_t *response; /* IPP response */
+ ipp_op_t op; /* Operation */
+ cups_lang_t *language; /* Language */
+
+
+ /*
+ * Setup to cancel individual print jobs...
+ */
+
+ op = IPP_CANCEL_JOB;
+ job_id = 0;
+ dest = cupsGetDefault();
+ response = NULL;
+
+ /*
+ * Open a connection to the server...
+ */
+
+ if ((http = httpConnect(cupsServer(), ippPort())) == NULL)
+ {
+ fputs("lprm: Unable to contact server!\n", stderr);
+ return (1);
+ }
+
+ /*
+ * Process command-line arguments...
+ */
+
+ for (i = 1; i < argc; i ++)
+ if (argv[i][0] == '-' && argv[i][1] != '\0')
+ switch (argv[i][1])
+ {
+ case 'P' : /* Cancel jobs on a printer */
+ if (argv[i][2])
+ dest = argv[i] + 2;
+ else
+ {
+ i ++;
+ dest = argv[i];
+ }
+ break;
+
+ default :
+ fprintf(stderr, "lprm: Unknown option \'%c\'!\n", argv[i][1]);
+ return (1);
+ }
+ else
+ {
+ /*
+ * Cancel a job or printer...
+ */
+
+ if (isdigit(argv[i][0]))
+ {
+ dest = NULL;
+ op = IPP_CANCEL_JOB;
+ job_id = atoi(argv[i]);
+ }
+ else if (strcmp(argv[i], "-") == 0)
+ {
+ /*
+ * Cancel all jobs
+ */
+
+ op = IPP_PURGE_JOBS;
+ }
+ else
+ job_id = 0;
+
+ /*
+ * Build an IPP request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * printer-uri + job-id *or* job-uri
+ * [requesting-user-name]
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = op;
+ request->request.op.request_id = 1;
+
+ language = cupsLangDefault();
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, cupsLangEncoding(language));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ if (dest)
+ {
+ sprintf(uri, "ipp://localhost/printers/%s", dest);
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
+ "printer-uri", NULL, uri);
+ ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id",
+ job_id);
+ }
+ else
+ {
+ sprintf(uri, "ipp://localhost/jobs/%d", job_id);
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", NULL,
+ uri);
+ }
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ if (op == IPP_PURGE_JOBS)
+ response = cupsDoRequest(http, request, "/admin/");
+ else
+ response = cupsDoRequest(http, request, "/jobs/");
+
+ if (response != NULL)
+ {
+ if (response->request.status.status_code == IPP_NOT_FOUND)
+ fputs("lprm: Job or printer not found!\n", stderr);
+ else if (response->request.status.status_code > IPP_OK_CONFLICT)
+ fputs("lprm: Unable to cancel job(s)!\n", stderr);
+
+ ippDelete(response);
+ }
+ else
+ {
+ fputs("lprm: Unable to cancel job(s)!\n", stderr);
+ return (1);
+ }
+ }
+
+ /*
+ * If nothing has been cancelled yet, cancel the current job on the specified
+ * (or default) printer...
+ */
+
+ if (response == NULL)
+ if (!cupsCancelJob(dest, 0))
+ {
+ fputs("lprm: Unable to cancel job(s)!\n", stderr);
+ return (1);
+ }
+
+ return (0);
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/cgi-bin/Makefile b/cgi-bin/Makefile
new file mode 100644
index 000000000..feffe13c7
--- /dev/null
+++ b/cgi-bin/Makefile
@@ -0,0 +1,83 @@
+#
+# "$Id$"
+#
+# CGI makefile for the Common UNIX Printing System (CUPS).
+#
+# Copyright 1997-1999 by Easy Software Products.
+#
+# These coded instructions, statements, and computer programs are the
+# property of Easy Software Products and are protected by Federal
+# copyright law. Distribution and use rights are outlined in the file
+# "LICENSE.txt" which should have been included with this file. If this
+# file is missing or damaged please contact Easy Software Products
+# at:
+#
+# Attn: CUPS Licensing Information
+# Easy Software Products
+# 44141 Airport View Drive, Suite 204
+# Hollywood, Maryland 20636-3111 USA
+#
+# Voice: (301) 373-9603
+# EMail: cups-info@cups.org
+# WWW: http://www.cups.org
+#
+
+include ../Makedefs
+
+TARGETS = classes.cgi jobs.cgi printers.cgi
+OBJS = classes.o jobs.o printers.o
+
+#
+# Make all targets...
+#
+
+all: $(TARGETS)
+
+#
+# Clean all object files...
+#
+
+clean:
+ rm -f $(OBJS) $(TARGETS)
+
+#
+# Install all targets...
+#
+
+install:
+ -$(MKDIR) $(SERVERROOT)/cgi-bin
+ $(CP) $(TARGETS) $(SERVERROOT)/cgi-bin
+
+#
+# classes.cgi
+#
+
+classes.cgi: classes.o ../Makedefs ../cups/$(LIBCUPS)
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o $@ classes.o $(LIBS)
+
+$(OBJS): ../Makedefs ../cups/cups.h ../cups/ipp.h ../cups/language.h
+
+#
+# jobs.cgi
+#
+
+jobs.cgi: jobs.o ../Makedefs ../cups/$(LIBCUPS)
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o $@ jobs.o $(LIBS)
+
+$(OBJS): ../Makedefs ../cups/cups.h ../cups/ipp.h ../cups/language.h
+
+#
+# printers.cgi
+#
+
+printers.cgi: printers.o ../Makedefs ../cups/$(LIBCUPS)
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o $@ printers.o $(LIBS)
+
+$(OBJS): ../Makedefs ../cups/cups.h ../cups/ipp.h ../cups/language.h
+
+#
+# End of "$Id$".
+#
diff --git a/cgi-bin/classes.c b/cgi-bin/classes.c
new file mode 100644
index 000000000..53a39e064
--- /dev/null
+++ b/cgi-bin/classes.c
@@ -0,0 +1,484 @@
+/*
+ * "$Id$"
+ *
+ * Class status CGI for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-1999 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * Contents:
+ *
+ * main() - Main entry for CGI.
+ * show_class_list() - Show a list of classes...
+ * show_class_info() - Show class information.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <cups/cups.h>
+#include <cups/language.h>
+#include <cups/debug.h>
+
+
+/*
+ * Local functions...
+ */
+
+static void show_class_list(http_t *http, cups_lang_t *language);
+static void show_class_info(http_t *http, cups_lang_t *language,
+ char *name);
+
+
+/*
+ * 'main()' - Main entry for CGI.
+ */
+
+int /* O - Exit status */
+main(int argc, /* I - Number of command-line arguments */
+ char *argv[]) /* I - Command-line arguments */
+{
+ cups_lang_t *language; /* Language information */
+ char *name; /* Class name */
+ http_t *http; /* Connection to the server */
+
+
+ /*
+ * Get the request language...
+ */
+
+ language = cupsLangDefault();
+
+ /*
+ * Connect to the HTTP server...
+ */
+
+ http = httpConnect("localhost", ippPort());
+
+ /*
+ * Tell the client to expect HTML...
+ */
+
+ printf("Content-Type: text/html;charset=%s\n\n", cupsLangEncoding(language));
+
+ /*
+ * See if we need to show a list of classes or the status of a
+ * single class...
+ */
+
+ name = argv[0];
+ if (strcmp(name, "/") == 0 || strcmp(name, "classes.cgi") == 0)
+ name = NULL;
+
+ /*
+ * Print the standard header...
+ */
+
+ puts("<HTML>");
+ puts("<HEAD>");
+ if (name)
+ puts("<META HTTP-EQUIV=\"Refresh\" CONTENT=\"10\">");
+ else
+ puts("<META HTTP-EQUIV=\"Refresh\" CONTENT=\"30\">");
+ printf("<TITLE>%s on %s - Common UNIX Printing System</TITLE>\n",
+ name == NULL ? "Classes" : name, getenv("SERVER_NAME"));
+ puts("<LINK REL=STYLESHEET TYPE=\"text/css\" HREF=\"/cups.css\">");
+ puts("<MAP NAME=\"navbar\">");
+ puts("<AREA SHAPE=\"RECT\" COORDS=\"10,10,85,30\" HREF=\"/printers\" ALT=\"Current Printer Status\">");
+ puts("<AREA SHAPE=\"RECT\" COORDS=\"95,10,175,30\" HREF=\"/classes\" ALT=\"Current Printer Classes Status\">");
+ puts("<AREA SHAPE=\"RECT\" COORDS=\"185,10,235,30\" HREF=\"/jobs\" ALT=\"Current Jobs Status\">");
+ puts("<AREA SHAPE=\"RECT\" COORDS=\"245,10,395,30\" HREF=\"/documentation.html\" ALT=\"Read CUPS Documentation On-Line\">");
+#ifdef ESPPRINTPRO
+ puts("<AREA SHAPE=\"RECT\" COORDS=\"405,10,490,30\" HREF=\"http://www.easysw.com/printpro/software.html\" ALT=\"Download the Current ESP Print Pro Software\">");
+ puts("<AREA SHAPE=\"RECT\" COORDS=\"505,10,585,30\" HREF=\"http://www.easysw.com/printpro/support.html\" ALT=\"Get Tech Support for Current ESP Print Pro\">");
+#else
+ puts("<AREA SHAPE=\"RECT\" COORDS=\"405,10,490,30\" HREF=\"http://www.cups.org\" ALT=\"Download the Current CUPS Software\">");
+#endif /* ESPPRINTPRO */
+ puts("</MAP>");
+ puts("</HEAD>");
+ puts("<BODY>");
+ puts("<P ALIGN=CENTER>");
+ puts("<A HREF=\"http://www.easysw.com\" ALT=\"Easy Software Products Home Page\">");
+ puts("<IMG SRC=\"/images/logo.gif\" WIDTH=\"71\" HEIGHT=\"40\" BORDER=0 ALT=\"Easy Software Products Home Page\"></A>");
+#ifdef ESPPRINTPRO
+ puts("<IMG SRC=\"/images/navbar.gif\" WIDTH=\"600\" HEIGHT=\"40\" USEMAP=\"#navbar\" BORDER=0>");
+#else
+ puts("<IMG SRC=\"/images/navbar.gif\" WIDTH=\"540\" HEIGHT=\"40\" USEMAP=\"#navbar\" BORDER=0>");
+#endif /* ESPPRINTPRO */
+
+ printf("<H1>%s on %s</H1>\n", name == NULL ? "Classes" : name,
+ getenv("SERVER_NAME"));
+ fflush(stdout);
+
+ puts("<CENTER>");
+ puts("<TABLE WIDTH=\"90%\" BORDER=\"1\">");
+ puts("<TR>");
+ puts("<TH>Name</TH>");
+ puts("<TH WIDTH=\"50%\">Status</TH>");
+ puts("<TH WIDTH=\"25%\">Jobs</TH>");
+ puts("</TR>");
+
+ /*
+ * Show the information...
+ */
+
+ if (name == NULL)
+ show_class_list(http, language);
+ else
+ show_class_info(http, language, name);
+
+ /*
+ * Write a standard trailer...
+ */
+
+ puts("</TABLE>");
+ puts("</CENTER>");
+
+ puts("<HR>");
+
+ puts("<P>The Common UNIX Printing System, CUPS, and the CUPS logo are the");
+ puts("trademark property of <A HREF=\"http://www.easysw.com\">Easy Software");
+ puts("Products</A>. CUPS is copyright 1997-1999 by Easy Software Products,");
+ puts("All Rights Reserved.");
+
+ puts("</BODY>");
+ puts("</HTML>");
+
+ /*
+ * Close the HTTP server connection...
+ */
+
+ httpClose(http);
+ cupsLangFree(language);
+
+ /*
+ * Return with no errors...
+ */
+
+ return (0);
+}
+
+
+/*
+ * 'show_class_list()' - Show a list of classes...
+ */
+
+static void
+show_class_list(http_t *http, /* I - HTTP connection */
+ cups_lang_t *language)/* I - Client's language */
+{
+ ipp_t *request, /* IPP request */
+ *response; /* IPP response */
+ ipp_attribute_t *attr; /* IPP attribute */
+
+
+ /*
+ * Build a CUPS_GET_CLASSES request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = CUPS_GET_CLASSES;
+ request->request.op.request_id = 1;
+
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, cupsLangEncoding(language));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ if ((response = cupsDoRequest(http, request, "/classes/")) != NULL)
+ {
+ /*
+ * Loop through the classes returned in the list and display
+ * their devices...
+ */
+
+ for (attr = response->attrs; attr != NULL; attr = attr->next)
+ {
+ /*
+ * Skip leading attributes until we hit a job...
+ */
+
+ while (attr != NULL && attr->group_tag != IPP_TAG_PRINTER)
+ attr = attr->next;
+
+ if (attr == NULL)
+ break;
+
+ /*
+ * Show the class status for each class...
+ */
+
+ while (attr != NULL && attr->group_tag == IPP_TAG_PRINTER)
+ {
+ if (strcmp(attr->name, "printer-name") == 0 &&
+ attr->value_tag == IPP_TAG_NAME)
+ show_class_info(http, language, attr->values[0].string.text);
+
+ attr = attr->next;
+ }
+
+ if (attr == NULL)
+ break;
+ }
+
+ ippDelete(response);
+ }
+}
+
+
+/*
+ * 'show_class_info()' - Show class information.
+ */
+
+static void
+show_class_info(http_t *http,
+ cups_lang_t *language,
+ char *name)
+{
+ ipp_t *request, /* IPP request */
+ *response, /* IPP response */
+ *jobs; /* IPP Get Jobs response */
+ int jobcount; /* Number of jobs */
+ ipp_attribute_t *attr; /* IPP attribute */
+ char *message; /* Printer state message */
+ int accepting; /* Accepting requests? */
+ ipp_pstate_t pstate; /* Printer state */
+ char uri[HTTP_MAX_URI];/* Printer URI */
+
+
+ /*
+ * Build a IPP_GET_PRINTER_ATTRIBUTES request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * printer-uri
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = IPP_GET_PRINTER_ATTRIBUTES;
+ request->request.op.request_id = 1;
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, cupsLangEncoding(language));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ sprintf(uri, "ipp://localhost/classes/%s", name);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri);
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ if ((response = cupsDoRequest(http, request, uri + 15)) == NULL)
+ {
+ puts("<P>Unable to communicate with CUPS server!");
+ return;
+ }
+
+ if (response->request.status.status_code == IPP_NOT_FOUND)
+ {
+ puts("<P>Class does not exist.");
+ ippDelete(response);
+ return;
+ }
+
+ /*
+ * Grab the needed class attributes...
+ */
+
+ if ((attr = ippFindAttribute(response, "printer-state", IPP_TAG_ENUM)) != NULL)
+ pstate = (ipp_pstate_t)attr->values[0].integer;
+ else
+ pstate = IPP_PRINTER_IDLE;
+
+ if ((attr = ippFindAttribute(response, "printer-state-message", IPP_TAG_TEXT)) != NULL)
+ message = attr->values[0].string.text;
+ else
+ message = NULL;
+
+ if ((attr = ippFindAttribute(response, "printer-is-accepting-jobs",
+ IPP_TAG_BOOLEAN)) != NULL)
+ accepting = attr->values[0].boolean;
+ else
+ accepting = 1;
+
+ if ((attr = ippFindAttribute(response, "printer-uri-supported", IPP_TAG_URI)) != NULL)
+ {
+ strcpy(uri, "http:");
+ strcpy(uri + 5, strchr(attr->values[0].string.text, '/'));
+ }
+
+ /*
+ * Display the class entry...
+ */
+
+ puts("<TR>");
+
+ printf("<TD VALIGN=TOP><A HREF=\"%s\">%s</A></TD>\n", uri, name);
+
+ puts("<TD VALIGN=TOP><IMG SRC=\"/images/classes.gif\" ALIGN=\"LEFT\">");
+
+ printf("%s: %s, %s<BR>\n",
+ cupsLangString(language, CUPS_MSG_PRINTER_STATE),
+ cupsLangString(language, pstate == IPP_PRINTER_IDLE ? CUPS_MSG_IDLE :
+ pstate == IPP_PRINTER_PROCESSING ?
+ CUPS_MSG_PROCESSING : CUPS_MSG_STOPPED),
+ cupsLangString(language, accepting ? CUPS_MSG_ACCEPTING_JOBS :
+ CUPS_MSG_NOT_ACCEPTING_JOBS));
+
+ if (message)
+ printf("<BR CLEAR=ALL><I>\"%s\"</I>\n", message);
+ else if (!accepting || pstate == IPP_PRINTER_STOPPED)
+ puts("<BR CLEAR=ALL><I>\"Reason Unknown\"</I>");
+
+ puts("</TD>");
+
+ /*
+ * Show a list of jobs as needed...
+ */
+
+ if (pstate != IPP_PRINTER_IDLE)
+ {
+ /*
+ * Build an IPP_GET_JOBS request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * printer-uri
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = IPP_GET_JOBS;
+ request->request.op.request_id = 1;
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL,
+ cupsLangEncoding(language));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL,
+ language->language);
+
+ sprintf(uri, "ipp://localhost/printers/%s", name);
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
+ "printer-uri", NULL, uri);
+
+ jobs = cupsDoRequest(http, request, uri + 15);
+ }
+ else
+ jobs = NULL;
+
+ puts("<TD VALIGN=\"TOP\">");
+ jobcount = 0;
+
+ if (jobs != NULL)
+ {
+ char *username; /* Pointer to job-originating-user-name */
+ int jobid, /* job-id */
+ size; /* job-k-octets */
+
+
+ for (attr = jobs->attrs; attr != NULL; attr = attr->next)
+ {
+ /*
+ * Skip leading attributes until we hit a job...
+ */
+
+ while (attr != NULL && attr->group_tag != IPP_TAG_JOB)
+ attr = attr->next;
+
+ if (attr == NULL)
+ break;
+
+ /*
+ * Pull the needed attributes from this job...
+ */
+
+ jobid = 0;
+ size = 0;
+ username = NULL;
+
+ while (attr != NULL && attr->group_tag == IPP_TAG_JOB)
+ {
+ if (strcmp(attr->name, "job-id") == 0 &&
+ attr->value_tag == IPP_TAG_INTEGER)
+ jobid = attr->values[0].integer;
+
+ if (strcmp(attr->name, "job-k-octets") == 0 &&
+ attr->value_tag == IPP_TAG_INTEGER)
+ size = attr->values[0].integer;
+
+ if (strcmp(attr->name, "job-originating-user-name") == 0 &&
+ attr->value_tag == IPP_TAG_NAME)
+ username = attr->values[0].string.text;
+
+ attr = attr->next;
+ }
+
+ /*
+ * Display the job if it matches the current class...
+ */
+
+ if (username != NULL)
+ {
+ jobcount ++;
+ printf("<A HREF=\"/jobs/%d\">%s-%d %s %dk</A><BR>\n", jobid, name,
+ jobid, username, size);
+ }
+
+ if (attr == NULL)
+ break;
+ }
+
+ ippDelete(jobs);
+ }
+
+ if (jobcount == 0)
+ puts("None");
+ puts("</TD>");
+ puts("</TR>");
+
+ ippDelete(response);
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/cgi-bin/jobs.c b/cgi-bin/jobs.c
new file mode 100644
index 000000000..a0bb945bc
--- /dev/null
+++ b/cgi-bin/jobs.c
@@ -0,0 +1,584 @@
+/*
+ * "$Id$"
+ *
+ * Job status CGI for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-1999 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * Contents:
+ *
+ * main() - Main entry for CGI.
+ * show_job_list() - Show a list of jobs...
+ * show_job_info() - Show job information.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <cups/cups.h>
+#include <cups/language.h>
+#include <cups/debug.h>
+
+
+/*
+ * Local functions...
+ */
+
+static void show_job_list(http_t *http, cups_lang_t *language);
+static void show_job_info(http_t *http, cups_lang_t *language,
+ char *name);
+
+
+/*
+ * 'main()' - Main entry for CGI.
+ */
+
+int /* O - Exit status */
+main(int argc, /* I - Number of command-line arguments */
+ char *argv[]) /* I - Command-line arguments */
+{
+ cups_lang_t *language; /* Language information */
+ char *job; /* Job name */
+ http_t *http; /* Connection to the server */
+
+
+ /*
+ * Get the request language...
+ */
+
+ language = cupsLangDefault();
+
+ /*
+ * Connect to the HTTP server...
+ */
+
+ http = httpConnect("localhost", ippPort());
+
+ /*
+ * Tell the client to expect HTML...
+ */
+
+ printf("Content-Type: text/html;charset=%s\n\n", cupsLangEncoding(language));
+
+ /*
+ * See if we need to show a list of jobs or the status of a
+ * single job...
+ */
+
+ job = argv[0];
+ if (strcmp(job, "/") == 0 || strcmp(job, "jobs.cgi") == 0)
+ job = NULL;
+
+ /*
+ * Print the standard header...
+ */
+
+ puts("<HTML>");
+ puts("<HEAD>");
+ if (job)
+ puts("<META HTTP-EQUIV=\"Refresh\" CONTENT=\"10\">");
+ else
+ puts("<META HTTP-EQUIV=\"Refresh\" CONTENT=\"30\">");
+ printf("<TITLE>%s on %s - Common UNIX Printing System</TITLE>\n",
+ job == NULL ? "Jobs" : job, getenv("SERVER_NAME"));
+ puts("<LINK REL=STYLESHEET TYPE=\"text/css\" HREF=\"/cups.css\">");
+ puts("<MAP NAME=\"navbar\">");
+ puts("<AREA SHAPE=\"RECT\" COORDS=\"10,10,85,30\" HREF=\"/printers\" ALT=\"Current Printer Status\">");
+ puts("<AREA SHAPE=\"RECT\" COORDS=\"95,10,175,30\" HREF=\"/classes\" ALT=\"Current Printer Classes Status\">");
+ puts("<AREA SHAPE=\"RECT\" COORDS=\"185,10,235,30\" HREF=\"/jobs\" ALT=\"Current Jobs Status\">");
+ puts("<AREA SHAPE=\"RECT\" COORDS=\"245,10,395,30\" HREF=\"/documentation.html\" ALT=\"Read CUPS Documentation On-Line\">");
+#ifdef ESPPRINTPRO
+ puts("<AREA SHAPE=\"RECT\" COORDS=\"405,10,490,30\" HREF=\"http://www.easysw.com/printpro/software.html\" ALT=\"Download the Current ESP Print Pro Software\">");
+ puts("<AREA SHAPE=\"RECT\" COORDS=\"505,10,585,30\" HREF=\"http://www.easysw.com/printpro/support.html\" ALT=\"Get Tech Support for Current ESP Print Pro\">");
+#else
+ puts("<AREA SHAPE=\"RECT\" COORDS=\"405,10,490,30\" HREF=\"http://www.cups.org\" ALT=\"Download the Current CUPS Software\">");
+#endif /* ESPPRINTPRO */
+ puts("</MAP>");
+ puts("</HEAD>");
+ puts("<BODY>");
+ puts("<P ALIGN=CENTER>");
+ puts("<A HREF=\"http://www.easysw.com\" ALT=\"Easy Software Products Home Page\">");
+ puts("<IMG SRC=\"/images/logo.gif\" WIDTH=\"71\" HEIGHT=\"40\" BORDER=0 ALT=\"Easy Software Products Home Page\"></A>");
+#ifdef ESPPRINTPRO
+ puts("<IMG SRC=\"/images/navbar.gif\" WIDTH=\"600\" HEIGHT=\"40\" USEMAP=\"#navbar\" BORDER=0>");
+#else
+ puts("<IMG SRC=\"/images/navbar.gif\" WIDTH=\"540\" HEIGHT=\"40\" USEMAP=\"#navbar\" BORDER=0>");
+#endif /* ESPPRINTPRO */
+
+ fflush(stdout);
+
+ /*
+ * Show the information...
+ */
+
+ if (job == NULL)
+ show_job_list(http, language);
+ else
+ show_job_info(http, language, job);
+
+ /*
+ * Write a standard trailer...
+ */
+
+ puts("<HR>");
+
+ puts("<P>The Common UNIX Printing System, CUPS, and the CUPS logo are the");
+ puts("trademark property of <A HREF=\"http://www.easysw.com\">Easy Software");
+ puts("Products</A>. CUPS is copyright 1997-1999 by Easy Software Products,");
+ puts("All Rights Reserved.");
+
+ puts("</BODY>");
+ puts("</HTML>");
+
+ /*
+ * Close the HTTP server connection...
+ */
+
+ httpClose(http);
+ cupsLangFree(language);
+
+ /*
+ * Return with no errors...
+ */
+
+ return (0);
+}
+
+
+/*
+ * 'show_job_list()' - Show a list of jobs...
+ */
+
+static void
+show_job_list(http_t *http, /* I - HTTP connection */
+ cups_lang_t *language) /* I - Client's language */
+{
+ ipp_t *request, /* IPP request */
+ *response; /* IPP response */
+ ipp_attribute_t *attr; /* IPP attribute */
+ char *job_uri, /* job-uri */
+ *printer_uri, /* job-printer-uri */
+ *job_name, /* job-name */
+ *job_user; /* job-originating-user-name */
+ int job_id, /* job-id */
+ job_priority, /* job-priority */
+ job_k_octets, /* job-k-octets */
+ copies; /* copies */
+ ipp_jstate_t job_state; /* job-state */
+
+
+ printf("<H1>Jobs on %s</H1>\n", getenv("SERVER_NAME"));
+
+ /*
+ * Build an IPP_GET_JOBS request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * job-uri
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = IPP_GET_JOBS;
+ request->request.op.request_id = 1;
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, cupsLangEncoding(language));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri",
+ NULL, "ipp://localhost/jobs/");
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ if ((response = cupsDoRequest(http, request, "/jobs/")) != NULL)
+ {
+ /*
+ * Do a table for the jobs...
+ */
+
+ puts("<CENTER>");
+ puts("<TABLE WIDTH=\"90%\" BORDER=\"1\">");
+ puts("<TR>");
+ printf("<TH>%s</TH>\n", cupsLangString(language, CUPS_MSG_PRINT_JOBS));
+ printf("<TH>%s</TH>\n", cupsLangString(language, CUPS_MSG_JOB_STATE));
+ printf("<TH>%s</TH>\n", cupsLangString(language, CUPS_MSG_JOB_NAME));
+ printf("<TH>%s</TH>\n", cupsLangString(language, CUPS_MSG_USER_NAME));
+ printf("<TH>%s</TH>\n", cupsLangString(language, CUPS_MSG_PRIORITY));
+ printf("<TH>%s</TH>\n", cupsLangString(language, CUPS_MSG_COPIES));
+ printf("<TH>%s</TH>\n", cupsLangString(language, CUPS_MSG_FILE_SIZE));
+ puts("</TR>");
+
+ /*
+ * Loop through the jobs returned in the list and display
+ * their devices...
+ */
+
+ for (attr = response->attrs; attr != NULL; attr = attr->next)
+ {
+ /*
+ * Skip leading attributes until we hit a job...
+ */
+
+ while (attr != NULL && attr->group_tag != IPP_TAG_JOB)
+ attr = attr->next;
+
+ if (attr == NULL)
+ break;
+
+ /*
+ * Show the job status for each job...
+ */
+
+ job_uri = NULL;
+ printer_uri = NULL;
+ job_name = "unknown";
+ job_user = "unknown";
+ job_id = 0;
+ job_priority = 50;
+ job_k_octets = 0;
+ copies = 1;
+ job_state = IPP_JOB_PENDING;
+
+ while (attr != NULL && attr->group_tag == IPP_TAG_JOB)
+ {
+ if (strcmp(attr->name, "job-uri") == 0 &&
+ attr->value_tag == IPP_TAG_URI)
+ job_uri = attr->values[0].string.text;
+
+ if (strcmp(attr->name, "job-printer-uri") == 0 &&
+ attr->value_tag == IPP_TAG_URI)
+ printer_uri = attr->values[0].string.text;
+
+ if (strcmp(attr->name, "job-name") == 0 &&
+ attr->value_tag == IPP_TAG_NAME)
+ job_name = attr->values[0].string.text;
+
+ if (strcmp(attr->name, "job-originating-user-name") == 0 &&
+ attr->value_tag == IPP_TAG_NAME)
+ job_user = attr->values[0].string.text;
+
+ if (strcmp(attr->name, "job-id") == 0 &&
+ attr->value_tag == IPP_TAG_INTEGER)
+ job_id = attr->values[0].integer;
+
+ if (strcmp(attr->name, "job-priority") == 0 &&
+ attr->value_tag == IPP_TAG_INTEGER)
+ job_priority = attr->values[0].integer;
+
+ if (strcmp(attr->name, "job-k-octets") == 0 &&
+ attr->value_tag == IPP_TAG_INTEGER)
+ job_k_octets = attr->values[0].integer;
+
+ if (strcmp(attr->name, "copies") == 0 &&
+ attr->value_tag == IPP_TAG_INTEGER)
+ copies = attr->values[0].integer;
+
+ if (strcmp(attr->name, "job-state") == 0 &&
+ attr->value_tag == IPP_TAG_ENUM)
+ job_state = (ipp_jstate_t)attr->values[0].integer;
+
+ attr = attr->next;
+ }
+
+ /*
+ * See if we have everything needed...
+ */
+
+ if (job_id && job_uri != NULL && printer_uri != NULL)
+ {
+ puts("<TR>");
+ printf("<TD><A HREF=\"http://%s:%s/jobs/%d\">%s-%d</A></TD>\n",
+ getenv("SERVER_NAME"), getenv("SERVER_PORT"), job_id,
+ strrchr(printer_uri, '/') + 1, job_id);
+ printf("<TD>%s</TD>\n", job_state == IPP_JOB_PROCESSING ?
+ cupsLangString(language, CUPS_MSG_PROCESSING) :
+ cupsLangString(language, CUPS_MSG_PENDING));
+ printf("<TD>%s</TD>\n", job_name);
+ printf("<TD>%s</TD>\n", job_user);
+ printf("<TD>%d</TD>\n", job_priority);
+ printf("<TD>%d</TD>\n", copies);
+ printf("<TD>%dk</TD>\n", job_k_octets);
+ puts("</TR>");
+ }
+
+ if (attr == NULL)
+ break;
+ }
+
+ ippDelete(response);
+
+ puts("</TABLE>");
+ puts("</CENTER>");
+ }
+ else
+ puts("<P>No jobs found.");
+}
+
+
+/*
+ * 'show_job_info()' - Show job information.
+ */
+
+static void
+show_job_info(http_t *http, /* I - Server connection */
+ cups_lang_t *language, /* I - Language */
+ char *name) /* I - Job "name" */
+{
+ int i; /* Looping var */
+ ipp_t *request, /* IPP request */
+ *response; /* IPP response */
+ ipp_attribute_t *attr; /* IPP attribute */
+ char uri[HTTP_MAX_URI];/* Real URI */
+ char *job_uri, /* job-uri */
+ *printer_uri, /* job-printer-uri */
+ *job_name, /* job-name */
+ *job_user; /* job-originating-user-name */
+ int job_id, /* job-id */
+ job_priority, /* job-priority */
+ job_k_octets; /* job-k-octets */
+ ipp_jstate_t job_state; /* job-state */
+
+
+ /*
+ * Build an IPP_GET_JOB_ATTRIBUTES request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * job-uri
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = IPP_GET_JOB_ATTRIBUTES;
+ request->request.op.request_id = 1;
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, cupsLangEncoding(language));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ sprintf(uri, "ipp://localhost/jobs/%s", name);
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", NULL, uri);
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ if ((response = cupsDoRequest(http, request, uri + 15)) == NULL)
+ {
+ puts("<P>Unable to communicate with CUPS server!");
+ return;
+ }
+
+ if (response->request.status.status_code == IPP_NOT_FOUND)
+ {
+ puts("<P>Job does not exist or has completed.");
+ ippDelete(response);
+ return;
+ }
+
+ /*
+ * Get the job status for this job...
+ */
+
+ if ((attr = ippFindAttribute(response, "job-uri", IPP_TAG_URI)) != NULL)
+ job_uri = attr->values[0].string.text;
+ else
+ {
+ puts("<P>Missing job-uri attribute!");
+ ippDelete(request);
+ return;
+ }
+
+ if ((attr = ippFindAttribute(response, "job-printer-uri", IPP_TAG_URI)) != NULL)
+ printer_uri = attr->values[0].string.text;
+ else
+ {
+ puts("<P>Missing job-printer-uri attribute!");
+ ippDelete(request);
+ return;
+ }
+
+ if ((attr = ippFindAttribute(response, "job-name", IPP_TAG_NAME)) != NULL)
+ job_name = attr->values[0].string.text;
+ else
+ job_name = "unknown";
+
+ if ((attr = ippFindAttribute(response, "job-originating-user-name",
+ IPP_TAG_NAME)) != NULL)
+ job_user = attr->values[0].string.text;
+ else
+ job_user = "unknown";
+
+ if ((attr = ippFindAttribute(response, "job-id", IPP_TAG_INTEGER)) != NULL)
+ job_id = attr->values[0].integer;
+ else
+ {
+ puts("<P>Missing job-id attribute!");
+ ippDelete(request);
+ return;
+ }
+
+ if ((attr = ippFindAttribute(response, "job-priority", IPP_TAG_INTEGER)) != NULL)
+ job_priority = attr->values[0].integer;
+ else
+ job_priority = 50;
+
+ if ((attr = ippFindAttribute(response, "job-k-octets", IPP_TAG_INTEGER)) != NULL)
+ job_k_octets = attr->values[0].integer;
+ else
+ job_k_octets = 0;
+
+ if ((attr = ippFindAttribute(response, "job-state", IPP_TAG_ENUM)) != NULL)
+ job_state = (ipp_jstate_t)attr->values[0].integer;
+ else
+ job_state = IPP_JOB_PENDING;
+
+ /*
+ * Do a table for the job...
+ */
+
+ printf("<H1><A HREF=\"http://%s:%s/printers/%s\">%s-%d</A></H1>\n",
+ getenv("SERVER_NAME"), getenv("SERVER_PORT"),
+ strrchr(printer_uri, '/') + 1, strrchr(printer_uri, '/') + 1, job_id);
+
+ puts("<CENTER>");
+ puts("<TABLE WIDTH=\"90%\" BORDER=\"1\">");
+
+ puts("<TR>");
+ printf("<TH>%s</TH>\n", cupsLangString(language, CUPS_MSG_JOB_STATE));
+ printf("<TD>%s</TD>\n", job_state == IPP_JOB_PROCESSING ?
+ cupsLangString(language, CUPS_MSG_PROCESSING) :
+ cupsLangString(language, CUPS_MSG_PENDING));
+ puts("</TR>");
+
+ puts("<TR>");
+ printf("<TH>%s</TH>\n", cupsLangString(language, CUPS_MSG_JOB_NAME));
+ printf("<TD>%s</TD>\n", job_name);
+ puts("</TR>");
+
+ puts("<TR>");
+ printf("<TH>%s</TH>\n", cupsLangString(language, CUPS_MSG_USER_NAME));
+ printf("<TD>%s</TD>\n", job_user);
+ puts("</TR>");
+
+ puts("<TR>");
+ printf("<TH>%s</TH>\n", cupsLangString(language, CUPS_MSG_PRIORITY));
+ printf("<TD>%d</TD>\n", job_priority);
+ puts("</TR>");
+
+ puts("<TR>");
+ printf("<TH>%s</TH>\n", cupsLangString(language, CUPS_MSG_FILE_SIZE));
+ printf("<TD>%dk</TD>\n", job_k_octets);
+ puts("</TR>");
+
+ puts("<TR VALIGN=\"TOP\">");
+ printf("<TH>%s</TH>\n", cupsLangString(language, CUPS_MSG_OPTIONS));
+ puts("<TD>");
+
+ for (attr = response->attrs; attr != NULL; attr = attr->next)
+ {
+ if (attr->group_tag != IPP_TAG_JOB &&
+ attr->group_tag != IPP_TAG_EXTENSION)
+ continue;
+
+ if (strcmp(attr->name, "job-uri") == 0 ||
+ strcmp(attr->name, "job-printer-uri") == 0 ||
+ strcmp(attr->name, "job-name") == 0 ||
+ strcmp(attr->name, "job-originating-user-name") == 0 ||
+ strcmp(attr->name, "job-id") == 0 ||
+ strcmp(attr->name, "job-priority") == 0 ||
+ strcmp(attr->name, "job-k-octets") == 0 ||
+ strcmp(attr->name, "job-state") == 0)
+ continue;
+
+ if (attr->value_tag != IPP_TAG_BOOLEAN)
+ printf("%s=", attr->name);
+
+ for (i = 0; i < attr->num_values; i ++)
+ {
+ if (i)
+ putchar(',');
+
+ switch (attr->value_tag)
+ {
+ case IPP_TAG_INTEGER :
+ case IPP_TAG_ENUM :
+ printf("%d", attr->values[i].integer);
+ break;
+
+ case IPP_TAG_BOOLEAN :
+ if (!attr->values[i].boolean)
+ printf("no");
+
+ case IPP_TAG_NOVALUE :
+ fputs(attr->name, stdout);
+ break;
+
+ case IPP_TAG_RANGE :
+ printf("%d-%d", attr->values[i].range.lower,
+ attr->values[i].range.upper);
+ break;
+
+ case IPP_TAG_RESOLUTION :
+ printf("%dx%d%s", attr->values[i].resolution.xres,
+ attr->values[i].resolution.yres,
+ attr->values[i].resolution.units == IPP_RES_PER_INCH ?
+ "dpi" : "dpc");
+ break;
+
+ case IPP_TAG_STRING :
+ case IPP_TAG_TEXT :
+ case IPP_TAG_NAME :
+ case IPP_TAG_KEYWORD :
+ case IPP_TAG_CHARSET :
+ case IPP_TAG_LANGUAGE :
+ case IPP_TAG_MIMETYPE :
+ case IPP_TAG_URI :
+ printf("\"%s\"", attr->values[i].string.text);
+ break;
+ }
+ }
+
+ puts("<BR>");
+ }
+
+ puts("</TD>");
+ puts("</TR>");
+ puts("</TABLE></CENTER>");
+
+ ippDelete(response);
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/cgi-bin/printers.c b/cgi-bin/printers.c
new file mode 100644
index 000000000..7756161ed
--- /dev/null
+++ b/cgi-bin/printers.c
@@ -0,0 +1,486 @@
+/*
+ * "$Id$"
+ *
+ * Printer status CGI for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-1999 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * Contents:
+ *
+ * main() - Main entry for CGI.
+ * show_printer_list() - Show a list of printers...
+ * show_printer_info() - Show printer information.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <cups/cups.h>
+#include <cups/language.h>
+#include <cups/debug.h>
+
+
+/*
+ * Local functions...
+ */
+
+static void show_printer_list(http_t *http, cups_lang_t *language);
+static void show_printer_info(http_t *http, cups_lang_t *language,
+ char *name);
+
+
+/*
+ * 'main()' - Main entry for CGI.
+ */
+
+int /* O - Exit status */
+main(int argc, /* I - Number of command-line arguments */
+ char *argv[]) /* I - Command-line arguments */
+{
+ cups_lang_t *language; /* Language information */
+ char *printer; /* Printer name */
+ http_t *http; /* Connection to the server */
+
+
+ /*
+ * Get the request language...
+ */
+
+ language = cupsLangDefault();
+
+ /*
+ * Connect to the HTTP server...
+ */
+
+ http = httpConnect("localhost", ippPort());
+
+ /*
+ * Tell the client to expect HTML...
+ */
+
+ printf("Content-Type: text/html;charset=%s\n\n", cupsLangEncoding(language));
+
+ /*
+ * See if we need to show a list of printers or the status of a
+ * single printer...
+ */
+
+ printer = argv[0];
+ if (strcmp(printer, "/") == 0 || strcmp(printer, "printers.cgi") == 0)
+ printer = NULL;
+
+ /*
+ * Print the standard header...
+ */
+
+ puts("<HTML>");
+ puts("<HEAD>");
+ if (printer)
+ puts("<META HTTP-EQUIV=\"Refresh\" CONTENT=\"10\">");
+ else
+ puts("<META HTTP-EQUIV=\"Refresh\" CONTENT=\"30\">");
+ printf("<TITLE>%s on %s - Common UNIX Printing System</TITLE>\n",
+ printer == NULL ? "Printers" : printer, getenv("SERVER_NAME"));
+ puts("<LINK REL=STYLESHEET TYPE=\"text/css\" HREF=\"/cups.css\">");
+ puts("<MAP NAME=\"navbar\">");
+ puts("<AREA SHAPE=\"RECT\" COORDS=\"10,10,85,30\" HREF=\"/printers\" ALT=\"Current Printer Status\">");
+ puts("<AREA SHAPE=\"RECT\" COORDS=\"95,10,175,30\" HREF=\"/classes\" ALT=\"Current Printer Classes Status\">");
+ puts("<AREA SHAPE=\"RECT\" COORDS=\"185,10,235,30\" HREF=\"/jobs\" ALT=\"Current Jobs Status\">");
+ puts("<AREA SHAPE=\"RECT\" COORDS=\"245,10,395,30\" HREF=\"/documentation.html\" ALT=\"Read CUPS Documentation On-Line\">");
+#ifdef ESPPRINTPRO
+ puts("<AREA SHAPE=\"RECT\" COORDS=\"405,10,490,30\" HREF=\"http://www.easysw.com/printpro/software.html\" ALT=\"Download the Current ESP Print Pro Software\">");
+ puts("<AREA SHAPE=\"RECT\" COORDS=\"505,10,585,30\" HREF=\"http://www.easysw.com/printpro/support.html\" ALT=\"Get Tech Support for Current ESP Print Pro\">");
+#else
+ puts("<AREA SHAPE=\"RECT\" COORDS=\"405,10,490,30\" HREF=\"http://www.cups.org\" ALT=\"Download the Current CUPS Software\">");
+#endif /* ESPPRINTPRO */
+ puts("</MAP>");
+ puts("</HEAD>");
+ puts("<BODY>");
+ puts("<P ALIGN=CENTER>");
+ puts("<A HREF=\"http://www.easysw.com\" ALT=\"Easy Software Products Home Page\">");
+ puts("<IMG SRC=\"/images/logo.gif\" WIDTH=\"71\" HEIGHT=\"40\" BORDER=0 ALT=\"Easy Software Products Home Page\"></A>");
+#ifdef ESPPRINTPRO
+ puts("<IMG SRC=\"/images/navbar.gif\" WIDTH=\"600\" HEIGHT=\"40\" USEMAP=\"#navbar\" BORDER=0>");
+#else
+ puts("<IMG SRC=\"/images/navbar.gif\" WIDTH=\"540\" HEIGHT=\"40\" USEMAP=\"#navbar\" BORDER=0>");
+#endif /* ESPPRINTPRO */
+
+ printf("<H1>%s on %s</H1>\n", printer == NULL ? "Printers" : printer,
+ getenv("SERVER_NAME"));
+ fflush(stdout);
+
+ puts("<CENTER>");
+ puts("<TABLE WIDTH=\"90%\" BORDER=\"1\">");
+ puts("<TR>");
+ puts("<TH>Name</TH>");
+ puts("<TH WIDTH=\"50%\">Status</TH>");
+ puts("<TH WIDTH=\"25%\">Jobs</TH>");
+ puts("</TR>");
+
+ /*
+ * Show the information...
+ */
+
+ if (printer == NULL)
+ show_printer_list(http, language);
+ else
+ show_printer_info(http, language, printer);
+
+ /*
+ * Write a standard trailer...
+ */
+
+ puts("</TABLE>");
+ puts("</CENTER>");
+
+ puts("<HR>");
+
+ puts("<P>The Common UNIX Printing System, CUPS, and the CUPS logo are the");
+ puts("trademark property of <A HREF=\"http://www.easysw.com\">Easy Software");
+ puts("Products</A>. CUPS is copyright 1997-1999 by Easy Software Products,");
+ puts("All Rights Reserved.");
+
+ puts("</BODY>");
+ puts("</HTML>");
+
+ /*
+ * Close the HTTP server connection...
+ */
+
+ httpClose(http);
+ cupsLangFree(language);
+
+ /*
+ * Return with no errors...
+ */
+
+ return (0);
+}
+
+
+/*
+ * 'show_printer_list()' - Show a list of printers...
+ */
+
+static void
+show_printer_list(http_t *http, /* I - HTTP connection */
+ cups_lang_t *language)/* I - Client's language */
+{
+ ipp_t *request, /* IPP request */
+ *response; /* IPP response */
+ ipp_attribute_t *attr; /* IPP attribute */
+
+
+ /*
+ * Build a CUPS_GET_PRINTERS request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = CUPS_GET_PRINTERS;
+ request->request.op.request_id = 1;
+
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, cupsLangEncoding(language));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ if ((response = cupsDoRequest(http, request, "/printers/")) != NULL)
+ {
+ /*
+ * Loop through the printers returned in the list and display
+ * their devices...
+ */
+
+ for (attr = response->attrs; attr != NULL; attr = attr->next)
+ {
+ /*
+ * Skip leading attributes until we hit a job...
+ */
+
+ while (attr != NULL && attr->group_tag != IPP_TAG_PRINTER)
+ attr = attr->next;
+
+ if (attr == NULL)
+ break;
+
+ /*
+ * Show the printer status for each printer...
+ */
+
+ while (attr != NULL && attr->group_tag == IPP_TAG_PRINTER)
+ {
+ if (strcmp(attr->name, "printer-name") == 0 &&
+ attr->value_tag == IPP_TAG_NAME)
+ show_printer_info(http, language, attr->values[0].string.text);
+
+ attr = attr->next;
+ }
+
+ if (attr == NULL)
+ break;
+ }
+
+ ippDelete(response);
+ }
+}
+
+
+/*
+ * 'show_printer_info()' - Show printer information.
+ */
+
+static void
+show_printer_info(http_t *http,
+ cups_lang_t *language,
+ char *name)
+{
+ ipp_t *request, /* IPP request */
+ *response, /* IPP response */
+ *jobs; /* IPP Get Jobs response */
+ int jobcount; /* Number of jobs */
+ ipp_attribute_t *attr; /* IPP attribute */
+ char *message; /* Printer state message */
+ int accepting; /* Accepting requests? */
+ ipp_pstate_t pstate; /* Printer state */
+ char uri[HTTP_MAX_URI];/* Printer URI */
+
+
+ /*
+ * Build a IPP_GET_PRINTER_ATTRIBUTES request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * printer-uri
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = IPP_GET_PRINTER_ATTRIBUTES;
+ request->request.op.request_id = 1;
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, cupsLangEncoding(language));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ sprintf(uri, "ipp://localhost/printers/%s", name);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri);
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ if ((response = cupsDoRequest(http, request, uri + 15)) == NULL)
+ {
+ puts("<P>Unable to communicate with CUPS server!");
+ return;
+ }
+
+ if (response->request.status.status_code == IPP_NOT_FOUND)
+ {
+ puts("<P>Printer does not exist.");
+ ippDelete(response);
+ return;
+ }
+
+ /*
+ * Grab the needed printer attributes...
+ */
+
+ if ((attr = ippFindAttribute(response, "printer-state", IPP_TAG_ENUM)) != NULL)
+ pstate = (ipp_pstate_t)attr->values[0].integer;
+ else
+ pstate = IPP_PRINTER_IDLE;
+
+ if ((attr = ippFindAttribute(response, "printer-state-message", IPP_TAG_TEXT)) != NULL)
+ message = attr->values[0].string.text;
+ else
+ message = NULL;
+
+ if ((attr = ippFindAttribute(response, "printer-is-accepting-jobs",
+ IPP_TAG_BOOLEAN)) != NULL)
+ accepting = attr->values[0].boolean;
+ else
+ accepting = 1;
+
+ if ((attr = ippFindAttribute(response, "printer-uri-supported", IPP_TAG_URI)) != NULL)
+ {
+ strcpy(uri, "http:");
+ strcpy(uri + 5, strchr(attr->values[0].string.text, '/'));
+ }
+
+ /*
+ * Display the printer entry...
+ */
+
+ puts("<TR>");
+
+ printf("<TD VALIGN=TOP><A HREF=\"%s\">%s</A></TD>\n", uri, name);
+
+ printf("<TD VALIGN=TOP><IMG SRC=\"/images/printer-%s.gif\" ALIGN=\"LEFT\">\n",
+ pstate == IPP_PRINTER_IDLE ? "idle" :
+ pstate == IPP_PRINTER_PROCESSING ? "processing" : "stopped");
+
+ printf("%s: %s, %s<BR>\n",
+ cupsLangString(language, CUPS_MSG_PRINTER_STATE),
+ cupsLangString(language, pstate == IPP_PRINTER_IDLE ? CUPS_MSG_IDLE :
+ pstate == IPP_PRINTER_PROCESSING ?
+ CUPS_MSG_PROCESSING : CUPS_MSG_STOPPED),
+ cupsLangString(language, accepting ? CUPS_MSG_ACCEPTING_JOBS :
+ CUPS_MSG_NOT_ACCEPTING_JOBS));
+
+ if (message)
+ printf("<BR CLEAR=ALL><I>\"%s\"</I>\n", message);
+ else if (!accepting || pstate == IPP_PRINTER_STOPPED)
+ puts("<BR CLEAR=ALL><I>\"Reason Unknown\"</I>");
+
+ puts("</TD>");
+
+ /*
+ * Show a list of jobs as needed...
+ */
+
+ if (pstate != IPP_PRINTER_IDLE)
+ {
+ /*
+ * Build an IPP_GET_JOBS request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * printer-uri
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = IPP_GET_JOBS;
+ request->request.op.request_id = 1;
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL,
+ cupsLangEncoding(language));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL,
+ language->language);
+
+ sprintf(uri, "ipp://localhost/printers/%s", name);
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
+ "printer-uri", NULL, uri);
+
+ jobs = cupsDoRequest(http, request, uri + 15);
+ }
+ else
+ jobs = NULL;
+
+ puts("<TD VALIGN=\"TOP\">");
+ jobcount = 0;
+
+ if (jobs != NULL)
+ {
+ char *username; /* Pointer to job-originating-user-name */
+ int jobid, /* job-id */
+ size; /* job-k-octets */
+
+
+ for (attr = jobs->attrs; attr != NULL; attr = attr->next)
+ {
+ /*
+ * Skip leading attributes until we hit a job...
+ */
+
+ while (attr != NULL && attr->group_tag != IPP_TAG_JOB)
+ attr = attr->next;
+
+ if (attr == NULL)
+ break;
+
+ /*
+ * Pull the needed attributes from this job...
+ */
+
+ jobid = 0;
+ size = 0;
+ username = NULL;
+
+ while (attr != NULL && attr->group_tag == IPP_TAG_JOB)
+ {
+ if (strcmp(attr->name, "job-id") == 0 &&
+ attr->value_tag == IPP_TAG_INTEGER)
+ jobid = attr->values[0].integer;
+
+ if (strcmp(attr->name, "job-k-octets") == 0 &&
+ attr->value_tag == IPP_TAG_INTEGER)
+ size = attr->values[0].integer;
+
+ if (strcmp(attr->name, "job-originating-user-name") == 0 &&
+ attr->value_tag == IPP_TAG_NAME)
+ username = attr->values[0].string.text;
+
+ attr = attr->next;
+ }
+
+ /*
+ * Display the job if it matches the current printer...
+ */
+
+ if (username != NULL)
+ {
+ jobcount ++;
+ printf("<A HREF=\"/jobs/%d\">%s-%d %s %dk</A><BR>\n", jobid, name,
+ jobid, username, size);
+ }
+
+ if (attr == NULL)
+ break;
+ }
+
+ ippDelete(jobs);
+ }
+
+ if (jobcount == 0)
+ puts("None");
+ puts("</TD>");
+ puts("</TR>");
+
+ ippDelete(response);
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/conf/Makefile b/conf/Makefile
new file mode 100644
index 000000000..2859b7064
--- /dev/null
+++ b/conf/Makefile
@@ -0,0 +1,68 @@
+#
+# "$Id$"
+#
+# Configuration file makefile for the Common UNIX Printing System (CUPS).
+#
+# Copyright 1993-1999 by Easy Software Products.
+#
+# These coded instructions, statements, and computer programs are the
+# property of Easy Software Products and are protected by Federal
+# copyright law. Distribution and use rights are outlined in the file
+# "LICENSE.txt" which should have been included with this file. If this
+# file is missing or damaged please contact Easy Software Products
+# at:
+#
+# Attn: CUPS Licensing Information
+# Easy Software Products
+# 44141 Airport View Drive, Suite 204
+# Hollywood, Maryland 20636-3111 USA
+#
+# Voice: (301) 373-9603
+# EMail: cups-info@cups.org
+# WWW: http://www.cups.org
+#
+
+include ../Makedefs
+
+#
+# Config files...
+#
+
+KEEP = classes.conf cupsd.conf printers.conf
+REPLACE = mime.convs mime.types
+
+#
+# Make everything...
+#
+
+all:
+
+#
+# Clean all config and object files...
+#
+
+clean:
+
+#
+# Install files...
+#
+
+install:
+ -$(MKDIR) $(SERVERROOT)/conf
+ for file in $(KEEP); do \
+ if test -e $(SERVERROOT)/conf/$$file ; then \
+ $(CP) $$file $(SERVERROOT)/conf/$$file.N ; \
+ else \
+ $(CP) $$file $(SERVERROOT)/conf ; \
+ fi ; \
+ done
+ for file in $(REPLACE); do \
+ if test -e $(SERVERROOT)/conf/$$file ; then \
+ $(MV) $(SERVERROOT)/conf/$$file $(SERVERROOT)/conf/$$file.O ; \
+ fi ; \
+ $(CP) $$file $(SERVERROOT)/conf ; \
+ done
+
+#
+# End of "$Id$".
+#
diff --git a/conf/classes.conf b/conf/classes.conf
new file mode 100644
index 000000000..cb6a341e8
--- /dev/null
+++ b/conf/classes.conf
@@ -0,0 +1,72 @@
+#
+# "$Id: classes.conf 333 1999-05-17 18:03:40Z mike $"
+#
+# Sample class configuration file for the Common UNIX Printing System
+# (CUPS) scheduler.
+#
+# Copyright 1997-1999 by Easy Software Products, all rights reserved.
+#
+# These coded instructions, statements, and computer programs are the
+# property of Easy Software Products and are protected by Federal
+# copyright law. Distribution and use rights are outlined in the file
+# "LICENSE.txt" which should have been included with this file. If this
+# file is missing or damaged please contact Easy Software Products
+# at:
+#
+# Attn: CUPS Licensing Information
+# Easy Software Products
+# 44145 Airport View Drive, Suite 204
+# Hollywood, Maryland 20636-3111 USA
+#
+# Voice: (301) 373-9603
+# EMail: cups-info@cups.org
+# WWW: http://www.cups.org
+#
+
+########################################################################
+# #
+# This is a sample class configuration file. This file is included #
+# from the main configuration file (cups.conf) and lists all of the #
+# printer classes known to the system. #
+# #
+########################################################################
+
+#
+# Each class starts with a <Class name> definition. Class names
+# can be up to 128 characters in length and are *not* case sensitive.
+#
+# One <DefaultClass name> entry can appear in this file; if you don't
+# define a default destination, the first printer or class becomes
+# the default.
+#
+
+#<Class sample>
+#
+# Info: the description for the class.
+#
+
+#Info Acme LaserPrint 1000 Printers
+
+#
+# MoreInfo: a URL for more information on the printer.
+#
+
+#MoreInfo http://www.acme.com/lp1000.html
+
+#
+# Location: the location of the printer.
+#
+
+#Location Room 101 in the activities building
+
+#
+# Printer: adds a printer to the class.
+#
+
+#Printer sample
+#Printer sample@host2
+#</Class>
+
+#
+# End of "$Id: classes.conf 333 1999-05-17 18:03:40Z mike $".
+#
diff --git a/conf/cupsd.conf b/conf/cupsd.conf
new file mode 100644
index 000000000..cb54f7ff1
--- /dev/null
+++ b/conf/cupsd.conf
@@ -0,0 +1,369 @@
+#
+# "$Id: cupsd.conf 628 1999-08-23 15:24:48Z mike $"
+#
+# Sample configuration file for the Common UNIX Printing System (CUPS)
+# scheduler.
+#
+# Copyright 1997-1999 by Easy Software Products, all rights reserved.
+#
+# These coded instructions, statements, and computer programs are the
+# property of Easy Software Products and are protected by Federal
+# copyright law. Distribution and use rights are outlined in the file
+# "LICENSE.txt" which should have been included with this file. If this
+# file is missing or damaged please contact Easy Software Products
+# at:
+#
+# Attn: CUPS Licensing Information
+# Easy Software Products
+# 44145 Airport View Drive, Suite 204
+# Hollywood, Maryland 20636-3111 USA
+#
+# Voice: (301) 373-9603
+# EMail: cups-info@cups.org
+# WWW: http://www.cups.org
+#
+
+########################################################################
+# #
+# This is the CUPS configuration file. If you are familiar with #
+# Apache or any of the other popular web servers, we've followed the #
+# same format. Any configuration variable used here has the same #
+# semantics as the corresponding variable in Apache. If we need #
+# different functionality then a different name is used to avoid #
+# confusion... #
+# #
+########################################################################
+
+#
+# Ports/addresses that we listen to. The default port 631 is reserved
+# for the Internet Printing Protocol (IPP) and is what we use here.
+#
+# You can have multiple Port/Listen lines to listen to more than one
+# port or address, or to restrict access:
+#
+# Port 80
+# Port 631
+# Listen hostname
+# Listen hostname:80
+# Listen hostname:631
+# Listen 1.2.3.4
+# Listen 1.2.3.4:631
+#
+
+#Port 80
+Port 631
+
+#
+# MaxClients: controls the maximum number of simultaneous clients that
+# will be handled. Defaults to 100.
+#
+
+#MaxClients 100
+
+#
+# User/Group: the user and group the server runs under. Normally this
+# must be lp and sys, however you can configure things for another user
+# or group as needed.
+#
+# Note: the server must be run initially as root to support the
+# default IPP port of 631. It changes users whenever an external
+# program is run...
+#
+
+#User lp
+#Group sys
+
+#
+# SystemGroup: the group name for "System" (printer administration)
+# access. The default varies depending on the operating system, but
+# will be "sys", "system", or "root" (checked for in that order.)
+#
+
+#SystemGroup sys
+
+#
+# ServerName: the hostname of your server, as advertised to the world.
+# By default CUPS will use the hostname of the system.
+#
+# This is also the name used by clients when connecting to the local
+# server, so you can use this to configure a client machine without
+# a local server running.
+#
+
+#ServerName myhost.domain.com
+
+#
+# ServerAdmin: the email address to send all complaints/problems to.
+# By default CUPS will use "root@hostname".
+#
+
+#ServerAdmin root@your.domain.com
+
+#
+# ServerRoot: the root directory for the scheduler.
+# By default the compiled-in value.
+#
+
+#ServerRoot /var/cups
+
+#
+# AccessLog: the access log file; if this does not start with a leading /
+# then it is assumed to be relative to ServerRoot. By default set to
+# "logs/access_log"
+#
+
+#AccessLog logs/access_log
+
+#
+# ErrorLog: the error log file; if this does not start with a leading /
+# then it is assumed to be relative to ServerRoot. By default set to
+# "logs/error_log"
+#
+
+#ErrorLog logs/error_log
+
+#
+# PageLog: the page log file; if this does not start with a leading /
+# then it is assumed to be relative to ServerRoot. By default set to
+# "logs/page_log"
+#
+
+#PageLog logs/page_log
+
+#
+# LogLevel: controls the number of messages logged to the ErrorLog
+# file and can be one of the following:
+#
+# debug Log everything.
+# info Log all requests and state changes.
+# warn Log errors and warnings.
+# error Log only errors.
+# none Log nothing.
+#
+
+LogLevel info
+
+#
+# MaxLogSize: controls the maximum size of each log file before they are
+# rotated. Defaults to 1048576 (1MB). Set to 0 to disable log rotating.
+#
+
+#MaxLogSize 0
+
+#
+# MaxRequestSize: controls the maximum size of print files. Set to 0 to
+# disable this feature (defaults to 0.)
+#
+
+#MaxRequestSize 0
+
+#
+# HostNameLookups: whether or not to do lookups on IP addresses to get a
+# fully-qualified hostname. This defaults to Off for performance reasons...
+#
+
+#HostNameLookups On
+
+#
+# Timeout: the timeout before requests time out. Default is 300 seconds.
+#
+
+#Timeout 300
+
+#
+# KeepAlive: whether or not to support the Keep-Alive connection
+# option. Default is on.
+#
+
+#KeepAlive On
+
+#
+# KeepAliveTimeout: the timeout before Keep-Alive connections are
+# automatically closed. Default is 60 seconds.
+#
+
+#KeepAliveTimeout 60
+
+#
+# ImplicitClasses: whether or not to use implicit classes.
+#
+# Printer classes can be specified explicitly in the classes.conf
+# file, implicitly based upon the printers available on the LAN, or
+# both.
+#
+# When ImplicitClasses is On, printers on the LAN with the same name
+# (e.g. Acme-LaserPrint-1000) will be put into a class with the same
+# name. This allows you to setup multiple redundant queues on a LAN
+# without a lot of administrative difficulties. If a user sends a
+# job to Acme-LaserPrint-1000, the job will go to the first available
+# queue.
+#
+# Enabled by default.
+#
+
+#ImplicitClasses On
+
+#
+# Browsing: whether or not to broadcast printer information to
+# other CUPS servers. Enabled by default.
+#
+
+#Browsing On
+
+#
+# BrowseInterval: the time between browsing updates in seconds. Default
+# is 30 seconds.
+#
+# Note that browsing information is sent whenever a printer's state changes
+# as well, so this represents the maximum time between updates.
+#
+
+#BrowseInterval 30
+
+#
+# BrowseTimeout: the timeout for network printers - if we don't
+# get an update within this time the printer will be removed
+# from the printer list. This number definitely should not be
+# less the BrowseInterval value for obvious reasons. Defaults
+# to 300 seconds.
+#
+
+#BrowseTimeout 300
+
+#
+# BrowsePort: the port used for UDP broadcasts. By default this is
+# the IPP port; if you change this you need to do it on all servers.
+# Only one BrowsePort is recognized.
+#
+
+#BrowsePort 631
+
+#
+# BrowseAddress: specifies a broadcast address to be used. By
+# default browsing information is broadcast to all active interfaces.
+#
+# Note: HP-UX 10.20 and earlier do not properly handle broadcast unless
+# you have a Class A, B, C, or D netmask (i.e. no CIDR support).
+#
+
+#BrowseAddress x.y.z.255
+#BrowseAddress x.y.255.255
+#BrowseAddress x.255.255.255
+
+#
+# DocumentRoot: the root directory for HTTP documents that are served.
+# By default the compiled in directory.
+#
+
+#DocumentRoot /usr/share/cups/doc
+
+#
+# DefaultLanguage: the default language if not specified by the browser.
+# If not specified, the current locale is used.
+#
+
+#DefaultLanguage en
+
+#
+# DefaultCharset: the default character set to use. If not specified,
+# defaults to iso-8859-1. Note that this can also be overridden in
+# HTML documents...
+#
+
+#DefaultCharset iso-8859-1
+
+#
+# RIPCache: the amount of memory that each RIP should use to cache
+# bitmaps. The value can be any real number followed by "k" for
+# kilobytes, "m" for megabytes, "g" for gigabytes, or "t" for tiles
+# (1 tile = 256x256 pixels.) Defaults to "8m" (8 megabytes).
+#
+
+#RIPCache 8m
+
+#
+# TempDir: the directory to put temporary files in. This directory must be
+# writable by the user defined above! Defaults to "/var/tmp" or the value
+# of the TMPDIR environment variable.
+#
+
+#TempDir /var/tmp
+
+#
+# Access permissions for each directory served by the scheduler.
+# Locations are relative to DocumentRoot...
+#
+# AuthType: the authorization to use; currently only "Basic" authorization is
+# supported.
+#
+# AuthClass: the authorization class; currently only "Anonymous", "User",
+# "System" (valid user belonging to group SystemGroup), and "Group"
+# (valid user belonging to the specified group) are supported.
+#
+# AuthGroupName: the group name for "Group" authorization.
+#
+# Order: the order of Allow/Deny processing.
+#
+# Allow: allows access from the specified hostname, domain, IP address, or
+# network.
+#
+# Deny: denies access from the specified hostname, domain, IP address, or
+# network.
+#
+# Both "Allow" and "Deny" accept the following notations for addresses:
+#
+# All
+# None
+# *.domain.com
+# .domain.com
+# host.domain.com
+# nnn.*
+# nnn.nnn.*
+# nnn.nnn.nnn.*
+# nnn.nnn.nnn.nnn
+# nnn.nnn.nnn.nnn/mm
+# nnn.nnn.nnn.nnn/mmm.mmm.mmm.mmm
+#
+# The host and domain address require that you enable hostname lookups
+# with "HostNameLookups On" above.
+#
+
+<Location />
+</Location>
+
+<Location /printers>
+#
+# You may wish to limit access to printers and classes, either with Allow
+# and Deny lines, or by requiring a username and password.
+#
+
+## Require a username and password
+#AuthType Basic
+#AuthClass User
+
+## Restrict access to local domain
+#Order Deny,Allow
+#Deny From All
+#Allow From .mydomain.com
+</Location>
+
+<Location /admin>
+#
+# You definitely will want to limit access to the administration tools.
+# The default configuration requires a local connection from a user who
+# is a member of the system group to do any admin tasks. You can change
+# the group name using the SystemGroup directive.
+#
+
+AuthType Basic
+AuthClass System
+
+## Restrict access to local domain
+Order Deny,Allow
+Deny From All
+Allow From 127.0.0.1
+</Location>
+
+#
+# End of "$Id: cupsd.conf 628 1999-08-23 15:24:48Z mike $".
+#
diff --git a/conf/cupsd.conf-personal b/conf/cupsd.conf-personal
new file mode 100644
index 000000000..b82b66d97
--- /dev/null
+++ b/conf/cupsd.conf-personal
@@ -0,0 +1,250 @@
+#
+# "$Id: cupsd.conf-personal 407 1999-06-17 20:02:43Z mike $"
+#
+# Scheduler configuration file for ESP Print Personal.
+#
+
+########################################################################
+# #
+# This is the CUPS configuration file. If you are familiar with #
+# Apache or any of the other popular web servers, we've followed the #
+# same format. Any configuration variable used here has the same #
+# semantics as the corresponding variable in Apache. If we need #
+# different functionality then a different name is used to avoid #
+# confusion... #
+# #
+########################################################################
+
+#
+# Ports/addresses that we listen to. The default port 631 is reserved
+# for the Internet Printing Protocol (IPP) and is what we use here.
+#
+# You can have multiple Listen lines to listen to more than one
+# port:
+#
+# Listen 127.0.0.1:80
+# Listen 127.0.0.1:631
+#
+# For ESP Print Personal, we can only listen on the local host...
+#
+
+#Listen 127.0.0.1:80
+Listen 127.0.0.1:631
+
+#
+# User/Group: the user and group the server runs under. Normally this
+# must be lp and sys, however you can configure things for another user
+# or group as needed.
+#
+# Note: the server must be run initially as root to support the
+# default IPP port of 631. It changes users whenever an external
+# program is run...
+#
+
+User lp
+Group sys
+
+#
+# SystemGroup: the group name for "System" (printer administration)
+# access.
+#
+
+SystemGroup sys
+
+#
+# ServerName: the hostname of your server, as advertised to the world.
+# By default CUPS will use the hostname of the system.
+#
+
+#ServerName myhost.domain.com
+
+#
+# ServerAdmin: the email address to send all complaints/problems to.
+# By default CUPS will use "root@hostname".
+#
+
+#ServerAdmin root@your.domain.com
+
+#
+# ServerRoot: the root directory for the scheduler.
+# By default the compiled-in value.
+#
+
+#ServerRoot /var/cups
+
+#
+# AccessLog: the access log file; if this does not start with a leading /
+# then it is assumed to be relative to ServerRoot. By default set to
+# "logs/access_log"
+#
+
+#AccessLog logs/access_log
+
+#
+# ErrorLog: the error log file; if this does not start with a leading /
+# then it is assumed to be relative to ServerRoot. By default set to
+# "logs/error_log"
+#
+
+#ErrorLog logs/error_log
+
+#
+# PageLog: the page log file; if this does not start with a leading /
+# then it is assumed to be relative to ServerRoot. By default set to
+# "logs/page_log"
+#
+
+#PageLog logs/page_log
+
+#
+# LogLevel: controls the number of messages logged to the ErrorLog
+# file and can be one of the following:
+#
+# debug Log everything.
+# info Log all requests and state changes.
+# warn Log errors and warnings.
+# error Log only errors.
+# none Log nothing.
+#
+
+LogLevel info
+
+#
+# MaxLogSize: controls the maximum size of each log file before they are
+# rotated. Defaults to 1048576 (1MB). Set to 0 to disable log rotating.
+#
+
+#MaxLogSize 0
+
+#
+# MaxRequestSize: controls the maximum size of print files. Set to 0 to
+# disable this feature (defaults to 0.)
+#
+
+#MaxRequestSize 0
+
+#
+# HostNameLookups: whether or not to do lookups on IP addresses to get a
+# fully-qualified hostname. This defaults to Off for performance reasons...
+#
+
+#HostNameLookups On
+
+#
+# Timeout: the timeout before requests time out. Default is 300 seconds.
+#
+
+#Timeout 300
+
+#
+# KeepAlive: whether or not to support the Keep-Alive connection
+# option. Default is on.
+#
+
+#KeepAlive On
+
+#
+# KeepAliveTimeout: the timeout before Keep-Alive connections are
+# automatically closed. Default is 60 seconds.
+#
+
+#KeepAliveTimeout 60
+
+#
+# Browsing: not available in ESP Print Personal.
+#
+
+Browsing Off
+
+#
+# DocumentRoot: the root directory for HTTP documents that are served.
+# By default the compiled in directory.
+#
+
+#DocumentRoot /usr/share/cups/doc
+
+#
+# DefaultLanguage: the default language if not specified by the browser.
+# If not specified, the current locale is used.
+#
+
+#DefaultLanguage en
+
+#
+# DefaultCharset: the default character set to use. If not specified,
+# defaults to iso-8859-1. Note that this can also be overridden in
+# HTML documents...
+#
+
+#DefaultCharset iso-8859-1
+
+#
+# RIPCache: the amount of memory that each RIP should use to cache
+# bitmaps. The value can be any real number followed by "k" for
+# kilobytes, "m" for megabytes, "g" for gigabytes, or "t" for tiles
+# (1 tile = 256x256 pixels.) Defaults to "32m" (32 megabytes).
+#
+
+#RIPCache: 32m
+
+#
+# Access permissions for each directory served by the scheduler.
+# Locations are relative to DocumentRoot...
+#
+# AuthType: the authorization to use; currently only "Basic" authorization is
+# supported.
+#
+# AuthClass: the authorization class; currently only "Anonymous", "User",
+# "System" (valid user belonging to group SystemGroup), and "Group"
+# (valid user belonging to the specified group) are supported.
+#
+# AuthGroupName: the group name for "Group" authorization.
+#
+# Order: the order of Allow/Deny processing.
+#
+# Allow: allows access from the specified hostname, domain, IP address, or
+# network.
+#
+# Deny: denies access from the specified hostname, domain, IP address, or
+# network.
+#
+
+<Location />
+</Location>
+
+<Location /printers>
+#
+# You may wish to limit access to printers and classes, either with Allow
+# and Deny lines, or by requiring a username and password.
+#
+
+## Require a username and password
+#AuthType Basic
+#AuthClass User
+
+## Restrict access to local domain
+#Order Deny,Allow
+#Deny From All
+#Allow From .mydomain.com
+</Location>
+
+<Location /admin>
+#
+# You definitely will want to limit access to the administration tools.
+# The default configuration requires a local connection from a user who
+# is a member of group "sys" to do any admin tasks. You can change the
+# group name using the SystemGroup directive.
+#
+
+AuthType Basic
+AuthClass System
+
+## Restrict access to local domain
+Order Deny,Allow
+Deny From All
+Allow From 127.0.0.1
+</Location>
+
+#
+# End of "$Id: cupsd.conf-personal 407 1999-06-17 20:02:43Z mike $".
+#
diff --git a/conf/cupsd.conf-professional b/conf/cupsd.conf-professional
new file mode 100644
index 000000000..c7bd3324b
--- /dev/null
+++ b/conf/cupsd.conf-professional
@@ -0,0 +1,313 @@
+#
+# "$Id: cupsd.conf-professional 407 1999-06-17 20:02:43Z mike $"
+#
+# Scheduler configuration file for ESP Print Professional.
+#
+
+########################################################################
+# #
+# This is the CUPS configuration file. If you are familiar with #
+# Apache or any of the other popular web servers, we've followed the #
+# same format. Any configuration variable used here has the same #
+# semantics as the corresponding variable in Apache. If we need #
+# different functionality then a different name is used to avoid #
+# confusion... #
+# #
+########################################################################
+
+#
+# Ports/addresses that we listen to. The default port 631 is reserved
+# for the Internet Printing Protocol (IPP) and is what we use here.
+#
+# You can have multiple Port/Listen lines to listen to more than one
+# port or address, or to restrict access:
+#
+# Port 80
+# Port 631
+# Listen hostname
+# Listen hostname:80
+# Listen hostname:631
+# Listen 1.2.3.4
+# Listen 1.2.3.4:631
+#
+
+#Port 80
+Port 631
+
+#
+# User/Group: the user and group the server runs under. Normally this
+# must be lp and sys, however you can configure things for another user
+# or group as needed.
+#
+# Note: the server must be run initially as root to support the
+# default IPP port of 631. It changes users whenever an external
+# program is run...
+#
+
+User lp
+Group sys
+
+#
+# SystemGroup: the group name for "System" (printer administration)
+# access.
+#
+
+SystemGroup sys
+
+#
+# ServerName: the hostname of your server, as advertised to the world.
+# By default CUPS will use the hostname of the system.
+#
+
+#ServerName myhost.domain.com
+
+#
+# ServerAdmin: the email address to send all complaints/problems to.
+# By default CUPS will use "root@hostname".
+#
+
+#ServerAdmin root@your.domain.com
+
+#
+# ServerRoot: the root directory for the scheduler.
+# By default the compiled-in value.
+#
+
+#ServerRoot /var/cups
+
+#
+# AccessLog: the access log file; if this does not start with a leading /
+# then it is assumed to be relative to ServerRoot. By default set to
+# "logs/access_log"
+#
+
+#AccessLog logs/access_log
+
+#
+# ErrorLog: the error log file; if this does not start with a leading /
+# then it is assumed to be relative to ServerRoot. By default set to
+# "logs/error_log"
+#
+
+#ErrorLog logs/error_log
+
+#
+# PageLog: the page log file; if this does not start with a leading /
+# then it is assumed to be relative to ServerRoot. By default set to
+# "logs/page_log"
+#
+
+#PageLog logs/page_log
+
+#
+# LogLevel: controls the number of messages logged to the ErrorLog
+# file and can be one of the following:
+#
+# debug Log everything.
+# info Log all requests and state changes.
+# warn Log errors and warnings.
+# error Log only errors.
+# none Log nothing.
+#
+
+LogLevel info
+
+#
+# MaxLogSize: controls the maximum size of each log file before they are
+# rotated. Defaults to 1048576 (1MB). Set to 0 to disable log rotating.
+#
+
+#MaxLogSize 0
+
+#
+# MaxRequestSize: controls the maximum size of print files. Set to 0 to
+# disable this feature (defaults to 0.)
+#
+
+#MaxRequestSize 0
+
+#
+# HostNameLookups: whether or not to do lookups on IP addresses to get a
+# fully-qualified hostname. This defaults to Off for performance reasons...
+#
+
+#HostNameLookups On
+
+#
+# Timeout: the timeout before requests time out. Default is 300 seconds.
+#
+
+#Timeout 300
+
+#
+# KeepAlive: whether or not to support the Keep-Alive connection
+# option. Default is on.
+#
+
+#KeepAlive On
+
+#
+# KeepAliveTimeout: the timeout before Keep-Alive connections are
+# automatically closed. Default is 60 seconds.
+#
+
+#KeepAliveTimeout 60
+
+#
+# ImplicitClasses: whether or not to use implicit classes.
+#
+# Printer classes can be specified explicitly in the classes.conf
+# file, implicitly based upon the printers available on the LAN, or
+# both.
+#
+# When ImplicitClasses is On, printers on the LAN with the same name
+# (e.g. Acme-LaserPrint-1000) will be put into a class with the same
+# name. This allows you to setup multiple redundant queues on a LAN
+# without a lot of administrative difficulties. If a user sends a
+# job to Acme-LaserPrint-1000, the job will go to the first available
+# queue.
+#
+# Enabled by default.
+#
+
+#ImplicitClasses On
+
+#
+# Browsing: whether or not to broadcast printer information to
+# other CUPS servers. Enabled by default.
+#
+
+#Browsing On
+
+#
+# BrowseInterval: the time between browsing updates in seconds. Default
+# is 30 seconds.
+#
+# Note that browsing information is sent whenever a printer's state changes
+# as well, so this represents the maximum time between updates.
+#
+
+#BrowseInterval 30
+
+#
+# BrowseTimeout: the timeout for network printers - if we don't
+# get an update within this time the printer will be removed
+# from the printer list. This number definitely should not be
+# less the BrowseInterval value for obvious reasons. Defaults
+# to 300 seconds.
+#
+
+#BrowseTimeout 300
+
+#
+# BrowsePort: the port used for UDP broadcasts. By default this is
+# the IPP port; if you change this you need to do it on all servers.
+# Only one BrowsePort is recognized.
+#
+
+#BrowsePort 631
+
+#
+# BrowseAddress: specifies a broadcast address to be used. By
+# default browsing information is broadcast to all active interfaces.
+#
+# Note: HP-UX 10.20 and earlier do not properly handle broadcast unless
+# you have a Class A, B, C, or D netmask (i.e. no CIDR support).
+#
+
+#BrowseAddress x.y.z.255
+#BrowseAddress x.y.255.255
+#BrowseAddress x.255.255.255
+
+#
+# DocumentRoot: the root directory for HTTP documents that are served.
+# By default the compiled in directory.
+#
+
+#DocumentRoot /usr/share/cups/doc
+
+#
+# DefaultLanguage: the default language if not specified by the browser.
+# If not specified, the current locale is used.
+#
+
+#DefaultLanguage en
+
+#
+# DefaultCharset: the default character set to use. If not specified,
+# defaults to iso-8859-1. Note that this can also be overridden in
+# HTML documents...
+#
+
+#DefaultCharset iso-8859-1
+
+#
+# RIPCache: the amount of memory that each RIP should use to cache
+# bitmaps. The value can be any real number followed by "k" for
+# kilobytes, "m" for megabytes, "g" for gigabytes, or "t" for tiles
+# (1 tile = 256x256 pixels.) Defaults to "32m" (32 megabytes).
+#
+
+#RIPCache: 32m
+
+#
+# Access permissions for each directory served by the scheduler.
+# Locations are relative to DocumentRoot...
+#
+# AuthType: the authorization to use; currently only "Basic" authorization is
+# supported.
+#
+# AuthClass: the authorization class; currently only "Anonymous", "User",
+# "System" (valid user belonging to group SystemGroup), and "Group"
+# (valid user belonging to the specified group) are supported.
+#
+# AuthGroupName: the group name for "Group" authorization.
+#
+# Order: the order of Allow/Deny processing.
+#
+# Allow: allows access from the specified hostname, domain, IP address, or
+# network.
+#
+# Deny: denies access from the specified hostname, domain, IP address, or
+# network.
+#
+
+<Location />
+</Location>
+
+<Location /printers>
+#
+# You may wish to limit access to printers and classes, either with Allow
+# and Deny lines, or by requiring a username and password.
+#
+
+## Require a username and password
+#AuthType Basic
+#AuthClass User
+
+## Restrict access to local domain
+#Order Deny,Allow
+#Deny From All
+#Allow From .mydomain.com
+</Location>
+
+<Location /admin>
+#
+# You definitely will want to limit access to the administration tools.
+# The default configuration requires a local connection from a user who
+# is a member of group "sys" to do any admin tasks. You can change the
+# group name using the SystemGroup directive.
+#
+
+AuthType Basic
+AuthClass System
+
+## Restrict access to local domain
+Order Deny,Allow
+Deny From All
+Allow From 127.0.0.1
+</Location>
+
+#
+# End of "$Id: cupsd.conf-professional 407 1999-06-17 20:02:43Z mike $".
+#
diff --git a/conf/mime.convs b/conf/mime.convs
new file mode 100644
index 000000000..72230bd4b
--- /dev/null
+++ b/conf/mime.convs
@@ -0,0 +1,62 @@
+#
+# "$Id: mime.convs 575 1999-07-30 13:57:16Z mike $"
+#
+# MIME converts file for the Common UNIX Printing System (CUPS).
+#
+# Copyright 1997-1999 by Easy Software Products.
+#
+# These coded instructions, statements, and computer programs are the
+# property of Easy Software Products and are protected by Federal
+# copyright law. Distribution and use rights are outlined in the file
+# "LICENSE.txt" which should have been included with this file. If this
+# file is missing or damaged please contact Easy Software Products
+# at:
+#
+# Attn: CUPS Licensing Information
+# Easy Software Products
+# 44141 Airport View Drive, Suite 204
+# Hollywood, Maryland 20636-3111 USA
+#
+# Voice: (301) 373-9603
+# EMail: cups-info@cups.org
+# WWW: http://www.cups.org
+#
+
+########################################################################
+#
+# Format of Lines:
+#
+# source/type destination/type cost filter
+#
+# General Notes:
+#
+# Currently the "cost" field is not used (all filters are assumed to
+# be equally costly in terms of speed/memory). Also, a filter program
+# *must* accept the standard command-line arguments (job-id, user, title,
+# copies,options,[filename or stdin]) or this won't work.
+#
+
+########################################################################
+#
+# PostScript filters
+#
+
+#application/msword application/postscript 50 mswordtops
+application/pdf application/postscript 50 pdftops
+application/postscript application/vnd.cups-postscript 50 pstops
+application/vnd.hp-HPGL application/postscript 50 hpgltops
+image/* application/vnd.cups-postscript 50 imagetops
+#text/html application/postscript 50 htmltops
+text/plain application/postscript 50 texttops
+
+########################################################################
+#
+# Raster filters...
+#
+
+image/* application/vnd.cups-raster 50 imagetoraster
+application/vnd.cups-postscript application/vnd.cups-raster 50 pstoraster
+
+#
+# End of "$Id: mime.convs 575 1999-07-30 13:57:16Z mike $".
+#
diff --git a/conf/mime.types b/conf/mime.types
new file mode 100644
index 000000000..e44dbcfa9
--- /dev/null
+++ b/conf/mime.types
@@ -0,0 +1,122 @@
+#
+# "$Id: mime.types 575 1999-07-30 13:57:16Z mike $"
+#
+# MIME types file for the Common UNIX Printing System (CUPS).
+#
+# Copyright 1997-1999 by Easy Software Products.
+#
+# These coded instructions, statements, and computer programs are the
+# property of Easy Software Products and are protected by Federal
+# copyright law. Distribution and use rights are outlined in the file
+# "LICENSE.txt" which should have been included with this file. If this
+# file is missing or damaged please contact Easy Software Products
+# at:
+#
+# Attn: CUPS Licensing Information
+# Easy Software Products
+# 44141 Airport View Drive, Suite 204
+# Hollywood, Maryland 20636-3111 USA
+#
+# Voice: (301) 373-9603
+# EMail: cups-info@cups.org
+# WWW: http://www.cups.org
+#
+
+########################################################################
+#
+# Format of Lines:
+#
+# super/type rules
+#
+# "rules" can be any combination of:
+#
+# ( expr ) Parenthesis for expression grouping
+# + Logical AND
+# , or whitespace Logical OR
+# ! Logical NOT
+# match("pattern") Pattern match on filename
+# extension Pattern match on "*.extension"
+# ascii(offset,length) True if bytes are valid printable ASCII
+# (CR, NL, TAB, BS, 32-126)
+# printable(offset,length) True if bytes are printable 8-bit chars
+# (CR, NL, TAB, BS, 32-126, 160-254)
+# string(offset,"string") True if bytes are identical to string
+# char(offset,value) True if byte is identical
+# short(offset,value) True if 16-bit integer is identical
+# int(offset,value) True if 32-bit integer is identical
+# locale("string") True if current locale matches string
+#
+# General Notes:
+#
+# MIME type names are case-insensitive. Internally they are converted
+# to lowercase. Multiple occurrences of a type will cause the provided
+# rules to be appended to the existing definition. Type names are sorted
+# in ascending order, so if two types use the same rules to resolve a type
+# (e.g. doc extension for two types), the returned type will be the first
+# type in the sorted list.
+#
+# The "printable" rule differs from the "ascii" rule in that it also
+# accepts 8-bit characters in the range 160-254.
+#
+# String constants must be surrounded by "" if they contain whitespace.
+# To instead binary data into a string, use the <hex> notation.
+#
+
+########################################################################
+#
+# Application-generated files...
+#
+
+application/msword doc string(0,<D0CF11E0A1B11AE1>)
+application/pdf pdf string(0,%PDF)
+application/postscript ai eps ps string(0,%!) string(0,<04>%!)
+application/vnd.hp-HPGL hpgl string(0,<1b>%) string(0,<1b>&)\
+ string(0,<1b>E) string(0,<201b>)\
+ string(0,BP;) string(0,IN;) string(0,DF;)
+
+########################################################################
+#
+# Image files...
+#
+
+image/gif gif string(0,GIF87a) string(0,GIF89a)
+image/png png string(0,<89>PNG)
+image/jpeg jpeg jpg jpe string(6,JFIF)
+image/tiff tiff tif string(0,MM) string(0,II)
+image/x-photocd pcd string(2048,PCD_IPI)
+image/x-portable-anymap pnm
+image/x-portable-bitmap pbm string(0,P1) string(0,P4)
+image/x-portable-graymap pgm string(0,P2) string(0,P5)
+image/x-portable-pixmap ppm string(0,P3) string(0,P6)
+image/x-sgi-rgb rgb sgi bw icon short(0,474)
+image/x-xbitmap xbm
+image/x-xpixmap xpm ascii(0,1024) + string(3,"XPM")
+image/x-xwindowdump xwd
+image/x-sun-raster ras
+
+# TODO: Add Alias, SoftImage, GIMP??? files
+#image/x-alias pix
+#image/x-softimage
+#image/x-gimp-xcf xcf xcf.gz
+
+########################################################################
+#
+# Text files...
+#
+
+text/html html htm printable(0,1024) +\
+ (string(0,"<HTML>") string(0,"<!DOCTYPE"))
+text/plain txt printable(0,1024)
+
+########################################################################
+#
+# CUPS-specific types...
+#
+
+application/vnd.cups-postscript string(0,<1B>%-12345X)
+application/vnd.cups-raster string(0,"RaSt") string(0,"tSaR")
+application/vnd.cups-raw
+
+#
+# End of "$Id: mime.types 575 1999-07-30 13:57:16Z mike $".
+#
diff --git a/conf/printers.conf b/conf/printers.conf
new file mode 100644
index 000000000..b9e33c87a
--- /dev/null
+++ b/conf/printers.conf
@@ -0,0 +1,89 @@
+#
+# "$Id: printers.conf 334 1999-05-17 18:11:26Z mike $"
+#
+# Sample printer configuration file for the Common UNIX Printing System
+# (CUPS) scheduler.
+#
+# Copyright 1997-1999 by Easy Software Products, all rights reserved.
+#
+# These coded instructions, statements, and computer programs are the
+# property of Easy Software Products and are protected by Federal
+# copyright law. Distribution and use rights are outlined in the file
+# "LICENSE.txt" which should have been included with this file. If this
+# file is missing or damaged please contact Easy Software Products
+# at:
+#
+# Attn: CUPS Licensing Information
+# Easy Software Products
+# 44145 Airport View Drive, Suite 204
+# Hollywood, Maryland 20636-3111 USA
+#
+# Voice: (301) 373-9603
+# EMail: cups-info@cups.org
+# WWW: http://www.cups.org
+#
+
+########################################################################
+# #
+# This is a sample printer configuration file. This file is included #
+# from the main configuration file (cups.conf) and lists all of the #
+# printers known to the system. #
+# #
+########################################################################
+
+#
+# Each printer starts with a <Printer name> definition. Printer names
+# can be up to 128 characters in length and are *not* case sensitive.
+#
+# One <DefaultPrinter name> entry can appear in this file; if you don't
+# define a default destination, the first printer or class becomes the
+# default.
+#
+
+#<Printer sample>
+#
+# Info: the description for the printer.
+#
+
+#Info Acme LaserPrint 1000
+
+#
+# MoreInfo: a URL for more information on the printer.
+#
+
+#MoreInfo http://www.acme.com/lp1000.html
+
+#
+# Location: the location of the printer.
+#
+
+#Location Room 101 in the activities building
+
+#
+# DeviceURI: the device URI for this printer.
+#
+
+#DeviceURI parallel:/dev/plp
+#DeviceURI serial:/dev/ttyd1?baud=38400+size=8+parity=none+flow=soft
+#DeviceURI scsi:/dev/scsi/sc1d6l0
+#DeviceURI socket://hostname:port
+#DeviceURI tftp://hostname/path
+#DeviceURI ftp://hostname/path
+#DeviceURI http://hostname[:port]/path
+#DeviceURI ipp://hostname/path
+#DeviceURI smb://hostname/printer
+
+#
+# State: sets the initial state of the printer. Can be one of the
+# following:
+#
+# Idle - Printer is available to accept new jobs.
+# Stopped - Printer is disabled but accepting new jobs.
+#
+
+#State Idle
+#</Printer>
+
+#
+# End of "$Id: printers.conf 334 1999-05-17 18:11:26Z mike $".
+#
diff --git a/config.h.in b/config.h.in
new file mode 100644
index 000000000..e8c7440bd
--- /dev/null
+++ b/config.h.in
@@ -0,0 +1,101 @@
+/*
+ * "$Id$"
+ *
+ * Configuration file for the Common UNIX Printing System (CUPS).
+ *
+ * @configure_input@
+ *
+ * Copyright 1997-1999 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ */
+
+/*
+ * Version of software...
+ */
+
+#define CUPS_SVERSION "CUPS v1.0"
+
+/*
+ * Where are files stored?
+ */
+
+#define CUPS_LOCALEDIR "/usr/lib/locale"
+#define CUPS_SERVERROOT "/var/cups"
+#define CUPS_DATADIR "/usr/share/cups"
+
+/*
+ * Do we have various image libraries?
+ */
+
+#undef HAVE_LIBPNG
+#undef HAVE_LIBZ
+#undef HAVE_LIBJPEG
+#undef HAVE_LIBTIFF
+
+/*
+ * Does this machine store words in big-endian (MSB-first) order?
+ */
+
+#undef WORDS_BIGENDIAN
+
+/*
+ * Which directory functions and headers do we use?
+ */
+
+#undef HAVE_DIRENT_H
+#undef HAVE_SYS_DIR_H
+#undef HAVE_SYS_NDIR_H
+#undef HAVE_NDIR_H
+
+/*
+ * Do we have <shadow.h>?
+ */
+
+#undef HAVE_SHADOW_H
+
+/*
+ * Do we have <crypt.h>?
+ */
+
+#undef HAVE_CRYPT_H
+
+/*
+ * Do we have the strXXX() functions?
+ */
+
+#undef HAVE_STRDUP
+#undef HAVE_STRCASECMP
+#undef HAVE_STRNCASECMP
+
+/*
+ * What signal functions to use?
+ */
+
+#undef HAVE_SIGSET
+#undef HAVE_SIGACTION
+
+/*
+ * What wait functions to use?
+ */
+
+#undef HAVE_WAITPID
+#undef HAVE_WAIT3
+
+/*
+ * End of "$Id$".
+ */
diff --git a/configure.in b/configure.in
new file mode 100644
index 000000000..1b4c98ab3
--- /dev/null
+++ b/configure.in
@@ -0,0 +1,316 @@
+dnl
+dnl "$Id$"
+dnl
+dnl Configuration script for the Common UNIX Printing System (CUPS).
+dnl
+dnl Copyright 1997-1999 by Easy Software Products, all rights reserved.
+dnl
+dnl These coded instructions, statements, and computer programs are the
+dnl property of Easy Software Products and are protected by Federal
+dnl copyright law. Distribution and use rights are outlined in the file
+dnl "LICENSE.txt" which should have been included with this file. If this
+dnl file is missing or damaged please contact Easy Software Products
+dnl at:
+dnl
+dnl Attn: CUPS Licensing Information
+dnl Easy Software Products
+dnl 44141 Airport View Drive, Suite 204
+dnl Hollywood, Maryland 20636-3111 USA
+dnl
+dnl Voice: (301) 373-9603
+dnl EMail: cups-info@cups.org
+dnl WWW: http://www.cups.org
+dnl
+
+AC_INIT(cups/cups.h)
+AC_CONFIG_HEADER(config.h)
+AC_PREFIX_DEFAULT(/usr)
+
+dnl Get the operating system and version number...
+
+uname=`uname`
+uversion=`uname -r | sed -e '1,$s/\.//g'`
+if test "$uname" = "IRIX64"; then
+ uname="IRIX"
+fi
+
+dnl Clear the debugging and non-shared library options unless the user asks
+dnl for them...
+
+OPTIM=""
+AC_SUBST(OPTIM)
+PICFLAG=1
+CFLAGS="${CFLAGS:=}"
+
+AC_ARG_ENABLE(debug, [ --enable-debug turn on debugging [default=no]],[if eval "test x$enable_debug = xyes"; then
+ OPTIM="-g "
+fi])
+AC_ARG_ENABLE(shared, [ --disable-shared turn off shared libraries [default=no]])
+if test "$disable_shared" != "yes"; then
+ case "$uname" in
+ SunOS* | UNIX_S*)
+ LIBCUPS="libcups.so.1"
+ LIBCUPSIMAGE="libcupsimage.so.1"
+ DSO="\$(CC) -Wl,-h,\$@ -G \$(OPTIM) -o"
+ ;;
+ HP-UX*)
+ LIBCUPS="libcups.sl.1"
+ LIBCUPSIMAGE="libcupsimage.sl.1"
+ DSO="ld -b -z +h \$@ -o"
+ ;;
+ OSF1* | Linux* | FreeBSD*)
+ LIBCUPS="libcups.so.1"
+ LIBCUPSIMAGE="libcupsimage.so.1"
+ DSO="\$(CC) -Wl,-soname,\$@ -shared \$(OPTIM) -o"
+ ;;
+ IRIX*)
+ LIBCUPS="libcups.so.1"
+ LIBCUPSIMAGE="libcupsimage.so.1"
+ DSO="\$(CC) -soname \$@ -shared \$(OPTIM) -o"
+ ;;
+ *)
+ echo "Warning: shared libraries may not be supported. Trying -shared"
+ echo " option with compiler."
+ LIBCUPS="libcups.so.1"
+ LIBCUPSIMAGE="libcupsimage.so.1"
+ DSO="\$(CC) -Wl,-soname,\$@ -shared \$(OPTIM) -o"
+ ;;
+ esac
+else
+ PICFLAG=0
+ LIBCUPS="libcups.a"
+ LIBCUPSIMAGE="libcupsimage.a"
+ DSO=":"
+fi
+
+dnl Checks for programs...
+AC_PROG_AWK
+AC_PROG_CC
+AC_PROG_CPP
+AC_PROG_RANLIB
+AC_PATH_PROG(AR,ar)
+AC_PATH_PROG(CHMOD,chmod)
+AC_PATH_PROG(CP,cp)
+AC_PATH_PROG(NROFF,nroff)
+if test "$NROFF" = ""; then
+ AC_PATH_PROG(GROFF,groff)
+ if test "$GROFF" = ""; then
+ NROFF="echo"
+ else
+ NROFF="$GROFF -T ascii"
+ fi
+fi
+AC_PATH_PROG(HTMLDOC,htmldoc)
+AC_PATH_PROG(MKDIR,mkdir)
+AC_PATH_PROG(PACK,pack)
+if test "$PACK" = ""; then
+ AC_PATH_PROG(PACK,gzip)
+ if test "$PACK" = ""; then
+ PACK="echo"
+ CAT="dummy"
+ else
+ PACK="$PACK -fv9"
+ CAT="gz"
+ fi
+else
+ PACK="$PACK -f"
+ CAT="z"
+fi
+AC_SUBST(CAT)
+AC_PATH_PROG(RM,rm)
+AC_PATH_PROG(SED,sed)
+AC_PATH_PROG(SMBCLIENT,smbclient)
+if test "$SMBCLIENT" = ""; then
+ echo "Looking for smbclient in standard locations..."
+ AC_PATH_PROG(SMBCLIENT,smbclient,samba_not_detected,
+ /usr/samba/bin:/usr/local/samba/bin:/usr/freeware/samba/bin:/opt/samba/bin)
+fi
+
+dnl Architecture checks...
+AC_C_BIGENDIAN
+
+dnl Check for libraries...
+AC_CHECK_LIB(c,crypt,LIBS="$LIBS",AC_CHECK_LIB(crypt,crypt))
+AC_CHECK_HEADER(crypt.h, AC_DEFINE(HAVE_CRYPT_H))
+AC_CHECK_LIB(sec,getspent)
+
+NETLIBS=""
+AC_SUBST(NETLIBS)
+AC_CHECK_LIB(socket,socket,
+if test "$uname" != "IRIX"; then
+ NETLIBS="-lsocket"
+else
+ echo "Not using -lsocket since you are running IRIX."
+fi)
+AC_CHECK_LIB(nsl,gethostbyaddr,
+if test "$uname" != "IRIX"; then
+ NETLIBS="$NETLIBS -lnsl"
+else
+ echo "Not using -lnsl since you are running IRIX."
+fi)
+
+LIBJPEG=""
+LIBPNG=""
+LIBTIFF=""
+LIBZ=""
+
+AC_SUBST(LIBJPEG)
+AC_SUBST(LIBPNG)
+AC_SUBST(LIBTIFF)
+AC_SUBST(LIBZ)
+
+AC_CHECK_HEADER(jpeglib.h,
+ AC_DEFINE(HAVE_LIBJPEG)
+ LIBJPEG="-ljpeg")
+AC_CHECK_HEADER(png.h,
+ AC_DEFINE(HAVE_LIBPNG)
+ LIBPNG="-lpng")
+AC_CHECK_HEADER(tiff.h,
+ AC_DEFINE(HAVE_LIBTIFF)
+ LIBTIFF="-ltiff")
+AC_CHECK_HEADER(zlib.h,
+ AC_DEFINE(HAVE_LIBZ)
+ LIBZ="-lz")
+
+dnl Checks for header files.
+AC_HEADER_STDC
+AC_HEADER_DIRENT
+AC_CHECK_HEADER(shadow.h,AC_DEFINE(HAVE_SHADOW_H))
+
+dnl Checks for string functions.
+AC_CHECK_FUNCS(strdup)
+AC_CHECK_FUNCS(strcasecmp)
+AC_CHECK_FUNCS(strncasecmp)
+
+dnl Checks for signal functions.
+AC_CHECK_FUNCS(sigset)
+AC_CHECK_FUNCS(sigaction)
+
+dnl Checks for wait functions.
+AC_CHECK_FUNCS(waitpid)
+AC_CHECK_FUNCS(wait3)
+
+dnl Update compiler options...
+if test -n "$GXX"; then
+ if test -z "$OPTIM"; then
+ OPTIM="-O2"
+ fi
+ if test $PICFLAG = 1; then
+ OPTIM="-fPIC $OPTIM"
+ fi
+else
+ case $uname in
+ IRIX*)
+ if test -z "$OPTIM"; then
+ OPTIM="-O2"
+ fi
+ if test $uversion -ge 62; then
+ OPTIM="$OPTIM -n32 -mips3"
+ fi
+ ;;
+ HP-UX*)
+ if test -z "$OPTIM"; then
+ OPTIM="+O2"
+ fi
+ OPTIM="-Ae $OPTIM"
+ ;;
+ SunOS*)
+ # Solaris
+ if test -z "$OPTIM"; then
+ OPTIM="-O"
+ fi
+ if test $PICFLAG = 1; then
+ OPTIM="-KPIC $OPTIM"
+ fi
+ ;;
+ *)
+ # Running some other operating system; inform the user they
+ # should contribute the necessary options to
+ # cups-support@cups.org...
+ echo "Building CUPS with default compiler optimizations; contact"
+ echo "cups-support@cups.org with uname and compiler options needed"
+ echo "for your platform, or set the CFLAGS environment variable"
+ echo "before running configure."
+ ;;
+ esac
+fi
+
+if test "$DSO" != ":"; then
+ # When using DSOs the image libraries are linked to libcupsimage.so
+ # rather than to the executables. This makes things smaller if you
+ # are using any static libraries, and it also allows us to distribute
+ # a single DSO rather than a bunch...
+ DSOLIBS="\$(LIBJPEG) \$(LIBPNG) \$(LIBTIFF) \$(LIBZ)"
+ IMGLIBS=""
+
+ # The HP-UX and Solaris run-time linkers are EXTREMELY stupid when
+ # it comes to deciding where to find a DSO. Add linker options to
+ # tell them where to find the DSO (usually in /usr/lib... duh!)
+ case $uname in
+ HP-UX*)
+ LDFLAGS="$LDFLAGS -Wl,+b,$libdir,+fb"
+ ;;
+ SunOS*)
+ # Solaris
+ LDFLAGS="-R$libdir"
+ ;;
+ esac
+else
+ DSOLIBS=""
+ IMGLIBS="\$(LIBJPEG) \$(LIBPNG) \$(LIBTIFF) \$(LIBZ)"
+fi
+
+AC_SUBST(DSO)
+AC_SUBST(DSOLIBS)
+AC_SUBST(IMGLIBS)
+AC_SUBST(LIBCUPS)
+AC_SUBST(LIBCUPSIMAGE)
+
+dnl Fix "prefix" variable if it hasn't been specified...
+if test "$prefix" = "NONE"; then
+ prefix="/usr"
+fi
+
+dnl Fix "libdir" variable for IRIX 6.x...
+if test "$uname" = "IRIX" -a $uversion -ge 62; then
+ libdir="/usr/lib32"
+fi
+
+dnl CUPS_SERVERROOT needs special attention for the default location...
+if test "$prefix" = "/usr"; then
+ CUPS_SERVERROOT="/var/cups"
+else
+ CUPS_SERVERROOT="$prefix/var/cups"
+fi
+AC_DEFINE_UNQUOTED(CUPS_SERVERROOT, "$CUPS_SERVERROOT")
+AC_SUBST(CUPS_SERVERROOT)
+
+dnl Set the CUPS_LOCALE directory...
+case "$uname" in
+ Linux)
+ CUPS_LOCALEDIR="$prefix/share/locale"
+ ;;
+
+ OSF1)
+ CUPS_LOCALEDIR="$prefix/lib/nls/msg"
+ ;;
+
+ *)
+ # This is the standard System V location...
+ CUPS_LOCALEDIR="$prefix/lib/locale"
+ ;;
+esac
+
+AC_DEFINE_UNQUOTED(CUPS_LOCALEDIR, "$CUPS_LOCALEDIR")
+AC_SUBST(CUPS_LOCALEDIR)
+
+dnl Set the CUPS_DATAFILE directory...
+CUPS_DATADIR="$prefix/share/cups"
+AC_DEFINE_UNQUOTED(CUPS_DATADIR, "$CUPS_DATADIR")
+AC_SUBST(CUPS_DATADIR)
+
+AC_OUTPUT(Makedefs)
+
+dnl
+dnl End of "$Id$".
+dnl
diff --git a/cups.dsw b/cups.dsw
new file mode 100644
index 000000000..e332b183d
--- /dev/null
+++ b/cups.dsw
@@ -0,0 +1,113 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "cups"=.\cups\cups.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Project: "hpgltops"=.\filter\hpgltops.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+ Begin Project Dependency
+ Project_Dep_Name cups
+ End Project Dependency
+}}}
+
+###############################################################################
+
+Project: "image"=.\filter\image.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Project: "pstops"=.\filter\pstops.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Project: "testmime"=.\cups\testmime.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+ Begin Project Dependency
+ Project_Dep_Name cups
+ End Project Dependency
+}}}
+
+###############################################################################
+
+Project: "testppd"=.\cups\testppd.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+ Begin Project Dependency
+ Project_Dep_Name cups
+ End Project Dependency
+}}}
+
+###############################################################################
+
+Project: "texttops"=.\filter\texttops.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+ Begin Project Dependency
+ Project_Dep_Name cups
+ End Project Dependency
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
diff --git a/cups.sh b/cups.sh
new file mode 100755
index 000000000..16c6d3fad
--- /dev/null
+++ b/cups.sh
@@ -0,0 +1,112 @@
+#!/bin/sh
+#
+# "$Id$"
+#
+# Startup/shutdown script for the Common UNIX Printing System (CUPS).
+#
+# Linux chkconfig stuff:
+#
+# chkconfig: 2345 60 60
+# description: Startup/shutdown script for the Common UNIX \
+# Printing System (CUPS).
+#
+# Copyright 1997-1999 by Easy Software Products, all rights reserved.
+#
+# These coded instructions, statements, and computer programs are the
+# property of Easy Software Products and are protected by Federal
+# copyright law. Distribution and use rights are outlined in the file
+# "LICENSE.txt" which should have been included with this file. If this
+# file is missing or damaged please contact Easy Software Products
+# at:
+#
+# Attn: CUPS Licensing Information
+# Easy Software Products
+# 44141 Airport View Drive, Suite 204
+# Hollywood, Maryland 20636-3111 USA
+#
+# Voice: (301) 373-9603
+# EMail: cups-info@cups.org
+# WWW: http://www.cups.org
+#
+
+# See what program to use for configuration stuff...
+case "`uname`" in
+ IRIX*)
+ IS_ON=/sbin/chkconfig
+ ;;
+
+ *)
+ IS_ON=/bin/true
+ ;;
+esac
+
+# The verbose flag controls the printing of the names of
+# daemons as they are started.
+if $IS_ON verbose; then
+ ECHO=echo
+else
+ ECHO=:
+fi
+
+# See if the CUPS server is running...
+case "`uname`" in
+ IRIX* | HP-UX | SunOS)
+ pid=`ps -e | awk '{print $1,$4}' | grep cupsd | awk '{print $1}'`
+ ;;
+ OSF1)
+ pid=`ps -e | awk '{print $1,$5}' | grep cupsd | awk '{print $1}'`
+ ;;
+ Linux)
+ pid=`ps ax | awk '{print $1,$5}' | grep cupsd | awk '{print $1}'`
+ ;;
+ *)
+ pid=""
+ ;;
+esac
+
+# Start or stop the CUPS server based upon the first argument to the script.
+case $1 in
+ start | restart | reload)
+ if test "$pid" != ""; then
+ if $IS_ON cups; then
+ kill -HUP $pid
+ $ECHO "cups: scheduler restarted."
+ else
+ kill $pid
+ $ECHO "cups: scheduler stopped."
+ fi
+ else
+ if $IS_ON cups; then
+ /usr/sbin/cupsd 2>&1 >/dev/null &
+ $ECHO "cups: scheduler started."
+ fi
+ fi
+ ;;
+
+ stop)
+ if test "$pid" != ""; then
+ kill $pid
+ $ECHO "cups: scheduler stopped."
+ fi
+ ;;
+
+ status)
+ if test "$pid" != ""; then
+ echo "cups: Scheduler is running."
+ else
+ echo "cups: Scheduler is not running."
+ fi
+ ;;
+
+ *)
+ echo "Usage: cups {reload|restart|start|status|stop}"
+ exit 1
+ ;;
+esac
+
+exit 0
+
+
+#
+# End of "$Id$".
+#
diff --git a/cups/Makefile b/cups/Makefile
new file mode 100644
index 000000000..14fe89b7f
--- /dev/null
+++ b/cups/Makefile
@@ -0,0 +1,150 @@
+#
+# "$Id$"
+#
+# Support library Makefile for the Common UNIX Printing System (CUPS).
+#
+# Copyright 1997-1999 by Easy Software Products, all rights reserved.
+#
+# These coded instructions, statements, and computer programs are the
+# property of Easy Software Products and are protected by Federal
+# copyright law. Distribution and use rights are outlined in the file
+# "LICENSE.txt" which should have been included with this file. If this
+# file is missing or damaged please contact Easy Software Products
+# at:
+#
+# Attn: CUPS Licensing Information
+# Easy Software Products
+# 44141 Airport View Drive, Suite 204
+# Hollywood, Maryland 20636-3111 USA
+#
+# Voice: (301) 373-9603
+# EMail: cups-info@cups.org
+# WWW: http://www.cups.org
+#
+
+include ../Makedefs
+
+#
+# Object files...
+#
+
+LIBOBJS = emit.o filter.o http.o ipp.o language.o mark.o mime.o \
+ options.o page.o ppd.o raster.o string.o type.o usersys.o \
+ util.o
+OBJS = $(LIBOBJS) testhttp.o testmime.o testppd.o
+
+#
+# Header files to install...
+#
+
+HEADERS = cups.h http.h ipp.h language.h mime.h ppd.h raster.h
+
+#
+# Targets in this directory...
+#
+
+TARGETS = $(LIBCUPS) testhttp testmime testppd
+
+#
+# Make all targets...
+#
+
+all: $(TARGETS)
+
+#
+# Remove object and target files...
+#
+
+clean:
+ rm -f $(OBJS) $(TARGETS)
+
+#
+# Install object and target files...
+#
+
+install: all
+ -$(MKDIR) $(INCLUDEDIR)/cups
+ $(CP) $(HEADERS) $(INCLUDEDIR)/cups
+ -$(MKDIR) $(LIBDIR)
+ $(CP) $(LIBCUPS) $(LIBDIR)
+ if test $(LIBCUPS) != "libcups.a"; then \
+ $(LN) $(LIBCUPS) `basename $(LIBCUPS) .1`; \
+ fi
+
+#
+# libcups.so.1, libcups.sl.1
+#
+
+libcups.so.1 libcups.sl.1: $(LIBOBJS) ../Makedefs
+ echo Linking $@...
+ $(DSO) $@ $(LIBOBJS)
+ -$(LN) $@ `basename $@ .1`
+
+#
+# libcups.a
+#
+
+libcups.a: $(LIBOBJS)
+ echo Archiving $@...
+ $(RM) $@
+ $(AR) $(ARFLAGS) $@ $(LIBOBJS)
+ $(RANLIB) $@
+
+#
+# cups_C.h - the default POSIX locale that is compiled in.
+#
+
+cups_C.h: ../locale/C/cups_C
+ echo Generating $@...
+ $(RM) cups_C.h
+ $(AWK) '{print "\"" $$0 "\","}' < ../locale/C/cups_C > cups_C.h
+
+emit.o: ppd.h ../config.h ../Makedefs
+filter.o: mime.h ../config.h ../Makedefs
+http.o: http.h ipp.h string.h ../config.h ../Makedefs
+ipp.o: http.h ipp.h ../config.h ../Makedefs
+language.o: cups_C.h language.h string.h ../config.h ../Makedefs
+mark.o: ppd.h ../config.h ../Makedefs
+mime.o: mime.h ../config.h ../Makedefs
+options.o: cups.h ../config.h ../Makedefs
+page.o: ppd.h ../config.h ../Makedefs
+ppd.o: language.h ppd.h ../config.h ../Makedefs
+raster.o: raster.h ../config.h ../Makedefs
+string.o: string.h ../config.h ../Makedefs
+type.o: mime.h ../config.h ../Makedefs
+usersys.o: cups.h ../config.h ../Makedefs
+util.o: cups.h http.h ipp.h ../config.h ../Makedefs
+
+#
+# testhttp (dependency on static CUPS library is intentional)
+#
+
+testhttp: testhttp.o libcups.a
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o $@ testhttp.o libcups.a $(NETLIBS)
+
+testhttp.o: http.h ../Makedefs
+
+#
+# testmime (dependency on static CUPS library is intentional)
+#
+
+testmime: testmime.o libcups.a
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o $@ testmime.o libcups.a
+
+testmime.o: mime.h ../Makedefs
+
+#
+# testppd (dependency on static CUPS library is intentional)
+#
+
+testppd: testppd.o libcups.a
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o $@ testppd.o libcups.a $(NETLIBS)
+
+testppd.o: ppd.h ../Makedefs
+
+#
+# End of "$Id$".
+#
diff --git a/cups/cups.dsp b/cups/cups.dsp
new file mode 100644
index 000000000..304345558
--- /dev/null
+++ b/cups/cups.dsp
@@ -0,0 +1,176 @@
+# Microsoft Developer Studio Project File - Name="cups" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Static Library" 0x0104
+
+CFG=cups - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "cups.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "cups.mak" CFG="cups - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "cups - Win32 Release" (based on "Win32 (x86) Static Library")
+!MESSAGE "cups - Win32 Debug" (based on "Win32 (x86) Static Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "cups - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
+# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\visualc" /I ".." /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo /out:"cups.lib"
+
+!ELSEIF "$(CFG)" == "cups - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I "..\visualc" /I ".." /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo /out:"cupsd.lib"
+
+!ENDIF
+
+# Begin Target
+
+# Name "cups - Win32 Release"
+# Name "cups - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\emit.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\filter.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\http.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\ipp.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\language.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\mark.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\mime.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\options.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\page.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\ppd.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\raster.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\string.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\type.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\util.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\cups.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\http.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ipp.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\language.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\mime.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ppd.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\raster.h
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/cups/cups.h b/cups/cups.h
new file mode 100644
index 000000000..9219f6606
--- /dev/null
+++ b/cups/cups.h
@@ -0,0 +1,143 @@
+/*
+ * "$Id$"
+ *
+ * API definitions for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-1999 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ */
+
+#ifndef _CUPS_CUPS_H_
+# define _CUPS_CUPS_H_
+
+/*
+ * Include necessary headers...
+ */
+
+# include <cups/ipp.h>
+# include <cups/mime.h>
+# include <cups/ppd.h>
+
+
+/*
+ * C++ magic...
+ */
+
+# ifdef __cplusplus
+extern "C" {
+# endif /* __cplusplus */
+
+
+/*
+ * Constants...
+ */
+
+# define CUPS_VERSION 1.0
+# define CUPS_DATE_ANY -1
+
+
+/*
+ * Types and structures...
+ */
+
+typedef unsigned cups_ptype_t; /**** Printer Type/Capability Bits ****/
+enum /* Not a typedef'd enum so we can OR */
+{
+ CUPS_PRINTER_LOCAL = 0x0000, /* Local printer or class */
+ CUPS_PRINTER_CLASS = 0x0001, /* Printer class */
+ CUPS_PRINTER_REMOTE = 0x0002, /* Remote printer or class */
+ CUPS_PRINTER_BW = 0x0004, /* Can do B&W printing */
+ CUPS_PRINTER_COLOR = 0x0008, /* Can do color printing */
+ CUPS_PRINTER_DUPLEX = 0x0010, /* Can do duplexing */
+ CUPS_PRINTER_STAPLE = 0x0020, /* Can staple output */
+ CUPS_PRINTER_COPIES = 0x0040, /* Can do copies */
+ CUPS_PRINTER_COLLATE = 0x0080, /* Can collage copies */
+ CUPS_PRINTER_PUNCH = 0x0100, /* Can punch output */
+ CUPS_PRINTER_COVER = 0x0200, /* Can cover output */
+ CUPS_PRINTER_BIND = 0x0400, /* Can bind output */
+ CUPS_PRINTER_SORT = 0x0800, /* Can sort output */
+ CUPS_PRINTER_SMALL = 0x1000, /* Can do Letter/Legal/A4 */
+ CUPS_PRINTER_MEDIUM = 0x2000, /* Can do Tabloid/B/C/A3/A2 */
+ CUPS_PRINTER_LARGE = 0x4000, /* Can do D/E/A1/A0 */
+ CUPS_PRINTER_VARIABLE = 0x8000, /* Can do variable sizes */
+ CUPS_PRINTER_IMPLICIT = 0x10000, /* Implicit class */
+ CUPS_PRINTER_OPTIONS = 0xfffc /* ~(CLASS | REMOTE | IMPLICIT) */
+};
+
+
+/*
+ * Types & structures...
+ */
+
+typedef struct /**** Printer Information ****/
+{
+ char name[IPP_MAX_NAME], /* Printer or class name */
+ uri[HTTP_MAX_URI]; /* Universal resource identifier */
+ unsigned char info[IPP_MAX_NAME], /* Printer or class info/description */
+ location[IPP_MAX_NAME]; /* Location text */
+ ipp_pstate_t state; /* Printer state */
+ unsigned char message[IPP_MAX_NAME]; /* State text */
+ cups_ptype_t type; /* Printer type/capability codes */
+} cups_browse_t;
+
+typedef struct /**** Printer Options ****/
+{
+ char *name; /* Name of option */
+ char *value; /* Value of option */
+} cups_option_t;
+
+
+/*
+ * Functions...
+ */
+
+extern int cupsCancelJob(const char *printer, int job);
+#define cupsDoRequest(http,request,resource) cupsDoFileRequest((http),(request),(resource),NULL)
+extern ipp_t *cupsDoFileRequest(http_t *http, ipp_t *request,
+ const char *resource, const char *filename);
+extern int cupsGetClasses(char ***classes);
+extern const char *cupsGetDefault(void);
+extern const char *cupsGetPPD(const char *printer);
+extern int cupsGetPrinters(char ***printers);
+extern int cupsPrintFile(const char *printer, const char *filename,
+ const char *title, int num_options,
+ cups_option_t *options);
+extern char *cupsTempFile(char *filename, int len);
+extern int cupsAddOption(const char *name, const char *value,
+ int num_options, cups_option_t **options);
+extern void cupsFreeOptions(int num_options, cups_option_t *options);
+extern const char *cupsGetOption(const char *name, int num_options,
+ cups_option_t *options);
+extern int cupsParseOptions(const char *arg, int num_options,
+ cups_option_t **options);
+extern int cupsMarkOptions(ppd_file_t *ppd, int num_options,
+ cups_option_t *options);
+
+extern const char *cupsGetPassword(const char *prompt);
+extern const char *cupsServer(void);
+extern const char *cupsUser(void);
+
+# ifdef __cplusplus
+}
+# endif /* __cplusplus */
+
+#endif /* !_CUPS_CUPS_H_ */
+
+/*
+ * End of "$Id$".
+ */
diff --git a/cups/cups_C.h b/cups/cups_C.h
new file mode 100644
index 000000000..ede3c9ab7
--- /dev/null
+++ b/cups/cups_C.h
@@ -0,0 +1,123 @@
+"us-ascii",
+"OK",
+"Cancel",
+"Help",
+"Quit",
+"Close",
+"Yes",
+"No",
+"On",
+"Off",
+"Save",
+"Discard",
+"Default",
+"Options",
+"More Info",
+"Black",
+"Color",
+"Cyan",
+"Magenta",
+"Yellow",
+"Copyright 1993-1999 by Easy Software Products, All Rights Reserved.",
+"General",
+"Printer",
+"Image Options",
+"HP-GL/2 Options",
+"Extra",
+"Document",
+"Other",
+"Print Pages: ",
+"Entire Document",
+"Page Range:",
+"Reverse Order: ",
+"Page Format: ",
+" 1-Up",
+" 2-Up",
+" 4-Up",
+"Image Scaling: ",
+"Use Natural Image Size",
+"Zoom by Percent",
+"Zoom by PPI",
+"Mirror Image: ",
+"Color Saturation: ",
+"Color Hue: ",
+"Fit to Page: ",
+"Shading: ",
+"Pen Width: ",
+"Gamma Correction: ",
+"Brightness: ",
+"Add",
+"Delete",
+"Modify",
+"Printer URI",
+"Printer Name",
+"Printer Location",
+"Printer Info",
+"Printer Make and Model",
+"Device URI",
+"Formatting Page",
+"Printing Page",
+"Initializing Printer",
+"Printer State",
+"Accepting Jobs",
+"Not Accepting Jobs",
+"Print Jobs",
+"Class",
+"Local",
+"Remote",
+"Duplexing",
+"Stapling",
+"Fast Copies",
+"Collated Copies",
+"Hole Punching",
+"Covering",
+"Binding",
+"Sorting",
+"Small (up to 9.5x14in)",
+"Medium (9.5x14in to 13x19in)",
+"Large (13x19in and larger)",
+"Custom Size",
+"Idle",
+"Processing",
+"Stopped",
+"All",
+"Odd",
+"Even Pages",
+"Darker Lighter",
+"Media Size",
+"Media Type",
+"Media Source",
+"Orientation: ",
+"Portrait",
+"Landscape",
+"Job State",
+"Job Name",
+"User Name",
+"Priority",
+"Copies",
+"File Size",
+"Pending",
+"Output Mode",
+"Resolution",
+"400 Your browser sent a request that this server could not understand.",
+"This server could not verify that you are authorized to access the resource.",
+"You must pay to access this server.",
+"You don't have permission to access the resource on this server.",
+"The requested resource was not found on this server.",
+"The requested method is not allowed with the resource.",
+"An appropriate representation for the resource was not found on this server.",
+"You don't have permission to use this server as a proxy host.",
+"The request has taken too long to complete and has been aborted.",
+"The requested resource has more than one value.",
+"The requested resource is gone and has not been replaced.",
+"The requested method requires a valid Content-Length.",
+"The precondition on the request evaluated to false.",
+"The request is too large for this server to process.",
+"The request URI is too large for this server to process.",
+"The request format is not understood by this server.",
+"500 The server has detected an unrecoverable error and cannot process your request.",
+"The requested method is not implemented by this server.",
+"The proxy server received an invalid response from an upstream server.",
+"The requested resource is currently unavailable on this server.",
+"The proxy server has taken too long to respond to this server.",
+"This server does not support the HTTP version required by your browser.",
diff --git a/cups/debug.h b/cups/debug.h
new file mode 100644
index 000000000..415fb5f85
--- /dev/null
+++ b/cups/debug.h
@@ -0,0 +1,57 @@
+/*
+ * "$Id$"
+ *
+ * Debugging macros for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-1999 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ */
+
+#ifndef _CUPS_DEBUG_H_
+# define _CUPS_DEBUG_H_
+
+/*
+ * Include necessary headers...
+ */
+
+# include <stdio.h>
+
+/*
+ * The debug macros are used if you compile with DEBUG defined.
+ *
+ * Usage:
+ *
+ * DEBUG_puts("string")
+ * DEBUG_printf(("format string", arg, arg, ...));
+ *
+ * Note the extra parenthesis around the DEBUG_printf macro...
+ */
+
+# ifdef DEBUG
+# define DEBUG_puts(x) puts(x)
+# define DEBUG_printf(x) printf x
+# else
+# define DEBUG_puts(x)
+# define DEBUG_printf(x)
+# endif /* DEBUG */
+
+#endif /* !_CUPS_DEBUG_H_ */
+
+/*
+ * End of "$Id$".
+ */
diff --git a/cups/emit.c b/cups/emit.c
new file mode 100644
index 000000000..b8681e69b
--- /dev/null
+++ b/cups/emit.c
@@ -0,0 +1,301 @@
+/*
+ * "$Id$"
+ *
+ * PPD code emission routines for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-1999 by Easy Software Products, all rights reserved.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * PostScript is a trademark of Adobe Systems, Inc.
+ *
+ * Contents:
+ *
+ * ppdEmit() - Emit code for marked options to a file.
+ * ppdEmitFd() - Emit code for marked options to a file.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "ppd.h"
+#include <stdlib.h>
+#include "string.h"
+
+#if defined(WIN32) || defined(__EMX__)
+# include <io.h>
+#else
+# include <unistd.h>
+#endif /* WIN32 || __EMX__ */
+
+
+/*
+ * Local functions...
+ */
+
+static int ppd_sort(ppd_choice_t **c1, ppd_choice_t **c2);
+static int ppd_collect(ppd_file_t *ppd, ppd_section_t section,
+ ppd_choice_t ***choices);
+
+
+/*
+ * 'ppdEmit()' - Emit code for marked options to a file.
+ */
+
+int /* O - 0 on success, -1 on failure */
+ppdEmit(ppd_file_t *ppd, /* I - PPD file record */
+ FILE *fp, /* I - File to write to */
+ ppd_section_t section) /* I - Section to write */
+{
+ int i, /* Looping var */
+ count; /* Number of choices */
+ ppd_choice_t **choices; /* Choices */
+ ppd_size_t *size; /* Custom page size */
+
+
+ if ((count = ppd_collect(ppd, section, &choices)) == 0)
+ return (0);
+
+ for (i = 0; i < count; i ++)
+ if (section != PPD_ORDER_EXIT && section != PPD_ORDER_JCL)
+ {
+ /*
+ * Send DSC comments with option...
+ */
+
+ if (fprintf(fp, "%%%%BeginFeature: %s %s\n",
+ ((ppd_option_t *)choices[i]->option)->keyword,
+ choices[i]->choice) < 0)
+ {
+ free(choices);
+ return (-1);
+ }
+
+ if (strcmp(((ppd_option_t *)choices[i]->option)->keyword, "PageSize") == 0 &&
+ strcmp(choices[i]->choice, "Custom") == 0)
+ {
+ /*
+ * Variable size; write out standard size options (this should
+ * eventually be changed to use the parameter positions defined
+ * in the PPD file...)
+ */
+
+ size = ppdPageSize(ppd, "Custom");
+ fprintf(fp, "%.0f %.0f 0 0 0\n", size->width, size->length);
+
+ if (choices[i]->code == NULL)
+ {
+ /*
+ * This can happen with certain buggy PPD files that don't include
+ * a CustomPageSize command sequence... We just use a generic
+ * Level 2 command sequence...
+ */
+
+ fputs("pop pop pop\n", fp);
+ fputs("<</PageSize[7 -2 roll]/ImagingBBox null>>setpagedevice\n", fp);
+ }
+ }
+
+ if (choices[i]->code != NULL && choices[i]->code[0] != '\0')
+ {
+ if (fputs(choices[i]->code, fp) < 0)
+ {
+ free(choices);
+ return (-1);
+ }
+
+ if (choices[i]->code[strlen(choices[i]->code) - 1] != '\n')
+ putc('\n', fp);
+ }
+
+ if (fputs("%%EndFeature\n", fp) < 0)
+ {
+ free(choices);
+ return (-1);
+ }
+ }
+ else if (fputs(choices[i]->code, fp) < 0)
+ {
+ free(choices);
+ return (-1);
+ }
+
+ free(choices);
+ return (0);
+}
+
+
+/*
+ * 'ppdEmitFd()' - Emit code for marked options to a file.
+ */
+
+int /* O - 0 on success, -1 on failure */
+ppdEmitFd(ppd_file_t *ppd, /* I - PPD file record */
+ int fd, /* I - File to write to */
+ ppd_section_t section) /* I - Section to write */
+{
+ int i, /* Looping var */
+ count; /* Number of choices */
+ ppd_choice_t **choices; /* Choices */
+ char buf[1024]; /* Output buffer for feature */
+
+
+ if ((count = ppd_collect(ppd, section, &choices)) == 0)
+ return (0);
+
+ for (i = 0; i < count; i ++)
+ if (section != PPD_ORDER_EXIT && section != PPD_ORDER_JCL)
+ {
+ /*
+ * Send DSC comments with option...
+ */
+
+ sprintf(buf, "%%%%BeginFeature: %s %s\n",
+ ((ppd_option_t *)choices[i]->option)->keyword, choices[i]->choice);
+
+ if (write(fd, buf, strlen(buf)) < 1)
+ {
+ free(choices);
+ return (-1);
+ }
+
+ if (write(fd, choices[i]->code, strlen(choices[i]->code)) < 1)
+ {
+ free(choices);
+ return (-1);
+ }
+
+ if (write(fd, "%%EndFeature\n", 13) < 1)
+ {
+ free(choices);
+ return (-1);
+ }
+ }
+ else if (write(fd, choices[i]->code, strlen(choices[i]->code)) < 1)
+ {
+ free(choices);
+ return (-1);
+ }
+
+ free(choices);
+ return (0);
+}
+
+
+/*
+ * 'ppd_sort()' - Sort options by ordering numbers...
+ */
+
+static int /* O - -1 if c1 < c2, 0 if equal, 1 otherwise */
+ppd_sort(ppd_choice_t **c1, /* I - First choice */
+ ppd_choice_t **c2) /* I - Second choice */
+{
+ if (((ppd_option_t *)(*c1)->option)->order < ((ppd_option_t *)(*c2)->option)->order)
+ return (-1);
+ else if (((ppd_option_t *)(*c1)->option)->order > ((ppd_option_t *)(*c2)->option)->order)
+ return (1);
+ else
+ return (0);
+}
+
+
+/*
+ * 'ppd_collect()' - Collect all marked options that reside in the specified
+ * section.
+ */
+
+static int /* O - Number of options marked */
+ppd_collect(ppd_file_t *ppd, /* I - PPD file data */
+ ppd_section_t section, /* I - Section to collect */
+ ppd_choice_t ***choices) /* O - Pointers to choices */
+{
+ int i, j, k, m; /* Looping vars */
+ ppd_group_t *g, /* Current group */
+ *sg; /* Current sub-group */
+ ppd_option_t *o; /* Current option */
+ ppd_choice_t *c; /* Current choice */
+ int count; /* Number of choices collected */
+ ppd_choice_t **collect; /* Collected choices */
+
+
+ if (ppd == NULL)
+ return (0);
+
+ /*
+ * Allocate memory for up to 1000 selected choices...
+ */
+
+ count = 0;
+ collect = calloc(sizeof(ppd_choice_t *), 1000);
+
+ /*
+ * Loop through all options and add choices as needed...
+ */
+
+ for (i = ppd->num_groups, g = ppd->groups; i > 0; i --, g ++)
+ {
+ for (j = g->num_options, o = g->options; j > 0; j --, o ++)
+ if (o->section == section)
+ for (k = o->num_choices, c = o->choices; k > 0; k --, c ++)
+ if (c->marked && count < 1000)
+ {
+ collect[count] = c;
+ count ++;
+ }
+
+ for (j = g->num_subgroups, sg = g->subgroups; j > 0; j --, sg ++)
+ for (k = sg->num_options, o = sg->options; k > 0; k --, o ++)
+ if (o->section == section)
+ for (m = o->num_choices, c = o->choices; m > 0; m --, c ++)
+ if (c->marked && count < 1000)
+ {
+ collect[count] = c;
+ count ++;
+ }
+ }
+
+ /*
+ * If we have more than 1 marked choice, sort them...
+ */
+
+ if (count > 1)
+ qsort(collect, count, sizeof(ppd_choice_t *),
+ (int (*)(const void *, const void *))ppd_sort);
+
+ /*
+ * Return the array and number of choices; if 0, free the array since
+ * it isn't needed.
+ */
+
+ if (count > 0)
+ {
+ *choices = collect;
+ return (count);
+ }
+ else
+ {
+ *choices = NULL;
+ free(collect);
+ return (0);
+ }
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/cups/filter.c b/cups/filter.c
new file mode 100644
index 000000000..39e17091b
--- /dev/null
+++ b/cups/filter.c
@@ -0,0 +1,297 @@
+/*
+ * "$Id$"
+ *
+ * File type conversion routines for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-1999 by Easy Software Products, all rights reserved.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * Contents:
+ *
+ * mimeAddFilter() - Add a filter to the current MIME database.
+ * mimeFilter() - Find the fastest way to convert from one type to another.
+ * compare() - Compare two filter types...
+ * lookup() - Lookup a filter...
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#include "string.h"
+#include "mime.h"
+
+
+/*
+ * Local functions...
+ */
+
+static int compare(mime_filter_t *, mime_filter_t *);
+static mime_filter_t *lookup(mime_t *, mime_type_t *, mime_type_t *);
+
+
+/*
+ * 'mimeAddFilter()' - Add a filter to the current MIME database.
+ */
+
+mime_filter_t * /* O - New filter */
+mimeAddFilter(mime_t *mime, /* I - MIME database */
+ mime_type_t *src, /* I - Source type */
+ mime_type_t *dst, /* I - Destination type */
+ int cost, /* I - Relative time/resource cost */
+ const char *filter) /* I - Filter program to run */
+{
+ mime_filter_t *temp; /* New filter */
+
+
+ /*
+ * Range-check the input...
+ */
+
+ if (mime == NULL || src == NULL || dst == NULL || filter == NULL)
+ return (NULL);
+
+ if (strlen(filter) > (MIME_MAX_FILTER - 1))
+ return (NULL);
+
+ /*
+ * See if we already have an existing filter for the given source and
+ * destination...
+ */
+
+ if ((temp = lookup(mime, src, dst)) != NULL)
+ {
+ /*
+ * Yup, does the existing filter have a higher cost? If so, copy the
+ * filter and cost to the existing filter entry and return it...
+ */
+
+ if (temp->cost > cost)
+ {
+ temp->cost = cost;
+ strcpy(temp->filter, filter);
+ }
+ }
+ else
+ {
+ /*
+ * Nope, add a new one...
+ */
+
+ if (mime->num_filters == 0)
+ temp = malloc(sizeof(mime_filter_t));
+ else
+ temp = realloc(mime->filters, sizeof(mime_filter_t) * (mime->num_filters + 1));
+
+ if (temp == NULL)
+ return (NULL);
+
+ mime->filters = temp;
+ temp += mime->num_filters;
+ mime->num_filters ++;
+
+ /*
+ * Copy the information over and sort if necessary...
+ */
+
+ temp->src = src;
+ temp->dst = dst;
+ temp->cost = cost;
+ strcpy(temp->filter, filter);
+
+ if (mime->num_filters > 1)
+ qsort(mime->filters, mime->num_filters, sizeof(mime_filter_t),
+ (int (*)(const void *, const void *))compare);
+ }
+
+ /*
+ * Return the new/updated filter...
+ */
+
+ return (temp);
+}
+
+
+/*
+ * 'mimeFilter()' - Find the fastest way to convert from one type to another.
+ */
+
+mime_filter_t * /* O - Array of filters to run */
+mimeFilter(mime_t *mime, /* I - MIME database */
+ mime_type_t *src, /* I - Source file type */
+ mime_type_t *dst, /* I - Destination file type */
+ int *num_filters) /* O - Number of filters to run */
+{
+ int i, j, /* Looping vars */
+ num_temp, /* Number of temporary filters */
+ num_mintemp, /* Number of filters in the minimum */
+ cost, /* Current cost */
+ mincost; /* Current minimum */
+ mime_filter_t *temp, /* Temporary filter */
+ *mintemp, /* Current minimum */
+ *mincurrent, /* Current filter for minimum */
+ *current, /* Current filter */
+ *filters; /* Filters to use */
+
+
+ /*
+ * Range-check the input...
+ */
+
+ if (mime == NULL || src == NULL || dst == NULL || num_filters == NULL)
+ return (NULL);
+
+ *num_filters = 0;
+
+ /*
+ * See if there is a filter that can convert the files directly...
+ */
+
+ if ((temp = lookup(mime, src, dst)) != NULL)
+ {
+ /*
+ * Got a direct filter!
+ */
+
+ if ((filters = (mime_filter_t *)malloc(sizeof(mime_filter_t))) == NULL)
+ return (NULL);
+
+ memcpy(filters, temp, sizeof(mime_filter_t));
+ *num_filters = 1;
+ return (filters);
+ }
+
+ /*
+ * OK, now look for filters from the source type to any other type...
+ */
+
+ mincost = 9999999;
+ mintemp = NULL;
+
+ for (i = mime->num_filters, current = mime->filters; i > 0; i --, current ++)
+ if (current->src == src)
+ {
+ /*
+ * See if we have any filters that can convert from the destination type
+ * of this filter to the final type...
+ */
+
+ if ((temp = mimeFilter(mime, current->dst, dst, &num_temp)) == NULL)
+ continue;
+
+ /*
+ * Found a match; see if this one is less costly than the last (if
+ * any...)
+ */
+
+ for (j = 0, cost = 0; j < num_temp; j ++)
+ cost += temp->cost;
+
+ if (cost < mincost)
+ {
+ if (mintemp != NULL)
+ free(mintemp);
+
+ mincost = cost;
+ mintemp = temp;
+ num_mintemp = num_temp;
+ mincurrent = current;
+ }
+ else
+ free(temp);
+ }
+
+ if (mintemp != NULL)
+ {
+ /*
+ * Hey, we got a match! Add the current filter to the beginning of the
+ * filter list...
+ */
+
+ filters = (mime_filter_t *)realloc(mintemp, sizeof(mime_filter_t) *
+ (num_mintemp + 1));
+
+ if (filters == NULL)
+ {
+ *num_filters = 0;
+ return (NULL);
+ }
+
+ memmove(filters + 1, filters, num_mintemp * sizeof(mime_filter_t));
+ memcpy(filters, mincurrent, sizeof(mime_filter_t));
+
+ *num_filters = num_mintemp + 1;
+
+ return (filters);
+ }
+
+ return (NULL);
+}
+
+
+/*
+ * 'compare()' - Compare two filter types...
+ */
+
+static int /* O - Comparison result */
+compare(mime_filter_t *f0, /* I - First filter */
+ mime_filter_t *f1) /* I - Second filter */
+{
+ int i; /* Result of comparison */
+
+
+ if ((i = strcmp(f0->src->super, f1->src->super)) == 0)
+ if ((i = strcmp(f0->src->type, f1->src->type)) == 0)
+ if ((i = strcmp(f0->dst->super, f1->dst->super)) == 0)
+ i = strcmp(f0->dst->type, f1->dst->type);
+
+ return (i);
+}
+
+
+/*
+ * 'lookup()' - Lookup a filter...
+ */
+
+static mime_filter_t * /* O - Filter for src->dst */
+lookup(mime_t *mime, /* I - MIME database */
+ mime_type_t *src, /* I - Source type */
+ mime_type_t *dst) /* I - Destination type */
+{
+ mime_filter_t key; /* Key record for filter search */
+
+
+ if (mime->num_filters == 0)
+ return (NULL);
+
+ key.src = src;
+ key.dst = dst;
+
+ return ((mime_filter_t *)bsearch(&key, mime->filters, mime->num_filters,
+ sizeof(mime_filter_t),
+ (int (*)(const void *, const void *))compare));
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/cups/http.c b/cups/http.c
new file mode 100644
index 000000000..b61ce6ffa
--- /dev/null
+++ b/cups/http.c
@@ -0,0 +1,1446 @@
+/*
+ * "$Id$"
+ *
+ * HTTP routines for the Common UNIX Printing System (CUPS) scheduler.
+ *
+ * Copyright 1997-1999 by Easy Software Products, all rights reserved.
+ *
+ * These statusd instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * Contents:
+ *
+ * httpInitialize() - Initialize the HTTP interface library and set the
+ * default HTTP proxy (if any).
+ * httpCheck() - Check to see if there is a pending response from
+ * the server.
+ * httpClose() - Close an HTTP connection...
+ * httpConnect() - Connect to a HTTP server.
+ * httpReconnect() - Reconnect to a HTTP server...
+ * httpSeparate() - Separate a Universal Resource Identifier into its
+ * components.
+ * httpSetField() - Set the value of an HTTP header.
+ * httpDelete() - Send a DELETE request to the server.
+ * httpGet() - Send a GET request to the server.
+ * httpHead() - Send a HEAD request to the server.
+ * httpOptions() - Send an OPTIONS request to the server.
+ * httpPost() - Send a POST request to the server.
+ * httpPut() - Send a PUT request to the server.
+ * httpTrace() - Send an TRACE request to the server.
+ * httpFlush() - Flush data from a HTTP connection.
+ * httpRead() - Read data from a HTTP connection.
+ * httpWrite() - Write data to a HTTP connection.
+ * httpGets() - Get a line of text from a HTTP connection.
+ * httpPrintf() - Print a formatted string to a HTTP connection.
+ * httpStatus() - Return a short string describing a HTTP status code.
+ * httpGetDateString() - Get a formatted date/time string from a time value.
+ * httpGetDateTime() - Get a time value from a formatted date/time string.
+ * httpUpdate() - Update the current HTTP state for incoming data.
+ * httpDecode64() - Base64-decode a string.
+ * httpEncode64() - Base64-encode a string.
+ * httpGetLength() - Get the amount of data remaining from the
+ * content-length or transfer-encoding fields.
+ * http_field() - Return the field index for a field name.
+ * http_send() - Send a request with all fields and the trailing
+ * blank line.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include "string.h"
+#include <fcntl.h>
+#include <errno.h>
+
+#include "http.h"
+#include "ipp.h"
+#include "debug.h"
+
+#if !defined(WIN32) && !defined(__EMX__)
+# include <signal.h>
+#endif /* !WIN32 && !__EMX__ */
+
+/*
+ * Some operating systems have done away with the Fxxxx constants for
+ * the fcntl() call; this works around that "feature"...
+ */
+
+#ifndef FNONBLK
+# define FNONBLK O_NONBLOCK
+#endif /* !FNONBLK */
+
+
+/*
+ * Local functions...
+ */
+
+static http_field_t http_field(const char *name);
+static int http_send(http_t *http, http_state_t request,
+ const char *uri);
+
+
+/*
+ * Local globals...
+ */
+
+static const char *http_fields[] =
+ {
+ "Accept-Language",
+ "Accept-Ranges",
+ "Authorization",
+ "Connection",
+ "Content-Encoding",
+ "Content-Language",
+ "Content-Length",
+ "Content-Location",
+ "Content-MD5",
+ "Content-Range",
+ "Content-Type",
+ "Content-Version",
+ "Date",
+ "Host",
+ "If-Modified-Since",
+ "If-Unmodified-since",
+ "Keep-Alive",
+ "Last-Modified",
+ "Link",
+ "Location",
+ "Range",
+ "Referer",
+ "Retry-After",
+ "Transfer-Encoding",
+ "Upgrade",
+ "User-Agent",
+ "WWW-Authenticate"
+ };
+static const char *days[7] =
+ {
+ "Sun",
+ "Mon",
+ "Tue",
+ "Wed",
+ "Thu",
+ "Fri",
+ "Sat"
+ };
+static const char *months[12] =
+ {
+ "Jan",
+ "Feb",
+ "Mar",
+ "Apr",
+ "May",
+ "Jun",
+ "Jul",
+ "Aug",
+ "Sep",
+ "Oct",
+ "Nov",
+ "Dec"
+ };
+
+
+/*
+ * 'httpInitialize()' - Initialize the HTTP interface library and set the
+ * default HTTP proxy (if any).
+ */
+
+void
+httpInitialize(void)
+{
+#if defined(WIN32) || defined(__EMX__)
+ WSADATA winsockdata; /* WinSock data */
+ static int initialized = 0;/* Has WinSock been initialized? */
+
+
+ if (!initialized)
+ WSAStartup(MAKEWORD(1,1), &winsockdata);
+#elif defined(HAVE_SIGSET)
+ sigset(SIGPIPE, SIG_IGN);
+#elif defined(HAVE_SIGACTION)
+ struct sigaction action; /* POSIX sigaction data */
+
+
+ /*
+ * Ignore SIGPIPE signals...
+ */
+
+ memset(&action, 0, sizeof(action));
+ action.sa_handler = SIG_IGN;
+ sigaction(SIGPIPE, &action, NULL);
+#else
+ signal(SIGPIPE, SIG_IGN);
+#endif /* WIN32 || __EMX__ */
+}
+
+
+/*
+ * 'httpCheck()' - Check to see if there is a pending response from the server.
+ */
+
+int /* O - 0 = no data, 1 = data available */
+httpCheck(http_t *http) /* I - HTTP connection */
+{
+ fd_set input; /* Input set for select() */
+ struct timeval timeout; /* Timeout */
+
+
+ /*
+ * First see if there is data in the buffer...
+ */
+
+ if (http == NULL)
+ return (0);
+
+ if (http->used)
+ return (1);
+
+ /*
+ * Then try doing a select() to poll the socket...
+ */
+
+ FD_ZERO(&input);
+ FD_SET(http->fd, &input);
+
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 0;
+
+ return (select(http->fd + 1, &input, NULL, NULL, &timeout) > 0);
+}
+
+
+/*
+ * 'httpClose()' - Close an HTTP connection...
+ */
+
+void
+httpClose(http_t *http) /* I - Connection to close */
+{
+ if (http == NULL)
+ return;
+
+#ifdef WIN32
+ closesocket(http->fd);
+#else
+ close(http->fd);
+#endif /* WIN32 */
+
+ free(http);
+}
+
+
+/*
+ * 'httpConnect()' - Connect to a HTTP server.
+ */
+
+http_t * /* O - New HTTP connection */
+httpConnect(const char *host, /* I - Host to connect to */
+ int port) /* I - Port number */
+{
+ http_t *http; /* New HTTP connection */
+ struct hostent *hostaddr; /* Host address data */
+
+
+ httpInitialize();
+
+ /*
+ * Lookup the host...
+ */
+
+ if ((hostaddr = gethostbyname(host)) == NULL)
+ return (NULL);
+
+ /*
+ * Allocate memory for the structure...
+ */
+
+ http = calloc(sizeof(http_t), 1);
+ if (http == NULL)
+ return (NULL);
+
+ http->version = HTTP_1_1;
+ http->blocking = 1;
+ http->activity = time(NULL);
+
+ /*
+ * Copy the hostname and port and then "reconnect"...
+ */
+
+ strcpy(http->hostname, host);
+ memset((char *)&(http->hostaddr), 0, sizeof(http->hostaddr));
+ memcpy((char *)&(http->hostaddr.sin_addr), hostaddr->h_addr, hostaddr->h_length);
+ http->hostaddr.sin_family = hostaddr->h_addrtype;
+#ifdef WIN32
+ http->hostaddr.sin_port = htons((u_short)port);
+#else
+ http->hostaddr.sin_port = htons(port);
+#endif /* WIN32 */
+ if (httpReconnect(http))
+ {
+ free(http);
+ return (NULL);
+ }
+ else
+ return (http);
+}
+
+
+/*
+ * 'httpReconnect()' - Reconnect to a HTTP server...
+ */
+
+int /* O - 0 on success, non-zero on failure */
+httpReconnect(http_t *http) /* I - HTTP data */
+{
+ int val; /* Socket option value */
+
+
+ /*
+ * Close any previously open socket...
+ */
+
+ if (http->fd)
+#ifdef WIN32
+ closesocket(http->fd);
+#else
+ close(http->fd);
+#endif /* WIN32 */
+
+ /*
+ * Create the socket and set options to allow reuse.
+ */
+
+ if ((http->fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
+ return (-1);
+
+#ifdef FD_CLOEXEC
+ fcntl(http->fd, F_SETFD, FD_CLOEXEC); /* Close this socket when starting *
+ * other processes... */
+#endif /* FD_CLOEXEC */
+
+ val = 1;
+ setsockopt(http->fd, SOL_SOCKET, SO_REUSEADDR, (char *)&val, sizeof(val));
+
+#ifdef SO_REUSEPORT
+ val = 1;
+ setsockopt(http->fd, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(val));
+#endif /* SO_REUSEPORT */
+
+ /*
+ * Connect to the server...
+ */
+
+ if (connect(http->fd, (struct sockaddr *)&(http->hostaddr),
+ sizeof(http->hostaddr)) < 0)
+ {
+#ifdef WIN32
+ closesocket(http->fd);
+#else
+ close(http->fd);
+#endif
+
+ return (-1);
+ }
+
+ return (0);
+}
+
+
+/*
+ * 'httpSeparate()' - Separate a Universal Resource Identifier into its
+ * components.
+ */
+
+void
+httpSeparate(const char *uri, /* I - Universal Resource Identifier */
+ char *method, /* O - Method (http, https, etc.) */
+ char *username, /* O - Username */
+ char *host, /* O - Hostname */
+ int *port, /* O - Port number to use */
+ char *resource) /* O - Resource/filename */
+{
+ char *ptr; /* Pointer into string... */
+
+
+ if (uri == NULL || method == NULL || username == NULL || host == NULL ||
+ port == NULL || resource == NULL)
+ return;
+
+ /*
+ * Grab the method portion of the URI...
+ */
+
+ ptr = host;
+ while (*uri != ':' && *uri != '\0')
+ *ptr++ = *uri++;
+
+ *ptr = '\0';
+ if (*uri == ':')
+ uri ++;
+
+ /*
+ * If the method contains a period or slash, then it's probably
+ * hostname/filename...
+ */
+
+ if (strchr(host, '.') != NULL || strchr(host, '/') != NULL || *uri == '\0')
+ {
+ if ((ptr = strchr(host, '/')) != NULL)
+ {
+ strcpy(resource, ptr);
+ *ptr = '\0';
+ }
+ else
+ resource[0] = '\0';
+
+ if (isdigit(*uri))
+ {
+ /*
+ * OK, we have "hostname:port[/resource]"...
+ */
+
+ *port = strtol(uri, (char **)&uri, 10);
+
+ if (*uri == '/')
+ strcpy(resource, uri);
+ }
+ else
+ *port = 0;
+
+ strcpy(method, "http");
+ username[0] = '\0';
+ return;
+ }
+ else
+ strcpy(method, host);
+
+ /*
+ * If the method starts with less than 2 slashes then it is a local resource...
+ */
+
+ if (strncmp(uri, "//", 2) != 0)
+ {
+ strcpy(resource, uri);
+ username[0] = '\0';
+ host[0] = '\0';
+ *port = 0;
+ return;
+ }
+
+ /*
+ * Grab the hostname...
+ */
+
+ while (*uri == '/')
+ uri ++;
+
+ ptr = host;
+ while (*uri != ':' && *uri != '@' && *uri != '/' && *uri != '\0')
+ *ptr ++ = *uri ++;
+
+ *ptr = '\0';
+
+ if (*uri == '@')
+ {
+ /*
+ * Got a username...
+ */
+
+ strcpy(username, host);
+
+ ptr = host;
+ while (*uri != ':' && *uri != '/' && *uri != '\0')
+ *ptr ++ = *uri ++;
+
+ *ptr = '\0';
+ }
+ else
+ username[0] = '\0';
+
+ if (*uri == '\0')
+ {
+ /*
+ * Hostname but no port or path...
+ */
+
+ *port = 0;
+ resource[0] = '/';
+ resource[1] = '\0';
+ return;
+ }
+ else if (*uri == ':')
+ {
+ /*
+ * Parse port number...
+ */
+
+ *port = 0;
+ uri ++;
+ while (isdigit(*uri))
+ {
+ *port = (*port * 10) + *uri - '0';
+ uri ++;
+ }
+ }
+ else
+ {
+ /*
+ * Figure out the default port number based on the method...
+ */
+
+ if (strcasecmp(method, "http") == 0)
+ *port = 80;
+ else if (strcasecmp(method, "https") == 0)
+ *port = 443;
+ else if (strcasecmp(method, "ipp") == 0) /* Not registered yet... */
+ *port = ippPort();
+ else if (strcasecmp(method, "socket") == 0) /* Not registered yet... */
+ *port = 9100;
+ else
+ *port = 0;
+ }
+
+ /*
+ * The remaining portion is the resource string...
+ */
+
+ strcpy(resource, uri);
+}
+
+
+/*
+ * 'httpSetField()' - Set the value of an HTTP header.
+ */
+
+void
+httpSetField(http_t *http, /* I - HTTP data */
+ http_field_t field, /* I - Field index */
+ const char *value) /* I - Value */
+{
+ strncpy(http->fields[field], value, HTTP_MAX_VALUE - 1);
+ http->fields[field][HTTP_MAX_VALUE - 1] = '\0';
+}
+
+
+/*
+ * 'httpDelete()' - Send a DELETE request to the server.
+ */
+
+int /* O - Status of call (0 = success) */
+httpDelete(http_t *http, /* I - HTTP data */
+ const char *uri) /* I - URI to delete */
+{
+ return (http_send(http, HTTP_DELETE, uri));
+}
+
+
+/*
+ * 'httpGet()' - Send a GET request to the server.
+ */
+
+int /* O - Status of call (0 = success) */
+httpGet(http_t *http, /* I - HTTP data */
+ const char *uri) /* I - URI to get */
+{
+ return (http_send(http, HTTP_GET, uri));
+}
+
+
+/*
+ * 'httpHead()' - Send a HEAD request to the server.
+ */
+
+int /* O - Status of call (0 = success) */
+httpHead(http_t *http, /* I - HTTP data */
+ const char *uri) /* I - URI for head */
+{
+ return (http_send(http, HTTP_HEAD, uri));
+}
+
+
+/*
+ * 'httpOptions()' - Send an OPTIONS request to the server.
+ */
+
+int /* O - Status of call (0 = success) */
+httpOptions(http_t *http, /* I - HTTP data */
+ const char *uri) /* I - URI for options */
+{
+ return (http_send(http, HTTP_OPTIONS, uri));
+}
+
+
+/*
+ * 'httpPost()' - Send a POST request to the server.
+ */
+
+int /* O - Status of call (0 = success) */
+httpPost(http_t *http, /* I - HTTP data */
+ const char *uri) /* I - URI for post */
+{
+ httpGetLength(http);
+
+ return (http_send(http, HTTP_POST, uri));
+}
+
+
+/*
+ * 'httpPut()' - Send a PUT request to the server.
+ */
+
+int /* O - Status of call (0 = success) */
+httpPut(http_t *http, /* I - HTTP data */
+ const char *uri) /* I - URI to put */
+{
+ httpGetLength(http);
+
+ return (http_send(http, HTTP_PUT, uri));
+}
+
+
+/*
+ * 'httpTrace()' - Send an TRACE request to the server.
+ */
+
+int /* O - Status of call (0 = success) */
+httpTrace(http_t *http, /* I - HTTP data */
+ const char *uri) /* I - URI for trace */
+{
+ return (http_send(http, HTTP_TRACE, uri));
+}
+
+
+/*
+ * 'httpFlush()' - Flush data from a HTTP connection.
+ */
+
+void
+httpFlush(http_t *http) /* I - HTTP data */
+{
+ char buffer[8192]; /* Junk buffer */
+
+
+ while (httpRead(http, buffer, sizeof(buffer)) > 0);
+}
+
+
+/*
+ * 'httpRead()' - Read data from a HTTP connection.
+ */
+
+int /* O - Number of bytes read */
+httpRead(http_t *http, /* I - HTTP data */
+ char *buffer, /* I - Buffer for data */
+ int length) /* I - Maximum number of bytes */
+{
+ int bytes; /* Bytes read */
+ char len[32]; /* Length string */
+
+
+ DEBUG_printf(("httpRead(%08x, %08x, %d)\n", http, buffer, length));
+
+ if (http == NULL || buffer == NULL)
+ return (-1);
+
+ http->activity = time(NULL);
+
+ if (length <= 0)
+ return (0);
+
+ if (http->data_encoding == HTTP_ENCODE_CHUNKED &&
+ http->data_remaining <= 0 &&
+ (http->state == HTTP_GET_SEND || http->state == HTTP_POST_RECV ||
+ http->state == HTTP_POST_SEND || http->state == HTTP_PUT_RECV))
+ {
+ if (httpGets(len, sizeof(len), http) == NULL)
+ return (0);
+
+ http->data_remaining = strtol(len, NULL, 16);
+ }
+
+ DEBUG_printf(("httpRead: data_remaining = %d\n", http->data_remaining));
+
+ if (http->data_remaining == 0)
+ {
+ /*
+ * A zero-length chunk ends a transfer; unless we are reading POST
+ * data, go idle...
+ */
+
+ if (http->state == HTTP_POST_RECV)
+ http->state ++;
+ else
+ http->state = HTTP_WAITING;
+
+ return (0);
+ }
+ else if (length > http->data_remaining)
+ length = http->data_remaining;
+
+ if (http->used > 0)
+ {
+ if (length > http->used)
+ length = http->used;
+
+ bytes = length;
+
+ DEBUG_printf(("httpRead: grabbing %d bytes from input buffer...\n", bytes));
+
+ memcpy(buffer, http->buffer, length);
+ http->used -= length;
+
+ if (http->used > 0)
+ memcpy(http->buffer, http->buffer + length, http->used);
+ }
+ else
+ {
+ DEBUG_printf(("httpRead: reading %d bytes from socket...\n", length));
+ bytes = recv(http->fd, buffer, length, 0);
+ DEBUG_printf(("httpRead: read %d bytes from socket...\n", bytes));
+ }
+
+ if (bytes > 0)
+ http->data_remaining -= bytes;
+
+ if (http->data_remaining == 0 && http->data_encoding != HTTP_ENCODE_CHUNKED)
+ {
+ if (http->state == HTTP_POST_RECV)
+ http->state ++;
+ else
+ http->state = HTTP_WAITING;
+ }
+
+ return (bytes);
+}
+
+
+/*
+ * 'httpWrite()' - Write data to a HTTP connection.
+ */
+
+int /* O - Number of bytes written */
+httpWrite(http_t *http, /* I - HTTP data */
+ const char *buffer, /* I - Buffer for data */
+ int length) /* I - Number of bytes to write */
+{
+ int tbytes, /* Total bytes sent */
+ bytes; /* Bytes sent */
+ char len[32]; /* Length string */
+
+
+ if (http == NULL || buffer == NULL)
+ return (-1);
+
+ http->activity = time(NULL);
+
+ if (http->data_encoding == HTTP_ENCODE_CHUNKED &&
+ (http->state == HTTP_GET_SEND || http->state == HTTP_POST_RECV ||
+ http->state == HTTP_POST_SEND || http->state == HTTP_PUT_RECV))
+ {
+ sprintf(len, "%x\r\n", length);
+ if (send(http->fd, len, strlen(len), 0) < 3)
+ return (-1);
+ }
+
+ if (length == 0)
+ {
+ /*
+ * A zero-length chunk ends a transfer; unless we are sending POST
+ * data, go idle...
+ */
+
+ if (http->state == HTTP_POST_RECV)
+ http->state ++;
+ else
+ http->state = HTTP_WAITING;
+
+ return (0);
+ }
+
+ tbytes = 0;
+
+ while (length > 0)
+ {
+ bytes = send(http->fd, buffer, length, 0);
+ if (bytes < 0)
+ {
+ DEBUG_puts("httpWrite: error writing data...\n");
+
+ return (-1);
+ }
+
+ buffer += bytes;
+ tbytes += bytes;
+ length -= bytes;
+ if (http->data_encoding == HTTP_ENCODE_LENGTH)
+ http->data_remaining -= bytes;
+ }
+
+ if (http->data_remaining == 0 && http->data_encoding == HTTP_ENCODE_LENGTH)
+ {
+ /*
+ * Finished with the transfer; unless we are sending POST data, go idle...
+ */
+
+ if (http->state == HTTP_POST_RECV)
+ http->state ++;
+ else
+ http->state = HTTP_WAITING;
+ }
+
+ DEBUG_printf(("httpWrite: wrote %d bytes...\n", tbytes));
+
+ return (tbytes);
+}
+
+
+/*
+ * 'httpGets()' - Get a line of text from a HTTP connection.
+ */
+
+char * /* O - Line or NULL */
+httpGets(char *line, /* I - Line to read into */
+ int length, /* I - Max length of buffer */
+ http_t *http) /* I - HTTP data */
+{
+ char *lineptr, /* Pointer into line */
+ *bufptr, /* Pointer into input buffer */
+ *bufend; /* Pointer to end of buffer */
+ int bytes; /* Number of bytes read */
+ int lasterror; /* Last error received */
+
+
+ DEBUG_printf(("httpGets(%08x, %d, %08x)\n", line, length, http));
+
+ if (http == NULL || line == NULL)
+ return (NULL);
+
+ /*
+ * Pre-scan the buffer and see if there is a newline in there...
+ */
+
+ lasterror = 0;
+ errno = 0;
+
+ do
+ {
+ bufptr = http->buffer;
+ bufend = http->buffer + http->used;
+
+ while (bufptr < bufend)
+ if (*bufptr == 0x0a)
+ break;
+ else
+ bufptr ++;
+
+ if (bufptr >= bufend)
+ {
+ /*
+ * No newline; see if there is more data to be read...
+ */
+
+ if ((bytes = recv(http->fd, bufend, HTTP_MAX_BUFFER - http->used, 0)) < 0)
+ {
+ /*
+ * Nope, can't get a line this time...
+ */
+
+ if (errno != lasterror && errno != ECONNRESET &&
+ errno != ECONNABORTED && errno != ENETRESET)
+ {
+ lasterror = errno;
+ continue;
+ }
+
+ DEBUG_printf(("httpGets(): recv() error %d!\n", errno));
+
+ return (NULL);
+ }
+ else if (bytes == 0)
+ return (NULL);
+
+ /*
+ * Yup, update the amount used and the end pointer...
+ */
+
+ http->used += bytes;
+ bufend += bytes;
+ }
+ }
+ while (bufptr >= bufend);
+
+ http->activity = time(NULL);
+
+ /*
+ * Read a line from the buffer...
+ */
+
+ lineptr = line;
+ bufptr = http->buffer;
+ bytes = 0;
+
+ while (bufptr < bufend && bytes < length)
+ {
+ bytes ++;
+
+ if (*bufptr == 0x0a)
+ {
+ bufptr ++;
+ *lineptr = '\0';
+
+ http->used -= bytes;
+ if (http->used > 0)
+ memcpy(http->buffer, bufptr, http->used);
+
+ DEBUG_printf(("httpGets(): Returning \"%s\"\n", line));
+ return (line);
+ }
+ else if (*bufptr == 0x0d)
+ bufptr ++;
+ else
+ *lineptr++ = *bufptr++;
+ }
+
+ DEBUG_puts("httpGets(): No new line available!");
+
+ return (NULL);
+}
+
+
+/*
+ * 'httpPrintf()' - Print a formatted string to a HTTP connection.
+ */
+
+int /* O - Number of bytes written */
+httpPrintf(http_t *http, /* I - HTTP data */
+ const char *format, /* I - printf-style format string */
+ ...) /* I - Additional args as needed */
+{
+ int bytes, /* Number of bytes to write */
+ nbytes, /* Number of bytes written */
+ tbytes; /* Number of bytes all together */
+ char buf[HTTP_MAX_BUFFER], /* Buffer for formatted string */
+ *bufptr; /* Pointer into buffer */
+ va_list ap; /* Variable argument pointer */
+
+
+ va_start(ap, format);
+ bytes = vsprintf(buf, format, ap);
+ va_end(ap);
+
+ DEBUG_printf(("httpPrintf: %s", buf));
+
+ for (tbytes = 0, bufptr = buf; tbytes < bytes; tbytes += nbytes, bufptr += nbytes)
+ if ((nbytes = send(http->fd, bufptr, bytes - tbytes, 0)) < 0)
+ return (-1);
+
+ return (bytes);
+}
+
+
+/*
+ * 'httpStatus()' - Return a short string describing a HTTP status code.
+ */
+
+const char * /* O - String or NULL */
+httpStatus(http_status_t status) /* I - HTTP status code */
+{
+ switch (status)
+ {
+ case HTTP_OK :
+ return ("OK");
+ case HTTP_CREATED :
+ return ("Created");
+ case HTTP_ACCEPTED :
+ return ("Accepted");
+ case HTTP_NO_CONTENT :
+ return ("No Content");
+ case HTTP_NOT_MODIFIED :
+ return ("Not Modified");
+ case HTTP_BAD_REQUEST :
+ return ("Bad Request");
+ case HTTP_UNAUTHORIZED :
+ return ("Unauthorized");
+ case HTTP_FORBIDDEN :
+ return ("Forbidden");
+ case HTTP_NOT_FOUND :
+ return ("Not Found");
+ case HTTP_REQUEST_TOO_LARGE :
+ return ("Request Entity Too Large");
+ case HTTP_URI_TOO_LONG :
+ return ("URI Too Long");
+ case HTTP_NOT_IMPLEMENTED :
+ return ("Not Implemented");
+ case HTTP_NOT_SUPPORTED :
+ return ("Not Supported");
+ default :
+ return ("Unknown");
+ }
+}
+
+
+/*
+ * 'httpGetDateString()' - Get a formatted date/time string from a time value.
+ */
+
+const char * /* O - Date/time string */
+httpGetDateString(time_t t) /* I - UNIX time */
+{
+ struct tm *tdate;
+ static char datetime[256];
+
+
+ tdate = gmtime(&t);
+ sprintf(datetime, "%s, %02d %s %d %02d:%02d:%02d GMT",
+ days[tdate->tm_wday], tdate->tm_mday, months[tdate->tm_mon],
+ tdate->tm_year + 1900, tdate->tm_hour, tdate->tm_min, tdate->tm_sec);
+
+ return (datetime);
+}
+
+
+/*
+ * 'httpGetDateTime()' - Get a time value from a formatted date/time string.
+ */
+
+time_t /* O - UNIX time */
+httpGetDateTime(const char *s) /* I - Date/time string */
+{
+ int i; /* Looping var */
+ struct tm tdate; /* Time/date structure */
+ char mon[16]; /* Abbreviated month name */
+ int day, year; /* Day of month and year */
+ int hour, min, sec; /* Time */
+
+
+ if (sscanf(s, "%*s%d%s%d%d:%d:%d", &day, mon, &year, &hour, &min, &sec) < 6)
+ return (0);
+
+ for (i = 0; i < 12; i ++)
+ if (strcasecmp(mon, months[i]) == 0)
+ break;
+
+ if (i >= 12)
+ return (0);
+
+ tdate.tm_mon = i;
+ tdate.tm_mday = day;
+ tdate.tm_year = year - 1900;
+ tdate.tm_hour = hour;
+ tdate.tm_min = min;
+ tdate.tm_sec = sec;
+ tdate.tm_isdst = 0;
+
+ return (mktime(&tdate));
+}
+
+
+/*
+ * 'httpUpdate()' - Update the current HTTP state for incoming data.
+ */
+
+http_status_t /* O - HTTP status */
+httpUpdate(http_t *http) /* I - HTTP data */
+{
+ char line[1024], /* Line from connection... */
+ *value; /* Pointer to value on line */
+ http_field_t field; /* Field index */
+ int major, minor; /* HTTP version numbers */
+ http_status_t status; /* Authorization status */
+
+
+ DEBUG_printf(("httpUpdate(%08x)\n", http));
+
+ /*
+ * If we haven't issued any commands, then there is nothing to "update"...
+ */
+
+ if (http->state == HTTP_WAITING)
+ return (HTTP_CONTINUE);
+
+ /*
+ * Grab all of the lines we can from the connection...
+ */
+
+ while (httpGets(line, sizeof(line), http) != NULL)
+ {
+ DEBUG_puts(line);
+
+ if (line[0] == '\0')
+ {
+ /*
+ * Blank line means the start of the data section (if any). Return
+ * the result code, too...
+ *
+ * If we get status 100 (HTTP_CONTINUE), then we *don't* change states.
+ * Instead, we just return HTTP_CONTINUE to the caller and keep on
+ * tryin'...
+ */
+
+ if (http->status == HTTP_CONTINUE)
+ return (http->status);
+
+ httpGetLength(http);
+
+ switch (http->state)
+ {
+ case HTTP_GET :
+ case HTTP_POST :
+ case HTTP_POST_RECV :
+ case HTTP_PUT :
+ http->state ++;
+ break;
+
+ default :
+ http->state = HTTP_WAITING;
+ break;
+ }
+
+ return (http->status);
+ }
+ else if (strncmp(line, "HTTP/", 5) == 0)
+ {
+ /*
+ * Got the beginning of a response...
+ */
+
+ if (sscanf(line, "HTTP/%d.%d%d", &major, &minor, &status) != 3)
+ return (HTTP_ERROR);
+
+ http->version = (http_version_t)(major * 100 + minor);
+ http->status = status;
+ }
+ else if ((value = strchr(line, ':')) != NULL)
+ {
+ /*
+ * Got a value...
+ */
+
+ *value++ = '\0';
+ while (isspace(*value))
+ value ++;
+
+ /*
+ * Be tolerants of servers that send unknown attribute fields...
+ */
+
+ if ((field = http_field(line)) == HTTP_FIELD_UNKNOWN)
+ {
+ DEBUG_printf(("httpUpdate: unknown field %s seen!\n", line));
+ continue;
+ }
+
+ httpSetField(http, field, value);
+ }
+ else
+ return (HTTP_ERROR);
+ }
+
+ /*
+ * See if there was an error...
+ */
+
+ if (errno)
+ return (HTTP_ERROR);
+
+ /*
+ * If we haven't already returned, then there is nothing new...
+ */
+
+ return (HTTP_CONTINUE);
+}
+
+
+/*
+ * 'httpDecode64()' - Base64-decode a string.
+ */
+
+char * /* O - Decoded string */
+httpDecode64(char *out, /* I - String to write to */
+ const char *in) /* I - String to read from */
+{
+ int pos, /* Bit position */
+ base64; /* Value of this character */
+ char *outptr; /* Output pointer */
+
+
+ for (outptr = out, pos = 0; *in != '\0'; in ++)
+ {
+ /*
+ * Decode this character into a number from 0 to 63...
+ */
+
+ if (*in >= 'A' && *in <= 'Z')
+ base64 = *in - 'A';
+ else if (*in >= 'a' && *in <= 'z')
+ base64 = *in - 'a' + 26;
+ else if (*in >= '0' && *in <= '9')
+ base64 = *in - '0' + 52;
+ else if (*in == '+')
+ base64 = 62;
+ else if (*in == '/')
+ base64 = 63;
+ else if (*in == '=')
+ break;
+ else
+ continue;
+
+ /*
+ * Store the result in the appropriate chars...
+ */
+
+ switch (pos)
+ {
+ case 0 :
+ *outptr = base64 << 2;
+ pos ++;
+ break;
+ case 1 :
+ *outptr++ |= (base64 >> 4) & 3;
+ *outptr = (base64 << 4) & 255;
+ pos ++;
+ break;
+ case 2 :
+ *outptr++ |= (base64 >> 2) & 15;
+ *outptr = (base64 << 6) & 255;
+ pos ++;
+ break;
+ case 3 :
+ *outptr++ |= base64;
+ pos = 0;
+ break;
+ }
+ }
+
+ *outptr = '\0';
+
+ /*
+ * Return the decoded string...
+ */
+
+ return (out);
+}
+
+
+/*
+ * 'httpEncode64()' - Base64-encode a string.
+ */
+
+char * /* O - Encoded string */
+httpEncode64(char *out, /* I - String to write to */
+ const char *in) /* I - String to read from */
+{
+ char *outptr; /* Output pointer */
+ static char base64[] = /* Base64 characters... */
+ {
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789"
+ "+/"
+ };
+
+
+ for (outptr = out; *in != '\0'; in ++)
+ {
+ /*
+ * Encode the up to 3 characters as 4 Base64 numbers...
+ */
+
+ *outptr ++ = base64[in[0] >> 2];
+ *outptr ++ = base64[((in[0] << 4) | (in[1] >> 4)) & 63];
+
+ in ++;
+ if (*in == '\0')
+ break;
+
+ *outptr ++ = base64[((in[0] << 2) | (in[1] >> 6)) & 63];
+
+ in ++;
+ if (*in == '\0')
+ break;
+
+ *outptr ++ = base64[in[0] & 63];
+ }
+
+ *outptr ++ = '=';
+ *outptr = '\0';
+
+ /*
+ * Return the encoded string...
+ */
+
+ return (out);
+}
+
+
+/*
+ * 'httpGetLength()' - Get the amount of data remaining from the
+ * content-length or transfer-encoding fields.
+ */
+
+int /* O - Content length */
+httpGetLength(http_t *http) /* I - HTTP data */
+{
+ if (strcasecmp(http->fields[HTTP_FIELD_TRANSFER_ENCODING], "chunked") == 0)
+ {
+ http->data_encoding = HTTP_ENCODE_CHUNKED;
+ http->data_remaining = 0;
+ }
+ else
+ {
+ http->data_encoding = HTTP_ENCODE_LENGTH;
+
+ /*
+ * The following is a hack for HTTP servers that don't send a
+ * content-length or transfer-encoding field...
+ *
+ * If there is no content-length then the connection must close
+ * after the transfer is complete...
+ */
+
+ if (http->fields[HTTP_FIELD_CONTENT_LENGTH][0] == '\0')
+ http->data_remaining = 2147483647;
+ else
+ http->data_remaining = atoi(http->fields[HTTP_FIELD_CONTENT_LENGTH]);
+ }
+
+ return (http->data_remaining);
+}
+
+
+/*
+ * 'http_field()' - Return the field index for a field name.
+ */
+
+static http_field_t /* O - Field index */
+http_field(const char *name) /* I - String name */
+{
+ int i; /* Looping var */
+
+
+ for (i = 0; i < HTTP_FIELD_MAX; i ++)
+ if (strcasecmp(name, http_fields[i]) == 0)
+ return ((http_field_t)i);
+
+ return (HTTP_FIELD_UNKNOWN);
+}
+
+
+/*
+ * 'http_send()' - Send a request with all fields and the trailing blank line.
+ */
+
+static int /* O - 0 on success, non-zero on error */
+http_send(http_t *http, /* I - HTTP data */
+ http_state_t request, /* I - Request code */
+ const char *uri) /* I - URI */
+{
+ int i; /* Looping var */
+ char *ptr, /* Pointer in buffer */
+ buf[1024]; /* Encoded URI buffer */
+ static const char *codes[] = /* Request code strings */
+ {
+ NULL,
+ "OPTIONS",
+ "GET",
+ NULL,
+ "HEAD",
+ "POST",
+ NULL,
+ NULL,
+ "PUT",
+ NULL,
+ "DELETE",
+ "TRACE",
+ "CLOSE"
+ };
+ static const char *hex = "0123456789ABCDEF";
+ /* Hex digits */
+
+
+ if (http == NULL || uri == NULL)
+ return (-1);
+
+ /*
+ * Encode the URI as needed...
+ */
+
+ for (ptr = buf; *uri != '\0'; uri ++)
+ if (*uri <= ' ' || *uri >= 127)
+ {
+ *ptr ++ = '%';
+ *ptr ++ = hex[(*uri >> 4) & 15];
+ *ptr ++ = hex[*uri & 15];
+ }
+ else
+ *ptr ++ = *uri;
+
+ *ptr = '\0';
+
+ /*
+ * Send the request header...
+ */
+
+ http->state = request;
+ if (request == HTTP_POST || request == HTTP_PUT)
+ http->state ++;
+
+ http->status = HTTP_CONTINUE;
+
+ if (httpPrintf(http, "%s %s HTTP/1.1\r\n", codes[request], buf) < 1)
+ {
+ /*
+ * Might have lost connection; try to reconnect...
+ */
+
+ if (httpReconnect(http))
+ return (-1);
+
+ /*
+ * OK, we've reconnected, send the request again...
+ */
+
+ if (httpPrintf(http, "%s %s HTTP/%d.%d\r\n", codes[request], buf,
+ http->version / 100, http->version % 100) < 1)
+ return (-1);
+ }
+
+ for (i = 0; i < HTTP_FIELD_MAX; i ++)
+ if (http->fields[i][0] != '\0')
+ {
+ DEBUG_printf(("%s: %s\n", http_fields[i], http->fields[i]));
+
+ if (httpPrintf(http, "%s: %s\r\n", http_fields[i], http->fields[i]) < 1)
+ return (-1);
+ }
+
+ if (httpPrintf(http, "\r\n") < 1)
+ return (-1);
+
+ httpClearFields(http);
+
+ return (0);
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/cups/http.h b/cups/http.h
new file mode 100644
index 000000000..c5f2d1ac9
--- /dev/null
+++ b/cups/http.h
@@ -0,0 +1,291 @@
+/*
+ * "$Id$"
+ *
+ * Hyper-Text Transport Protocol definitions for the Common UNIX Printing
+ * System (CUPS).
+ *
+ * Copyright 1997-1999 by Easy Software Products, all rights reserved.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ */
+
+#ifndef _CUPS_HTTP_H_
+# define _CUPS_HTTP_H_
+
+/*
+ * Include necessary headers...
+ */
+
+# include <string.h>
+# include <time.h>
+# if defined(WIN32) || defined(__EMX__)
+# include <winsock.h>
+# else
+# include <unistd.h>
+# include <sys/time.h>
+# include <sys/types.h>
+# include <sys/socket.h>
+# include <netdb.h>
+# include <netinet/in.h>
+# include <arpa/inet.h>
+# include <netinet/in_systm.h>
+# include <netinet/ip.h>
+# include <netinet/tcp.h>
+# endif /* WIN32 || __EMX__ */
+
+
+/*
+ * C++ magic...
+ */
+
+# ifdef __cplusplus
+extern "C" {
+# endif /* __cplusplus */
+
+
+/*
+ * Limits...
+ */
+
+# define HTTP_MAX_URI 1024 /* Max length of URI string */
+# define HTTP_MAX_HOST 256 /* Max length of hostname string */
+# define HTTP_MAX_BUFFER 2048 /* Max length of data buffer */
+# define HTTP_MAX_VALUE 256 /* Max header field value length */
+
+
+/*
+ * HTTP state values...
+ */
+
+typedef enum /* States are server-oriented */
+{
+ HTTP_WAITING, /* Waiting for command */
+ HTTP_OPTIONS, /* OPTIONS command, waiting for blank line */
+ HTTP_GET, /* GET command, waiting for blank line */
+ HTTP_GET_SEND, /* GET command, sending data */
+ HTTP_HEAD, /* HEAD command, waiting for blank line */
+ HTTP_POST, /* POST command, waiting for blank line */
+ HTTP_POST_RECV, /* POST command, receiving data */
+ HTTP_POST_SEND, /* POST command, sending data */
+ HTTP_PUT, /* PUT command, waiting for blank line */
+ HTTP_PUT_RECV, /* PUT command, receiving data */
+ HTTP_DELETE, /* DELETE command, waiting for blank line */
+ HTTP_TRACE, /* TRACE command, waiting for blank line */
+ HTTP_CLOSE, /* CLOSE command, waiting for blank line */
+ HTTP_STATUS /* Command complete, sending status */
+} http_state_t;
+
+
+/*
+ * HTTP version numbers...
+ */
+
+typedef enum
+{
+ HTTP_0_9 = 9, /* HTTP/0.9 */
+ HTTP_1_0 = 100, /* HTTP/1.0 */
+ HTTP_1_1 = 101 /* HTTP/1.1 */
+} http_version_t;
+
+
+/*
+ * HTTP keep-alive values...
+ */
+
+typedef enum
+{
+ HTTP_KEEPALIVE_OFF = 0,
+ HTTP_KEEPALIVE_ON
+} http_keepalive_t;
+
+
+/*
+ * HTTP transfer encoding values...
+ */
+
+typedef enum
+{
+ HTTP_ENCODE_LENGTH, /* Data is sent with Content-Length */
+ HTTP_ENCODE_CHUNKED /* Data is chunked */
+} http_encoding_t;
+
+
+/*
+ * HTTP status codes...
+ */
+
+typedef enum
+{
+ HTTP_ERROR = -1, /* An error response from httpXxxx() */
+
+ HTTP_CONTINUE = 100, /* Everything OK, keep going... */
+
+ HTTP_OK = 200, /* OPTIONS/GET/HEAD/POST/TRACE command was successful */
+ HTTP_CREATED, /* PUT command was successful */
+ HTTP_ACCEPTED, /* DELETE command was successful */
+ HTTP_NOT_AUTHORITATIVE, /* Information isn't authoritative */
+ HTTP_NO_CONTENT, /* Successful command, no new data */
+ HTTP_RESET_CONTENT, /* Content was reset/recreated */
+ HTTP_PARTIAL_CONTENT, /* Only a partial file was recieved/sent */
+
+ HTTP_MULTIPLE_CHOICES = 300, /* Multiple files match request */
+ HTTP_MOVED_PERMANENTLY, /* Document has moved permanently */
+ HTTP_MOVED_TEMPORARILY, /* Document has moved temporarily */
+ HTTP_SEE_OTHER, /* See this other link... */
+ HTTP_NOT_MODIFIED, /* File not modified */
+ HTTP_USE_PROXY, /* Must use a proxy to access this URI */
+
+ HTTP_BAD_REQUEST = 400, /* Bad request */
+ HTTP_UNAUTHORIZED, /* Unauthorized to access host */
+ HTTP_PAYMENT_REQUIRED, /* Payment required */
+ HTTP_FORBIDDEN, /* Forbidden to access this URI */
+ HTTP_NOT_FOUND, /* URI was not found */
+ HTTP_METHOD_NOT_ALLOWED, /* Method is not allowed */
+ HTTP_NOT_ACCEPTABLE, /* Not Acceptable */
+ HTTP_PROXY_AUTHENTICATION, /* Proxy Authentication is Required */
+ HTTP_REQUEST_TIMEOUT, /* Request timed out */
+ HTTP_CONFLICT, /* Request is self-conflicting */
+ HTTP_GONE, /* Server has gone away */
+ HTTP_LENGTH_REQUIRED, /* A content length or encoding is required */
+ HTTP_PRECONDITION, /* Precondition failed */
+ HTTP_REQUEST_TOO_LARGE, /* Request entity too large */
+ HTTP_URI_TOO_LONG, /* URI too long */
+ HTTP_UNSUPPORTED_MEDIATYPE, /* The requested media type is unsupported */
+
+ HTTP_SERVER_ERROR = 500, /* Internal server error */
+ HTTP_NOT_IMPLEMENTED, /* Feature not implemented */
+ HTTP_BAD_GATEWAY, /* Bad gateway */
+ HTTP_SERVICE_UNAVAILABLE, /* Service is unavailable */
+ HTTP_GATEWAY_TIMEOUT, /* Gateway connection timed out */
+ HTTP_NOT_SUPPORTED /* HTTP version not supported */
+} http_status_t;
+
+
+/*
+ * HTTP field names...
+ */
+
+typedef enum
+{
+ HTTP_FIELD_UNKNOWN = -1,
+ HTTP_FIELD_ACCEPT_LANGUAGE,
+ HTTP_FIELD_ACCEPT_RANGES,
+ HTTP_FIELD_AUTHORIZATION,
+ HTTP_FIELD_CONNECTION,
+ HTTP_FIELD_CONTENT_ENCODING,
+ HTTP_FIELD_CONTENT_LANGUAGE,
+ HTTP_FIELD_CONTENT_LENGTH,
+ HTTP_FIELD_CONTENT_LOCATION,
+ HTTP_FIELD_CONTENT_MD5,
+ HTTP_FIELD_CONTENT_RANGE,
+ HTTP_FIELD_CONTENT_TYPE,
+ HTTP_FIELD_CONTENT_VERSION,
+ HTTP_FIELD_DATE,
+ HTTP_FIELD_HOST,
+ HTTP_FIELD_IF_MODIFIED_SINCE,
+ HTTP_FIELD_IF_UNMODIFIED_SINCE,
+ HTTP_FIELD_KEEP_ALIVE,
+ HTTP_FIELD_LAST_MODIFIED,
+ HTTP_FIELD_LINK,
+ HTTP_FIELD_LOCATION,
+ HTTP_FIELD_RANGE,
+ HTTP_FIELD_REFERER,
+ HTTP_FIELD_RETRY_AFTER,
+ HTTP_FIELD_TRANSFER_ENCODING,
+ HTTP_FIELD_UPGRADE,
+ HTTP_FIELD_USER_AGENT,
+ HTTP_FIELD_WWW_AUTHENTICATE,
+ HTTP_FIELD_MAX
+} http_field_t;
+
+
+/*
+ * HTTP connection structure...
+ */
+
+typedef struct
+{
+ int fd; /* File descriptor for this socket */
+ int blocking; /* To block or not to block */
+ time_t activity; /* Time since last read/write */
+ http_state_t state; /* State of client */
+ http_status_t status; /* Status of last request */
+ http_version_t version; /* Protocol version */
+ http_keepalive_t keep_alive; /* Keep-alive supported? */
+ struct sockaddr_in hostaddr; /* Address of connected host */
+ char hostname[HTTP_MAX_HOST],
+ /* Name of connected host */
+ fields[HTTP_FIELD_MAX][HTTP_MAX_VALUE];
+ /* Field values */
+ char *data; /* Pointer to data buffer */
+ http_encoding_t data_encoding; /* Chunked or not */
+ int data_remaining; /* Number of bytes left */
+ int used; /* Number of bytes used in buffer */
+ char buffer[HTTP_MAX_BUFFER];
+ /* Buffer for messages */
+} http_t;
+
+
+/*
+ * Prototypes...
+ */
+
+# define httpBlocking(http,b) (http)->blocking = (b)
+extern int httpCheck(http_t *http);
+# define httpClearFields(http) memset((http)->fields, 0, sizeof((http)->fields)),\
+ httpSetField((http), HTTP_FIELD_HOST, (http)->hostname)
+extern void httpClose(http_t *http);
+extern http_t *httpConnect(const char *host, int port);
+extern int httpDelete(http_t *http, const char *uri);
+extern void httpFlush(http_t *http);
+extern int httpGet(http_t *http, const char *uri);
+extern char *httpGets(char *line, int length, http_t *http);
+extern const char *httpGetDateString(time_t t);
+extern time_t httpGetDateTime(const char *s);
+# define httpGetField(http,field) (http)->fields[field]
+extern int httpHead(http_t *http, const char *uri);
+extern void httpInitialize(void);
+extern int httpOptions(http_t *http, const char *uri);
+extern int httpPost(http_t *http, const char *uri);
+extern int httpPrintf(http_t *http, const char *format, ...);
+extern int httpPut(http_t *http, const char *uri);
+extern int httpRead(http_t *http, char *buffer, int length);
+extern int httpReconnect(http_t *http);
+extern void httpSeparate(const char *uri, char *method, char *username,
+ char *host, int *port, char *resource);
+extern void httpSetField(http_t *http, http_field_t field, const char *value);
+extern const char *httpStatus(http_status_t status);
+extern int httpTrace(http_t *http, const char *uri);
+extern http_status_t httpUpdate(http_t *http);
+extern int httpWrite(http_t *http, const char *buffer, int length);
+extern char *httpEncode64(char *out, const char *in);
+extern char *httpDecode64(char *out, const char *in);
+extern int httpGetLength(http_t *http);
+
+
+/*
+ * C++ magic...
+ */
+
+# ifdef __cplusplus
+}
+# endif /* __cplusplus */
+#endif /* !_CUPS_HTTP_H_ */
+
+/*
+ * End of "$Id$".
+ */
diff --git a/cups/ipp.c b/cups/ipp.c
new file mode 100644
index 000000000..37aab1d98
--- /dev/null
+++ b/cups/ipp.c
@@ -0,0 +1,1459 @@
+/*
+ * "$Id$"
+ *
+ * Internet Printing Protocol support functions for the Common UNIX
+ * Printing System (CUPS).
+ *
+ * Copyright 1997-1999 by Easy Software Products, all rights reserved.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * Contents:
+ *
+ * ippAddBoolean() - Add a boolean attribute to an IPP request.
+ * ippAddBooleans() - Add an array of boolean values.
+ * ippAddDate() - Add a date attribute to an IPP request.
+ * ippAddInteger() - Add a integer attribute to an IPP request.
+ * ippAddIntegers() - Add an array of integer values.
+ * ippAddString() - Add a language-encoded string to an IPP request.
+ * ippAddStrings() - Add language-encoded strings to an IPP request.
+ * ippAddRange() - Add a range of values to an IPP request.
+ * ippAddRanges() - Add ranges of values to an IPP request.
+ * ippAddResolution() - Add a resolution value to an IPP request.
+ * ippAddResolutions() - Add resolution values to an IPP request.
+ * ippAddSeparator() - Add a group separator to an IPP request.
+ * ippDateToTime() - Convert from RFC 1903 Date/Time format to UNIX time
+ * ippDelete() - Delete an IPP request.
+ * ippFindAttribute() - Find a named attribute in a request...
+ * ippLength() - Compute the length of an IPP request.
+ * ippPort() - Return the default IPP port number.
+ * ippRead() - Read data for an IPP request.
+ * ippTimeToDate() - Convert from UNIX time to RFC 1903 format.
+ * ippWrite() - Write data for an IPP request.
+ * add_attr() - Add a new attribute to the request.
+ * ipp_read() - Semi-blocking read on a HTTP connection...
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "ipp.h"
+#include "debug.h"
+
+
+/*
+ * Local functions...
+ */
+
+static ipp_attribute_t *add_attr(ipp_t *ipp, int num_values);
+static int ipp_read(http_t *http, unsigned char *buffer, int length);
+
+
+/*
+ * 'ippAddBoolean()' - Add a boolean attribute to an IPP request.
+ */
+
+ipp_attribute_t * /* O - New attribute */
+ippAddBoolean(ipp_t *ipp, /* I - IPP request */
+ ipp_tag_t group, /* I - IPP group */
+ const char *name, /* I - Name of attribute */
+ char value) /* I - Value of attribute */
+{
+ ipp_attribute_t *attr; /* New attribute */
+
+
+ DEBUG_printf(("ippAddBoolean(%08x, %02x, \'%s\', %d)\n", ipp, group, name, value));
+
+ if (ipp == NULL || name == NULL)
+ return (NULL);
+
+ if ((attr = add_attr(ipp, 1)) == NULL)
+ return (NULL);
+
+ attr->name = strdup(name);
+ attr->group_tag = group;
+ attr->value_tag = IPP_TAG_BOOLEAN;
+ attr->values[0].boolean = value;
+
+ return (attr);
+}
+
+
+/*
+ * 'ippAddBooleans()' - Add an array of boolean values.
+ */
+
+ipp_attribute_t * /* O - New attribute */
+ippAddBooleans(ipp_t *ipp, /* I - IPP request */
+ ipp_tag_t group, /* I - IPP group */
+ const char *name, /* I - Name of attribute */
+ int num_values, /* I - Number of values */
+ const char *values) /* I - Values */
+{
+ int i; /* Looping var */
+ ipp_attribute_t *attr; /* New attribute */
+
+
+ DEBUG_printf(("ippAddBooleans(%08x, %02x, \'%s\', %d, %08x)\n", ipp,
+ group, name, num_values, values));
+
+ if (ipp == NULL || name == NULL)
+ return (NULL);
+
+ if ((attr = add_attr(ipp, num_values)) == NULL)
+ return (NULL);
+
+ attr->name = strdup(name);
+ attr->group_tag = group;
+ attr->value_tag = IPP_TAG_BOOLEAN;
+
+ if (values != NULL)
+ for (i = 0; i < num_values; i ++)
+ attr->values[i].boolean = values[i];
+
+ return (attr);
+}
+
+
+/*
+ * 'ippAddDate()' - Add a date attribute to an IPP request.
+ */
+
+ipp_attribute_t * /* O - New attribute */
+ippAddDate(ipp_t *ipp, /* I - IPP request */
+ ipp_tag_t group, /* I - IPP group */
+ const char *name, /* I - Name of attribute */
+ const ipp_uchar_t *value) /* I - Value */
+{
+ ipp_attribute_t *attr; /* New attribute */
+
+
+ DEBUG_printf(("ippAddDate(%08x, %02x, \'%s\', %08x)\n", ipp, group, name,
+ value));
+
+ if (ipp == NULL || name == NULL || value == NULL)
+ return (NULL);
+
+ if ((attr = add_attr(ipp, 1)) == NULL)
+ return (NULL);
+
+ attr->name = strdup(name);
+ attr->group_tag = group;
+ attr->value_tag = IPP_TAG_DATE;
+ memcpy(attr->values[0].date, value, 11);
+
+ return (attr);
+}
+
+
+/*
+ * 'ippAddInteger()' - Add a integer attribute to an IPP request.
+ */
+
+ipp_attribute_t * /* O - New attribute */
+ippAddInteger(ipp_t *ipp, /* I - IPP request */
+ ipp_tag_t group, /* I - IPP group */
+ ipp_tag_t type, /* I - Type of attribute */
+ const char *name, /* I - Name of attribute */
+ int value) /* I - Value of attribute */
+{
+ ipp_attribute_t *attr; /* New attribute */
+
+
+ DEBUG_printf(("ippAddInteger(%08x, %d, \'%s\', %d)\n", ipp, group, name,
+ value));
+
+ if (ipp == NULL || name == NULL)
+ return (NULL);
+
+ if ((attr = add_attr(ipp, 1)) == NULL)
+ return (NULL);
+
+ attr->name = strdup(name);
+ attr->group_tag = group;
+ attr->value_tag = type;
+ attr->values[0].integer = value;
+
+ return (attr);
+}
+
+
+/*
+ * 'ippAddIntegers()' - Add an array of integer values.
+ */
+
+ipp_attribute_t * /* O - New attribute */
+ippAddIntegers(ipp_t *ipp, /* I - IPP request */
+ ipp_tag_t group, /* I - IPP group */
+ ipp_tag_t type, /* I - Type of attribute */
+ const char *name, /* I - Name of attribute */
+ int num_values, /* I - Number of values */
+ const int *values) /* I - Values */
+{
+ int i; /* Looping var */
+ ipp_attribute_t *attr; /* New attribute */
+
+
+ if (ipp == NULL || name == NULL)
+ return (NULL);
+
+ if ((attr = add_attr(ipp, num_values)) == NULL)
+ return (NULL);
+
+ attr->name = strdup(name);
+ attr->group_tag = group;
+ attr->value_tag = type;
+
+ if (values != NULL)
+ for (i = 0; i < num_values; i ++)
+ attr->values[i].integer = values[i];
+
+ return (attr);
+}
+
+
+/*
+ * 'ippAddString()' - Add a language-encoded string to an IPP request.
+ */
+
+ipp_attribute_t * /* O - New attribute */
+ippAddString(ipp_t *ipp, /* I - IPP request */
+ ipp_tag_t group, /* I - IPP group */
+ ipp_tag_t type, /* I - Type of attribute */
+ const char *name, /* I - Name of attribute */
+ const char *charset, /* I - Character set */
+ const char *value) /* I - Value */
+{
+ ipp_attribute_t *attr; /* New attribute */
+
+
+ if (ipp == NULL || name == NULL)
+ return (NULL);
+
+ if ((attr = add_attr(ipp, 1)) == NULL)
+ return (NULL);
+
+ attr->name = strdup(name);
+ attr->group_tag = group;
+ attr->value_tag = type;
+ attr->values[0].string.charset = charset ? strdup(charset) : NULL;
+ attr->values[0].string.text = strdup(value);
+
+ return (attr);
+}
+
+
+/*
+ * 'ippAddStrings()' - Add language-encoded strings to an IPP request.
+ */
+
+ipp_attribute_t * /* O - New attribute */
+ippAddStrings(ipp_t *ipp, /* I - IPP request */
+ ipp_tag_t group, /* I - IPP group */
+ ipp_tag_t type, /* I - Type of attribute */
+ const char *name, /* I - Name of attribute */
+ int num_values, /* I - Number of values */
+ const char *charset, /* I - Character set */
+ const char **values) /* I - Values */
+{
+ int i; /* Looping var */
+ ipp_attribute_t *attr; /* New attribute */
+
+
+ if (ipp == NULL || name == NULL)
+ return (NULL);
+
+ if ((attr = add_attr(ipp, num_values)) == NULL)
+ return (NULL);
+
+ attr->name = strdup(name);
+ attr->group_tag = group;
+ attr->value_tag = type;
+
+ if (values != NULL)
+ for (i = 0; i < num_values; i ++)
+ {
+ if (i == 0)
+ attr->values[0].string.charset = charset ? strdup(charset) : NULL;
+ else
+ attr->values[i].string.charset = attr->values[0].string.charset;
+
+ attr->values[i].string.text = strdup(values[i]);
+ }
+
+ return (attr);
+}
+
+
+/*
+ * 'ippAddRange()' - Add a range of values to an IPP request.
+ */
+
+ipp_attribute_t * /* O - New attribute */
+ippAddRange(ipp_t *ipp, /* I - IPP request */
+ ipp_tag_t group, /* I - IPP group */
+ const char *name, /* I - Name of attribute */
+ int lower, /* I - Lower value */
+ int upper) /* I - Upper value */
+{
+ ipp_attribute_t *attr; /* New attribute */
+
+
+ if (ipp == NULL || name == NULL)
+ return (NULL);
+
+ if ((attr = add_attr(ipp, 1)) == NULL)
+ return (NULL);
+
+ attr->name = strdup(name);
+ attr->group_tag = group;
+ attr->value_tag = IPP_TAG_RANGE;
+ attr->values[0].range.lower = lower;
+ attr->values[0].range.upper = upper;
+
+ return (attr);
+}
+
+
+/*
+ * 'ippAddRanges()' - Add ranges of values to an IPP request.
+ */
+
+ipp_attribute_t * /* O - New attribute */
+ippAddRanges(ipp_t *ipp, /* I - IPP request */
+ ipp_tag_t group, /* I - IPP group */
+ const char *name, /* I - Name of attribute */
+ int num_values, /* I - Number of values */
+ const int *lower, /* I - Lower values */
+ const int *upper) /* I - Upper values */
+{
+ int i; /* Looping var */
+ ipp_attribute_t *attr; /* New attribute */
+
+
+ if (ipp == NULL || name == NULL)
+ return (NULL);
+
+ if ((attr = add_attr(ipp, num_values)) == NULL)
+ return (NULL);
+
+ attr->name = strdup(name);
+ attr->group_tag = group;
+ attr->value_tag = IPP_TAG_RANGE;
+
+ if (lower != NULL && upper != NULL)
+ for (i = 0; i < num_values; i ++)
+ {
+ attr->values[i].range.lower = lower[i];
+ attr->values[i].range.upper = upper[i];
+ }
+
+ return (attr);
+}
+
+
+/*
+ * 'ippAddResolution()' - Add a resolution value to an IPP request.
+ */
+
+ipp_attribute_t * /* O - New attribute */
+ippAddResolution(ipp_t *ipp, /* I - IPP request */
+ ipp_tag_t group, /* I - IPP group */
+ const char *name, /* I - Name of attribute */
+ ipp_res_t units, /* I - Units for resolution */
+ int xres, /* I - X resolution */
+ int yres) /* I - Y resolution */
+{
+ ipp_attribute_t *attr; /* New attribute */
+
+
+ if (ipp == NULL || name == NULL)
+ return (NULL);
+
+ if ((attr = add_attr(ipp, 1)) == NULL)
+ return (NULL);
+
+ attr->name = strdup(name);
+ attr->group_tag = group;
+ attr->value_tag = IPP_TAG_RESOLUTION;
+ attr->values[0].resolution.xres = xres;
+ attr->values[0].resolution.yres = yres;
+ attr->values[0].resolution.units = units;
+
+ return (attr);
+}
+
+
+/*
+ * 'ippAddResolutions()' - Add resolution values to an IPP request.
+ */
+
+ipp_attribute_t * /* O - New attribute */
+ippAddResolutions(ipp_t *ipp, /* I - IPP request */
+ ipp_tag_t group, /* I - IPP group */
+ const char *name, /* I - Name of attribute */
+ int num_values,/* I - Number of values */
+ ipp_res_t units, /* I - Units for resolution */
+ const int *xres, /* I - X resolutions */
+ const int *yres) /* I - Y resolutions */
+{
+ int i; /* Looping var */
+ ipp_attribute_t *attr; /* New attribute */
+
+
+ if (ipp == NULL || name == NULL)
+ return (NULL);
+
+ if ((attr = add_attr(ipp, num_values)) == NULL)
+ return (NULL);
+
+ attr->name = strdup(name);
+ attr->group_tag = group;
+ attr->value_tag = IPP_TAG_RESOLUTION;
+
+ if (xres != NULL && yres != NULL)
+ for (i = 0; i < num_values; i ++)
+ {
+ attr->values[i].resolution.xres = xres[i];
+ attr->values[i].resolution.yres = yres[i];
+ attr->values[i].resolution.units = units;
+ }
+
+ return (attr);
+}
+
+
+/*
+ * 'ippAddSeparator()' - Add a group separator to an IPP request.
+ */
+
+ipp_attribute_t * /* O - New attribute */
+ippAddSeparator(ipp_t *ipp) /* I - IPP request */
+{
+ ipp_attribute_t *attr; /* New attribute */
+
+
+ DEBUG_printf(("ippAddSeparator(%08x)\n", ipp));
+
+ if (ipp == NULL)
+ return (NULL);
+
+ if ((attr = add_attr(ipp, 0)) == NULL)
+ return (NULL);
+
+ attr->group_tag = IPP_TAG_ZERO;
+ attr->value_tag = IPP_TAG_ZERO;
+
+ return (attr);
+}
+
+
+/*
+ * 'ippDateToTime()' - Convert from RFC 1903 Date/Time format to UNIX time
+ * in seconds.
+ */
+
+time_t /* O - UNIX time value */
+ippDateToTime(const ipp_uchar_t *date) /* I - RFC 1903 date info */
+{
+ struct tm unixdate; /* UNIX date/time info */
+ time_t t; /* Computed time */
+
+
+ memset(&unixdate, 0, sizeof(unixdate));
+
+ /*
+ * RFC-1903 date/time format is:
+ *
+ * Byte(s) Description
+ * ------- -----------
+ * 0-1 Year (0 to 65535)
+ * 2 Month (1 to 12)
+ * 3 Day (1 to 31)
+ * 4 Hours (0 to 23)
+ * 5 Minutes (0 to 59)
+ * 6 Seconds (0 to 60, 60 = "leap second")
+ * 7 Deciseconds (0 to 9)
+ * 8 +/- UTC
+ * 9 UTC hours (0 to 11)
+ * 10 UTC minutes (0 to 59)
+ */
+
+ unixdate.tm_year = ((date[0] << 8) | date[1]) - 1900;
+ unixdate.tm_mon = date[2] - 1;
+ unixdate.tm_mday = date[3];
+ unixdate.tm_hour = date[4];
+ unixdate.tm_min = date[5];
+ unixdate.tm_sec = date[6];
+
+ t = mktime(&unixdate);
+
+ if (date[8] == '-')
+ t += date[9] * 3600 + date[10] * 60;
+ else
+ t -= date[9] * 3600 + date[10] * 60;
+
+ t -= timezone;
+
+ return (t);
+}
+
+
+/*
+ * 'ippDelete()' - Delete an IPP request.
+ */
+
+void
+ippDelete(ipp_t *ipp) /* I - IPP request */
+{
+ int i; /* Looping var */
+ ipp_attribute_t *attr, /* Current attribute */
+ *next; /* Next attribute */
+
+
+ if (ipp == NULL)
+ return;
+
+ for (attr = ipp->attrs; attr != NULL; attr = next)
+ {
+ switch (attr->value_tag)
+ {
+ case IPP_TAG_TEXT :
+ case IPP_TAG_NAME :
+ case IPP_TAG_KEYWORD :
+ case IPP_TAG_STRING :
+ case IPP_TAG_URI :
+ case IPP_TAG_URISCHEME :
+ case IPP_TAG_CHARSET :
+ case IPP_TAG_LANGUAGE :
+ case IPP_TAG_MIMETYPE :
+ for (i = 0; i < attr->num_values; i ++)
+ free(attr->values[i].string.text);
+ break;
+
+ case IPP_TAG_TEXTLANG :
+ case IPP_TAG_NAMELANG :
+ for (i = 0; i < attr->num_values; i ++)
+ {
+ if (attr->values[i].string.charset)
+ free(attr->values[i].string.charset);
+ free(attr->values[i].string.text);
+ }
+ break;
+ }
+
+ next = attr->next;
+
+ if (attr->name != NULL)
+ free(attr->name);
+
+ free(attr);
+ }
+
+ free(ipp);
+}
+
+
+/*
+ * 'ippFindAttribute()' - Find a named attribute in a request...
+ */
+
+ipp_attribute_t * /* O - Matching attribute */
+ippFindAttribute(ipp_t *ipp, /* I - IPP request */
+ const char *name, /* I - Name of attribute */
+ ipp_tag_t type) /* I - Type of attribute */
+{
+ ipp_attribute_t *attr; /* Current atttribute */
+
+
+ DEBUG_printf(("ippFindAttribute(%08x, \'%s\')\n", ipp, name));
+
+ if (ipp == NULL || name == NULL)
+ return (NULL);
+
+ for (attr = ipp->attrs; attr != NULL; attr = attr->next)
+ {
+ DEBUG_printf(("ippFindAttribute: attr = %08x, name = \'%s\'\n", attr,
+ attr->name));
+
+ if (attr->name != NULL && strcmp(attr->name, name) == 0 &&
+ (attr->value_tag == type ||
+ (attr->value_tag == IPP_TAG_TEXTLANG && type == IPP_TAG_TEXT) ||
+ (attr->value_tag == IPP_TAG_NAMELANG && type == IPP_TAG_NAME)))
+ return (attr);
+ }
+
+ return (NULL);
+}
+
+
+/*
+ * 'ippLength()' - Compute the length of an IPP request.
+ */
+
+size_t /* O - Size of IPP request */
+ippLength(ipp_t *ipp) /* I - IPP request */
+{
+ int i; /* Looping var */
+ int bytes; /* Number of bytes */
+ ipp_attribute_t *attr; /* Current attribute */
+ ipp_tag_t group; /* Current group */
+
+
+ if (ipp == NULL)
+ return (0);
+
+ /*
+ * Start with 8 bytes for the IPP request or status header...
+ */
+
+ bytes = 8;
+
+ /*
+ * Then add the lengths of each attribute...
+ */
+
+ group = IPP_TAG_ZERO;
+
+ for (attr = ipp->attrs; attr != NULL; attr = attr->next)
+ {
+ if (attr->group_tag != group)
+ {
+ group = attr->group_tag;
+ if (group == IPP_TAG_ZERO)
+ continue;
+
+ bytes ++; /* Group tag */
+ }
+
+ DEBUG_printf(("attr->name = %s, attr->num_values = %d, bytes = %d\n",
+ attr->name, attr->num_values, bytes));
+
+ bytes += strlen(attr->name); /* Name */
+ bytes += attr->num_values; /* Value tag for each value */
+ bytes += 2 * attr->num_values; /* Name lengths */
+ bytes += 2 * attr->num_values; /* Value lengths */
+
+ switch (attr->value_tag)
+ {
+ case IPP_TAG_INTEGER :
+ case IPP_TAG_ENUM :
+ bytes += 4 * attr->num_values;
+ break;
+
+ case IPP_TAG_BOOLEAN :
+ bytes += attr->num_values;
+ break;
+
+ case IPP_TAG_TEXT :
+ case IPP_TAG_NAME :
+ case IPP_TAG_KEYWORD :
+ case IPP_TAG_STRING :
+ case IPP_TAG_URI :
+ case IPP_TAG_URISCHEME :
+ case IPP_TAG_CHARSET :
+ case IPP_TAG_LANGUAGE :
+ case IPP_TAG_MIMETYPE :
+ for (i = 0; i < attr->num_values; i ++)
+ bytes += strlen(attr->values[i].string.text);
+ break;
+
+ case IPP_TAG_DATE :
+ bytes += 11 * attr->num_values;
+ break;
+
+ case IPP_TAG_RESOLUTION :
+ bytes += 9 * attr->num_values;
+ break;
+
+ case IPP_TAG_RANGE :
+ bytes += 8 * attr->num_values;
+ break;
+
+ case IPP_TAG_TEXTLANG :
+ case IPP_TAG_NAMELANG :
+ bytes += 2 * attr->num_values;/* Charset length */
+ for (i = 0; i < attr->num_values; i ++)
+ bytes += strlen(attr->values[i].string.charset) +
+ strlen(attr->values[i].string.text);
+ break;
+ }
+ }
+
+ /*
+ * Finally, add 1 byte for the "end of attributes" tag and return...
+ */
+
+ DEBUG_printf(("bytes = %d\n", bytes + 1));
+
+ return (bytes + 1);
+}
+
+
+ipp_t * /* O - New IPP request */
+ippNew(void)
+{
+ return ((ipp_t *)calloc(sizeof(ipp_t), 1));
+}
+
+
+/*
+ * 'ippRead()' - Read data for an IPP request.
+ */
+
+ipp_state_t /* O - Current state */
+ippRead(http_t *http, /* I - HTTP data */
+ ipp_t *ipp) /* I - IPP data */
+{
+ int n; /* Length of data */
+ unsigned char buffer[8192]; /* Data buffer */
+ ipp_attribute_t *attr; /* Current attribute */
+ ipp_tag_t tag; /* Current tag */
+
+
+ DEBUG_printf(("ippRead(%08x, %08x)\n", http, ipp));
+
+ if (http == NULL || ipp == NULL)
+ return (IPP_ERROR);
+
+ switch (ipp->state)
+ {
+ case IPP_IDLE :
+ ipp->state ++; /* Avoid common problem... */
+
+ case IPP_HEADER :
+ /*
+ * Get the request header...
+ */
+
+ if ((n = ipp_read(http, buffer, 8)) < 8)
+ {
+ DEBUG_printf(("ippRead: Unable to read header (%d bytes read)!\n", n));
+ return (n == 0 ? IPP_IDLE : IPP_ERROR);
+ }
+
+ /*
+ * Verify the major version number...
+ */
+
+ if (buffer[0] != 1)
+ {
+ DEBUG_printf(("ippRead: version number (%d.%d) is bad.\n", buffer[0],
+ buffer[1]));
+ return (IPP_ERROR);
+ }
+
+ /*
+ * Then copy the request header over...
+ */
+
+ ipp->request.any.version[0] = buffer[0];
+ ipp->request.any.version[1] = buffer[1];
+ ipp->request.any.op_status = (buffer[2] << 8) | buffer[3];
+ ipp->request.any.request_id = (((((buffer[4] << 8) | buffer[5]) << 8) |
+ buffer[6]) << 8) | buffer[7];
+
+ ipp->state = IPP_ATTRIBUTE;
+ ipp->current = NULL;
+ ipp->curtag = IPP_TAG_ZERO;
+
+ /*
+ * If blocking is disabled, stop here...
+ */
+
+ if (!http->blocking && http->used == 0)
+ break;
+
+ case IPP_ATTRIBUTE :
+ while (ipp_read(http, buffer, 1) > 0)
+ {
+ /*
+ * Read this attribute...
+ */
+
+ tag = (ipp_tag_t)buffer[0];
+
+ if (tag == IPP_TAG_END)
+ {
+ /*
+ * No more attributes left...
+ */
+
+ DEBUG_puts("ippRead: IPP_TAG_END!");
+
+ ipp->state = IPP_DATA;
+ break;
+ }
+ else if (tag <= IPP_TAG_UNSUPPORTED)
+ {
+ /*
+ * Group tag... Set the current group and continue...
+ */
+
+ if (ipp->curtag == tag)
+ ippAddSeparator(ipp);
+
+ ipp->curtag = tag;
+ ipp->current = NULL;
+ DEBUG_printf(("ippRead: group tag = %x\n", tag));
+ continue;
+ }
+
+ DEBUG_printf(("ippRead: value tag = %x\n", tag));
+
+ /*
+ * Get the name...
+ */
+
+ if (ipp_read(http, buffer, 2) < 2)
+ {
+ DEBUG_puts("ippRead: unable to read name length!");
+ return (IPP_ERROR);
+ }
+
+ n = (buffer[0] << 8) | buffer[1];
+
+ DEBUG_printf(("ippRead: name length = %d\n", n));
+
+ if (n == 0)
+ {
+ /*
+ * More values for current attribute...
+ */
+
+ if (ipp->current == NULL)
+ return (IPP_ERROR);
+
+ attr = ipp->current;
+
+ if (attr->num_values >= IPP_MAX_VALUES)
+ return (IPP_ERROR);
+ }
+ else
+ {
+ /*
+ * New attribute; read the name and add it...
+ */
+
+ if (ipp_read(http, buffer, n) < n)
+ {
+ DEBUG_puts("ippRead: unable to read name!");
+ return (IPP_ERROR);
+ }
+
+ buffer[n] = '\0';
+ DEBUG_printf(("ippRead: name = \'%s\'\n", buffer));
+
+ attr = ipp->current = add_attr(ipp, IPP_MAX_VALUES);
+
+ attr->group_tag = ipp->curtag;
+ attr->value_tag = tag;
+ attr->name = strdup((char *)buffer);
+ attr->num_values = 0;
+ }
+
+ if (ipp_read(http, buffer, 2) < 2)
+ {
+ DEBUG_puts("ippRead: unable to read value length!");
+ return (IPP_ERROR);
+ }
+
+ n = (buffer[0] << 8) | buffer[1];
+ DEBUG_printf(("ippRead: value length = %d\n", n));
+
+ switch (tag)
+ {
+ case IPP_TAG_INTEGER :
+ case IPP_TAG_ENUM :
+ if (ipp_read(http, buffer, 4) < 4)
+ return (IPP_ERROR);
+
+ n = (((((buffer[0] << 8) | buffer[1]) << 8) | buffer[2]) << 8) |
+ buffer[3];
+
+ attr->values[attr->num_values].integer = n;
+ break;
+ case IPP_TAG_BOOLEAN :
+ if (ipp_read(http, buffer, 1) < 1)
+ return (IPP_ERROR);
+
+ attr->values[attr->num_values].boolean = buffer[0];
+ break;
+ case IPP_TAG_TEXT :
+ case IPP_TAG_NAME :
+ case IPP_TAG_KEYWORD :
+ case IPP_TAG_STRING :
+ case IPP_TAG_URI :
+ case IPP_TAG_URISCHEME :
+ case IPP_TAG_CHARSET :
+ case IPP_TAG_LANGUAGE :
+ case IPP_TAG_MIMETYPE :
+ if (ipp_read(http, buffer, n) < n)
+ return (IPP_ERROR);
+
+ buffer[n] = '\0';
+ DEBUG_printf(("ippRead: value = \'%s\'\n", buffer));
+
+ attr->values[attr->num_values].string.text = strdup((char *)buffer);
+ break;
+ case IPP_TAG_DATE :
+ if (ipp_read(http, buffer, 11) < 11)
+ return (IPP_ERROR);
+
+ memcpy(attr->values[attr->num_values].date, buffer, 11);
+ break;
+ case IPP_TAG_RESOLUTION :
+ if (ipp_read(http, buffer, 9) < 9)
+ return (IPP_ERROR);
+
+ attr->values[attr->num_values].resolution.xres =
+ (((((buffer[0] << 8) | buffer[1]) << 8) | buffer[2]) << 8) |
+ buffer[3];
+ attr->values[attr->num_values].resolution.yres =
+ (((((buffer[4] << 8) | buffer[5]) << 8) | buffer[6]) << 8) |
+ buffer[7];
+ attr->values[attr->num_values].resolution.units =
+ (ipp_res_t)buffer[8];
+ break;
+ case IPP_TAG_RANGE :
+ if (ipp_read(http, buffer, 8) < 8)
+ return (IPP_ERROR);
+
+ attr->values[attr->num_values].range.lower =
+ (((((buffer[0] << 8) | buffer[1]) << 8) | buffer[2]) << 8) |
+ buffer[3];
+ attr->values[attr->num_values].range.upper =
+ (((((buffer[4] << 8) | buffer[5]) << 8) | buffer[6]) << 8) |
+ buffer[7];
+ break;
+ case IPP_TAG_TEXTLANG :
+ case IPP_TAG_NAMELANG :
+ if (ipp_read(http, buffer, n) < n)
+ return (IPP_ERROR);
+
+ buffer[n] = '\0';
+
+ attr->values[attr->num_values].string.charset = strdup((char *)buffer);
+
+ if (ipp_read(http, buffer, 2) < 2)
+ return (IPP_ERROR);
+
+ n = (buffer[0] << 8) | buffer[1];
+
+ if (ipp_read(http, buffer, n) < n)
+ return (IPP_ERROR);
+
+ buffer[n] = '\0';
+
+ attr->values[attr->num_values].string.text = strdup((char *)buffer);
+ break;
+ }
+
+ attr->num_values ++;
+
+ /*
+ * If blocking is disabled, stop here...
+ */
+
+ if (!http->blocking && http->used == 0)
+ break;
+ }
+ break;
+
+ case IPP_DATA :
+ break;
+ }
+
+ return (ipp->state);
+}
+
+
+/*
+ * 'ippTimeToDate()' - Convert from UNIX time to RFC 1903 format.
+ */
+
+const ipp_uchar_t * /* O - RFC-1903 date/time data */
+ippTimeToDate(time_t t) /* I - UNIX time value */
+{
+ struct tm *unixdate; /* UNIX unixdate/time info */
+ static ipp_uchar_t date[11]; /* RFC-1903 date/time data */
+
+
+ /*
+ * RFC-1903 date/time format is:
+ *
+ * Byte(s) Description
+ * ------- -----------
+ * 0-1 Year (0 to 65535)
+ * 2 Month (1 to 12)
+ * 3 Day (1 to 31)
+ * 4 Hours (0 to 23)
+ * 5 Minutes (0 to 59)
+ * 6 Seconds (0 to 60, 60 = "leap second")
+ * 7 Deciseconds (0 to 9)
+ * 8 +/- UTC
+ * 9 UTC hours (0 to 11)
+ * 10 UTC minutes (0 to 59)
+ */
+
+ unixdate = gmtime(&t);
+ unixdate->tm_year += 1900;
+
+ date[0] = unixdate->tm_year >> 8;
+ date[1] = unixdate->tm_year;
+ date[2] = unixdate->tm_mon + 1;
+ date[3] = unixdate->tm_mday;
+ date[4] = unixdate->tm_hour;
+ date[5] = unixdate->tm_min;
+ date[6] = unixdate->tm_sec;
+ date[7] = 0;
+ date[8] = '+';
+ date[9] = 0;
+ date[10] = 0;
+
+ return (date);
+}
+
+
+/*
+ * 'ippWrite()' - Write data for an IPP request.
+ */
+
+ipp_state_t /* O - Current state */
+ippWrite(http_t *http, /* I - HTTP data */
+ ipp_t *ipp) /* I - IPP data */
+{
+ int i; /* Looping var */
+ int n; /* Length of data */
+ unsigned char buffer[8192], /* Data buffer */
+ *bufptr; /* Pointer into buffer */
+ ipp_attribute_t *attr; /* Current attribute */
+
+
+ if (http == NULL || ipp == NULL)
+ return (IPP_ERROR);
+
+ switch (ipp->state)
+ {
+ case IPP_IDLE :
+ ipp->state ++; /* Avoid common problem... */
+
+ case IPP_HEADER :
+ /*
+ * Send the request header...
+ */
+
+ bufptr = buffer;
+
+ *bufptr++ = 1;
+ *bufptr++ = 0;
+ *bufptr++ = ipp->request.any.op_status >> 8;
+ *bufptr++ = ipp->request.any.op_status;
+ *bufptr++ = ipp->request.any.request_id >> 24;
+ *bufptr++ = ipp->request.any.request_id >> 16;
+ *bufptr++ = ipp->request.any.request_id >> 8;
+ *bufptr++ = ipp->request.any.request_id;
+
+ if (httpWrite(http, (char *)buffer, bufptr - buffer) < 0)
+ {
+ DEBUG_puts("ippWrite: Could not write IPP header...");
+ return (IPP_ERROR);
+ }
+
+ ipp->state = IPP_ATTRIBUTE;
+ ipp->current = ipp->attrs;
+ ipp->curtag = IPP_TAG_ZERO;
+
+ /*
+ * If blocking is disabled, stop here...
+ */
+
+ if (!http->blocking)
+ break;
+
+ case IPP_ATTRIBUTE :
+ while (ipp->current != NULL)
+ {
+ /*
+ * Write this attribute...
+ */
+
+ bufptr = buffer;
+ attr = ipp->current;
+
+ ipp->current = ipp->current->next;
+
+ if (ipp->curtag != attr->group_tag)
+ {
+ /*
+ * Send a group operation tag...
+ */
+
+ ipp->curtag = attr->group_tag;
+
+ if (attr->group_tag == IPP_TAG_ZERO)
+ continue;
+
+ DEBUG_printf(("ippWrite: wrote group tag = %x\n", attr->group_tag));
+ *bufptr++ = attr->group_tag;
+ }
+
+ n = strlen(attr->name);
+
+ DEBUG_printf(("ippWrite: writing value tag = %x\n", attr->value_tag));
+ DEBUG_printf(("ippWrite: writing name = %d, \'%s\'\n", n, attr->name));
+
+ *bufptr++ = attr->value_tag;
+ *bufptr++ = n >> 8;
+ *bufptr++ = n;
+ memcpy(bufptr, attr->name, n);
+ bufptr += n;
+
+ switch (attr->value_tag)
+ {
+ case IPP_TAG_INTEGER :
+ case IPP_TAG_ENUM :
+ for (i = 0; i < attr->num_values; i ++)
+ {
+ if (i)
+ {
+ /*
+ * Arrays and sets are done by sending additional
+ * values with a zero-length name...
+ */
+
+ *bufptr++ = attr->value_tag;
+ *bufptr++ = 0;
+ *bufptr++ = 0;
+ }
+
+ *bufptr++ = 0;
+ *bufptr++ = 4;
+ *bufptr++ = attr->values[i].integer >> 24;
+ *bufptr++ = attr->values[i].integer >> 16;
+ *bufptr++ = attr->values[i].integer >> 8;
+ *bufptr++ = attr->values[i].integer;
+ }
+ break;
+
+ case IPP_TAG_BOOLEAN :
+ for (i = 0; i < attr->num_values; i ++)
+ {
+ if (i)
+ {
+ /*
+ * Arrays and sets are done by sending additional
+ * values with a zero-length name...
+ */
+
+ *bufptr++ = attr->value_tag;
+ *bufptr++ = 0;
+ *bufptr++ = 0;
+ }
+
+ *bufptr++ = 0;
+ *bufptr++ = 1;
+ *bufptr++ = attr->values[i].boolean;
+ }
+ break;
+
+ case IPP_TAG_TEXT :
+ case IPP_TAG_NAME :
+ case IPP_TAG_KEYWORD :
+ case IPP_TAG_STRING :
+ case IPP_TAG_URI :
+ case IPP_TAG_URISCHEME :
+ case IPP_TAG_CHARSET :
+ case IPP_TAG_LANGUAGE :
+ case IPP_TAG_MIMETYPE :
+ for (i = 0; i < attr->num_values; i ++)
+ {
+ if (i)
+ {
+ /*
+ * Arrays and sets are done by sending additional
+ * values with a zero-length name...
+ */
+
+ DEBUG_printf(("ippWrite: writing value tag = %x\n",
+ attr->value_tag));
+ DEBUG_printf(("ippWrite: writing name = 0, \'\'\n"));
+
+ *bufptr++ = attr->value_tag;
+ *bufptr++ = 0;
+ *bufptr++ = 0;
+ }
+
+ n = strlen(attr->values[i].string.text);
+
+ DEBUG_printf(("ippWrite: writing string = %d, \'%s\'\n", n,
+ attr->values[i].string.text));
+
+ *bufptr++ = n >> 8;
+ *bufptr++ = n;
+ memcpy(bufptr, attr->values[i].string.text, n);
+ bufptr += n;
+ }
+ break;
+
+ case IPP_TAG_DATE :
+ for (i = 0; i < attr->num_values; i ++)
+ {
+ if (i)
+ {
+ /*
+ * Arrays and sets are done by sending additional
+ * values with a zero-length name...
+ */
+
+ *bufptr++ = attr->value_tag;
+ *bufptr++ = 0;
+ *bufptr++ = 0;
+ }
+
+ *bufptr++ = 0;
+ *bufptr++ = 11;
+ memcpy(bufptr, attr->values[i].date, 11);
+ bufptr += 11;
+ }
+ break;
+
+ case IPP_TAG_RESOLUTION :
+ for (i = 0; i < attr->num_values; i ++)
+ {
+ if (i)
+ {
+ /*
+ * Arrays and sets are done by sending additional
+ * values with a zero-length name...
+ */
+
+ *bufptr++ = attr->value_tag;
+ *bufptr++ = 0;
+ *bufptr++ = 0;
+ }
+
+ *bufptr++ = 0;
+ *bufptr++ = 9;
+ *bufptr++ = attr->values[i].resolution.xres >> 24;
+ *bufptr++ = attr->values[i].resolution.xres >> 16;
+ *bufptr++ = attr->values[i].resolution.xres >> 8;
+ *bufptr++ = attr->values[i].resolution.xres;
+ *bufptr++ = attr->values[i].resolution.yres >> 24;
+ *bufptr++ = attr->values[i].resolution.yres >> 16;
+ *bufptr++ = attr->values[i].resolution.yres >> 8;
+ *bufptr++ = attr->values[i].resolution.yres;
+ *bufptr++ = attr->values[i].resolution.units;
+ }
+ break;
+
+ case IPP_TAG_RANGE :
+ for (i = 0; i < attr->num_values; i ++)
+ {
+ if (i)
+ {
+ /*
+ * Arrays and sets are done by sending additional
+ * values with a zero-length name...
+ */
+
+ *bufptr++ = attr->value_tag;
+ *bufptr++ = 0;
+ *bufptr++ = 0;
+ }
+
+ *bufptr++ = 0;
+ *bufptr++ = 8;
+ *bufptr++ = attr->values[i].range.lower >> 24;
+ *bufptr++ = attr->values[i].range.lower >> 16;
+ *bufptr++ = attr->values[i].range.lower >> 8;
+ *bufptr++ = attr->values[i].range.lower;
+ *bufptr++ = attr->values[i].range.upper >> 24;
+ *bufptr++ = attr->values[i].range.upper >> 16;
+ *bufptr++ = attr->values[i].range.upper >> 8;
+ *bufptr++ = attr->values[i].range.upper;
+ }
+ break;
+
+ case IPP_TAG_TEXTLANG :
+ case IPP_TAG_NAMELANG :
+ for (i = 0; i < attr->num_values; i ++)
+ {
+ if (i)
+ {
+ /*
+ * Arrays and sets are done by sending additional
+ * values with a zero-length name...
+ */
+
+ *bufptr++ = attr->value_tag;
+ *bufptr++ = 0;
+ *bufptr++ = 0;
+ }
+
+ n = strlen(attr->values[i].string.charset);
+ *bufptr++ = n >> 8;
+ *bufptr++ = n;
+ memcpy(bufptr, attr->values[i].string.charset, n);
+ bufptr += n;
+
+ n = strlen(attr->values[i].string.text);
+ *bufptr++ = n >> 8;
+ *bufptr++ = n;
+ memcpy(bufptr, attr->values[i].string.text, n);
+ bufptr += n;
+ }
+ break;
+ }
+
+ /*
+ * Write the data out...
+ */
+
+ if (httpWrite(http, (char *)buffer, bufptr - buffer) < 0)
+ {
+ DEBUG_puts("ippWrite: Could not write IPP attribute...");
+ return (IPP_ERROR);
+ }
+
+ DEBUG_printf(("ippWrite: wrote %d bytes\n", bufptr - buffer));
+
+ /*
+ * If blocking is disabled, stop here...
+ */
+
+ if (!http->blocking)
+ break;
+ }
+
+ if (ipp->current == NULL)
+ {
+ /*
+ * Done with all of the attributes; add the end-of-attributes tag...
+ */
+
+ buffer[0] = IPP_TAG_END;
+ if (httpWrite(http, (char *)buffer, 1) < 0)
+ {
+ DEBUG_puts("ippWrite: Could not write IPP end-tag...");
+ return (IPP_ERROR);
+ }
+
+ ipp->state = IPP_DATA;
+ }
+ break;
+
+ case IPP_DATA :
+ break;
+ }
+
+ return (ipp->state);
+}
+
+
+/*
+ * 'ippPort()' - Return the default IPP port number.
+ */
+
+int /* O - Port number */
+ippPort(void)
+{
+ const char *server_port; /* SERVER_PORT environment variable */
+ struct servent *port; /* Port number info */
+
+
+ if ((server_port = getenv("IPP_PORT")) != NULL)
+ return (atoi(server_port));
+ else if ((port = getservbyname("ipp", NULL)) == NULL)
+ return (IPP_PORT);
+ else
+ return (ntohs(port->s_port));
+}
+
+
+/*
+ * 'add_attr()' - Add a new attribute to the request.
+ */
+
+static ipp_attribute_t * /* O - New attribute */
+add_attr(ipp_t *ipp, /* I - IPP request */
+ int num_values) /* I - Number of values */
+{
+ ipp_attribute_t *attr; /* New attribute */
+
+
+ DEBUG_printf(("add_attr(%08x, %d)\n", ipp, num_values));
+
+ if (ipp == NULL || num_values < 0)
+ return (NULL);
+
+ attr = calloc(sizeof(ipp_attribute_t) +
+ (num_values - 1) * sizeof(ipp_value_t), 1);
+
+ attr->num_values = num_values;
+
+ if (attr == NULL)
+ return (NULL);
+
+ if (ipp->last == NULL)
+ ipp->attrs = attr;
+ else
+ ipp->last->next = attr;
+
+ ipp->last = attr;
+
+ return (attr);
+}
+
+
+/*
+ * 'ipp_read()' - Semi-blocking read on a HTTP connection...
+ */
+
+static int /* O - Number of bytes read */
+ipp_read(http_t *http, /* I - Client connection */
+ unsigned char *buffer, /* O - Buffer for data */
+ int length) /* I - Total length */
+{
+ int tbytes, /* Total bytes read */
+ bytes; /* Bytes read this pass */
+
+
+ /*
+ * Loop until all bytes are read...
+ */
+
+ for (tbytes = 0; tbytes < length; tbytes += bytes, buffer += bytes)
+ if ((bytes = httpRead(http, (char *)buffer, length - tbytes)) <= 0)
+ break;
+
+ /*
+ * Return the number of bytes read...
+ */
+
+ return (tbytes);
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/cups/ipp.h b/cups/ipp.h
new file mode 100644
index 000000000..0b343f561
--- /dev/null
+++ b/cups/ipp.h
@@ -0,0 +1,343 @@
+/*
+ * "$Id$"
+ *
+ * Internet Printing Protocol definitions for the Common UNIX Printing
+ * System (CUPS).
+ *
+ * Copyright 1997-1999 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ */
+
+#ifndef _CUPS_IPP_H_
+# define _CUPS_IPP_H_
+
+/*
+ * Include necessary headers...
+ */
+
+# include <cups/http.h>
+
+
+/*
+ * C++ magic...
+ */
+
+# ifdef __cplusplus
+extern "C" {
+# endif /* __cplusplus */
+
+
+/*
+ * IPP version string...
+ */
+
+# define IPP_VERSION "\001\000"
+
+/*
+ * IPP registered port number... This is the default value - applications
+ * should use the ippPort() function so that you can customize things in
+ * /etc/services if needed!
+ */
+
+# define IPP_PORT 631
+
+/*
+ * Common limits...
+ */
+
+# define IPP_MAX_NAME 256
+# define IPP_MAX_VALUES 100
+
+
+/*
+ * Types and structures...
+ */
+
+typedef enum /**** Format tags for attribute formats... ****/
+{
+ IPP_TAG_ZERO = 0x00,
+ IPP_TAG_OPERATION,
+ IPP_TAG_JOB,
+ IPP_TAG_END,
+ IPP_TAG_PRINTER,
+ IPP_TAG_EXTENSION,
+ IPP_TAG_UNSUPPORTED = 0x10,
+ IPP_TAG_DEFAULT,
+ IPP_TAG_UNKNOWN,
+ IPP_TAG_NOVALUE,
+ IPP_TAG_INTEGER = 0x21,
+ IPP_TAG_BOOLEAN,
+ IPP_TAG_ENUM,
+ IPP_TAG_STRING = 0x30,
+ IPP_TAG_DATE,
+ IPP_TAG_RESOLUTION,
+ IPP_TAG_RANGE,
+ IPP_TAG_COLLECTION,
+ IPP_TAG_TEXTLANG,
+ IPP_TAG_NAMELANG,
+ IPP_TAG_TEXT = 0x41,
+ IPP_TAG_NAME,
+ IPP_TAG_KEYWORD = 0x44,
+ IPP_TAG_URI,
+ IPP_TAG_URISCHEME,
+ IPP_TAG_CHARSET,
+ IPP_TAG_LANGUAGE,
+ IPP_TAG_MIMETYPE
+} ipp_tag_t;
+
+typedef enum /**** Resolution units... ****/
+{
+ IPP_RES_PER_INCH = 3,
+ IPP_RES_PER_CM
+} ipp_res_t;
+
+typedef enum /**** Multiple Document Handling ****/
+{
+ IPP_DOC_SINGLE,
+ IPP_DOC_UNCOLLATED,
+ IPP_DOC_COLLATED,
+ IPP_DOC_SEPARATE
+} ipp_doc_t;
+
+typedef enum /**** Finishings... ****/
+{
+ IPP_FINISH_NONE = 3,
+ IPP_FINISH_STAPLE,
+ IPP_FINISH_PUNCH,
+ IPP_FINISH_COVER,
+ IPP_FINISH_BIND
+} ipp_finish_t;
+
+typedef enum /**** Orientation... ****/
+{
+ IPP_PORTRAIT = 3, /* No rotation */
+ IPP_LANDSCAPE, /* 90 degrees counter-clockwise */
+ IPP_REVERSE_LANDSCAPE, /* 90 degrees clockwise */
+ IPP_REVERSE_PORTRAIT /* 180 degrees */
+} ipp_orient_t;
+
+typedef enum /**** Qualities... ****/
+{
+ IPP_QUALITY_DRAFT = 3,
+ IPP_QUALITY_NORMAL,
+ IPP_QUALITY_HIGH
+} ipp_quality_t;
+
+typedef enum /**** Job States.... */
+{
+ IPP_JOB_PENDING = 3,
+ IPP_JOB_HELD,
+ IPP_JOB_PROCESSING,
+ IPP_JOB_STOPPED,
+ IPP_JOB_CANCELED,
+ IPP_JOB_ABORTED,
+ IPP_JOB_COMPLETED
+} ipp_jstate_t;
+
+typedef enum /**** Printer States.... */
+{
+ IPP_PRINTER_IDLE = 3,
+ IPP_PRINTER_PROCESSING,
+ IPP_PRINTER_STOPPED
+} ipp_pstate_t;
+
+typedef enum /**** IPP states... ****/
+{
+ IPP_ERROR = -1, /* An error occurred */
+ IPP_IDLE, /* Nothing is happening/request completed */
+ IPP_HEADER, /* The request header needs to be sent/received */
+ IPP_ATTRIBUTE, /* One or more attributes need to be sent/received */
+ IPP_DATA /* IPP request data needs to be sent/received */
+} ipp_state_t;
+
+typedef enum /**** IPP operations... ****/
+{
+ IPP_PRINT_JOB = 0x0002,
+ IPP_PRINT_URI,
+ IPP_VALIDATE_JOB,
+ IPP_CREATE_JOB,
+ IPP_SEND_DOCUMENT,
+ IPP_SEND_URI,
+ IPP_CANCEL_JOB,
+ IPP_GET_JOB_ATTRIBUTES,
+ IPP_GET_JOBS,
+ IPP_GET_PRINTER_ATTRIBUTES,
+ IPP_HOLD_JOB = 0x000c,
+ IPP_RELEASE_JOB,
+ IPP_RESTART_JOB,
+ IPP_PAUSE_PRINTER = 0x0010,
+ IPP_RESUME_PRINTER,
+ IPP_PURGE_JOBS,
+ IPP_PRIVATE = 0x4000,
+ CUPS_GET_DEFAULT,
+ CUPS_GET_PRINTERS,
+ CUPS_ADD_PRINTER,
+ CUPS_DELETE_PRINTER,
+ CUPS_GET_CLASSES,
+ CUPS_ADD_CLASS,
+ CUPS_DELETE_CLASS,
+ CUPS_ACCEPT_JOBS,
+ CUPS_REJECT_JOBS,
+ CUPS_SET_DEFAULT
+} ipp_op_t;
+
+typedef enum /**** IPP status codes... ****/
+{
+ IPP_OK = 0x0000,
+ IPP_OK_SUBST,
+ IPP_OK_CONFLICT,
+ IPP_BAD_REQUEST = 0x0400,
+ IPP_FORBIDDEN,
+ IPP_NOT_AUTHENTICATED,
+ IPP_NOT_AUTHORIZED,
+ IPP_NOT_POSSIBLE,
+ IPP_TIMEOUT,
+ IPP_NOT_FOUND,
+ IPP_GONE,
+ IPP_REQUEST_ENTITY,
+ IPP_REQUEST_VALUE,
+ IPP_DOCUMENT_FORMAT,
+ IPP_ATTRIBUTES,
+ IPP_URI_SCHEME,
+ IPP_CHARSET,
+ IPP_CONFLICT,
+ IPP_INTERNAL_ERROR = 0x0500,
+ IPP_OPERATION_NOT_SUPPORTED,
+ IPP_SERVICE_UNAVAILABLE,
+ IPP_VERSION_NOT_SUPPORTED,
+ IPP_DEVICE_UNAVAILABLE,
+ IPP_TEMPORARY_ERROR,
+ IPP_NOT_ACCEPTING,
+ IPP_PRINTER_BUSY
+} ipp_status_t;
+
+typedef unsigned char ipp_uchar_t;/**** Unsigned 8-bit integer/character ****/
+
+typedef union /**** Request Header ****/
+{
+ struct /* Any Header */
+ {
+ ipp_uchar_t version[2]; /* Protocol version number */
+ int op_status; /* Operation ID or status code*/
+ int request_id; /* Request ID */
+ } any;
+
+ struct /* Operation Header */
+ {
+ ipp_uchar_t version[2]; /* Protocol version number */
+ ipp_op_t operation_id; /* Operation ID */
+ int request_id; /* Request ID */
+ } op;
+
+ struct /* Status Header */
+ {
+ ipp_uchar_t version[2]; /* Protocol version number */
+ ipp_status_t status_code; /* Status code */
+ int request_id; /* Request ID */
+ } status;
+} ipp_request_t;
+
+
+typedef union /**** Attribute Value ****/
+{
+ int integer; /* Integer/enumerated value */
+
+ char boolean; /* Boolean value */
+
+ ipp_uchar_t date[11]; /* Date/time value */
+
+ struct
+ {
+ int xres, /* Horizontal resolution */
+ yres; /* Vertical resolution */
+ ipp_res_t units; /* Resolution units */
+ } resolution; /* Resolution value */
+
+ struct
+ {
+ int lower, /* Lower value */
+ upper; /* Upper value */
+ } range; /* Range of integers value */
+
+ struct
+ {
+ char *charset; /* Character set */
+ char *text; /* String */
+ } string; /* String with language value */
+} ipp_value_t;
+
+typedef struct ipp_attribute_s /**** Attribute ****/
+{
+ struct ipp_attribute_s *next;
+ /* Next atrtribute in list */
+ ipp_tag_t group_tag, /* Job/Printer/Operation group tag */
+ value_tag; /* What type of value is it? */
+ char *name; /* Name of attribute */
+ int num_values; /* Number of values */
+ ipp_value_t values[1]; /* Values */
+} ipp_attribute_t;
+
+typedef struct /**** Request State ****/
+{
+ ipp_state_t state; /* State of request */
+ ipp_request_t request; /* Request header */
+ ipp_attribute_t *attrs, /* Attributes */
+ *last, /* Last attribute in list */
+ *current; /* Current attribute (for read/write) */
+ ipp_tag_t curtag; /* Current attribute group tag */
+} ipp_t;
+
+
+/*
+ * Prototypes...
+ */
+
+extern time_t ippDateToTime(const ipp_uchar_t *date);
+extern ipp_attribute_t *ippAddBoolean(ipp_t *ipp, ipp_tag_t group, const char *name, char value);
+extern ipp_attribute_t *ippAddBooleans(ipp_t *ipp, ipp_tag_t group, const char *name, int num_values, const char *values);
+extern ipp_attribute_t *ippAddDate(ipp_t *ipp, ipp_tag_t group, const char *name, const ipp_uchar_t *value);
+extern ipp_attribute_t *ippAddInteger(ipp_t *ipp, ipp_tag_t group, ipp_tag_t type, const char *name, int value);
+extern ipp_attribute_t *ippAddIntegers(ipp_t *ipp, ipp_tag_t group, ipp_tag_t type, const char *name, int num_values, const int *values);
+extern ipp_attribute_t *ippAddRange(ipp_t *ipp, ipp_tag_t group, const char *name, int lower, int upper);
+extern ipp_attribute_t *ippAddRanges(ipp_t *ipp, ipp_tag_t group, const char *name, int num_values, const int *lower, const int *upper);
+extern ipp_attribute_t *ippAddResolution(ipp_t *ipp, ipp_tag_t group, const char *name, ipp_res_t units, int xres, int yres);
+extern ipp_attribute_t *ippAddResolutions(ipp_t *ipp, ipp_tag_t group, const char *name, int num_values, ipp_res_t units, const int *xres, const int *yres);
+extern ipp_attribute_t *ippAddSeparator(ipp_t *ipp);
+extern ipp_attribute_t *ippAddString(ipp_t *ipp, ipp_tag_t group, ipp_tag_t type, const char *name, const char *charset, const char *value);
+extern ipp_attribute_t *ippAddStrings(ipp_t *ipp, ipp_tag_t group, ipp_tag_t type, const char *name, int num_values, const char *charset, const char **values);
+extern void ippDelete(ipp_t *ipp);
+extern ipp_attribute_t *ippFindAttribute(ipp_t *ipp, const char *name, ipp_tag_t type);
+extern size_t ippLength(ipp_t *ipp);
+extern ipp_t *ippNew(void);
+extern ipp_state_t ippRead(http_t *http, ipp_t *ipp);
+extern const ipp_uchar_t *ippTimeToDate(time_t t);
+extern ipp_state_t ippWrite(http_t *http, ipp_t *ipp);
+extern int ippPort(void);
+
+/*
+ * C++ magic...
+ */
+
+# ifdef __cplusplus
+}
+# endif /* __cplusplus */
+#endif /* !_CUPS_IPP_H_ */
+
+/*
+ * End of "$Id$".
+ */
diff --git a/cups/language.c b/cups/language.c
new file mode 100644
index 000000000..575344e90
--- /dev/null
+++ b/cups/language.c
@@ -0,0 +1,373 @@
+/*
+ * "$Id$"
+ *
+ * I18N/language support for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-1999 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * Contents:
+ *
+ * cupsLangEncoding() - Return the character encoding (us-ascii, etc.)
+ * for the given language.
+ * cupsLangFlush() - Flush all language data out of the cache.
+ * cupsLangFree() - Free language data.
+ * cupsLangGet() - Get a language.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include "string.h"
+#include "language.h"
+
+
+/*
+ * Local globals...
+ */
+
+static cups_lang_t *lang_cache = NULL; /* Language string cache */
+static char *lang_blank = ""; /* Blank constant string */
+static char *lang_encodings[] = /* Encoding strings */
+ {
+ "us-ascii",
+ "iso8859-1",
+ "iso8859-2",
+ "iso8859-3",
+ "iso8859-4",
+ "iso8859-5",
+ "iso8859-6",
+ "iso8859-7",
+ "iso8859-8",
+ "iso8859-9",
+ "iso8859-10",
+ "utf8"
+ };
+static char *lang_default[] = /* Default POSIX locale */
+ {
+#include "cups_C.h"
+ NULL
+ };
+
+
+/*
+ * 'cupsLangEncoding()' - Return the character encoding (us-ascii, etc.)
+ * for the given language.
+ */
+
+char * /* O - Character encoding */
+cupsLangEncoding(cups_lang_t *lang) /* I - Language data */
+{
+ if (lang == NULL)
+ return (lang_encodings[0]);
+ else
+ return (lang_encodings[lang->encoding]);
+}
+
+
+/*
+ * 'cupsLangFlush()' - Flush all language data out of the cache.
+ */
+
+void
+cupsLangFlush(void)
+{
+ int i; /* Looping var */
+ cups_lang_t *lang, /* Current language */
+ *next; /* Next language */
+
+
+ for (lang = lang_cache; lang != NULL; lang = next)
+ {
+ for (i = 0; i < CUPS_MSG_MAX; i ++)
+ if (lang->messages[i] != NULL && lang->messages[i] != lang_blank)
+ free(lang->messages[i]);
+
+ next = lang->next;
+ free(lang);
+ }
+}
+
+
+/*
+ * 'cupsLangFree()' - Free language data.
+ *
+ * This does not actually free anything; use cupsLangFlush() for that.
+ */
+
+void
+cupsLangFree(cups_lang_t *lang) /* I - Language to free */
+{
+ if (lang != NULL && lang->used > 0)
+ lang->used --;
+}
+
+
+/*
+ * 'cupsLangGet()' - Get a language.
+ */
+
+cups_lang_t * /* O - Language data */
+cupsLangGet(const char *language) /* I - Language or locale */
+{
+ int i, count; /* Looping vars */
+ char langname[16], /* Requested language name */
+ real[16], /* Real language name */
+ filename[1024], /* Filename for language locale file */
+ *localedir; /* Directory for locale files */
+ FILE *fp; /* Language locale file pointer */
+ char line[1024]; /* Line from file */
+ cups_msg_t msg; /* Message number */
+ char *text; /* Message text */
+ cups_lang_t *lang; /* Current language... */
+
+
+ /*
+ * Convert the language string passed in to a locale string. "C" is the
+ * standard POSIX locale and is copied unchanged. Otherwise the
+ * language string is converted from ll-cc (language-country) to ll_CC
+ * to match the file naming convention used by all POSIX-compliant
+ * operating systems.
+ */
+
+ if (language == NULL || language[0] == '\0')
+ strcpy(langname, "C");
+ else
+ strcpy(langname, language);
+
+ if (strlen(langname) < 2)
+ strcpy(real, "C");
+ else
+ {
+ real[0] = tolower(langname[0]);
+ real[1] = tolower(langname[1]);
+
+ if (langname[2] == '_' || langname[2] == '-')
+ {
+ real[2] = '_';
+ real[3] = toupper(langname[3]);
+ real[4] = toupper(langname[4]);
+ real[5] = '\0';
+ langname[5] = '\0';
+ }
+ else
+ {
+ langname[2] = '\0';
+ real[2] = '\0';
+ }
+ }
+
+ /*
+ * Next try to open a locale file; we will try the country-localized file
+ * first, and then look for generic language file. If all else fails we
+ * will use the POSIX locale.
+ */
+
+ if ((localedir = getenv("LOCALEDIR")) == NULL)
+ localedir = CUPS_LOCALEDIR;
+
+ sprintf(filename, "%s/%s/cups_%s", localedir, real, real);
+
+ if ((fp = fopen(filename, "r")) == NULL)
+ if (strlen(real) > 2)
+ {
+ /*
+ * Nope, see if we can open a generic language file...
+ */
+
+ real[2] = '\0';
+ sprintf(filename, "%s/%s/cups_%s", localedir, real, real);
+ fp = fopen(filename, "r");
+ }
+
+ /*
+ * Then see if we already have this language loaded...
+ */
+
+ for (lang = lang_cache; lang != NULL; lang = lang->next)
+ if (strcmp(lang->language, langname) == 0)
+ {
+ lang->used ++;
+
+ if (fp != NULL)
+ fclose(fp);
+
+ return (lang);
+ }
+
+ /*
+ * OK, we have an open messages file; the first line will contain the
+ * language encoding (us-ascii, iso-8859-1, etc.), and the rest will
+ * be messages consisting of:
+ *
+ * #### SP message text
+ *
+ * or:
+ *
+ * message text
+ *
+ * If the line starts with a number, then message processing picks up
+ * where the number indicates. Otherwise the last message number is
+ * incremented.
+ *
+ * All leading whitespace is deleted.
+ */
+
+ if (fp == NULL)
+ strcpy(line, lang_default[0]);
+ else if (fgets(line, sizeof(line), fp) == NULL)
+ {
+ /*
+ * Can't read encoding!
+ */
+
+ fclose(fp);
+ return (NULL);
+ }
+
+ i = strlen(line) - 1;
+ if (line[i] == '\n')
+ line[i] = '\0'; /* Strip LF */
+
+ /*
+ * See if there is a free language available; if so, use that
+ * record...
+ */
+
+ for (lang = lang_cache; lang != NULL; lang = lang->next)
+ if (lang->used == 0)
+ break;
+
+ if (lang == NULL)
+ {
+ /*
+ * Allocate memory for the language and add it to the cache.
+ */
+
+ if ((lang = calloc(sizeof(cups_lang_t), 1)) == NULL)
+ {
+ fclose(fp);
+ return (NULL);
+ }
+
+ lang->next = lang_cache;
+ lang_cache = lang;
+ }
+
+
+ /*
+ * Free all old strings as needed...
+ */
+
+ for (i = 0; i < CUPS_MSG_MAX; i ++)
+ {
+ if (lang->messages[i] != NULL && lang->messages[i] != lang_blank)
+ free(lang->messages[i]);
+
+ lang->messages[i] = lang_blank;
+ }
+
+ /*
+ * Then assign the language and encoding fields...
+ */
+
+ lang->used ++;
+ strcpy(lang->language, langname);
+
+ for (i = 0; i < (sizeof(lang_encodings) / sizeof(lang_encodings[0])); i ++)
+ if (strcmp(lang_encodings[i], line) == 0)
+ {
+ lang->encoding = (cups_encoding_t)i;
+ break;
+ }
+
+ /*
+ * Read the strings from the file...
+ */
+
+ msg = (cups_msg_t)-1;
+ count = 1;
+
+ for (;;)
+ {
+ /*
+ * Read a line from memory or from a file...
+ */
+
+ if (fp == NULL)
+ {
+ if (lang_default[count] == NULL)
+ break;
+
+ strcpy(line, lang_default[count]);
+ }
+ else if (fgets(line, sizeof(line), fp) == NULL)
+ break;
+
+ count ++;
+
+ /*
+ * Ignore blank lines...
+ */
+
+ i = strlen(line) - 1;
+ if (line[i] == '\n')
+ line[i] = '\0'; /* Strip LF */
+
+ if (line[0] == '\0')
+ continue;
+
+ /*
+ * Grab the message number and text...
+ */
+
+ if (isdigit(line[0]))
+ msg = (cups_msg_t)atoi(line);
+ else
+ msg ++;
+
+ if (msg < 0 || msg >= CUPS_MSG_MAX)
+ continue;
+
+ text = line;
+ while (isdigit(*text))
+ text ++;
+ while (isspace(*text))
+ text ++;
+
+ lang->messages[msg] = strdup(text);
+ }
+
+ /*
+ * Close the file and return...
+ */
+
+ if (fp != NULL)
+ fclose(fp);
+
+ return (lang);
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/cups/language.h b/cups/language.h
new file mode 100644
index 000000000..6bdff5a2c
--- /dev/null
+++ b/cups/language.h
@@ -0,0 +1,195 @@
+/*
+ * "$Id$"
+ *
+ * Multi-language support for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-1999 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ */
+
+#ifndef _CUPS_LANGUAGE_H_
+# define _CUPS_LANGUAGE_H_
+
+/*
+ * Include necessary headers...
+ */
+
+# include <locale.h>
+
+# ifdef __cplusplus
+extern "C" {
+# endif /* __cplusplus */
+
+/*
+ * Messages...
+ */
+
+typedef enum /**** Message Indices ****/
+{
+ CUPS_MSG_OK,
+ CUPS_MSG_CANCEL,
+ CUPS_MSG_HELP,
+ CUPS_MSG_QUIT,
+ CUPS_MSG_CLOSE,
+ CUPS_MSG_YES,
+ CUPS_MSG_NO,
+ CUPS_MSG_ON,
+ CUPS_MSG_OFF,
+ CUPS_MSG_SAVE,
+ CUPS_MSG_DISCARD,
+ CUPS_MSG_DEFAULT,
+ CUPS_MSG_OPTIONS,
+ CUPS_MSG_MORE_INFO,
+ CUPS_MSG_BLACK,
+ CUPS_MSG_COLOR,
+ CUPS_MSG_CYAN,
+ CUPS_MSG_MAGENTA,
+ CUPS_MSG_YELLOW,
+ CUPS_MSG_COPYRIGHT,
+ CUPS_MSG_GENERAL,
+ CUPS_MSG_PRINTER,
+ CUPS_MSG_IMAGE,
+ CUPS_MSG_HPGL2,
+ CUPS_MSG_EXTRA,
+ CUPS_MSG_DOCUMENT,
+ CUPS_MSG_OTHER,
+ CUPS_MSG_PRINT_PAGES,
+ CUPS_MSG_ENTIRE_DOCUMENT,
+ CUPS_MSG_PAGE_RANGE,
+ CUPS_MSG_REVERSE_ORDER,
+ CUPS_MSG_PAGE_FORMAT,
+ CUPS_MSG_1_UP,
+ CUPS_MSG_2_UP,
+ CUPS_MSG_4_UP,
+ CUPS_MSG_IMAGE_SCALING,
+ CUPS_MSG_USE_NATURAL_IMAGE_SIZE,
+ CUPS_MSG_ZOOM_BY_PERCENT,
+ CUPS_MSG_ZOOM_BY_PPI,
+ CUPS_MSG_MIRROR_IMAGE,
+ CUPS_MSG_COLOR_SATURATION,
+ CUPS_MSG_COLOR_HUE,
+ CUPS_MSG_FIT_TO_PAGE,
+ CUPS_MSG_SHADING,
+ CUPS_MSG_DEFAULT_PEN_WIDTH,
+ CUPS_MSG_GAMMA_CORRECTION,
+ CUPS_MSG_BRIGHTNESS,
+ CUPS_MSG_ADD,
+ CUPS_MSG_DELETE,
+ CUPS_MSG_MODIFY,
+ CUPS_MSG_PRINTER_URI,
+ CUPS_MSG_PRINTER_NAME,
+ CUPS_MSG_PRINTER_LOCATION,
+ CUPS_MSG_PRINTER_INFO,
+ CUPS_MSG_PRINTER_MAKE_AND_MODEL,
+ CUPS_MSG_DEVICE_URI,
+ CUPS_MSG_FORMATTING_PAGE,
+ CUPS_MSG_PRINTING_PAGE,
+ CUPS_MSG_INITIALIZING_PRINTER,
+ CUPS_MSG_PRINTER_STATE,
+ CUPS_MSG_ACCEPTING_JOBS,
+ CUPS_MSG_NOT_ACCEPTING_JOBS,
+ CUPS_MSG_PRINT_JOBS,
+ CUPS_MSG_CLASS,
+ CUPS_MSG_LOCAL,
+ CUPS_MSG_REMOTE,
+ CUPS_MSG_DUPLEXING,
+ CUPS_MSG_STAPLING,
+ CUPS_MSG_FAST_COPIES,
+ CUPS_MSG_COLLATED_COPIES,
+ CUPS_MSG_PUNCHING,
+ CUPS_MSG_COVERING,
+ CUPS_MSG_BINDING,
+ CUPS_MSG_SORTING,
+ CUPS_MSG_SMALL,
+ CUPS_MSG_MEDIUM,
+ CUPS_MSG_LARGE,
+ CUPS_MSG_VARIABLE,
+ CUPS_MSG_IDLE,
+ CUPS_MSG_PROCESSING,
+ CUPS_MSG_STOPPED,
+ CUPS_MSG_ALL,
+ CUPS_MSG_ODD,
+ CUPS_MSG_EVEN_PAGES,
+ CUPS_MSG_DARKER_LIGHTER,
+ CUPS_MSG_MEDIA_SIZE,
+ CUPS_MSG_MEDIA_TYPE,
+ CUPS_MSG_MEDIA_SOURCE,
+ CUPS_MSG_ORIENTATION,
+ CUPS_MSG_PORTRAIT,
+ CUPS_MSG_LANDSCAPE,
+ CUPS_MSG_JOB_STATE,
+ CUPS_MSG_JOB_NAME,
+ CUPS_MSG_USER_NAME,
+ CUPS_MSG_PRIORITY,
+ CUPS_MSG_COPIES,
+ CUPS_MSG_FILE_SIZE,
+ CUPS_MSG_PENDING,
+ CUPS_MSG_OUTPUT_MODE,
+ CUPS_MSG_RESOLUTION,
+ CUPS_MSG_HTTP_BASE = 200,
+ CUPS_MSG_HTTP_END = 505,
+ CUPS_MSG_MAX
+} cups_msg_t;
+
+typedef enum /**** Language Encodings ****/
+{
+ CUPS_US_ASCII,
+ CUPS_ISO8859_1,
+ CUPS_ISO8859_2,
+ CUPS_ISO8859_3,
+ CUPS_ISO8859_4,
+ CUPS_ISO8859_5,
+ CUPS_ISO8859_6,
+ CUPS_ISO8859_7,
+ CUPS_ISO8859_8,
+ CUPS_ISO8859_9,
+ CUPS_ISO8859_10,
+ CUPS_UTF8
+} cups_encoding_t;
+
+typedef struct cups_lang_str /**** Language Cache Structure ****/
+{
+ struct cups_lang_str *next; /* Next language in cache */
+ int used; /* Number of times this entry has been used. */
+ cups_encoding_t encoding; /* Text encoding */
+ char language[16]; /* Language/locale name */
+ char *messages[CUPS_MSG_MAX];
+ /* Message array */
+} cups_lang_t;
+
+
+/*
+ * Prototypes...
+ */
+
+# define cupsLangDefault() cupsLangGet(setlocale(LC_ALL, ""))
+extern char *cupsLangEncoding(cups_lang_t *lang);
+extern void cupsLangFlush(void);
+extern void cupsLangFree(cups_lang_t *lang);
+extern cups_lang_t *cupsLangGet(const char *language);
+# define cupsLangString(lang,msg) (lang)->messages[(msg)]
+
+# ifdef __cplusplus
+}
+# endif /* __cplusplus */
+
+#endif /* !_CUPS_LANGUAGE_H_ */
+
+/*
+ * End of "$Id$".
+ */
diff --git a/cups/mark.c b/cups/mark.c
new file mode 100644
index 000000000..8061766bb
--- /dev/null
+++ b/cups/mark.c
@@ -0,0 +1,412 @@
+/*
+ * "$Id$"
+ *
+ * Option marking routines for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-1999 by Easy Software Products, all rights reserved.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * PostScript is a trademark of Adobe Systems, Inc.
+ *
+ * Contents:
+ *
+ * ppdConflicts() - Check to see if there are any conflicts.
+ * ppdFindChoice() - Return a pointer to an option choice.
+ * ppdFindMarkedChoice() - Return the marked choice for the specified option.
+ * ppdFindOption() - Return a pointer to the specified option.
+ * ppdIsMarked() - Check to see if an option is marked...
+ * ppdMarkDefaults() - Mark all default options in the PPD file.
+ * ppdMarkOption() - Mark an option in a PPD file.
+ * ppd_defaults() - Set the defaults for this group and all sub-groups.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "ppd.h"
+#include "string.h"
+
+
+/*
+ * Local functions...
+ */
+
+static void ppd_defaults(ppd_file_t *ppd, ppd_group_t *g);
+
+
+/*
+ * 'ppdConflicts()' - Check to see if there are any conflicts.
+ */
+
+int /* O - Number of conflicts found */
+ppdConflicts(ppd_file_t *ppd) /* I - PPD to check */
+{
+ int i, j, k, /* Looping variables */
+ conflicts; /* Number of conflicts */
+ ppd_const_t *c; /* Current constraint */
+ ppd_group_t *g, *sg; /* Groups */
+ ppd_option_t *o1, *o2; /* Options */
+ ppd_choice_t *c1, *c2; /* Choices */
+
+
+ if (ppd == NULL)
+ return (0);
+
+ /*
+ * Clear all conflicts...
+ */
+
+ conflicts = 0;
+
+ for (i = ppd->num_groups, g = ppd->groups; i > 0; i --, g ++)
+ {
+ for (j = g->num_options, o1 = g->options; j > 0; j --, o1 ++)
+ o1->conflicted = 0;
+
+ for (j = g->num_subgroups, sg = g->subgroups; j > 0; j --, sg ++)
+ for (k = sg->num_options, o1 = sg->options; k > 0; k --, o1 ++)
+ o1->conflicted = 0;
+ }
+
+ /*
+ * Loop through all of the UI constraints and flag any options
+ * that conflict...
+ */
+
+ for (i = ppd->num_consts, c = ppd->consts; i > 0; i --, c ++)
+ {
+ /*
+ * Grab pointers to the first option...
+ */
+
+ o1 = ppdFindOption(ppd, c->option1);
+
+ if (o1 == NULL)
+ continue;
+ else if (c->choice1[0] != '\0')
+ {
+ /*
+ * This constraint maps to a specific choice.
+ */
+
+ c1 = ppdFindChoice(o1, c->choice1);
+ }
+ else
+ {
+ /*
+ * This constraint applies to any choice for this option.
+ */
+
+ for (j = o1->num_choices, c1 = o1->choices; j > 0; j --, c1 ++)
+ if (c1->marked)
+ break;
+
+ if (j == 0)
+ c1 = NULL;
+ }
+
+ /*
+ * Grab pointers to the second option...
+ */
+
+ o2 = ppdFindOption(ppd, c->option2);
+
+ if (o2 == NULL)
+ continue;
+ else if (c->choice2[0] != '\0')
+ {
+ /*
+ * This constraint maps to a specific choice.
+ */
+
+ c2 = ppdFindChoice(o2, c->choice2);
+ }
+ else
+ {
+ /*
+ * This constraint applies to any choice for this option.
+ */
+
+ for (j = o2->num_choices, c2 = o2->choices; j > 0; j --, c2 ++)
+ if (c2->marked)
+ break;
+
+ if (j == 0)
+ c2 = NULL;
+ }
+
+ /*
+ * If both options are marked then there is a conflict...
+ */
+
+ if (c1 != NULL && c1->marked &&
+ c2 != NULL && c2->marked)
+ {
+ conflicts ++;
+ o1->conflicted = 1;
+ o2->conflicted = 1;
+ }
+ }
+
+ /*
+ * Return the number of conflicts found...
+ */
+
+ return (conflicts);
+}
+
+
+/*
+ * 'ppdFindChoice()' - Return a pointer to an option choice.
+ */
+
+ppd_choice_t * /* O - Choice pointer or NULL */
+ppdFindChoice(ppd_option_t *o, /* I - Pointer to option */
+ const char *choice) /* I - Name of choice */
+{
+ int i; /* Looping var */
+ ppd_choice_t *c; /* Current choice */
+
+
+ if (o == NULL || choice == NULL)
+ return (NULL);
+
+ for (i = o->num_choices, c = o->choices; i > 0; i --, c ++)
+ if (strcmp(c->choice, choice) == 0)
+ return (c);
+
+ return (NULL);
+}
+
+
+/*
+ * 'ppdFindMarkedChoice()' - Return the marked choice for the specified option.
+ */
+
+ppd_choice_t * /* O - Pointer to choice or NULL */
+ppdFindMarkedChoice(ppd_file_t *ppd, /* I - PPD file */
+ const char *option) /* I - Keyword/option name */
+{
+ int i; /* Looping var */
+ ppd_option_t *o; /* Pointer to option */
+ ppd_choice_t *c; /* Pointer to choice */
+
+
+ if ((o = ppdFindOption(ppd, option)) == NULL)
+ return (NULL);
+
+ for (i = o->num_choices, c = o->choices; i > 0; i --, c ++)
+ if (c->marked)
+ return (c);
+
+ return (NULL);
+}
+
+
+/*
+ * 'ppdFindOption()' - Return a pointer to the specified option.
+ */
+
+ppd_option_t * /* O - Pointer to option or NULL */
+ppdFindOption(ppd_file_t *ppd, /* I - PPD file data */
+ const char *option) /* I - Option/Keyword name */
+{
+ int i, j, k; /* Looping vars */
+ ppd_option_t *o; /* Pointer to option */
+ ppd_group_t *g, /* Pointer to group */
+ *sg; /* Pointer to subgroup */
+
+
+ if (ppd == NULL || option == NULL)
+ return (NULL);
+
+ for (i = ppd->num_groups, g = ppd->groups; i > 0; i --, g ++)
+ {
+ for (j = g->num_options, o = g->options; j > 0; j --, o ++)
+ if (strcmp(o->keyword, option) == 0)
+ return (o);
+
+ for (j = g->num_subgroups, sg = g->subgroups; j > 0; j --, sg ++)
+ for (k = sg->num_options, o = sg->options; k > 0; k --, o ++)
+ if (strcmp(o->keyword, option) == 0)
+ return (o);
+ }
+
+ return (NULL);
+}
+
+
+/*
+ * 'ppdIsMarked()' - Check to see if an option is marked...
+ */
+
+int /* O - Non-zero if option is marked */
+ppdIsMarked(ppd_file_t *ppd, /* I - PPD file data */
+ const char *option, /* I - Option/Keyword name */
+ const char *choice) /* I - Choice name */
+{
+ ppd_option_t *o; /* Option pointer */
+ ppd_choice_t *c; /* Choice pointer */
+
+
+ if (ppd == NULL)
+ return (0);
+
+ if ((o = ppdFindOption(ppd, option)) == NULL)
+ return (0);
+
+ if ((c = ppdFindChoice(o, choice)) == NULL)
+ return (0);
+
+ return (c->marked);
+}
+
+
+/*
+ * 'ppdMarkDefaults()' - Mark all default options in the PPD file.
+ */
+
+void
+ppdMarkDefaults(ppd_file_t *ppd)/* I - PPD file record */
+{
+ int i; /* Looping variables */
+ ppd_group_t *g; /* Current group */
+ ppd_option_t *o; /* PageSize option */
+
+
+ if (ppd == NULL)
+ return;
+
+ for (i = ppd->num_groups, g = ppd->groups; i > 0; i --, g ++)
+ ppd_defaults(ppd, g);
+}
+
+
+/*
+ * 'ppdMarkOption()' - Mark an option in a PPD file.
+ *
+ * Notes:
+ *
+ * -1 is returned if the given option would conflict with any currently
+ * selected option.
+ */
+
+int /* O - Number of conflicts */
+ppdMarkOption(ppd_file_t *ppd, /* I - PPD file record */
+ const char *option, /* I - Keyword */
+ const char *choice) /* I - Option name */
+{
+ int i; /* Looping var */
+ ppd_option_t *o; /* Option pointer */
+ ppd_choice_t *c; /* Choice pointer */
+
+
+ if (ppd == NULL)
+ return (0);
+
+ if (strcmp(option, "PageSize") == 0 && strncmp(choice, "Custom.", 7) == 0)
+ {
+ /*
+ * Handle variable page sizes...
+ */
+
+ ppdPageSize(ppd, choice);
+ choice = "Custom";
+ }
+
+ if ((o = ppdFindOption(ppd, option)) == NULL)
+ return (0);
+
+ for (i = o->num_choices, c = o->choices; i > 0; i --, c ++)
+ if (strcmp(c->choice, choice) == 0)
+ break;
+
+ if (i)
+ {
+ /*
+ * Option found; mark it and then handle unmarking any other options.
+ */
+
+ c->marked = 1;
+
+ if (o->ui != PPD_UI_PICKMANY)
+ for (i = o->num_choices, c = o->choices; i > 0; i --, c ++)
+ if (strcmp(c->choice, choice) != 0)
+ c->marked = 0;
+
+ if (strcmp(option, "PageSize") == 0 || strcmp(option, "PageRegion") == 0)
+ {
+ /*
+ * Mark current page size...
+ */
+
+ for (i = 0; i < ppd->num_sizes; i ++)
+ ppd->sizes[i].marked = strcmp(ppd->sizes[i].name, choice) == 0;
+
+ /*
+ * Unmark the current PageSize or PageRegion setting, as appropriate...
+ */
+
+ if (strcmp(option, "PageSize") == 0)
+ {
+ o = ppdFindOption(ppd, "PageRegion");
+ for (i = 0; i < o->num_choices; i ++)
+ o->choices[i].marked = 0;
+ }
+ else
+ {
+ o = ppdFindOption(ppd, "PageSize");
+ for (i = 0; i < o->num_choices; i ++)
+ o->choices[i].marked = 0;
+ }
+ }
+ }
+
+ return (ppdConflicts(ppd));
+}
+
+
+/*
+ * 'ppd_defaults()' - Set the defaults for this group and all sub-groups.
+ */
+
+static void
+ppd_defaults(ppd_file_t *ppd, /* I - PPD file */
+ ppd_group_t *g) /* I - Group to default */
+{
+ int i; /* Looping var */
+ ppd_option_t *o; /* Current option */
+ ppd_group_t *sg; /* Current sub-group */
+
+
+ if (g == NULL)
+ return;
+
+ for (i = g->num_options, o = g->options; i > 0; i --, o ++)
+ if (strcmp(o->keyword, "PageRegion") != 0)
+ ppdMarkOption(ppd, o->keyword, o->defchoice);
+
+ for (i = g->num_subgroups, sg = g->subgroups; i > 0; i --, sg ++)
+ ppd_defaults(ppd, sg);
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/cups/mime.c b/cups/mime.c
new file mode 100644
index 000000000..555d50cd0
--- /dev/null
+++ b/cups/mime.c
@@ -0,0 +1,617 @@
+/*
+ * "$Id$"
+ *
+ * MIME database file routines for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-1999 by Easy Software Products, all rights reserved.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * Contents:
+ *
+ * mimeDelete() - Delete (free) a MIME database.
+ * mimeMerge() - Merge a MIME database from disk with the current one.
+ * mimeNew() - Create a new, empty MIME database.
+ * load_types() - Load a xyz.types file...
+ * delete_rules() - Free all memory for the given rule tree.
+ * load_convs() - Load a xyz.convs file...
+ *
+ * Revision History:
+ *
+ * $Log: mime.c,v $
+ * Revision 1.14 1999/07/12 16:09:38 mike
+ * Fixed all constant arrays to use "const" modifier.
+ *
+ * Revision 1.13 1999/06/18 18:36:10 mike
+ * Fixed address to 44141 Airport View Drive...
+ *
+ * Revision 1.12 1999/04/21 21:19:33 mike
+ * Changes for HP-UX.
+ *
+ * Revision 1.11 1999/04/21 19:31:29 mike
+ * Changed the directory header stuff to use the autoconf-recommended
+ * sequence of #ifdef's.
+ *
+ * Changed the language routines to look for the LOCALEDIR environment
+ * variable, and if it is not defined to use the LOCALEDIR string defined
+ * in config.h.
+ *
+ * Revision 1.10 1999/03/01 20:51:53 mike
+ * Code cleanup - removed extraneous semi-colons...
+ *
+ * Revision 1.9 1999/02/26 22:00:51 mike
+ * Added more debug statements.
+ *
+ * Fixed bugs in cupsPrintFile() - wasn't setting the IPP_TAG_MIMETYPE
+ * value tag for the file type.
+ *
+ * Updated conversion filter code to handle wildcards for super-type.
+ *
+ * Revision 1.8 1999/02/20 16:04:38 mike
+ * Updated mime.c to scan directories under WIN32.
+ *
+ * Fixed some compiler warnings under WIN32.
+ *
+ * Updated VC++ project files.
+ *
+ * Updated mime.types and mime.convs files for actual registered
+ * MIME type names.
+ *
+ * Revision 1.7 1999/02/05 17:40:53 mike
+ * Added IPP client read/write code.
+ *
+ * Added string functions missing from some UNIXs.
+ *
+ * Added option parsing functions.
+ *
+ * Added IPP convenience functions (not implemented yet).
+ *
+ * Updated source files to use local string.h as needed (for
+ * missing string functions)
+ *
+ * Revision 1.6 1999/02/01 22:08:39 mike
+ * Restored original directory-scanning functionality of mimeLoad().
+ *
+ * Revision 1.4 1999/01/27 18:31:56 mike
+ * Updated PPD routines to handle emulations and patch files.
+ *
+ * Added DSC comments to emit output as appropriate.
+ *
+ * Revision 1.3 1999/01/24 14:18:43 mike
+ * Check-in prior to CVS use.
+ *
+ * Revision 1.2 1998/08/06 14:38:38 mike
+ * Finished coding and testing for CUPS 1.0.
+ *
+ * Revision 1.1 1998/06/11 20:50:53 mike
+ * Initial revision
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#include "string.h"
+#include "mime.h"
+
+#if defined(WIN32) || defined(__EMX__)
+# include <windows.h>
+#elif HAVE_DIRENT_H
+# include <dirent.h>
+typedef struct dirent DIRENT;
+# define NAMLEN(dirent) strlen((dirent)->d_name)
+#else
+# if HAVE_SYS_NDIR_H
+# include <sys/ndir.h>
+# endif
+# if HAVE_SYS_DIR_H
+# include <sys/dir.h>
+# endif
+# if HAVE_NDIR_H
+# include <ndir.h>
+# endif
+typedef struct direct DIRENT;
+# define NAMLEN(dirent) (dirent)->d_namlen
+#endif
+
+
+/*
+ * Local functions...
+ */
+
+static void load_types(mime_t *mime, char *filename);
+static void load_convs(mime_t *mime, char *filename);
+static void delete_rules(mime_magic_t *rules);
+
+
+/*
+ * 'mimeDelete()' - Delete (free) a MIME database.
+ */
+
+void
+mimeDelete(mime_t *mime) /* I - MIME database */
+{
+ int i; /* Looping var */
+
+
+ if (mime == NULL)
+ return;
+
+ /*
+ * Loop through the file types and delete any rules...
+ */
+
+ for (i = 0; i < mime->num_types; i ++)
+ {
+ delete_rules(mime->types[i]->rules);
+ free(mime->types[i]);
+ }
+
+ /*
+ * Free the types and filters arrays, and then the MIME database structure.
+ */
+
+ free(mime->types);
+ free(mime->filters);
+ free(mime);
+}
+
+
+/*
+ * 'mimeMerge()' - Merge a MIME database from disk with the current one.
+ */
+
+mime_t * /* O - Updated MIME database */
+mimeMerge(mime_t *mime, /* I - MIME database to add to */
+ const char *pathname) /* I - Directory to load */
+{
+#if defined(WIN32) || defined(__EMX__)
+ HANDLE dir; /* Directory handle */
+ WIN32_FIND_DATA dent; /* Directory entry */
+ char filename[1024], /* Full filename of types/converts file */
+ *pathsep; /* Last character in path */
+
+
+ /*
+ * First open the directory specified by pathname... Return NULL if nothing
+ * was read or if the pathname is NULL...
+ */
+
+ if (pathname == NULL)
+ return (NULL);
+
+ strcpy(filename, pathname);
+ pathsep = filename + strlen(filename);
+ if (pathsep == filename ||
+ (pathsep[-1] != '/' && pathsep[-1] != '\\'))
+ {
+ strcpy(pathsep, "/");
+ pathsep ++;
+ }
+
+ strcpy(pathsep, "*.types");
+
+ if ((dir = FindFirstFile(filename, &dent)) == 0)
+ return (NULL);
+
+ /*
+ * If "mime" is NULL, make a new, blank database...
+ */
+
+ if (mime == NULL)
+ if ((mime = mimeNew()) == NULL)
+ return (NULL);
+
+ /*
+ * Read all the .types files...
+ */
+
+ do
+ {
+ /*
+ * Load a mime.types file...
+ */
+
+ strcpy(pathsep, dent.cFileName);
+ load_types(mime, filename);
+ }
+ while (FindNextFile(dir, &dent));
+
+ FindClose(dir);
+
+ /*
+ * Read all the .convs files...
+ */
+
+ strcpy(pathsep, "*.convs");
+
+ if ((dir = FindFirstFile(filename, &dent)) == 0)
+ return (mime);
+
+ do
+ {
+ /*
+ * Load a mime.convs file...
+ */
+
+ strcpy(pathsep, dent.cFileName);
+ load_convs(mime, filename);
+ }
+ while (FindNextFile(dir, &dent));
+
+ FindClose(dir);
+
+ return (mime);
+#else
+ DIR *dir; /* Directory */
+ DIRENT *dent; /* Directory entry */
+ char filename[1024]; /* Full filename of types/converts file */
+
+
+ /*
+ * First open the directory specified by pathname... Return NULL if nothing
+ * was read or if the pathname is NULL...
+ */
+
+ if (pathname == NULL)
+ return (NULL);
+
+ if ((dir = opendir(pathname)) == NULL)
+ return (NULL);
+
+ /*
+ * If "mime" is NULL, make a new, blank database...
+ */
+
+ if (mime == NULL)
+ if ((mime = mimeNew()) == NULL)
+ return (NULL);
+
+ /*
+ * Read all the .types files...
+ */
+
+ while ((dent = readdir(dir)) != NULL)
+ {
+ if (NAMLEN(dent) > 6 &&
+ strcmp(dent->d_name + NAMLEN(dent) - 6, ".types") == 0)
+ {
+ /*
+ * Load a mime.types file...
+ */
+
+ sprintf(filename, "%s/%s", pathname, dent->d_name);
+ load_types(mime, filename);
+ }
+ }
+
+ rewinddir(dir);
+
+ /*
+ * Read all the .convs files...
+ */
+
+ while ((dent = readdir(dir)) != NULL)
+ {
+ if (NAMLEN(dent) > 6 &&
+ strcmp(dent->d_name + NAMLEN(dent) - 6, ".convs") == 0)
+ {
+ /*
+ * Load a mime.convs file...
+ */
+
+ sprintf(filename, "%s/%s", pathname, dent->d_name);
+ load_convs(mime, filename);
+ }
+ }
+
+ closedir(dir);
+
+ return (mime);
+#endif /* WIN32 || __EMX__ */
+}
+
+
+/*
+ * 'mimeNew()' - Create a new, empty MIME database.
+ */
+
+mime_t * /* O - MIME database */
+mimeNew(void)
+{
+ return ((mime_t *)calloc(1, sizeof(mime_t)));
+}
+
+
+/*
+ * 'load_types()' - Load a xyz.types file...
+ */
+
+static void
+load_types(mime_t *mime, /* I - MIME database */
+ char *filename) /* I - Types file to load */
+{
+ FILE *fp; /* Types file */
+ int linelen; /* Length of line */
+ char line[65536], /* Input line from file */
+ *lineptr, /* Current position in line */
+ super[MIME_MAX_SUPER], /* Super-type name */
+ type[MIME_MAX_TYPE], /* Type name */
+ *temp; /* Temporary pointer */
+ mime_type_t *typeptr; /* New MIME type */
+
+
+ /*
+ * First try to open the file...
+ */
+
+ if ((fp = fopen(filename, "r")) == NULL)
+ return;
+
+ /*
+ * Then read each line from the file, skipping any comments in the file...
+ */
+
+ while (fgets(line, sizeof(line), fp) != NULL)
+ {
+ linelen = strlen(line);
+
+ /*
+ * While the last character in the line is a backslash, continue on to the
+ * next line (and the next, etc.)
+ */
+
+ if (line[linelen - 1] == '\n')
+ {
+ line[linelen - 1] = '\0';
+ linelen --;
+ }
+
+ while (line[linelen - 1] == '\\')
+ {
+ linelen --;
+
+ if (fgets(line + linelen, sizeof(line) - linelen, fp) == NULL)
+ line[linelen] = '\0';
+ else
+ {
+ linelen += strlen(line + linelen);
+ if (line[linelen - 1] == '\n')
+ {
+ line[linelen - 1] = '\0';
+ linelen --;
+ }
+ }
+ }
+
+ /*
+ * Skip blank lines and lines starting with a #...
+ */
+
+ if (line[0] == '\n' || line[0] == '#')
+ continue;
+
+ /*
+ * Extract the super-type and type names from the beginning of the line.
+ */
+
+ lineptr = line;
+ temp = super;
+
+ while (*lineptr != '/' && *lineptr != '\n' && *lineptr != '\0' &&
+ (temp - super + 1) < MIME_MAX_SUPER)
+ *temp++ = tolower(*lineptr++);
+
+ *temp = '\0';
+
+ if (*lineptr != '/')
+ continue;
+
+ lineptr ++;
+ temp = type;
+
+ while (*lineptr != ' ' && *lineptr != '\t' && *lineptr != '\n' &&
+ *lineptr != '\0' && (temp - type + 1) < MIME_MAX_TYPE)
+ *temp++ = tolower(*lineptr++);
+
+ *temp = '\0';
+
+ /*
+ * Add the type and rules to the MIME database...
+ */
+
+ typeptr = mimeAddType(mime, super, type);
+ mimeAddTypeRule(typeptr, lineptr);
+ }
+}
+
+
+/*
+ * 'load_convs()' - Load a xyz.convs file...
+ */
+
+static void
+load_convs(mime_t *mime, /* I - MIME database */
+ char *filename) /* I - Convs file to load */
+{
+ int i; /* Looping var */
+ FILE *fp; /* Convs file */
+ char line[1024], /* Input line from file */
+ *lineptr, /* Current position in line */
+ super[MIME_MAX_SUPER], /* Super-type name */
+ type[MIME_MAX_TYPE], /* Type name */
+ *temp, /* Temporary pointer */
+ *filter; /* Filter program */
+ mime_type_t **temptype, /* MIME type looping var */
+ *dsttype; /* Destination MIME type */
+ int cost; /* Cost of filter */
+
+
+ /*
+ * First try to open the file...
+ */
+
+ if ((fp = fopen(filename, "r")) == NULL)
+ return;
+
+ /*
+ * Then read each line from the file, skipping any comments in the file...
+ */
+
+ while (fgets(line, sizeof(line), fp) != NULL)
+ {
+ /*
+ * Skip blank lines and lines starting with a #...
+ */
+
+ if (line[0] == '\n' || line[0] == '#')
+ continue;
+
+ /*
+ * Extract the destination super-type and type names from the middle of
+ * the line.
+ */
+
+ lineptr = line;
+ while (*lineptr != ' ' && *lineptr != '\t' && *lineptr != '\0')
+ lineptr ++;
+
+ while (*lineptr == ' ' || *lineptr == '\t')
+ lineptr ++;
+
+ temp = super;
+
+ while (*lineptr != '/' && *lineptr != '\n' && *lineptr != '\0' &&
+ (temp - super + 1) < MIME_MAX_SUPER)
+ *temp++ = tolower(*lineptr++);
+
+ *temp = '\0';
+
+ if (*lineptr != '/')
+ continue;
+
+ lineptr ++;
+ temp = type;
+
+ while (*lineptr != ' ' && *lineptr != '\t' && *lineptr != '\n' &&
+ *lineptr != '\0' && (temp - type + 1) < MIME_MAX_TYPE)
+ *temp++ = tolower(*lineptr++);
+
+ *temp = '\0';
+
+ if (*lineptr == '\0' || *lineptr == '\n')
+ continue;
+
+ if ((dsttype = mimeType(mime, super, type)) == NULL)
+ continue;
+
+ /*
+ * Then get the cost and filter program...
+ */
+
+ while (*lineptr == ' ' || *lineptr == '\t')
+ lineptr ++;
+
+ if (*lineptr < '0' || *lineptr > '9')
+ continue;
+
+ cost = atoi(lineptr);
+
+ while (*lineptr != ' ' && *lineptr != '\t' && *lineptr != '\0')
+ lineptr ++;
+ while (*lineptr == ' ' || *lineptr == '\t')
+ lineptr ++;
+
+ if (*lineptr == '\0' || *lineptr == '\n')
+ continue;
+
+ filter = lineptr;
+ if (filter[strlen(filter) - 1] == '\n')
+ filter[strlen(filter) - 1] = '\0';
+
+ /*
+ * Finally, get the source super-type and type names from the beginning of
+ * the line. We do it here so we can support wildcards...
+ */
+
+ lineptr = line;
+ temp = super;
+
+ while (*lineptr != '/' && *lineptr != '\n' && *lineptr != '\0' &&
+ (temp - super + 1) < MIME_MAX_SUPER)
+ *temp++ = tolower(*lineptr++);
+
+ *temp = '\0';
+
+ if (*lineptr != '/')
+ continue;
+
+ lineptr ++;
+ temp = type;
+
+ while (*lineptr != ' ' && *lineptr != '\t' && *lineptr != '\n' &&
+ *lineptr != '\0' && (temp - type + 1) < MIME_MAX_TYPE)
+ *temp++ = tolower(*lineptr++);
+
+ *temp = '\0';
+
+ /*
+ * Add the filter to the MIME database, supporting wildcards as needed...
+ */
+
+ for (temptype = mime->types, i = 0; i < mime->num_types; i ++, temptype ++)
+ if ((super[0] == '*' || strcmp((*temptype)->super, super) == 0) &&
+ (type[0] == '*' || strcmp((*temptype)->type, type) == 0))
+ mimeAddFilter(mime, *temptype, dsttype, cost, filter);
+ }
+}
+
+
+/*
+ * 'delete_rules()' - Free all memory for the given rule tree.
+ */
+
+static void
+delete_rules(mime_magic_t *rules) /* I - Rules to free */
+{
+ mime_magic_t *next; /* Next rule to free */
+
+
+ /*
+ * Free the rules list, descending recursively to free any child rules.
+ */
+
+ while (rules != NULL)
+ {
+ next = rules->next;
+
+ if (rules->child != NULL)
+ delete_rules(rules->child);
+
+ free(rules);
+ rules = next;
+ }
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/cups/mime.h b/cups/mime.h
new file mode 100644
index 000000000..a7625d82f
--- /dev/null
+++ b/cups/mime.h
@@ -0,0 +1,137 @@
+/*
+ * "$Id$"
+ *
+ * MIME type/conversion database definitions for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-1999 by Easy Software Products, all rights reserved.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ */
+
+#ifndef _MIME_H_
+# define _MIME_H_
+
+/*
+ * C++ magic...
+ */
+
+# ifdef _cplusplus
+extern "C" {
+# endif /* _cplusplus */
+
+
+/*
+ * Constants...
+ */
+
+# define MIME_MAX_SUPER 16 /* Maximum size of supertype name */
+# define MIME_MAX_TYPE 32 /* Maximum size of type name */
+# define MIME_MAX_FILTER 256 /* Maximum size of filter pathname */
+# define MIME_MAX_BUFFER 8192 /* Maximum size of file buffer */
+
+
+/*
+ * Types/structures...
+ */
+
+typedef enum
+{
+ MIME_MAGIC_NOP, /* No operation */
+ MIME_MAGIC_AND, /* Logical AND of all children */
+ MIME_MAGIC_OR, /* Logical OR of all children */
+ MIME_MAGIC_MATCH, /* Filename match */
+ MIME_MAGIC_ASCII, /* ASCII characters in range */
+ MIME_MAGIC_PRINTABLE, /* Printable characters (32-255) in range */
+ MIME_MAGIC_STRING, /* String matches */
+ MIME_MAGIC_CHAR, /* Character/byte matches */
+ MIME_MAGIC_SHORT, /* Short/16-bit word matches */
+ MIME_MAGIC_INT, /* Integer/32-bit word matches */
+ MIME_MAGIC_LOCALE /* Current locale matches string */
+} mime_op_t;
+
+typedef struct mime_magic_str /**** MIME Magic Data ****/
+{
+ struct mime_magic_str *prev, /* Previous rule */
+ *next, /* Next rule */
+ *parent, /* Parent rules */
+ *child; /* Child rules */
+ short op, /* Operation code (see above) */
+ invert; /* Invert the result */
+ int offset, /* Offset in file */
+ length; /* Length of data */
+ union
+ {
+ char matchv[64]; /* Match value */
+ char localev[64]; /* Locale value */
+ char stringv[64]; /* String value */
+ char charv; /* Byte value */
+ short shortv; /* Short value */
+ int intv; /* Integer value */
+ } value;
+} mime_magic_t;
+
+typedef struct /**** MIME Type Data ****/
+{
+ char super[MIME_MAX_SUPER], /* Super-type name ("image", "application", etc.) */
+ type[MIME_MAX_TYPE]; /* Type name ("png", "postscript", etc.) */
+ mime_magic_t *rules; /* Rules used to detect this type */
+} mime_type_t;
+
+typedef struct /**** MIME Conversion Filter Data ****/
+{
+ mime_type_t *src, /* Source type */
+ *dst; /* Destination type */
+ int cost; /* Relative cost */
+ char filter[MIME_MAX_FILTER];/* Filter program to use */
+} mime_filter_t;
+
+typedef struct /**** MIME Database ****/
+{
+ int num_types; /* Number of file types */
+ mime_type_t **types; /* File types */
+ int num_filters; /* Number of type conversion filters */
+ mime_filter_t *filters; /* Type conversion filters */
+} mime_t;
+
+
+/*
+ * Functions...
+ */
+
+extern void mimeDelete(mime_t *mime);
+#define mimeLoad(pathname) mimeMerge((mime_t *)0, (pathname));
+extern mime_t *mimeMerge(mime_t *mime, const char *pathname);
+extern mime_t *mimeNew(void);
+
+extern mime_type_t *mimeAddType(mime_t *mime, const char *super, const char *type);
+extern int mimeAddTypeRule(mime_type_t *mt, const char *rule);
+extern mime_type_t *mimeFileType(mime_t *mime, const char *pathname);
+extern mime_type_t *mimeType(mime_t *mime, const char *super, const char *type);
+
+extern mime_filter_t *mimeAddFilter(mime_t *mime, mime_type_t *src, mime_type_t *dst,
+ int cost, const char *filter);
+extern mime_filter_t *mimeFilter(mime_t *mime, mime_type_t *src, mime_type_t *dst,
+ int *num_filters);
+
+# ifdef _cplusplus
+}
+# endif /* _cplusplus */
+#endif /* !_MIME_H_ */
+
+/*
+ * End of "$Id$".
+ */
diff --git a/cups/options.c b/cups/options.c
new file mode 100644
index 000000000..d5d222ce9
--- /dev/null
+++ b/cups/options.c
@@ -0,0 +1,378 @@
+/*
+ * "$Id$"
+ *
+ * Option routines for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-1999 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * Contents:
+ *
+ * cupsAddOption() - Add an option to an option array.
+ * cupsFreeOptions() - Free all memory used by options.
+ * cupsGetOption() - Get an option value.
+ * cupsParseOptions() - Parse options from a command-line argument.
+ * cupsMarkOptions() - Mark command-line options in a PPD file.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "cups.h"
+#include <stdlib.h>
+#include <ctype.h>
+#include "string.h"
+
+
+/*
+ * 'cupsAddOption()' - Add an option to an option array.
+ */
+
+int /* O - Number of options */
+cupsAddOption(const char *name, /* I - Name of option */
+ const char *value, /* I - Value of option */
+ int num_options, /* I - Number of options */
+ cups_option_t **options) /* IO - Pointer to options */
+{
+ int i; /* Looping var */
+ cups_option_t *temp; /* Pointer to new option */
+
+
+ if (name == NULL || value == NULL || options == NULL)
+ return (0);
+
+ /*
+ * Look for an existing option with the same name...
+ */
+
+ for (i = 0, temp = *options; i < num_options; i ++, temp ++)
+ if (strcmp(temp->name, name) == 0)
+ break;
+
+ if (i >= num_options)
+ {
+ /*
+ * No matching option name...
+ */
+
+ if (num_options == 0)
+ temp = (cups_option_t *)malloc(sizeof(cups_option_t));
+ else
+ temp = (cups_option_t *)realloc(*options, sizeof(cups_option_t) *
+ (num_options + 1));
+
+ if (temp == NULL)
+ return (0);
+
+ *options = temp;
+ temp += num_options;
+ temp->name = strdup(name);
+ num_options ++;
+ }
+ else
+ {
+ /*
+ * Match found; free the old value...
+ */
+
+ free(temp->value);
+ }
+
+ temp->value = strdup(value);
+
+ return (num_options);
+}
+
+
+/*
+ * 'cupsFreeOptions()' - Free all memory used by options.
+ */
+
+void
+cupsFreeOptions(int num_options, /* I - Number of options */
+ cups_option_t *options) /* I - Pointer to options */
+{
+ int i; /* Looping var */
+
+
+ if (num_options == 0 || options == NULL)
+ return;
+
+ for (i = 0; i < num_options; i ++)
+ {
+ free(options[i].name);
+ free(options[i].value);
+ }
+
+ free(options);
+}
+
+
+/*
+ * 'cupsGetOption()' - Get an option value.
+ */
+
+const char * /* O - Option value or NULL */
+cupsGetOption(const char *name, /* I - Name of option */
+ int num_options,/* I - Number of options */
+ cups_option_t *options) /* I - Options */
+{
+ int i; /* Looping var */
+
+
+ if (name == NULL || num_options == 0 || options == NULL)
+ return (NULL);
+
+ for (i = 0; i < num_options; i ++)
+ if (strcmp(options[i].name, name) == 0)
+ return (options[i].value);
+
+ return (NULL);
+}
+
+
+/*
+ * 'cupsParseOptions()' - Parse options from a command-line argument.
+ */
+
+int /* O - Number of options found */
+cupsParseOptions(const char *arg, /* I - Argument to parse */
+ int num_options, /* I - Number of options */
+ cups_option_t **options) /* O - Options found */
+{
+ char *copyarg, /* Copy of input string */
+ *ptr, /* Pointer into string */
+ *name, /* Pointer to name */
+ *value; /* Pointer to value */
+
+
+ if (arg == NULL || options == NULL)
+ return (0);
+
+ /*
+ * Make a copy of the argument string and then divide it up...
+ */
+
+ copyarg = strdup(arg);
+ ptr = copyarg;
+
+ while (*ptr != '\0')
+ {
+ /*
+ * Get the name up to a SPACE, =, or end-of-string...
+ */
+
+ name = ptr;
+ while (!isspace(*ptr) && *ptr != '=' && *ptr != '\0')
+ ptr ++;
+
+ /*
+ * Skip trailing spaces...
+ */
+
+ while (isspace(*ptr))
+ *ptr++ = '\0';
+
+ if (*ptr != '=')
+ {
+ /*
+ * Start of another option...
+ */
+
+ num_options = cupsAddOption(name, "", num_options, options);
+ continue;
+ }
+
+ /*
+ * Remove = and parse the value...
+ */
+
+ *ptr++ = '\0';
+
+ if (*ptr == '\'')
+ {
+ /*
+ * Quoted string constant...
+ */
+
+ ptr ++;
+ value = ptr;
+
+ while (*ptr != '\'' && *ptr != '\0')
+ ptr ++;
+
+ if (*ptr != '\0')
+ *ptr++ = '\0';
+ }
+ else if (*ptr == '\"')
+ {
+ /*
+ * Double-quoted string constant...
+ */
+
+ ptr ++;
+ value = ptr;
+
+ while (*ptr != '\"' && *ptr != '\0')
+ ptr ++;
+
+ if (*ptr != '\0')
+ *ptr++ = '\0';
+ }
+ else
+ {
+ /*
+ * Normal space-delimited string...
+ */
+
+ value = ptr;
+
+ while (!isspace(*ptr) && *ptr != '\0')
+ ptr ++;
+
+ while (isspace(*ptr))
+ *ptr++ = '\0';
+ }
+
+ /*
+ * Add the string value...
+ */
+
+ num_options = cupsAddOption(name, value, num_options, options);
+ }
+
+ /*
+ * Free the copy of the argument we made and return the number of options
+ * found.
+ */
+
+ free(copyarg);
+
+ return (num_options);
+}
+
+
+/*
+ * 'cupsMarkOptions()' - Mark command-line options in a PPD file.
+ */
+
+int /* O - 1 if conflicting */
+cupsMarkOptions(ppd_file_t *ppd, /* I - PPD file */
+ int num_options, /* I - Number of options */
+ cups_option_t *options) /* I - Options */
+{
+ int i; /* Looping var */
+ int conflict; /* Option conflicts */
+ char *val, /* Pointer into value */
+ *ptr, /* Pointer into string */
+ s[255]; /* Temporary string */
+
+
+ conflict = 0;
+
+ for (i = num_options; i > 0; i --, options ++)
+ if (strcmp(options->name, "media") == 0)
+ {
+ /*
+ * Loop through the option string, separating it at commas and
+ * marking each individual option.
+ */
+
+ for (val = options->value; *val;)
+ {
+ /*
+ * Extract the sub-option from the string...
+ */
+
+ for (ptr = s; *val && *val != ',' && (ptr - s) < (sizeof(s) - 1);)
+ *ptr++ = *val++;
+ *ptr++ = '\0';
+
+ if (*val == ',')
+ val ++;
+
+ /*
+ * Mark it...
+ */
+
+ if (ppdMarkOption(ppd, "PageSize", s))
+ conflict = 1;
+ if (ppdMarkOption(ppd, "InputSlot", s))
+ conflict = 1;
+ if (ppdMarkOption(ppd, "MediaType", s))
+ conflict = 1;
+ if (ppdMarkOption(ppd, "EFMediaQualityMode", s)) /* EFI */
+ conflict = 1;
+ if (strcasecmp(s, "manual") == 0)
+ if (ppdMarkOption(ppd, "ManualFeed", "True"))
+ conflict = 1;
+ }
+ }
+ else if (strcmp(options->name, "sides") == 0)
+ {
+ if (strcmp(options->value, "one-sided") == 0)
+ {
+ if (ppdMarkOption(ppd, "Duplex", "None"))
+ conflict = 1;
+ if (ppdMarkOption(ppd, "EFDuplex", "None")) /* EFI */
+ conflict = 1;
+ if (ppdMarkOption(ppd, "KD03Duplex", "None")) /* Kodak */
+ conflict = 1;
+ }
+ else if (strcmp(options->value, "two-sided-long-edge") == 0)
+ {
+ if (ppdMarkOption(ppd, "Duplex", "DuplexNoTumble"))
+ conflict = 1;
+ if (ppdMarkOption(ppd, "EFDuplex", "DuplexNoTumble")) /* EFI */
+ conflict = 1;
+ if (ppdMarkOption(ppd, "KD03Duplex", "DuplexNoTumble")) /* Kodak */
+ conflict = 1;
+ }
+ else if (strcmp(options->value, "two-sided-short-edge") == 0)
+ {
+ if (ppdMarkOption(ppd, "Duplex", "DuplexTumble"))
+ conflict = 1;
+ if (ppdMarkOption(ppd, "EFDuplex", "DuplexTumble")) /* EFI */
+ conflict = 1;
+ if (ppdMarkOption(ppd, "KD03Duplex", "DuplexTumble")) /* Kodak */
+ conflict = 1;
+ }
+ }
+ else if (strcmp(options->name, "resolution") == 0)
+ {
+ if (ppdMarkOption(ppd, "Resolution", options->value))
+ conflict = 1;
+ if (ppdMarkOption(ppd, "SetResolution", options->value))
+ /* Calcomp, Linotype, QMS, Summagraphics, Tektronix, Varityper */
+ conflict = 1;
+ if (ppdMarkOption(ppd, "JCLResolution", options->value)) /* HP */
+ conflict = 1;
+ if (ppdMarkOption(ppd, "CNRes_PGP", options->value)) /* Canon */
+ conflict = 1;
+ }
+ else if (ppdMarkOption(ppd, options->name, options->value))
+ conflict = 1;
+
+ return (conflict);
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/cups/page.c b/cups/page.c
new file mode 100644
index 000000000..89c88926d
--- /dev/null
+++ b/cups/page.c
@@ -0,0 +1,189 @@
+/*
+ * "$Id$"
+ *
+ * Page size functions for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-1999 by Easy Software Products, all rights reserved.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * PostScript is a trademark of Adobe Systems, Inc.
+ *
+ * Contents:
+ *
+ * ppdPageSize() - Get the page size record for the given size.
+ * ppdPageWidth() - Get the page width for the given size.
+ * ppdPageLength() - Get the page length for the given size.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "ppd.h"
+#include "string.h"
+#include <ctype.h>
+
+
+/*
+ * 'ppdPageSize()' - Get the page size record for the given size.
+ */
+
+ppd_size_t * /* O - Size record for page or NULL */
+ppdPageSize(ppd_file_t *ppd, /* I - PPD file record */
+ const char *name) /* I - Size name */
+{
+ int i; /* Looping var */
+ float w, l; /* Width and length of page */
+ char units[255]; /* Page size units... */
+
+
+ if (ppd == NULL)
+ return (NULL);
+
+ if (name != NULL)
+ {
+ if (strncmp(name, "Custom.", 7) == 0 && ppd->variable_sizes)
+ {
+ /*
+ * Find the custom page size...
+ */
+
+ for (i = 0; i < ppd->num_sizes; i ++)
+ if (strcmp("Custom", ppd->sizes[i].name) == 0)
+ break;
+
+ if (i == ppd->num_sizes)
+ return (NULL);
+
+ /*
+ * Variable size; size name can be one of the following:
+ *
+ * Custom.WIDTHxLENGTHin - Size in inches
+ * Custom.WIDTHxLENGTHcm - Size in centimeters
+ * Custom.WIDTHxLENGTHmm - Size in millimeters
+ * Custom.WIDTHxLENGTH[pt] - Size in points
+ */
+
+ units[0] = '\0';
+ if (sscanf(name + 7, "%fx%f%s", &w, &l, units) < 2)
+ return (NULL);
+
+ if (strcasecmp(units, "in") == 0)
+ {
+ ppd->sizes[i].width = w * 72.0;
+ ppd->sizes[i].length = l * 72.0;
+ ppd->sizes[i].left = ppd->custom_margins[0];
+ ppd->sizes[i].bottom = ppd->custom_margins[1];
+ ppd->sizes[i].right = w * 72.0 - ppd->custom_margins[2];
+ ppd->sizes[i].top = l * 72.0 - ppd->custom_margins[3];
+ }
+ else if (strcasecmp(units, "cm") == 0)
+ {
+ ppd->sizes[i].width = w * 2.54 * 72.0;
+ ppd->sizes[i].length = l * 2.54 * 72.0;
+ ppd->sizes[i].left = ppd->custom_margins[0];
+ ppd->sizes[i].bottom = ppd->custom_margins[1];
+ ppd->sizes[i].right = w * 2.54 * 72.0 - ppd->custom_margins[2];
+ ppd->sizes[i].top = l * 2.54 * 72.0 - ppd->custom_margins[3];
+ }
+ else if (strcasecmp(units, "mm") == 0)
+ {
+ ppd->sizes[i].width = w * 25.4 * 72.0;
+ ppd->sizes[i].length = l * 25.4 * 72.0;
+ ppd->sizes[i].left = ppd->custom_margins[0];
+ ppd->sizes[i].bottom = ppd->custom_margins[1];
+ ppd->sizes[i].right = w * 25.4 * 72.0 - ppd->custom_margins[2];
+ ppd->sizes[i].top = l * 25.4 * 72.0 - ppd->custom_margins[3];
+ }
+ else
+ {
+ ppd->sizes[i].width = w;
+ ppd->sizes[i].length = l;
+ ppd->sizes[i].left = ppd->custom_margins[0];
+ ppd->sizes[i].bottom = ppd->custom_margins[1];
+ ppd->sizes[i].right = w - ppd->custom_margins[2];
+ ppd->sizes[i].top = l - ppd->custom_margins[3];
+ }
+
+ return (ppd->sizes + i);
+ }
+ else
+ {
+ /*
+ * Lookup by name...
+ */
+
+ for (i = 0; i < ppd->num_sizes; i ++)
+ if (strcmp(name, ppd->sizes[i].name) == 0)
+ return (ppd->sizes + i);
+ }
+ }
+ else
+ {
+ /*
+ * Find default...
+ */
+
+ for (i = 0; i < ppd->num_sizes; i ++)
+ if (ppd->sizes[i].marked)
+ return (ppd->sizes + i);
+ }
+
+ return (NULL);
+}
+
+
+/*
+ * 'ppdPageWidth()' - Get the page width for the given size.
+ */
+
+float /* O - Width of page in points or 0.0 */
+ppdPageWidth(ppd_file_t *ppd, /* I - PPD file record */
+ const char *name) /* I - Size name */
+{
+ ppd_size_t *size; /* Page size */
+
+
+ if ((size = ppdPageSize(ppd, name)) == NULL)
+ return (0.0);
+ else
+ return (size->width);
+}
+
+
+/*
+ * 'ppdPageLength()' - Get the page length for the given size.
+ */
+
+float /* O - Length of page in points or 0.0 */
+ppdPageLength(ppd_file_t *ppd, /* I - PPD file */
+ const char *name) /* I - Size name */
+{
+ ppd_size_t *size; /* Page size */
+
+
+ if ((size = ppdPageSize(ppd, name)) == NULL)
+ return (0.0);
+ else
+ return (size->length);
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/cups/ppd.c b/cups/ppd.c
new file mode 100644
index 000000000..d4d2d587f
--- /dev/null
+++ b/cups/ppd.c
@@ -0,0 +1,1769 @@
+/*
+ * "$Id$"
+ *
+ * PPD file routines for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-1999 by Easy Software Products, all rights reserved.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * PostScript is a trademark of Adobe Systems, Inc.
+ *
+ * This code and any derivative of it may be used and distributed
+ * freely under the terms of the GNU General Public License when
+ * used with GNU Ghostscript or its derivatives. Use of the code
+ * (or any derivative of it) with software other than GNU
+ * GhostScript (or its derivatives) is governed by the CUPS license
+ * agreement.
+ *
+ * Contents:
+ *
+ * ppdClose() - Free all memory used by the PPD file.
+ * ppd_free_group() - Free a single UI group.
+ * ppd_free_option() - Free a single option.
+ * ppdOpen() - Read a PPD file into memory.
+ * ppdOpenFd() - Read a PPD file into memory.
+ * ppdOpenFile() - Read a PPD file into memory.
+ * ppd_read() - Read a line from a PPD file, skipping comment lines
+ * as necessary.
+ * compare_strings() - Compare two strings.
+ * compare_groups() - Compare two groups.
+ * compare_options() - Compare two options.
+ * compare_choices() - Compare two choices.
+ */
+
+/*
+ * Include necessary headers.
+ */
+
+#include "ppd.h"
+#include <stdlib.h>
+#include <ctype.h>
+#include "string.h"
+#include "language.h"
+#include "debug.h"
+
+
+/*
+ * Definitions...
+ */
+
+#if defined(WIN32) || defined(__EMX__)
+# define READ_BINARY "rb" /* Open a binary file for reading */
+# define WRITE_BINARY "wb" /* Open a binary file for writing */
+#else
+# define READ_BINARY "r" /* Open a binary file for reading */
+# define WRITE_BINARY "w" /* Open a binary file for writing */
+#endif /* WIN32 || __EMX__ */
+
+#define safe_free(p) if (p) free(p) /* Safe free macro */
+
+#define PPD_KEYWORD 1 /* Line contained a keyword */
+#define PPD_OPTION 2 /* Line contained an option name */
+#define PPD_TEXT 4 /* Line contained human-readable text */
+#define PPD_STRING 8 /* Line contained a string or code */
+
+
+/*
+ * Local functions...
+ */
+
+static int compare_strings(char *s, char *t);
+static int compare_groups(ppd_group_t *g0, ppd_group_t *g1);
+static int compare_options(ppd_option_t *o0, ppd_option_t *o1);
+static int compare_choices(ppd_choice_t *c0, ppd_choice_t *c1);
+static int ppd_read(FILE *fp, char *keyword, char *option,
+ char *text, char **string);
+static void ppd_decode(char *string);
+static void ppd_fix(char *string);
+static void ppd_free_group(ppd_group_t *group);
+static void ppd_free_option(ppd_option_t *option);
+static ppd_group_t *ppd_get_group(ppd_file_t *ppd, char *name);
+static ppd_option_t *ppd_get_option(ppd_group_t *group, char *name);
+static ppd_choice_t *ppd_add_choice(ppd_option_t *option, char *name);
+
+
+/*
+ * 'ppdClose()' - Free all memory used by the PPD file.
+ */
+
+void
+ppdClose(ppd_file_t *ppd) /* I - PPD file record */
+{
+ int i; /* Looping var */
+ ppd_emul_t *emul; /* Current emulation */
+ ppd_group_t *group; /* Current group */
+ char **font; /* Current font */
+
+
+ /*
+ * Range check the PPD file record...
+ */
+
+ if (ppd == NULL)
+ return;
+
+ /*
+ * Free all strings at the top level...
+ */
+
+ safe_free(ppd->lang_encoding);
+ safe_free(ppd->lang_version);
+ safe_free(ppd->modelname);
+ safe_free(ppd->ttrasterizer);
+ safe_free(ppd->manufacturer);
+ safe_free(ppd->product);
+ safe_free(ppd->nickname);
+ safe_free(ppd->shortnickname);
+
+ /*
+ * Free any emulations...
+ */
+
+ if (ppd->num_emulations > 0)
+ {
+ for (i = ppd->num_emulations, emul = ppd->emulations; i > 0; i --, emul ++)
+ {
+ safe_free(emul->start);
+ safe_free(emul->stop);
+ }
+
+ safe_free(ppd->emulations);
+ }
+
+ /*
+ * Free any UI groups, subgroups, and options...
+ */
+
+ if (ppd->num_groups > 0)
+ {
+ for (i = ppd->num_groups, group = ppd->groups; i > 0; i --, group ++)
+ ppd_free_group(group);
+
+ safe_free(ppd->groups);
+ }
+
+ /*
+ * Free any page sizes...
+ */
+
+ if (ppd->num_sizes > 0)
+ safe_free(ppd->sizes);
+
+ /*
+ * Free any constraints...
+ */
+
+ if (ppd->num_consts > 0)
+ safe_free(ppd->consts);
+
+ /*
+ * Free any fonts...
+ */
+
+ if (ppd->num_fonts > 0)
+ {
+ for (i = ppd->num_fonts, font = ppd->fonts; i > 0; i --, font ++)
+ safe_free(*font);
+
+ safe_free(ppd->fonts);
+ }
+
+ /*
+ * Free any profiles...
+ */
+
+ if (ppd->num_profiles > 0)
+ safe_free(ppd->profiles);
+
+ /*
+ * Free the whole record...
+ */
+
+ safe_free(ppd);
+}
+
+
+/*
+ * 'ppd_free_group()' - Free a single UI group.
+ */
+
+static void
+ppd_free_group(ppd_group_t *group) /* I - Group to free */
+{
+ int i; /* Looping var */
+ ppd_option_t *option; /* Current option */
+ ppd_group_t *subgroup; /* Current sub-group */
+
+
+ if (group->num_options > 0)
+ {
+ for (i = group->num_options, option = group->options;
+ i > 0;
+ i --, option ++)
+ ppd_free_option(option);
+
+ safe_free(group->options);
+ }
+
+ if (group->num_subgroups > 0)
+ {
+ for (i = group->num_subgroups, subgroup = group->subgroups;
+ i > 0;
+ i --, subgroup ++)
+ ppd_free_group(subgroup);
+
+ safe_free(group->subgroups);
+ }
+}
+
+
+/*
+ * 'ppd_free_option()' - Free a single option.
+ */
+
+static void
+ppd_free_option(ppd_option_t *option) /* I - Option to free */
+{
+ int i; /* Looping var */
+ ppd_choice_t *choice; /* Current choice */
+
+
+ if (option->num_choices > 0)
+ {
+ for (i = option->num_choices, choice = option->choices;
+ i > 0;
+ i --, choice ++)
+ safe_free(choice->code);
+
+ safe_free(option->choices);
+ }
+}
+
+
+/*
+ * 'ppd_get_group()' - Find or create the named group as needed.
+ */
+
+static ppd_group_t * /* O - Named group */
+ppd_get_group(ppd_file_t *ppd, /* I - PPD file */
+ char *name) /* I - Name of group */
+{
+ int i; /* Looping var */
+ ppd_group_t *group; /* Group */
+
+
+ for (i = ppd->num_groups, group = ppd->groups; i > 0; i --, group ++)
+ if (strcmp(group->text, name) == 0)
+ break;
+
+ if (i == 0)
+ {
+ if (ppd->num_groups == 0)
+ group = malloc(sizeof(ppd_group_t));
+ else
+ group = realloc(ppd->groups,
+ (ppd->num_groups + 1) * sizeof(ppd_group_t));
+
+ if (group == NULL)
+ return (NULL);
+
+ ppd->groups = group;
+ group += ppd->num_groups;
+ ppd->num_groups ++;
+
+ memset(group, 0, sizeof(ppd_group_t));
+ strncpy(group->text, name, sizeof(group->text) - 1);
+ }
+
+ return (group);
+}
+
+
+/*
+ * 'ppd_get_option()' - Find or create the named option as needed.
+ */
+
+static ppd_option_t * /* O - Named option */
+ppd_get_option(ppd_group_t *group, /* I - Group */
+ char *name) /* I - Name of option */
+{
+ int i; /* Looping var */
+ ppd_option_t *option; /* Option */
+
+
+ for (i = group->num_options, option = group->options; i > 0; i --, option ++)
+ if (strcmp(option->keyword, name) == 0)
+ break;
+
+ if (i == 0)
+ {
+ if (group->num_options == 0)
+ option = malloc(sizeof(ppd_option_t));
+ else
+ option = realloc(group->options,
+ (group->num_options + 1) * sizeof(ppd_option_t));
+
+ if (option == NULL)
+ return (NULL);
+
+ group->options = option;
+ option += group->num_options;
+ group->num_options ++;
+
+ memset(option, 0, sizeof(ppd_option_t));
+ strncpy(option->keyword, name, sizeof(option->keyword) - 1);
+ }
+
+ return (option);
+}
+
+
+/*
+ * 'ppd_add_choice()' - Add a choice to an option.
+ */
+
+static ppd_choice_t * /* O - Named choice */
+ppd_add_choice(ppd_option_t *option, /* I - Option */
+ char *name) /* I - Name of choice */
+{
+ ppd_choice_t *choice; /* Choice */
+
+
+ if (option->num_choices == 0)
+ choice = malloc(sizeof(ppd_choice_t));
+ else
+ choice = realloc(option->choices,
+ sizeof(ppd_choice_t) * (option->num_choices + 1));
+
+ if (choice == NULL)
+ return (NULL);
+
+ option->choices = choice;
+ choice += option->num_choices;
+ option->num_choices ++;
+
+ memset(choice, 0, sizeof(ppd_choice_t));
+ strncpy(choice->choice, name, sizeof(choice->choice) - 1);
+
+ return (choice);
+}
+
+
+/*
+ * 'ppd_add_size()' - Add a page size.
+ */
+
+static ppd_size_t * /* O - Named size */
+ppd_add_size(ppd_file_t *ppd, /* I - PPD file */
+ char *name) /* I - Name of size */
+{
+ ppd_size_t *size; /* Size */
+
+
+ if (ppd->num_sizes == 0)
+ size = malloc(sizeof(ppd_size_t));
+ else
+ size = realloc(ppd->sizes, sizeof(ppd_size_t) * (ppd->num_sizes + 1));
+
+ if (size == NULL)
+ return (NULL);
+
+ ppd->sizes = size;
+ size += ppd->num_sizes;
+ ppd->num_sizes ++;
+
+ memset(size, 0, sizeof(ppd_size_t));
+ strncpy(size->name, name, sizeof(size->name) - 1);
+
+ return (size);
+}
+
+
+/*
+ * 'ppdOpen()' - Read a PPD file into memory.
+ */
+
+ppd_file_t * /* O - PPD file record */
+ppdOpen(FILE *fp) /* I - File to read from */
+{
+ int i, j, k, m; /* Looping vars */
+ int count; /* Temporary count */
+ ppd_file_t *ppd; /* PPD file record */
+ ppd_group_t *group, /* Current group */
+ *subgroup; /* Current sub-group */
+ ppd_option_t *option; /* Current option */
+ ppd_choice_t *choice; /* Current choice */
+ ppd_const_t *constraint; /* Current constraint */
+ ppd_size_t *size; /* Current page size */
+ int mask; /* Line data mask */
+ char keyword[41], /* Keyword from file */
+ name[41], /* Option from file */
+ text[81], /* Human-readable text from file */
+ *string, /* Code/text from file */
+ *sptr, /* Pointer into string */
+ *nameptr; /* Pointer into name */
+ float order; /* Order dependency number */
+ ppd_section_t section; /* Order dependency section */
+ ppd_profile_t *profile; /* Pointer to color profile */
+ char **filter; /* Pointer to filter */
+ cups_lang_t *language; /* Default language */
+
+
+ /*
+ * Get the default language for the user...
+ */
+
+ language = cupsLangDefault();
+
+ /*
+ * Range check input...
+ */
+
+ if (fp == NULL)
+ return (NULL);
+
+ /*
+ * Grab the first line and make sure it reads '*PPD-Adobe: "major.minor"'...
+ */
+
+ mask = ppd_read(fp, keyword, name, text, &string);
+
+ if (mask == 0 ||
+ strcmp(keyword, "PPD-Adobe") != 0 ||
+ string == NULL || string[0] != '4')
+ {
+ /*
+ * Either this is not a PPD file, or it is not a 4.x PPD file.
+ */
+
+ safe_free(string);
+
+ return (NULL);
+ }
+
+ safe_free(string);
+
+ /*
+ * Allocate memory for the PPD file record...
+ */
+
+ if ((ppd = calloc(sizeof(ppd_file_t), 1)) == NULL)
+ return (NULL);
+
+ ppd->language_level = 1;
+ ppd->color_device = 0;
+ ppd->colorspace = PPD_CS_GRAY;
+ ppd->landscape = 90;
+
+ /*
+ * Read lines from the PPD file and add them to the file record...
+ */
+
+ group = NULL;
+ subgroup = NULL;
+ option = NULL;
+ choice = NULL;
+
+ while ((mask = ppd_read(fp, keyword, name, text, &string)) != 0)
+ {
+#ifdef DEBUG
+ printf("mask = %x, keyword = \"%s\"", mask, keyword);
+
+ if (name[0] != '\0')
+ printf(", name = \"%s\"", name);
+
+ if (text[0] != '\0')
+ printf(", text = \"%s\"", text);
+
+ if (string != NULL)
+ {
+ if (strlen(string) > 40)
+ printf(", string = %08x", string);
+ else
+ printf(", string = \"%s\"", string);
+ }
+
+ puts("");
+#endif /* DEBUG */
+
+ if (strcmp(keyword, "LanguageLevel") == 0)
+ ppd->language_level = atoi(string);
+ else if (strcmp(keyword, "LanguageEncoding") == 0)
+ {
+ ppd->lang_encoding = string;
+ string = NULL; /* Don't free this string below */
+ }
+ else if (strcmp(keyword, "LanguageVersion") == 0)
+ {
+ ppd->lang_version = string;
+ string = NULL; /* Don't free this string below */
+ }
+ else if (strcmp(keyword, "Manufacturer") == 0)
+ {
+ ppd->manufacturer = string;
+ string = NULL; /* Don't free this string below */
+ }
+ else if (strcmp(keyword, "ModelName") == 0)
+ {
+ ppd->modelname = string;
+ string = NULL; /* Don't free this string below */
+ }
+ else if (strcmp(keyword, "NickName") == 0)
+ {
+ ppd->nickname = string;
+ string = NULL; /* Don't free this string below */
+ }
+ else if (strcmp(keyword, "Product") == 0)
+ {
+ ppd->product = string;
+ string = NULL; /* Don't free this string below */
+ }
+ else if (strcmp(keyword, "ShortNickName") == 0)
+ {
+ ppd->shortnickname = string;
+ string = NULL; /* Don't free this string below */
+ }
+ else if (strcmp(keyword, "TTRasterizer") == 0)
+ {
+ ppd->ttrasterizer = string;
+ string = NULL; /* Don't free this string below */
+ }
+ else if (strcmp(keyword, "JCLBegin") == 0)
+ {
+ ppd_decode(string); /* Decode quoted string */
+ ppd->jcl_begin = string;
+ string = NULL; /* Don't free this string below */
+ }
+ else if (strcmp(keyword, "JCLEnd") == 0)
+ {
+ ppd_decode(string); /* Decode quoted string */
+ ppd->jcl_end = string;
+ string = NULL; /* Don't free this string below */
+ }
+ else if (strcmp(keyword, "JCLToPSInterpreter") == 0)
+ {
+ ppd_decode(string); /* Decode quoted string */
+ ppd->jcl_ps = string;
+ string = NULL; /* Don't free this string below */
+ }
+ else if (strcmp(keyword, "AccurateScreensSupport") == 0)
+ ppd->accurate_screens = strcmp(string, "True") == 0;
+ else if (strcmp(keyword, "ColorDevice") == 0)
+ ppd->color_device = strcmp(string, "True") == 0;
+ else if (strcmp(keyword, "ContoneOnly") == 0)
+ ppd->contone_only = strcmp(string, "True") == 0;
+ else if (strcmp(keyword, "DefaultColorSpace") == 0)
+ {
+ if (strcmp(string, "CMY") == 0)
+ ppd->colorspace = PPD_CS_CMY;
+ else if (strcmp(string, "CMYK") == 0)
+ ppd->colorspace = PPD_CS_CMYK;
+ else if (strcmp(string, "RGB") == 0)
+ ppd->colorspace = PPD_CS_RGB;
+ else if (strcmp(string, "RGBK") == 0)
+ ppd->colorspace = PPD_CS_RGBK;
+ else if (strcmp(string, "N") == 0)
+ ppd->colorspace = PPD_CS_N;
+ else
+ ppd->colorspace = PPD_CS_GRAY;
+ }
+ else if (strcmp(keyword, "cupsManualCopies") == 0)
+ ppd->manual_copies = strcmp(string, "True") == 0;
+ else if (strcmp(keyword, "cupsModelNumber") == 0)
+ ppd->model_number = atoi(string);
+ else if (strcmp(keyword, "cupsColorProfile") == 0)
+ {
+ if (ppd->num_profiles == 0)
+ profile = malloc(sizeof(ppd_profile_t));
+ else
+ profile = realloc(ppd->profiles, sizeof(ppd_profile_t) *
+ (ppd->num_profiles + 1));
+
+ ppd->profiles = profile;
+ profile += ppd->num_profiles;
+ ppd->num_profiles ++;
+
+ memset(profile, 0, sizeof(ppd_profile_t));
+ strncpy(profile->resolution, name, sizeof(profile->resolution) - 1);
+ strncpy(profile->media_type, text, sizeof(profile->media_type) - 1);
+ sscanf(string, "%f%f%f%f%f%f%f%f%f%f%f", &(profile->density),
+ &(profile->gamma),
+ profile->matrix[0] + 0, profile->matrix[0] + 1,
+ profile->matrix[0] + 2, profile->matrix[1] + 0,
+ profile->matrix[1] + 1, profile->matrix[1] + 2,
+ profile->matrix[2] + 0, profile->matrix[2] + 1,
+ profile->matrix[2] + 2);
+ }
+ else if (strcmp(keyword, "cupsFilter") == 0)
+ {
+ if (ppd->num_filters == 0)
+ filter = malloc(sizeof(char *));
+ else
+ filter = realloc(ppd->filters, sizeof(char *) * (ppd->num_filters + 1));
+
+ ppd->filters = filter;
+ filter += ppd->num_filters;
+ ppd->num_filters ++;
+
+ /*
+ * Copy filter string and prevent it from being freed below...
+ */
+
+ *filter = string;
+ string = NULL;
+ }
+ else if (strcmp(keyword, "VariablePaperSize") == 0 &&
+ strcmp(string, "True") == 0)
+ {
+ ppd->variable_sizes = 1;
+
+ /*
+ * Add a "Custom" page size entry...
+ */
+
+ ppd_add_size(ppd, "Custom");
+
+ /*
+ * Add a "Custom" page size option...
+ */
+
+ if ((group = ppd_get_group(ppd,
+ cupsLangString(language,
+ CUPS_MSG_GENERAL))) == NULL)
+ {
+ ppdClose(ppd);
+ safe_free(string);
+ return (NULL);
+ }
+
+ if ((option = ppd_get_option(group, "PageSize")) == NULL)
+ {
+ ppdClose(ppd);
+ safe_free(string);
+ return (NULL);
+ }
+
+ if ((choice = ppd_add_choice(option, "Custom")) == NULL)
+ {
+ ppdClose(ppd);
+ safe_free(string);
+ return (NULL);
+ }
+
+ strncpy(choice->text, cupsLangString(language, CUPS_MSG_VARIABLE),
+ sizeof(choice->text) - 1);
+ group = NULL;
+ option = NULL;
+ }
+ else if (strcmp(keyword, "MaxMediaWidth") == 0)
+ ppd->custom_max[0] = atof(string);
+ else if (strcmp(keyword, "MaxMediaHeight") == 0)
+ ppd->custom_max[1] = atof(string);
+ else if (strcmp(keyword, "ParamCustomPageSize") == 0)
+ {
+ if (strcmp(name, "Width") == 0)
+ sscanf(string, "%*s%*s%f%f", ppd->custom_min + 0,
+ ppd->custom_max + 0);
+ else if (strcmp(name, "Height") == 0)
+ sscanf(string, "%*s%*s%f%f", ppd->custom_min + 1,
+ ppd->custom_max + 1);
+ }
+ else if (strcmp(keyword, "HWMargins") == 0)
+ sscanf(string, "%f%f%f%f", ppd->custom_margins + 0,
+ ppd->custom_margins + 1, ppd->custom_margins + 2,
+ ppd->custom_margins + 3);
+ else if (strcmp(keyword, "CustomPageSize") == 0 &&
+ strcmp(name, "True") == 0 &&
+ ppd->variable_sizes)
+ {
+ if ((option = ppdFindOption(ppd, "PageSize")) == NULL)
+ {
+ ppdClose(ppd);
+ safe_free(string);
+ return (NULL);
+ }
+
+ if ((choice = ppdFindChoice(option, "Custom")) == NULL)
+ {
+ ppdClose(ppd);
+ safe_free(string);
+ return (NULL);
+ }
+
+ choice->code = string;
+ string = NULL;
+ option = NULL;
+ }
+ else if (strcmp(keyword, "LandscapeOrientation") == 0)
+ {
+ if (strcmp(string, "Minus90") == 0)
+ ppd->landscape = -90;
+ else
+ ppd->landscape = 90;
+ }
+ else if (strcmp(keyword, "Emulators") == 0)
+ {
+ for (count = 1, sptr = string; sptr != NULL;)
+ if ((sptr = strchr(sptr, ' ')) != NULL)
+ {
+ count ++;
+ while (*sptr == ' ')
+ sptr ++;
+ }
+
+ ppd->num_emulations = count;
+ ppd->emulations = calloc(sizeof(ppd_emul_t), count);
+
+ for (i = 0, sptr = string; i < count; i ++)
+ {
+ for (nameptr = ppd->emulations[i].name; *sptr != '\0' && *sptr != ' ';)
+ *nameptr ++ = *sptr ++;
+
+ *nameptr = '\0';
+
+ while (*sptr == ' ')
+ sptr ++;
+ }
+ }
+ else if (strncmp(keyword, "StartEmulator_", 14) == 0)
+ {
+ ppd_decode(string);
+
+ for (i = 0; i < ppd->num_emulations; i ++)
+ if (strcmp(keyword + 14, ppd->emulations[i].name) == 0)
+ {
+ ppd->emulations[i].start = string;
+ string = NULL;
+ }
+ }
+ else if (strncmp(keyword, "StopEmulator_", 13) == 0)
+ {
+ ppd_decode(string);
+
+ for (i = 0; i < ppd->num_emulations; i ++)
+ if (strcmp(keyword + 13, ppd->emulations[i].name) == 0)
+ {
+ ppd->emulations[i].stop = string;
+ string = NULL;
+ }
+ }
+ else if (strcmp(keyword, "JobPatchFile") == 0)
+ {
+ if (ppd->patches == NULL)
+ {
+ ppd->patches = string;
+ string = NULL;
+ }
+ else
+ {
+ ppd->patches = realloc(ppd->patches, strlen(ppd->patches) +
+ strlen(string) + 1);
+
+ strcpy(ppd->patches + strlen(ppd->patches), string);
+ }
+ }
+ else if (strcmp(keyword, "OpenUI") == 0)
+ {
+ /*
+ * Add an option record to the current sub-group, group, or file...
+ */
+
+ if (name[0] == '*')
+ strcpy(name, name + 1);
+
+ if (string == NULL)
+ {
+ ppdClose(ppd);
+ return (NULL);
+ }
+
+ if (subgroup != NULL)
+ option = ppd_get_option(subgroup, name);
+ else if (group == NULL)
+ {
+ if (strcmp(name, "Collate") != 0 &&
+ strcmp(name, "Duplex") != 0 &&
+ strcmp(name, "InputSlot") != 0 &&
+ strcmp(name, "ManualFeed") != 0 &&
+ strcmp(name, "MediaType") != 0 &&
+ strcmp(name, "MediaColor") != 0 &&
+ strcmp(name, "MediaWeight") != 0 &&
+ strcmp(name, "OutputBin") != 0 &&
+ strcmp(name, "OutputMode") != 0 &&
+ strcmp(name, "OutputOrder") != 0 &&
+ strcmp(name, "PageSize") != 0 &&
+ strcmp(name, "PageRegion") != 0)
+ group = ppd_get_group(ppd, cupsLangString(language, CUPS_MSG_EXTRA));
+ else
+ group = ppd_get_group(ppd, cupsLangString(language, CUPS_MSG_GENERAL));
+
+ if (group == NULL)
+ {
+ ppdClose(ppd);
+ safe_free(string);
+ return (NULL);
+ }
+
+ option = ppd_get_option(group, name);
+ group = NULL;
+ }
+ else
+ option = ppd_get_option(group, name);
+
+ if (option == NULL)
+ {
+ ppdClose(ppd);
+ safe_free(string);
+ return (NULL);
+ }
+
+ /*
+ * Now fill in the initial information for the option...
+ */
+
+ if (strcmp(string, "PickMany") == 0)
+ option->ui = PPD_UI_PICKMANY;
+ else if (strcmp(string, "Boolean") == 0)
+ option->ui = PPD_UI_BOOLEAN;
+ else
+ option->ui = PPD_UI_PICKONE;
+
+ if (text[0])
+ {
+ strncpy(option->text, text, sizeof(option->text) - 1);
+ ppd_fix(option->text);
+ }
+ else
+ {
+ if (strcmp(name, "PageSize") == 0)
+ strncpy(option->text, cupsLangString(language, CUPS_MSG_MEDIA_SIZE),
+ sizeof(option->text) - 1);
+ else if (strcmp(name, "MediaType") == 0)
+ strncpy(option->text, cupsLangString(language, CUPS_MSG_MEDIA_TYPE),
+ sizeof(option->text) - 1);
+ else if (strcmp(name, "InputSlot") == 0)
+ strncpy(option->text, cupsLangString(language, CUPS_MSG_MEDIA_SOURCE),
+ sizeof(option->text) - 1);
+ else if (strcmp(name, "ColorModel") == 0)
+ strncpy(option->text, cupsLangString(language, CUPS_MSG_OUTPUT_MODE),
+ sizeof(option->text) - 1);
+ else if (strcmp(name, "Resolution") == 0)
+ strncpy(option->text, cupsLangString(language, CUPS_MSG_RESOLUTION),
+ sizeof(option->text) - 1);
+ else
+ strncpy(option->text, name, sizeof(option->text) - 1);
+ }
+
+ option->section = PPD_ORDER_ANY;
+ }
+ else if (strcmp(keyword, "JCLOpenUI") == 0)
+ {
+ /*
+ * Find the JCL group, and add if needed...
+ */
+
+ group = ppd_get_group(ppd, "JCL");
+
+ if (group == NULL)
+ {
+ ppdClose(ppd);
+ safe_free(string);
+ return (NULL);
+ }
+
+ /*
+ * Add an option record to the current JCLs...
+ */
+
+ if (name[0] == '*')
+ strcpy(name, name + 1);
+
+ option = ppd_get_option(group, name);
+
+ if (option == NULL)
+ {
+ ppdClose(ppd);
+ safe_free(string);
+ return (NULL);
+ }
+
+ /*
+ * Now fill in the initial information for the option...
+ */
+
+ if (strcmp(string, "PickMany") == 0)
+ option->ui = PPD_UI_PICKMANY;
+ else if (strcmp(string, "Boolean") == 0)
+ option->ui = PPD_UI_BOOLEAN;
+ else
+ option->ui = PPD_UI_PICKONE;
+
+ strncpy(option->text, text, sizeof(option->text) - 1);
+
+ option->section = PPD_ORDER_JCL;
+ group = NULL;
+ }
+ else if (strcmp(keyword, "CloseUI") == 0 ||
+ strcmp(keyword, "JCLCloseUI") == 0)
+ option = NULL;
+ else if (strcmp(keyword, "OpenGroup") == 0)
+ {
+ /*
+ * Open a new group...
+ */
+
+ if (group != NULL)
+ {
+ ppdClose(ppd);
+ safe_free(string);
+ return (NULL);
+ }
+
+ if (strchr(string, '/') != NULL) /* Just show human readable text */
+ strcpy(string, strchr(string, '/') + 1);
+
+ ppd_decode(string);
+ ppd_fix(string);
+ group = ppd_get_group(ppd, string);
+ }
+ else if (strcmp(keyword, "CloseGroup") == 0)
+ group = NULL;
+ else if (strcmp(keyword, "OpenSubGroup") == 0)
+ {
+ /*
+ * Open a new sub-group...
+ */
+
+ if (group == NULL || subgroup != NULL)
+ {
+ ppdClose(ppd);
+ safe_free(string);
+ return (NULL);
+ }
+
+ if (group->num_subgroups == 0)
+ subgroup = malloc(sizeof(ppd_group_t));
+ else
+ subgroup = realloc(group->subgroups,
+ (group->num_subgroups + 1) * sizeof(ppd_group_t));
+
+ if (subgroup == NULL)
+ {
+ ppdClose(ppd);
+ safe_free(string);
+ return (NULL);
+ }
+
+ group->subgroups = subgroup;
+ subgroup += group->num_subgroups;
+ group->num_subgroups ++;
+
+ memset(subgroup, 0, sizeof(ppd_group_t));
+ ppd_decode(string);
+ ppd_fix(string);
+ strncpy(subgroup->text, string, sizeof(subgroup->text) - 1);
+ }
+ else if (strcmp(keyword, "CloseSubGroup") == 0)
+ subgroup = NULL;
+ else if (strcmp(keyword, "OrderDependency") == 0 ||
+ strcmp(keyword, "NonUIOrderDependency") == 0)
+ {
+ if (sscanf(string, "%f%s%s", &order, name, keyword) != 3)
+ {
+ ppdClose(ppd);
+ safe_free(string);
+ return (NULL);
+ }
+
+ if (keyword[0] == '*')
+ strcpy(keyword, keyword + 1);
+
+ if (strcmp(name, "ExitServer") == 0)
+ section = PPD_ORDER_EXIT;
+ else if (strcmp(name, "Prolog") == 0)
+ section = PPD_ORDER_PROLOG;
+ else if (strcmp(name, "DocumentSetup") == 0)
+ section = PPD_ORDER_DOCUMENT;
+ else if (strcmp(name, "PageSetup") == 0)
+ section = PPD_ORDER_PAGE;
+ else if (strcmp(name, "JCLSetup") == 0)
+ section = PPD_ORDER_JCL;
+ else
+ section = PPD_ORDER_ANY;
+
+ if (option == NULL)
+ {
+ /*
+ * Only valid for Non-UI options...
+ */
+
+ for (i = ppd->num_groups, group = ppd->groups; i > 0; i --, group ++)
+ if (group->text[0] == '\0')
+ break;
+
+ if (i > 0)
+ for (i = 0; i < group->num_options; i ++)
+ if (strcmp(keyword, group->options[i].keyword) == 0)
+ {
+ group->options[i].section = section;
+ group->options[i].order = order;
+ break;
+ }
+
+ group = NULL;
+ }
+ else
+ {
+ option->section = section;
+ option->order = order;
+ }
+ }
+ else if (strncmp(keyword, "Default", 7) == 0)
+ {
+ if (string == NULL)
+ continue;
+
+ if (strchr(string, '/') != NULL)
+ *strchr(string, '/') = '\0';
+
+ if (option == NULL)
+ {
+ /*
+ * Only valid for Non-UI options...
+ */
+
+ for (i = ppd->num_groups, group = ppd->groups; i > 0; i --, group ++)
+ if (group->text[0] == '\0')
+ break;
+
+ if (i > 0)
+ for (i = 0; i < group->num_options; i ++)
+ if (strcmp(keyword, group->options[i].keyword) == 0)
+ {
+ strncpy(group->options[i].defchoice, string,
+ sizeof(group->options[i].defchoice) - 1);
+ break;
+ }
+
+ group = NULL;
+ }
+ else
+ strncpy(option->defchoice, string, sizeof(option->defchoice) - 1);
+ }
+ else if (strcmp(keyword, "UIConstraints") == 0 ||
+ strcmp(keyword, "NonUIConstraints") == 0)
+ {
+ if (ppd->num_consts == 0)
+ constraint = calloc(sizeof(ppd_const_t), 1);
+ else
+ constraint = realloc(ppd->consts,
+ (ppd->num_consts + 1) * sizeof(ppd_const_t));
+
+ if (constraint == NULL)
+ {
+ ppdClose(ppd);
+ safe_free(string);
+ return (NULL);
+ }
+
+ ppd->consts = constraint;
+ constraint += ppd->num_consts;
+ ppd->num_consts ++;
+
+ switch (sscanf(string, "%40s%40s%40s%40s", constraint->option1,
+ constraint->choice1, constraint->option2,
+ constraint->choice2))
+ {
+ case 0 : /* Error */
+ case 1 : /* Error */
+ ppdClose(ppd);
+ safe_free(string);
+ break;
+
+ case 2 : /* Two options... */
+ if (constraint->option1[0] == '*')
+ strcpy(constraint->option1, constraint->option1 + 1);
+
+ if (constraint->choice1[0] == '*')
+ strcpy(constraint->option2, constraint->choice1 + 1);
+ else
+ strcpy(constraint->option2, constraint->choice1);
+
+ constraint->choice1[0] = '\0';
+ constraint->choice2[0] = '\0';
+ break;
+
+ case 3 : /* Two options, one choice... */
+ if (constraint->option1[0] == '*')
+ strcpy(constraint->option1, constraint->option1 + 1);
+
+ if (constraint->choice1[0] == '*')
+ {
+ strcpy(constraint->choice2, constraint->option2);
+ strcpy(constraint->option2, constraint->choice1 + 1);
+ constraint->choice1[0] = '\0';
+ }
+ else
+ {
+ if (constraint->option2[0] == '*')
+ strcpy(constraint->option2, constraint->option2 + 1);
+
+ constraint->choice2[0] = '\0';
+ }
+ break;
+
+ case 4 : /* Two options, two choices... */
+ if (constraint->option1[0] == '*')
+ strcpy(constraint->option1, constraint->option1 + 1);
+
+ if (constraint->option2[0] == '*')
+ strcpy(constraint->option2, constraint->option2 + 1);
+ break;
+ }
+ }
+ else if (strcmp(keyword, "PaperDimension") == 0)
+ {
+ if ((size = ppdPageSize(ppd, name)) != NULL)
+ sscanf(string, "%f%f", &(size->width), &(size->length));
+ }
+ else if (strcmp(keyword, "ImageableArea") == 0)
+ {
+ if ((size = ppdPageSize(ppd, name)) != NULL)
+ sscanf(string, "%f%f%f%f", &(size->left), &(size->bottom),
+ &(size->right), &(size->top));
+ }
+ else if (option != NULL &&
+ (mask & (PPD_KEYWORD | PPD_OPTION | PPD_STRING)) ==
+ (PPD_KEYWORD | PPD_OPTION | PPD_STRING))
+ {
+ if (strcmp(keyword, "PageSize") == 0)
+ {
+ /*
+ * Add a page size...
+ */
+
+ ppd_add_size(ppd, name);
+ }
+
+ /*
+ * Add the option choice...
+ */
+
+ choice = ppd_add_choice(option, name);
+
+ if (mask & PPD_TEXT)
+ {
+ strncpy(choice->text, text, sizeof(choice->text) - 1);
+ ppd_fix(choice->text);
+ }
+ else if (strcmp(name, "True") == 0)
+ strcpy(choice->text, "Yes");
+ else if (strcmp(name, "False") == 0)
+ strcpy(choice->text, "No");
+ else
+ strncpy(choice->text, name, sizeof(choice->text) - 1);
+
+ if (strncmp(keyword, "JCL", 3) == 0)
+ ppd_decode(string); /* Decode quoted string */
+
+ choice->code = string;
+ string = NULL; /* Don't free this string below */
+ }
+
+ safe_free(string);
+ }
+
+#ifdef DEBUG
+ if (!feof(fp))
+ printf("Premature EOF at %d...\n", ftell(fp));
+#endif /* DEBUG */
+
+ /*
+ * Set the option back-pointer for each choice...
+ */
+
+ qsort(ppd->groups, ppd->num_groups, sizeof(ppd_group_t),
+ (int (*)(const void *, const void *))compare_groups);
+
+ for (i = ppd->num_groups, group = ppd->groups;
+ i > 0;
+ i --, group ++)
+ {
+ qsort(group->options, group->num_options, sizeof(ppd_option_t),
+ (int (*)(const void *, const void *))compare_options);
+
+ for (j = group->num_options, option = group->options;
+ j > 0;
+ j --, option ++)
+ {
+ qsort(option->choices, option->num_choices, sizeof(ppd_choice_t),
+ (int (*)(const void *, const void *))compare_choices);
+
+ for (k = 0; k < option->num_choices; k ++)
+ option->choices[k].option = (void *)option;
+ }
+
+ qsort(group->subgroups, group->num_subgroups, sizeof(ppd_group_t),
+ (int (*)(const void *, const void *))compare_groups);
+
+ for (j = group->num_subgroups, subgroup = group->subgroups;
+ j > 0;
+ j --, subgroup ++)
+ {
+ qsort(subgroup->options, subgroup->num_options, sizeof(ppd_option_t),
+ (int (*)(const void *, const void *))compare_options);
+
+ for (k = group->num_options, option = group->options;
+ k > 0;
+ k --, option ++)
+ {
+ qsort(option->choices, option->num_choices, sizeof(ppd_choice_t),
+ (int (*)(const void *, const void *))compare_choices);
+
+ for (m = 0; m < option->num_choices; m ++)
+ option->choices[m].option = (void *)option;
+ }
+ }
+ }
+
+ return (ppd);
+}
+
+
+/*
+ * 'ppdOpenFd()' - Read a PPD file into memory.
+ */
+
+ppd_file_t * /* O - PPD file record */
+ppdOpenFd(int fd) /* I - File to read from */
+{
+ FILE *fp; /* File pointer */
+ ppd_file_t *ppd; /* PPD file record */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (fd < 0)
+ return (NULL);
+
+ /*
+ * Try to open the file and parse it...
+ */
+
+ if ((fp = fdopen(fd, "r")) != NULL)
+ {
+ setbuf(fp, NULL);
+
+ ppd = ppdOpen(fp);
+
+ safe_free(fp);
+ }
+ else
+ ppd = NULL;
+
+ return (ppd);
+}
+
+
+/*
+ * 'ppdOpenFile()' - Read a PPD file into memory.
+ */
+
+ppd_file_t * /* O - PPD file record */
+ppdOpenFile(const char *filename) /* I - File to read from */
+{
+ FILE *fp; /* File pointer */
+ ppd_file_t *ppd; /* PPD file record */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (filename == NULL)
+ return (NULL);
+
+ /*
+ * Try to open the file and parse it...
+ */
+
+ if ((fp = fopen(filename, "r")) != NULL)
+ {
+ ppd = ppdOpen(fp);
+
+ fclose(fp);
+ }
+ else
+ ppd = NULL;
+
+ return (ppd);
+}
+
+
+/*
+ * 'compare_strings()' - Compare two strings.
+ */
+
+int /* O - Result of comparison */
+compare_strings(char *s, /* I - First string */
+ char *t) /* I - Second string */
+{
+ int diff, /* Difference between digits */
+ digits; /* Number of digits */
+
+
+ /*
+ * Loop through both strings, returning only when a difference is
+ * seen. Also, compare whole numbers rather than just characters, too!
+ */
+
+ while (*s && *t)
+ {
+ if (isdigit(*s) && isdigit(*t))
+ {
+ /*
+ * Got a number; start by skipping leading 0's...
+ */
+
+ while (*s == '0')
+ s ++;
+ while (*t == '0')
+ t ++;
+
+ /*
+ * Skip equal digits...
+ */
+
+ while (isdigit(*s) && *s == *t)
+ {
+ s ++;
+ t ++;
+ }
+
+ /*
+ * Bounce out if *s and *t aren't both digits...
+ */
+
+ if (isdigit(*s) && !isdigit(*t))
+ return (1);
+ else if (!isdigit(*s) && isdigit(*t))
+ return (-1);
+ else if (!isdigit(*s) || !isdigit(*t))
+ continue;
+
+ if (*s < *t)
+ diff = -1;
+ else
+ diff = 1;
+
+ /*
+ * Figure out how many more digits there are...
+ */
+
+ digits = 0;
+
+ while (isdigit(*s))
+ {
+ digits ++;
+ s ++;
+ }
+
+ while (isdigit(*t))
+ {
+ digits --;
+ t ++;
+ }
+
+ /*
+ * Return if the number or value of the digits is different...
+ */
+
+ if (digits < 0)
+ return (-1);
+ else if (digits > 0)
+ return (1);
+ else
+ return (diff);
+ }
+ else if (tolower(*s) < tolower(*t))
+ return (-1);
+ else if (tolower(*s) > tolower(*t))
+ return (1);
+ else
+ {
+ s ++;
+ t ++;
+ }
+ }
+
+ /*
+ * Return the results of the final comparison...
+ */
+
+ if (*s)
+ return (1);
+ else if (*t)
+ return (-1);
+ else
+ return (0);
+}
+
+
+/*
+ * 'compare_groups()' - Compare two groups.
+ */
+
+static int /* O - Result of comparison */
+compare_groups(ppd_group_t *g0, /* I - First group */
+ ppd_group_t *g1) /* I - Second group */
+{
+ return (compare_strings(g0->text, g1->text));
+}
+
+
+/*
+ * 'compare_options()' - Compare two options.
+ */
+
+static int /* O - Result of comparison */
+compare_options(ppd_option_t *o0,/* I - First option */
+ ppd_option_t *o1)/* I - Second option */
+{
+ return (compare_strings(o0->text, o1->text));
+}
+
+
+/*
+ * 'compare_choices()' - Compare two choices.
+ */
+
+static int /* O - Result of comparison */
+compare_choices(ppd_choice_t *c0,/* I - First choice */
+ ppd_choice_t *c1)/* I - Second choice */
+{
+ return (compare_strings(c0->text, c1->text));
+}
+
+
+/*
+ * 'ppd_read()' - Read a line from a PPD file, skipping comment lines as
+ * necessary.
+ */
+
+static int /* O - Bitmask of fields read */
+ppd_read(FILE *fp, /* I - File to read from */
+ char *keyword, /* O - Keyword from line */
+ char *option, /* O - Option from line */
+ char *text, /* O - Human-readable text from line */
+ char **string) /* O - Code/string data */
+{
+ int ch, /* Character from file */
+ endquote, /* Waiting for an end quote */
+ mask; /* Mask to be returned */
+ char *keyptr, /* Keyword pointer */
+ *optptr, /* Option pointer */
+ *textptr, /* Text pointer */
+ *strptr, /* Pointer into string */
+ *lineptr, /* Current position in line buffer */
+ line[262144]; /* Line buffer (256k) */
+
+
+ /*
+ * Range check everything...
+ */
+
+ if (fp == NULL || keyword == NULL || option == NULL || text == NULL ||
+ string == NULL)
+ return (0);
+
+ /*
+ * Now loop until we have a valid line...
+ */
+
+ do
+ {
+ /*
+ * Read the line...
+ */
+
+ lineptr = line;
+ endquote = 0;
+
+ while ((ch = getc(fp)) != EOF &&
+ (lineptr - line) < (sizeof(line) - 1))
+ {
+ if (ch == '\r' || ch == '\n')
+ {
+ /*
+ * Line feed or carriage return...
+ */
+
+ if (lineptr == line) /* Skip blank lines */
+ continue;
+
+ if (ch == '\r')
+ {
+ /*
+ * Check for a trailing line feed...
+ */
+
+ if ((ch = getc(fp)) == EOF)
+ break;
+ if (ch != 0x0a)
+ ungetc(ch, fp);
+ }
+
+ *lineptr++ = '\n';
+
+ if (!endquote) /* Continue for multi-line text */
+ break;
+ }
+ else
+ {
+ /*
+ * Any other character...
+ */
+
+ *lineptr++ = ch;
+
+ if (ch == '\"')
+ endquote = !endquote;
+ }
+ }
+
+ if (lineptr > line && lineptr[-1] == '\n')
+ lineptr --;
+
+ *lineptr = '\0';
+
+ if (ch == EOF && lineptr == line)
+ return (0);
+
+ /*
+ * Now parse it...
+ */
+
+ mask = 0;
+ lineptr = line + 1;
+
+ keyword[0] = '\0';
+ option[0] = '\0';
+ text[0] = '\0';
+ *string = NULL;
+
+ if (line[0] != '*') /* All lines start with an asterisk */
+ continue;
+
+ if (strncmp(line, "*%", 2) == 0 || /* Comment line */
+ strncmp(line, "*?", 2) == 0 || /* Query line */
+ strcmp(line, "*End") == 0) /* End of multi-line string */
+ continue;
+
+ /*
+ * Get a keyword...
+ */
+
+ keyptr = keyword;
+
+ while (*lineptr != '\0' && *lineptr != ':' && !isspace(*lineptr) &&
+ (keyptr - keyword) < 40)
+ *keyptr++ = *lineptr++;
+
+ *keyptr = '\0';
+ mask |= PPD_KEYWORD;
+
+ if (*lineptr == ' ' || *lineptr == '\t')
+ {
+ /*
+ * Get an option name...
+ */
+
+ while (*lineptr == ' ' || *lineptr == '\t')
+ lineptr ++;
+
+ optptr = option;
+
+ while (*lineptr != '\0' && *lineptr != '\n' && *lineptr != ':' &&
+ *lineptr != '/' && (optptr - option) < 40)
+ *optptr++ = *lineptr++;
+
+ *optptr = '\0';
+ mask |= PPD_OPTION;
+
+ if (*lineptr == '/')
+ {
+ /*
+ * Get human-readable text...
+ */
+
+ lineptr ++;
+
+ textptr = text;
+
+ while (*lineptr != '\0' && *lineptr != '\n' && *lineptr != ':' &&
+ (textptr - text) < 80)
+ *textptr++ = *lineptr++;
+
+ *textptr = '\0';
+ ppd_decode(text);
+
+ mask |= PPD_TEXT;
+ }
+ }
+
+ if (*lineptr == ':')
+ {
+ /*
+ * Get string...
+ */
+
+ *string = malloc(strlen(lineptr) + 1);
+
+ while (*lineptr == ':' || isspace(*lineptr))
+ lineptr ++;
+
+ strptr = *string;
+
+ while (*lineptr != '\0')
+ {
+ if (*lineptr != '\"')
+ *strptr++ = *lineptr++;
+ else
+ lineptr ++;
+ }
+
+ *strptr = '\0';
+
+ mask |= PPD_STRING;
+ }
+ }
+ while (mask == 0);
+
+ return (mask);
+}
+
+
+/*
+ * 'ppd_decode()' - Decode a string value...
+ */
+
+static void
+ppd_decode(char *string) /* I - String to decode */
+{
+ char *inptr, /* Input pointer */
+ *outptr; /* Output pointer */
+
+
+ inptr = string;
+ outptr = string;
+
+ while (*inptr != '\0')
+ if (*inptr == '<' && isxdigit(inptr[1]))
+ {
+ /*
+ * Convert hex to 8-bit values...
+ */
+
+ inptr ++;
+ while (isxdigit(*inptr))
+ {
+ if (isalpha(*inptr))
+ *outptr = (tolower(*inptr) - 'a' + 10) << 4;
+ else
+ *outptr = (*inptr - '0') << 4;
+
+ inptr ++;
+
+ if (isalpha(*inptr))
+ *outptr |= tolower(*inptr) - 'a' + 10;
+ else
+ *outptr |= *inptr - '0';
+
+ inptr ++;
+ outptr ++;
+ }
+
+ while (*inptr != '>' && *inptr != '\0')
+ inptr ++;
+ while (*inptr == '>')
+ inptr ++;
+ }
+ else
+ *outptr++ = *inptr++;
+
+ *outptr = '\0';
+}
+
+
+/*
+ * 'ppd_fix()' - Fix WinANSI characters in the range 0x80 to 0x9f to be
+ * valid ISO-8859-1 characters...
+ */
+
+static void
+ppd_fix(char *string) /* IO - String to fix */
+{
+ unsigned char *p; /* Pointer into string */
+ static unsigned char lut[32] =/* Lookup table for characters */
+ {
+ 0x20,
+ 0x20,
+ 0x20,
+ 0x20,
+ 0x20,
+ 0x20,
+ 0x20,
+ 0x20,
+ 0x20,
+ 0x20,
+ 0x20,
+ 0x20,
+ 0x20,
+ 0x20,
+ 0x20,
+ 0x20,
+ 'l',
+ '`',
+ '\'',
+ '^',
+ '~',
+ 0x20, /* bar */
+ 0x20, /* circumflex */
+ 0x20, /* dot */
+ 0x20, /* double dot */
+ 0x20,
+ 0x20, /* circle */
+ 0x20, /* ??? */
+ 0x20,
+ '\"', /* should be right quotes */
+ 0x20, /* ??? */
+ 0x20 /* accent */
+ };
+
+
+ for (p = (unsigned char *)string; *p; p ++)
+ if (*p >= 0x80 && *p < 0xa0)
+ *p = lut[*p - 0x80];
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/cups/ppd.h b/cups/ppd.h
new file mode 100644
index 000000000..75000fe27
--- /dev/null
+++ b/cups/ppd.h
@@ -0,0 +1,239 @@
+/*
+ * "$Id$"
+ *
+ * PostScript Printer Description definitions for the Common UNIX Printing
+ * System (CUPS).
+ *
+ * Copyright 1997-1999 by Easy Software Products, all rights reserved.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * PostScript is a trademark of Adobe Systems, Inc.
+ *
+ * This code and any derivative of it may be used and distributed
+ * freely under the terms of the GNU General Public License when
+ * used with GNU Ghostscript or its derivatives. Use of the code
+ * (or any derivative of it) with software other than GNU
+ * GhostScript (or its derivatives) is governed by the CUPS license
+ * agreement.
+ */
+
+#ifndef _CUPS_PPD_H_
+# define _CUPS_PPD_H_
+
+/*
+ * Include necessary headers...
+ */
+
+# include <stdio.h>
+
+
+/*
+ * C++ magic...
+ */
+
+# ifdef __cplusplus
+extern "C" {
+# endif /* __cplusplus */
+
+
+/*
+ * PPD version...
+ */
+
+# define PPD_VERSION 4.3 /* Kept in sync with Adobe version number */
+
+
+/*
+ * Types and structures...
+ */
+
+typedef enum /**** UI types ****/
+{
+ PPD_UI_BOOLEAN, /* True or False option */
+ PPD_UI_PICKONE, /* Pick one from a list */
+ PPD_UI_PICKMANY /* Pick zero or more from a list */
+} ppd_ui_t;
+
+typedef enum /**** Order dependency sections ****/
+{
+ PPD_ORDER_ANY, /* Option code can be anywhere in the file */
+ PPD_ORDER_DOCUMENT, /* ... must be in the DocumentSetup section */
+ PPD_ORDER_EXIT, /* ... must be sent prior to the document */
+ PPD_ORDER_JCL, /* ... must be sent as a JCL command */
+ PPD_ORDER_PAGE, /* ... must be in the PageSetup section */
+ PPD_ORDER_PROLOG /* ... must be in the Prolog section */
+} ppd_section_t;
+
+typedef enum /**** Colorspaces ****/
+{
+ PPD_CS_CMYK = -4, /* CMYK colorspace */
+ PPD_CS_CMY, /* CMY colorspace */
+ PPD_CS_GRAY = 1, /* Grayscale colorspace */
+ PPD_CS_RGB = 3, /* RGB colorspace */
+ PPD_CS_RGBK, /* RGBK (K = gray) colorspace */
+ PPD_CS_N /* DeviceN colorspace */
+} ppd_cs_t;
+
+typedef struct /**** Option choices ****/
+{
+ char marked, /* 0 if not selected, 1 otherwise */
+ choice[41], /* Computer-readable option name */
+ text[81], /* Human-readable option name */
+ *code; /* Code to send for this option */
+ void *option; /* Pointer to parent option structure */
+} ppd_choice_t;
+
+typedef struct /**** Options ****/
+{
+ char conflicted, /* 0 if no conflicts exist, 1 otherwise */
+ keyword[41], /* Option keyword name ("PageSize", etc.) */
+ defchoice[41], /* Default option choice */
+ text[81]; /* Human-readable text */
+ ppd_ui_t ui; /* Type of UI option */
+ ppd_section_t section; /* Section for command */
+ float order; /* Order number */
+ int num_choices; /* Number of option choices */
+ ppd_choice_t *choices; /* Option choices */
+} ppd_option_t;
+
+typedef struct ppd_group_str /**** Groups ****/
+{
+ char text[81]; /* Human-readable group name */
+ int num_options; /* Number of options */
+ ppd_option_t *options; /* Options */
+ int num_subgroups; /* Number of sub-groups */
+ struct ppd_group_str *subgroups;
+ /* Sub-groups (max depth = 1) */
+} ppd_group_t;
+
+typedef struct /**** Constraints ****/
+{
+ char option1[41], /* First keyword */
+ choice1[41], /* First option/choice (blank for all) */
+ option2[41], /* Second keyword */
+ choice2[41]; /* Second option/choice (blank for all) */
+} ppd_const_t;
+
+typedef struct /**** Page Sizes ****/
+{
+ int marked; /* Page size selected? */
+ char name[41]; /* Media size option */
+ float width, /* Width of media in points */
+ length, /* Length of media in points */
+ left, /* Left printable margin in points */
+ bottom, /* Bottom printable margin in points */
+ right, /* Right printable margin in points */
+ top; /* Top printable margin in points */
+} ppd_size_t;
+
+typedef struct /**** Emulators ****/
+{
+ char name[41], /* Emulator name */
+ *start, /* Code to switch to this emulation */
+ *stop; /* Code to stop this emulation */
+} ppd_emul_t;
+
+typedef struct /**** sRGB Color Profiles ****/
+{
+ char resolution[41], /* Resolution or "-" */
+ media_type[41]; /* Media type of "-" */
+ float density, /* Ink density to use */
+ gamma, /* Gamma correction to use */
+ matrix[3][3]; /* Transform matrix */
+} ppd_profile_t;
+
+typedef struct /**** Files ****/
+{
+ int language_level, /* Language level of device */
+ color_device, /* 1 = color device, 0 = grayscale */
+ variable_sizes, /* 1 = supports variable sizes, 0 = doesn't */
+ accurate_screens,/* 1 = supports accurate screens, 0 = not */
+ contone_only, /* 1 = continuous tone only, 0 = not */
+ landscape, /* -90 or 90 */
+ model_number, /* Device-specific model number */
+ manual_copies; /* 1 = Copies done manually, 0 = hardware */
+ ppd_cs_t colorspace; /* Default colorspace */
+ char *patches; /* Patch commands to be sent to printer */
+ int num_emulations; /* Number of emulations supported */
+ ppd_emul_t *emulations; /* Emulations and the code to invoke them */
+ char *jcl_begin, /* Start JCL commands */
+ *jcl_ps, /* Enter PostScript interpreter */
+ *jcl_end, /* End JCL commands */
+ *lang_encoding, /* Language encoding */
+ *lang_version, /* Language version (English, Spanish, etc.) */
+ *modelname, /* Model name (general) */
+ *ttrasterizer, /* Truetype rasterizer */
+ *manufacturer, /* Manufacturer name */
+ *product, /* Product name (from PS RIP/interpreter) */
+ *nickname, /* Nickname (specific) */
+ *shortnickname; /* Short version of nickname */
+ int num_groups; /* Number of UI groups */
+ ppd_group_t *groups; /* UI groups */
+ int num_sizes; /* Number of page sizes */
+ ppd_size_t *sizes; /* Page sizes */
+ float custom_min[2], /* Minimum variable page size */
+ custom_max[2], /* Maximum variable page size */
+ custom_margins[4];/* Margins around page */
+ int num_consts; /* Number of UI/Non-UI constraints */
+ ppd_const_t *consts; /* UI/Non-UI constraints */
+ int num_fonts; /* Number of pre-loaded fonts */
+ char **fonts; /* Pre-loaded fonts */
+ int num_profiles; /* Number of sRGB color profiles */
+ ppd_profile_t *profiles; /* sRGB color profiles */
+ int num_filters; /* Number of filters */
+ char **filters; /* Filter strings... */
+} ppd_file_t;
+
+
+/*
+ * Prototypes...
+ */
+
+extern void ppdClose(ppd_file_t *ppd);
+extern int ppdConflicts(ppd_file_t *ppd);
+extern int ppdEmit(ppd_file_t *ppd, FILE *fp,
+ ppd_section_t section);
+extern int ppdEmitFd(ppd_file_t *ppd, int fd,
+ ppd_section_t section);
+extern int ppdIsMarked(ppd_file_t *ppd, const char *keyword,
+ const char *option);
+extern void ppdMarkDefaults(ppd_file_t *ppd);
+extern int ppdMarkOption(ppd_file_t *ppd, const char *keyword,
+ const char *option);
+extern ppd_choice_t *ppdFindChoice(ppd_option_t *o, const char *option);
+extern ppd_choice_t *ppdFindMarkedChoice(ppd_file_t *ppd, const char *keyword);
+extern ppd_option_t *ppdFindOption(ppd_file_t *ppd, const char *keyword);
+extern ppd_file_t *ppdOpen(FILE *fp);
+extern ppd_file_t *ppdOpenFd(int fd);
+extern ppd_file_t *ppdOpenFile(const char *filename);
+extern float ppdPageLength(ppd_file_t *ppd, const char *name);
+extern ppd_size_t *ppdPageSize(ppd_file_t *ppd, const char *name);
+extern float ppdPageWidth(ppd_file_t *ppd, const char *name);
+
+/*
+ * C++ magic...
+ */
+
+# ifdef __cplusplus
+}
+# endif /* __cplusplus */
+#endif /* !_CUPS_PPD_H_ */
+
+/*
+ * End of "$Id$".
+ */
diff --git a/cups/raster.c b/cups/raster.c
new file mode 100644
index 000000000..665472fdf
--- /dev/null
+++ b/cups/raster.c
@@ -0,0 +1,252 @@
+/*
+ * "$Id$"
+ *
+ * Raster file routines for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-1999 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights for the CUPS Raster source
+ * files are outlined in the GNU Library General Public License, located
+ * in the "pstoraster" directory. If this file is missing or damaged
+ * please contact Easy Software Products at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * This code and any derivative of it may be used and distributed
+ * freely under the terms of the GNU General Public License when
+ * used with GNU Ghostscript or its derivatives. Use of the code
+ * (or any derivative of it) with software other than GNU
+ * GhostScript (or its derivatives) is governed by the CUPS license
+ * agreement.
+ *
+ * Contents:
+ *
+ * cupsRasterClose() - Close a raster stream.
+ * cupsRasterOpen() - Open a raster stream.
+ * cupsRasterReadHeader() - Read a raster page header.
+ * cupsRasterReadPixels() - Read raster pixels.
+ * cupsRasterWriteHeader() - Write a raster page header.
+ * cupsRasterWritePixels() - Write raster pixels.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "raster.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#if defined(WIN32) || defined(__EMX__)
+# include <io.h>
+#else
+# include <unistd.h>
+#endif /* WIN32 || __EMX__ */
+
+
+/*
+ * 'cupsRasterClose()' - Close a raster stream.
+ */
+
+void
+cupsRasterClose(cups_raster_t *r) /* I - Stream to close */
+{
+ if (r != NULL)
+ free(r);
+}
+
+
+/*
+ * 'cupsRasterOpen()' - Open a raster stream.
+ */
+
+cups_raster_t * /* O - New stream */
+cupsRasterOpen(int fd, /* I - File descriptor */
+ cups_mode_t mode) /* I - Mode */
+{
+ cups_raster_t *r; /* New stream */
+
+
+ if ((r = calloc(sizeof(cups_raster_t), 1)) == NULL)
+ return (NULL);
+
+ r->fd = fd;
+ r->mode = mode;
+
+ if (mode == CUPS_RASTER_READ)
+ {
+ /*
+ * Open for read - get sync word...
+ */
+
+ if (read(fd, &(r->sync), sizeof(r->sync)) < sizeof(r->sync))
+ {
+ free(r);
+ return (NULL);
+ }
+
+ if (r->sync != CUPS_RASTER_SYNC &&
+ r->sync != CUPS_RASTER_REVSYNC)
+ {
+ free(r);
+ return (NULL);
+ }
+ }
+ else
+ {
+ /*
+ * Open for write - put sync word...
+ */
+
+ r->sync = CUPS_RASTER_SYNC;
+ if (write(fd, &(r->sync), sizeof(r->sync)) < sizeof(r->sync))
+ {
+ free(r);
+ return (NULL);
+ }
+ }
+
+ return (r);
+}
+
+
+/*
+ * 'cupsRasterReadHeader()' - Read a raster page header.
+ */
+
+unsigned /* O - 1 on success, 0 on fail */
+cupsRasterReadHeader(cups_raster_t *r, /* I - Raster stream */
+ cups_page_header_t *h) /* I - Pointer to header data */
+{
+ int len; /* Number of words to swap */
+ union swap_s /* Swapping structure */
+ {
+ unsigned char b[4];
+ unsigned v;
+ } *s;
+
+
+ if (r == NULL || r->mode != CUPS_RASTER_READ)
+ return (0);
+
+ if (cupsRasterReadPixels(r, (unsigned char *)h, sizeof(cups_page_header_t)) <
+ sizeof(cups_page_header_t))
+ return (0);
+
+ if (r->sync == CUPS_RASTER_REVSYNC)
+ for (len = (sizeof(cups_page_header_t) - 256) / 4,
+ s = (union swap_s *)&(h->AdvanceDistance);
+ len > 0;
+ len --, s ++)
+ s->v = (((((s->b[3] << 8) | s->b[2]) << 8) | s->b[1]) << 8) | s->b[0];
+
+ return (1);
+}
+
+
+/*
+ * 'cupsRasterReadPixels()' - Read raster pixels.
+ */
+
+unsigned /* O - Number of bytes read */
+cupsRasterReadPixels(cups_raster_t *r, /* I - Raster stream */
+ unsigned char *p, /* I - Pointer to pixel buffer */
+ unsigned len) /* I - Number of bytes to read */
+{
+ int bytes; /* Bytes read */
+ unsigned remaining; /* Bytes remaining */
+
+
+ if (r == NULL || r->mode != CUPS_RASTER_READ)
+ return (0);
+
+ remaining = len;
+
+ while (remaining > 0)
+ {
+ bytes = read(r->fd, p, remaining);
+
+ if (bytes <= 0)
+ {
+ if (errno != EAGAIN && errno != EINTR)
+ return (0);
+ else
+ continue;
+ }
+
+ remaining -= bytes;
+ p += bytes;
+ }
+
+ return (len);
+}
+
+
+/*
+ * 'cupsRasterWriteHeader()' - Write a raster page header.
+ */
+
+unsigned
+cupsRasterWriteHeader(cups_raster_t *r,
+ cups_page_header_t *h)
+{
+ if (r == NULL || r->mode != CUPS_RASTER_WRITE)
+ return (0);
+
+ return (cupsRasterWritePixels(r, (unsigned char *)h,
+ sizeof(cups_page_header_t)) ==
+ sizeof(cups_page_header_t));
+}
+
+
+/*
+ * 'cupsRasterWritePixels()' - Write raster pixels.
+ */
+
+unsigned /* O - Number of bytes written */
+cupsRasterWritePixels(cups_raster_t *r, /* I - Raster stream */
+ unsigned char *p, /* I - Bytes to write */
+ unsigned len)/* I - Number of bytes to write */
+{
+ int bytes; /* Bytes read */
+ unsigned remaining; /* Bytes remaining */
+
+
+ if (r == NULL || r->mode != CUPS_RASTER_WRITE)
+ return (0);
+
+ remaining = len;
+
+ while (remaining > 0)
+ {
+ bytes = write(r->fd, p, remaining);
+
+ if (bytes <= 0)
+ {
+ if (errno != EAGAIN && errno != EINTR)
+ return (0);
+ else
+ continue;
+ }
+
+ remaining -= bytes;
+ p += bytes;
+ }
+
+ return (len);
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/cups/raster.h b/cups/raster.h
new file mode 100644
index 000000000..a435af3ac
--- /dev/null
+++ b/cups/raster.h
@@ -0,0 +1,233 @@
+/*
+ * "$Id$"
+ *
+ * Raster file definitions for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-1999 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights for the CUPS Raster source
+ * files are outlined in the GNU Library General Public License, located
+ * in the "pstoraster" directory. If this file is missing or damaged
+ * please contact Easy Software Products at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * This code and any derivative of it may be used and distributed
+ * freely under the terms of the GNU General Public License when
+ * used with GNU Ghostscript or its derivatives. Use of the code
+ * (or any derivative of it) with software other than GNU
+ * GhostScript (or its derivatives) is governed by the CUPS license
+ * agreement.
+ */
+
+#ifndef _CUPS_RASTER_H_
+# define _CUPS_RASTER_H_
+
+# ifdef __cplusplus
+extern "C" {
+# endif /* __cplusplus */
+
+/*
+ * Every non-PostScript printer driver that supports raster images should
+ * use the application/vnd.cups-raster image file format. Since both the
+ * PostScript RIP (pstoraster, based on GNU Ghostscript 4.03) and Image RIP
+ * (imagetoraster, located in the filter directory) use it, using this format
+ * saves you a lot of work. Also, the PostScript RIP passes any printer
+ * options that are in a PS file to your driver this way as well...
+ */
+
+/*
+ * Constants...
+ */
+
+# define CUPS_RASTER_SYNC 0x52615374 /* RaSt */
+# define CUPS_RASTER_REVSYNC 0x74536152 /* tSaR */
+
+
+/*
+ * Types...
+ */
+
+typedef enum
+{
+ CUPS_RASTER_READ, /* Open stream for reading */
+ CUPS_RASTER_WRITE /* Open stream for writing */
+} cups_mode_t;
+
+typedef enum
+{
+ CUPS_FALSE, /* Logical false */
+ CUPS_TRUE /* Logical true */
+} cups_bool_t;
+
+typedef enum
+{
+ CUPS_JOG_NONE, /* Never move pages */
+ CUPS_JOG_FILE, /* Move pages after this file */
+ CUPS_JOG_JOB, /* Move pages after this job */
+ CUPS_JOG_SET /* Move pages after this set */
+} cups_jog_t;
+
+typedef enum
+{
+ CUPS_ORIENT_0, /* Don't rotate the page */
+ CUPS_ORIENT_90, /* Rotate the page counter-clockwise */
+ CUPS_ORIENT_180, /* Turn the page upside down */
+ CUPS_ORIENT_270 /* Rotate the page clockwise */
+} cups_orient_t;
+
+typedef enum
+{
+ CUPS_CUT_NONE, /* Never cut the roll */
+ CUPS_CUT_FILE, /* Cut the roll after this file */
+ CUPS_CUT_JOB, /* Cut the roll after this job */
+ CUPS_CUT_SET, /* Cut the roll after this set */
+ CUPS_CUT_PAGE /* Cut the roll after this page */
+} cups_cut_t;
+
+typedef enum
+{
+ CUPS_ADVANCE_NONE, /* Never advance the roll */
+ CUPS_ADVANCE_FILE, /* Advance the roll after this file */
+ CUPS_ADVANCE_JOB, /* Advance the roll after this job */
+ CUPS_ADVANCE_SET, /* Advance the roll after this set */
+ CUPS_ADVANCE_PAGE /* Advance the roll after this page */
+} cups_adv_t;
+
+typedef enum
+{
+ CUPS_EDGE_TOP, /* Leading edge is the top of the page */
+ CUPS_EDGE_RIGHT, /* Leading edge is the right of the page */
+ CUPS_EDGE_BOTTOM, /* Leading edge is the bottom of the page */
+ CUPS_EDGE_LEFT /* Leading edge is the left of the page */
+} cups_edge_t;
+
+typedef enum
+{
+ CUPS_ORDER_CHUNKED, /* CMYK CMYK CMYK ... */
+ CUPS_ORDER_BANDED, /* CCC MMM YYY KKK ... */
+ CUPS_ORDER_PLANAR /* CCC ... MMM ... YYY ... KKK ... */
+} cups_order_t;
+
+typedef enum
+{
+ CUPS_CSPACE_W, /* Luminance */
+ CUPS_CSPACE_RGB, /* Red, green, blue */
+ CUPS_CSPACE_RGBA, /* Red, green, blue, alpha */
+ CUPS_CSPACE_K, /* Black */
+ CUPS_CSPACE_CMY, /* Cyan, magenta, yellow */
+ CUPS_CSPACE_YMC, /* Yellow, magenta, cyan */
+ CUPS_CSPACE_CMYK, /* Cyan, magenta, yellow, black */
+ CUPS_CSPACE_YMCK, /* Yellow, magenta, cyan, black */
+ CUPS_CSPACE_KCMY, /* Black, cyan, magenta, yellow */
+ CUPS_CSPACE_KCMYcm, /* Black, cyan, magenta, yellow, *
+ * light-cyan, light-magenta */
+ CUPS_CSPACE_GMCK, /* Gold, magenta, yellow, black */
+ CUPS_CSPACE_GMCS, /* Gold, magenta, yellow, silver */
+ CUPS_CSPACE_WHITE, /* White ink (as black) */
+ CUPS_CSPACE_GOLD, /* Gold foil */
+ CUPS_CSPACE_SILVER /* Silver foil */
+} cups_cspace_t;
+
+
+/*
+ * The page header structure contains the standard PostScript page device
+ * dictionary, along with some CUPS-specific parameters that are provided
+ * by the RIPs...
+ */
+
+typedef struct
+{
+ /**** Standard Page Device Dictionary String Values ****/
+ char MediaClass[64]; /* MediaClass string */
+ char MediaColor[64]; /* MediaColor string */
+ char MediaType[64]; /* MediaType string */
+ char OutputType[64]; /* OutputType string */
+
+ /**** Standard Page Device Dictionary Integer Values ****/
+ unsigned AdvanceDistance; /* AdvanceDistance value in points */
+ cups_adv_t AdvanceMedia; /* AdvanceMedia value (see above) */
+ cups_bool_t Collate; /* Collated copies value */
+ cups_cut_t CutMedia; /* CutMedia value (see above) */
+ cups_bool_t Duplex; /* Duplexed (double-sided) value */
+ unsigned HWResolution[2]; /* Resolution in dots-per-inch */
+ unsigned ImagingBoundingBox[4]; /* Pixel region that is painted (points) */
+ cups_bool_t InsertSheet; /* InsertSheet value */
+ cups_jog_t Jog; /* Jog value (see above) */
+ cups_edge_t LeadingEdge; /* LeadingEdge value (see above) */
+ unsigned Margins[2]; /* Lower-lefthand margins in points */
+ cups_bool_t ManualFeed; /* ManualFeed value */
+ unsigned MediaPosition; /* MediaPosition value */
+ unsigned MediaWeight; /* MediaWeight value in grams/m^2 */
+ cups_bool_t MirrorPrint; /* MirrorPrint value */
+ cups_bool_t NegativePrint; /* NegativePrint value */
+ unsigned NumCopies; /* Number of copies to produce */
+ cups_orient_t Orientation; /* Orientation value (see above) */
+ cups_bool_t OutputFaceUp; /* OutputFaceUp value */
+ unsigned PageSize[2]; /* Width and length of page in points */
+ cups_bool_t Separations; /* Separations value */
+ cups_bool_t TraySwitch; /* TraySwitch value */
+ cups_bool_t Tumble; /* Tumble value */
+
+ /**** CUPS Page Device Dictionary Values ****/
+ unsigned cupsWidth; /* Width of page image in pixels */
+ unsigned cupsHeight; /* Height of page image in pixels */
+ unsigned cupsMediaType; /* Media type code */
+ unsigned cupsBitsPerColor; /* Number of bits for each color */
+ unsigned cupsBitsPerPixel; /* Number of bits for each pixel */
+ unsigned cupsBytesPerLine; /* Number of bytes per line */
+ cups_order_t cupsColorOrder; /* Order of colors */
+ cups_cspace_t cupsColorSpace; /* True colorspace */
+ unsigned cupsCompression; /* Device compression to use */
+ unsigned cupsRowCount; /* Rows per band */
+ unsigned cupsRowFeed; /* Feed between bands */
+ unsigned cupsRowStep; /* Spacing between lines */
+} cups_page_header_t;
+
+
+/*
+ * The raster structure maintains information about a raster data
+ * stream...
+ */
+
+typedef struct
+{
+ unsigned sync; /* Sync word from start of stream */
+ int fd; /* File descriptor */
+ cups_mode_t mode; /* Read/write mode */
+} cups_raster_t;
+
+
+/*
+ * Prototypes...
+ */
+
+extern void cupsRasterClose(cups_raster_t *r);
+extern cups_raster_t *cupsRasterOpen(int fd, cups_mode_t mode);
+extern unsigned cupsRasterReadHeader(cups_raster_t *r,
+ cups_page_header_t *h);
+extern unsigned cupsRasterReadPixels(cups_raster_t *r,
+ unsigned char *p, unsigned len);
+extern unsigned cupsRasterWriteHeader(cups_raster_t *r,
+ cups_page_header_t *h);
+extern unsigned cupsRasterWritePixels(cups_raster_t *r,
+ unsigned char *p, unsigned len);
+
+# ifdef __cplusplus
+}
+# endif /* __cplusplus */
+
+#endif /* !_CUPS_RASTER_H_ */
+
+/*
+ * End of "$Id$".
+ */
diff --git a/cups/string.c b/cups/string.c
new file mode 100644
index 000000000..4b64cbb4a
--- /dev/null
+++ b/cups/string.c
@@ -0,0 +1,125 @@
+/*
+ * "$Id$"
+ *
+ * String functions for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-1999 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * Contents:
+ *
+ * strdup() - Duplicate a string.
+ * strcasecmp() - Do a case-insensitive comparison.
+ * strncasecmp() - Do a case-insensitive comparison on up to N chars.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "string.h"
+
+
+/*
+ * 'strdup()' - Duplicate a string.
+ */
+
+# ifndef HAVE_STRDUP
+char * /* O - New string pointer */
+strdup(const char *s) /* I - String to duplicate */
+{
+ char *t; /* New string pointer */
+
+
+ if (s == NULL)
+ return (NULL);
+
+ if ((t = malloc(strlen(s) + 1)) == NULL)
+ return (NULL);
+
+ return (strcpy(t, s));
+}
+# endif /* !HAVE_STRDUP */
+
+
+/*
+ * 'strcasecmp()' - Do a case-insensitive comparison.
+ */
+
+# ifndef HAVE_STRCASECMP
+int /* O - Result of comparison (-1, 0, or 1) */
+strcasecmp(const char *s, /* I - First string */
+ const char *t) /* I - Second string */
+{
+ while (*s != '\0' && *t != '\0')
+ {
+ if (tolower(*s) < tolower(*t))
+ return (-1);
+ else if (tolower(*s) > tolower(*t))
+ return (1);
+
+ s ++;
+ t ++;
+ }
+
+ if (*s == '\0' && *t == '\0')
+ return (0);
+ else if (*s != '\0')
+ return (1);
+ else
+ return (-1);
+}
+# endif /* !HAVE_STRCASECMP */
+
+/*
+ * 'strncasecmp()' - Do a case-insensitive comparison on up to N chars.
+ */
+
+# ifndef HAVE_STRNCASECMP
+int /* O - Result of comparison (-1, 0, or 1) */
+strncasecmp(const char *s, /* I - First string */
+ const char *t, /* I - Second string */
+ size_t n) /* I - Maximum number of characters to compare */
+{
+ while (*s != '\0' && *t != '\0' && n > 0)
+ {
+ if (tolower(*s) < tolower(*t))
+ return (-1);
+ else if (tolower(*s) > tolower(*t))
+ return (1);
+
+ s ++;
+ t ++;
+ n --;
+ }
+
+ if (n == 0)
+ return (0);
+ else if (*s == '\0' && *t == '\0')
+ return (0);
+ else if (*s != '\0')
+ return (1);
+ else
+ return (-1);
+}
+# endif /* !HAVE_STRNCASECMP */
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/cups/string.h b/cups/string.h
new file mode 100644
index 000000000..a5d130d0e
--- /dev/null
+++ b/cups/string.h
@@ -0,0 +1,66 @@
+/*
+ * "$Id$"
+ *
+ * String definitions for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-1999 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ */
+
+#ifndef _CUPS_STRING_H_
+# define _CUPS_STRING_H_
+
+/*
+ * Include necessary headers...
+ */
+
+# include <config.h>
+# include <string.h>
+
+
+/*
+ * Stuff for WIN32 and OS/2...
+ */
+
+# if defined(WIN32) || defined(__EMX__)
+# define strcasecmp stricmp
+# define strncasecmp strnicmp
+# endif /* WIN32 || __EMX__ */
+
+
+/*
+ * Prototypes...
+ */
+
+# ifndef HAVE_STRDUP
+extern char *strdup(const char *);
+# endif /* !HAVE_STRDUP */
+
+# ifndef HAVE_STRCASECMP
+extern int strcasecmp(const char *, const char *);
+# endif /* !HAVE_STRCASECMP */
+
+# ifndef HAVE_STRNCASECMP
+extern int strncasecmp(const char *, const char *, size_t n);
+# endif /* !HAVE_STRNCASECMP */
+
+#endif /* !_CUPS_STRING_H_ */
+
+/*
+ * End of "$Id$".
+ */
diff --git a/cups/testhttp.c b/cups/testhttp.c
new file mode 100644
index 000000000..b29c3eb2c
--- /dev/null
+++ b/cups/testhttp.c
@@ -0,0 +1,109 @@
+/*
+ * "$Id$"
+ *
+ * HTTP test program for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-1999 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * Contents:
+ *
+ * main() - Main entry.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include <stdio.h>
+#include "http.h"
+
+
+/*
+ * 'main()' - Main entry.
+ */
+
+int /* O - Exit status */
+main(int argc, /* I - Number of command-line arguments */
+ char *argv[]) /* I - Command-line arguments */
+{
+ int i; /* Looping var */
+ http_t *http; /* HTTP connection */
+ http_status_t status; /* Status of GET command */
+ char buffer[1024]; /* Input buffer */
+ int bytes; /* Number of bytes read */
+ FILE *out; /* Output file */
+
+#define HOST "dns.easysw.com"
+#define PORT 80
+
+ puts("Connecting to " HOST "...");
+
+ httpInitialize();
+ http = httpConnect(HOST, PORT);
+ if (http == NULL)
+ {
+ puts("Unable to connect to " HOST "!");
+ return (1);
+ }
+
+ puts("Connected to " HOST "...");
+
+ out = stdout;
+
+ for (i = 1; i < argc; i ++)
+ {
+ if (strcmp(argv[i], "-o") == 0)
+ {
+ i ++;
+ out = fopen(argv[i], "wb");
+ continue;
+ }
+
+ printf("Requesting file \"%s\"...\n", argv[i]);
+ httpClearFields(http);
+ httpSetField(http, HTTP_FIELD_ACCEPT_LANGUAGE, "en");
+ httpGet(http, argv[i]);
+ status = httpUpdate(http);
+
+ if (status == HTTP_OK)
+ puts("GET OK:");
+ else
+ printf("GET failed with status %d...\n", status);
+
+ while ((bytes = httpRead(http, buffer, sizeof(buffer))) > 0)
+ {
+ fwrite(buffer, bytes, 1, out);
+ if (out != stdout)
+ printf("Read %d bytes, %d total...\n", bytes, ftell(out));
+ }
+ }
+
+ puts("Closing connection to server...");
+ httpClose(http);
+
+ if (out != stdout)
+ fclose(out);
+
+ return (0);
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/cups/testmime.c b/cups/testmime.c
new file mode 100644
index 000000000..4724ab524
--- /dev/null
+++ b/cups/testmime.c
@@ -0,0 +1,199 @@
+/*
+ * "$Id$"
+ *
+ * MIME test program for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-1999 by Easy Software Products, all rights reserved.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * Contents:
+ *
+ * main() - Main entry for the test program.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include <stdio.h>
+#include "mime.h"
+
+
+/*
+ * Local functions...
+ */
+
+static void print_rules(mime_magic_t *rules);
+
+
+/*
+ * 'main()' - Main entry for the test program.
+ */
+
+int /* O - Exit status */
+main(int argc, /* I - Number of command-line args */
+ char *argv[]) /* I - Command-line arguments */
+{
+ int i; /* Looping var */
+ char super[MIME_MAX_SUPER], /* Super-type name */
+ type[MIME_MAX_TYPE]; /* Type name */
+ mime_t *mime; /* MIME database */
+ mime_type_t *src, /* Source type */
+ *dst, /* Destination type */
+ **types; /* File type array pointer */
+ mime_filter_t *filters; /* Filters for the file */
+ int num_filters; /* Number of filters for the file */
+
+
+ mime = mimeLoad("../conf");
+
+ puts("MIME database types:");
+ for (i = 0, types = mime->types; i < mime->num_types; i ++, types ++)
+ {
+ printf("\t%s/%s: ", (*types)->super, (*types)->type);
+ print_rules((*types)->rules);
+ puts("");
+ }
+
+ puts("");
+
+ puts("MIME database filters:");
+ for (i = 0, filters = mime->filters; i < mime->num_filters; i ++, filters ++)
+ printf("\t%s/%s to %s/%s: %s (%d)\n",
+ filters->src->super, filters->src->type,
+ filters->dst->super, filters->dst->type,
+ filters->filter, filters->cost);
+
+ puts("");
+
+ switch (argc)
+ {
+ default :
+ fputs("Usage: testmime source-file [destination-type]\n", stderr);
+ return (1);
+
+ case 2 :
+ src = mimeFileType(mime, argv[1]);
+
+ if (src != NULL)
+ {
+ printf("%s: %s/%s\n", argv[1], src->super, src->type);
+ return (0);
+ }
+ else
+ {
+ printf("%s: unknown\n", argv[1]);
+ return (1);
+ }
+
+ case 3 :
+ src = mimeFileType(mime, argv[1]);
+
+ sscanf(argv[2], "%[^/]/%s", super, type);
+ dst = mimeType(mime, super, type);
+
+ filters = mimeFilter(mime, src, dst, &num_filters);
+
+ if (filters == NULL)
+ {
+ printf("No filters to convert from %s to %s.\n", argv[1], argv[2]);
+ return (1);
+ }
+ else
+ {
+ for (i = 0; i < num_filters; i ++)
+ if (i < (num_filters - 1))
+ printf("%s | ", filters[i].filter);
+ else
+ puts(filters[i].filter);
+
+ return (0);
+ }
+ }
+}
+
+
+/*
+ * 'print_rules()' - Print the rules for a file type...
+ */
+
+static void
+print_rules(mime_magic_t *rules) /* I - Rules to print */
+{
+ char logic; /* Logic separator */
+
+
+ if (rules == NULL)
+ return;
+
+ if (rules->parent == NULL ||
+ rules->parent->op == MIME_MAGIC_OR)
+ logic = ',';
+ else
+ logic = '+';
+
+ while (rules != NULL)
+ {
+ if (rules->prev != NULL)
+ putchar(logic);
+
+ switch (rules->op)
+ {
+ case MIME_MAGIC_MATCH :
+ printf("match(%s)", rules->value.matchv);
+ break;
+ case MIME_MAGIC_LOCALE :
+ printf("locale(%s)", rules->value.localev);
+ break;
+ case MIME_MAGIC_ASCII :
+ printf("ascii(%d,%d)", rules->offset, rules->length);
+ break;
+ case MIME_MAGIC_PRINTABLE :
+ printf("printable(%d,%d)", rules->offset, rules->length);
+ break;
+ case MIME_MAGIC_STRING :
+ printf("string(%d,%s)", rules->offset, rules->value.stringv);
+ break;
+ case MIME_MAGIC_CHAR :
+ printf("char(%d,%d)", rules->offset, rules->value.charv);
+ break;
+ case MIME_MAGIC_SHORT :
+ printf("short(%d,%d)", rules->offset, rules->value.shortv);
+ break;
+ case MIME_MAGIC_INT :
+ printf("int(%d,%d)", rules->offset, rules->value.intv);
+ break;
+ default :
+ if (rules->child != NULL)
+ {
+ putchar('(');
+ print_rules(rules->child);
+ putchar(')');
+ }
+ break;
+ }
+
+ rules = rules->next;
+ }
+}
+
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/cups/testmime.dsp b/cups/testmime.dsp
new file mode 100644
index 000000000..d41065c5d
--- /dev/null
+++ b/cups/testmime.dsp
@@ -0,0 +1,102 @@
+# Microsoft Developer Studio Project File - Name="testmime" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+CFG=testmime - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "testmime.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "testmime.mak" CFG="testmime - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "testmime - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "testmime - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "testmime - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD CPP /nologo /MT /W3 /GX /O2 /I ".." /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 cups.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 /out:"testmime.exe"
+
+!ELSEIF "$(CFG)" == "testmime - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "testmime___Win32_Debug"
+# PROP BASE Intermediate_Dir "testmime___Win32_Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 cupsd.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /incremental:no /debug /machine:I386 /out:"testmimed.exe" /pdbtype:sept
+
+!ENDIF
+
+# Begin Target
+
+# Name "testmime - Win32 Release"
+# Name "testmime - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\testmime.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\mime.h
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/cups/testppd.c b/cups/testppd.c
new file mode 100644
index 000000000..b2c7bd7e9
--- /dev/null
+++ b/cups/testppd.c
@@ -0,0 +1,183 @@
+/*
+ * "$Id$"
+ *
+ * PPD test program for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-1999 by Easy Software Products, all rights reserved.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * PostScript is a trademark of Adobe Systems, Inc.
+ *
+ * Contents:
+ *
+ * main() - Main entry for test program.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "cups.h"
+#include "string.h"
+
+
+/*
+ * 'main()' - Main entry for test program.
+ */
+
+int /* O - Exit status */
+main(int argc, /* I - Number of command-line arguments */
+ char *argv[]) /* I - Command-line arguments */
+{
+ int i, j, k, m, n; /* Looping vars */
+ const char *filename; /* File to load */
+ ppd_file_t *ppd; /* PPD file record */
+ ppd_size_t *size; /* Size record */
+ ppd_group_t *group; /* UI group */
+ ppd_option_t *option; /* Standard UI option */
+ ppd_choice_t *choice; /* Standard UI option choice */
+ static char *uis[] = { "BOOLEAN", "PICKONE", "PICKMANY" };
+ static char *sections[] = { "ANY", "DOCUMENT", "EXIT",
+ "JCL", "PAGE", "PROLOG" };
+
+
+ /*
+ * Display PPD files for each file listed on the command-line...
+ */
+
+ if (argc == 1)
+ {
+ fputs("Usage: ppdtest filename1.ppd [... filenameN.ppd]\n", stderr);
+ return (1);
+ }
+
+ for (i = 1; i < argc; i ++)
+ {
+ if (strstr(argv[i], ".ppd"))
+ filename = argv[i];
+ else
+ filename = cupsGetPPD(argv[i]);
+
+ if ((ppd = ppdOpenFile(filename)) == NULL)
+ {
+ fprintf(stderr, "Unable to open \'%s\' as a PPD file!\n", filename);
+ continue;
+ }
+
+ printf("FILE: %s\n", filename);
+ printf(" language_level = %d\n", ppd->language_level);
+ printf(" color_device = %s\n", ppd->color_device ? "TRUE" : "FALSE");
+ printf(" variable_sizes = %s\n", ppd->variable_sizes ? "TRUE" : "FALSE");
+ printf(" landscape = %d\n", ppd->landscape);
+
+ switch (ppd->colorspace)
+ {
+ case PPD_CS_CMYK :
+ puts(" colorspace = PPD_CS_CMYK");
+ break;
+ case PPD_CS_CMY :
+ puts(" colorspace = PPD_CS_CMY");
+ break;
+ case PPD_CS_GRAY :
+ puts(" colorspace = PPD_CS_GRAY");
+ break;
+ case PPD_CS_RGB :
+ puts(" colorspace = PPD_CS_RGB");
+ break;
+ default :
+ puts(" colorspace = <unknown>");
+ break;
+ }
+
+ printf(" num_emulations = %d\n", ppd->num_emulations);
+ for (j = 0; j < ppd->num_emulations; j ++)
+ printf(" emulations[%d] = %s\n", j, ppd->emulations[j].name);
+
+ printf(" lang_encoding = %s\n", ppd->lang_encoding);
+ printf(" lang_version = %s\n", ppd->lang_version);
+ printf(" modelname = %s\n", ppd->modelname);
+ printf(" ttrasterizer = %s\n",
+ ppd->ttrasterizer == NULL ? "None" : ppd->ttrasterizer);
+ printf(" manufacturer = %s\n", ppd->manufacturer);
+ printf(" product = %s\n", ppd->product);
+ printf(" nickname = %s\n", ppd->nickname);
+ printf(" shortnickname = %s\n", ppd->shortnickname);
+ printf(" patches = %d bytes\n",
+ ppd->patches == NULL ? 0 : strlen(ppd->patches));
+
+ printf(" num_groups = %d\n", ppd->num_groups);
+ for (j = 0, group = ppd->groups; j < ppd->num_groups; j ++, group ++)
+ {
+ printf(" group[%d] = %s\n", j, group->text);
+
+ for (k = 0, option = group->options; k < group->num_options; k ++, option ++)
+ {
+ printf(" options[%d] = %s (%s) %s %s %.0f\n", k,
+ option->keyword, option->text, uis[option->ui],
+ sections[option->section], option->order);
+
+ if (strcmp(option->keyword, "PageSize") == 0 ||
+ strcmp(option->keyword, "PageRegion") == 0)
+ {
+ for (m = option->num_choices, choice = option->choices;
+ m > 0;
+ m --, choice ++)
+ {
+ size = ppdPageSize(ppd, choice->choice);
+
+ if (size == NULL)
+ printf(" %s (%s) = ERROR", choice->choice, choice->text);
+ else
+ printf(" %s (%s) = %.2fx%.2fin (%.1f,%.1f,%.1f,%.1f)", choice->choice,
+ choice->text, size->width / 72.0, size->length / 72.0,
+ size->left / 72.0, size->bottom / 72.0,
+ size->right / 72.0, size->top / 72.0);
+
+ if (strcmp(option->defchoice, choice->choice) == 0)
+ puts(" *");
+ else
+ putchar('\n');
+ }
+ }
+ else
+ {
+ for (m = option->num_choices, choice = option->choices;
+ m > 0;
+ m --, choice ++)
+ {
+ printf(" %s (%s)", choice->choice, choice->text);
+
+ if (strcmp(option->defchoice, choice->choice) == 0)
+ puts(" *");
+ else
+ putchar('\n');
+ }
+ }
+ }
+ }
+
+ ppdClose(ppd);
+ }
+
+ return (0);
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/cups/testppd.dsp b/cups/testppd.dsp
new file mode 100644
index 000000000..18014f798
--- /dev/null
+++ b/cups/testppd.dsp
@@ -0,0 +1,102 @@
+# Microsoft Developer Studio Project File - Name="testppd" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+CFG=testppd - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "testppd.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "testppd.mak" CFG="testppd - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "testppd - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "testppd - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "testppd - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD CPP /nologo /MT /W3 /GX /O2 /I ".." /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 cups.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 /out:"testppd.exe"
+
+!ELSEIF "$(CFG)" == "testppd - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "testppd___Win32_Debug"
+# PROP BASE Intermediate_Dir "testppd___Win32_Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 cupsd.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /incremental:no /debug /machine:I386 /out:"testppdd.exe" /pdbtype:sept
+
+!ENDIF
+
+# Begin Target
+
+# Name "testppd - Win32 Release"
+# Name "testppd - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\testppd.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\ppd.h
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/cups/type.c b/cups/type.c
new file mode 100644
index 000000000..23b110a49
--- /dev/null
+++ b/cups/type.c
@@ -0,0 +1,1011 @@
+/*
+ * "$Id$"
+ *
+ * MIME typing routines for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-1999 by Easy Software Products, all rights reserved.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * Contents:
+ *
+ * mimeAddType() - Add a MIME type to a database.
+ * mimeAddRule() - Add a detection rule for a file type.
+ * mimeFileType() - Determine the type of a file.
+ * mimeType() - Lookup a file type.
+ * compare() - Compare two MIME super/type names.
+ * checkrules() - Check each rule in a list.
+ * patmatch() - Pattern matching...
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <locale.h>
+
+#include "string.h"
+#include "mime.h"
+
+
+/*
+ * Local functions...
+ */
+
+static int compare(mime_type_t **, mime_type_t **);
+static int checkrules(const char *, FILE *, mime_magic_t *);
+static int patmatch(const char *, const char *);
+
+
+/*
+ * 'mimeAddType()' - Add a MIME type to a database.
+ */
+
+mime_type_t * /* O - New (or existing) MIME type */
+mimeAddType(mime_t *mime, /* I - MIME database */
+ const char *super, /* I - Super-type name */
+ const char *type) /* I - Type name */
+{
+ mime_type_t *temp, /* New MIME type */
+ **types; /* New MIME types array */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (mime == NULL || super == NULL || type == NULL)
+ return (NULL);
+
+ if (strlen(super) > (MIME_MAX_SUPER - 1) ||
+ strlen(type) > (MIME_MAX_TYPE - 1))
+ return (NULL);
+
+ /*
+ * See if the type already exists; if so, return the existing type...
+ */
+
+ if ((temp = mimeType(mime, super, type)) != NULL)
+ return (temp);
+
+ /*
+ * The type doesn't exist; add it...
+ */
+
+ if ((temp = calloc(1, sizeof(mime_type_t))) == NULL)
+ return (NULL);
+
+ if (mime->num_types == 0)
+ types = (mime_type_t **)malloc(sizeof(mime_type_t *));
+ else
+ types = (mime_type_t **)realloc(mime->types, sizeof(mime_type_t *) * (mime->num_types + 1));
+
+ if (types == NULL)
+ {
+ free(temp);
+ return (NULL);
+ }
+
+ mime->types = types;
+ types += mime->num_types;
+ mime->num_types ++;
+
+ *types = temp;
+ strcpy(temp->super, super);
+ strcpy(temp->type, type);
+
+ if (mime->num_types > 1)
+ qsort(mime->types, mime->num_types, sizeof(mime_type_t *),
+ (int (*)(const void *, const void *))compare);
+
+ return (temp);
+}
+
+
+/*
+ * 'mimeAddRule()' - Add a detection rule for a file type.
+ */
+
+int /* O - 0 on success, -1 on failure */
+mimeAddTypeRule(mime_type_t *mt, /* I - Type to add to */
+ const char *rule) /* I - Rule to add */
+{
+ int num_values, /* Number of values seen */
+ op, /* Operation code */
+ logic, /* Logic for next rule */
+ invert; /* Invert following rule? */
+ char name[255], /* Name in rule string */
+ value[2][255], /* Value in rule string */
+ *ptr, /* Position in name or value */
+ quote; /* Quote character */
+ int length[2]; /* Length of each parameter */
+ mime_magic_t *temp, /* New rule */
+ *current; /* Current rule */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (mt == NULL || rule == NULL)
+ return (-1);
+
+ /*
+ * Find the last rule in the top-level of the rules tree.
+ */
+
+ for (current = mt->rules; current != NULL; current = current->next)
+ if (current->next == NULL)
+ break;
+
+ /*
+ * Parse the rules string. Most rules are either a file extension or a
+ * comparison function:
+ *
+ * extension
+ * function(parameters)
+ */
+
+ logic = MIME_MAGIC_NOP;
+ invert = 0;
+
+ while (*rule != '\0')
+ {
+ while (isspace(*rule))
+ rule ++;
+
+ if (*rule == '(')
+ {
+ logic = MIME_MAGIC_NOP;
+ rule ++;
+ }
+ else if (*rule == ')')
+ {
+ if (current == NULL || current->parent == NULL)
+ return (-1);
+
+ current = current->parent;
+
+ if (current->parent == NULL)
+ logic = MIME_MAGIC_OR;
+ else
+ logic = current->parent->op;
+
+ rule ++;
+ }
+ else if (*rule == '+' && current != NULL)
+ {
+ if (logic != MIME_MAGIC_AND &&
+ current != NULL && current->prev != NULL && current->prev->prev != NULL)
+ {
+ /*
+ * OK, we have more than 1 rule in the current tree level... Make a
+ * new group tree and move the previous rule to it...
+ */
+
+ if ((temp = calloc(1, sizeof(mime_magic_t))) == NULL)
+ return (-1);
+
+ temp->op = MIME_MAGIC_AND;
+ temp->child = current;
+ temp->parent = current->parent;
+ current->prev->next = temp;
+ temp->prev = current->prev;
+
+ current->prev = NULL;
+ current->parent = temp;
+ }
+ else
+ current->parent->op = MIME_MAGIC_AND;
+
+ logic = MIME_MAGIC_AND;
+ rule ++;
+ }
+ else if (*rule == ',')
+ {
+ if (logic != MIME_MAGIC_OR && current != NULL)
+ {
+ /*
+ * OK, we have two possibilities; either this is the top-level rule or
+ * we have a bunch of AND rules at this level.
+ */
+
+ if (current->parent == NULL)
+ {
+ /*
+ * This is the top-level rule; we have to move *all* of the AND rules
+ * down a level, as AND has precedence over OR.
+ */
+
+ if ((temp = calloc(1, sizeof(mime_magic_t))) == NULL)
+ return (-1);
+
+ while (current->prev != NULL)
+ {
+ current->parent = temp;
+ current = current->prev;
+ }
+
+ current->parent = temp;
+ temp->op = MIME_MAGIC_AND;
+ temp->child = current;
+
+ mt->rules = current = temp;
+ }
+ else
+ {
+ /*
+ * This isn't the top rule, so go up one level...
+ */
+
+ current = current->parent;
+ }
+ }
+
+ logic = MIME_MAGIC_OR;
+ rule ++;
+ }
+ else if (*rule == '!')
+ {
+ invert = 1;
+ rule ++;
+ }
+ else if (isalnum(*rule))
+ {
+ /*
+ * Read an extension name or a function...
+ */
+
+ for (ptr = name; isalnum(*rule) && (ptr - name) < (sizeof(name) - 1);)
+ *ptr++ = *rule++;
+
+ *ptr = '\0';
+ num_values = 0;
+
+ if (*rule == '(')
+ {
+ /*
+ * Read function parameters...
+ */
+
+ rule ++;
+ for (num_values = 0; num_values < 2; num_values ++)
+ {
+ ptr = value[num_values];
+
+ while ((ptr - value[num_values]) < (sizeof(value[0]) - 1) &&
+ *rule != '\0' && *rule != ',' && *rule != ')')
+ {
+ if (isspace(*rule))
+ {
+ /*
+ * Ignore whitespace...
+ */
+
+ rule ++;
+ continue;
+ }
+ else if (*rule == '\"' || *rule == '\'')
+ {
+ /*
+ * Copy quoted strings literally...
+ */
+
+ quote = *rule++;
+
+ while (*rule != '\0' && *rule != quote)
+ *ptr++ = *rule++;
+
+ if (*rule == quote)
+ rule ++;
+ else
+ return (-1);
+ }
+ else if (*rule == '<')
+ {
+ rule ++;
+
+ while (*rule != '>' && *rule != '\0')
+ {
+ if (isxdigit(rule[0]) && isxdigit(rule[1]))
+ {
+ if (isdigit(*rule))
+ *ptr = (*rule++ - '0') << 4;
+ else
+ *ptr = (tolower(*rule++) - 'a' + 10) << 4;
+
+ if (isdigit(*rule))
+ *ptr++ |= *rule++ - '0';
+ else
+ *ptr++ |= tolower(*rule++) - 'a' + 10;
+ }
+ else
+ return (-1);
+ }
+
+ if (*rule == '>')
+ rule ++;
+ else
+ return (-1);
+ }
+ else
+ *ptr++ = *rule++;
+ }
+
+ *ptr = '\0';
+ length[num_values] = ptr - value[num_values];
+
+ if (*rule != ',')
+ break;
+
+ rule ++;
+ }
+
+ if (*rule != ')')
+ return (-1);
+
+ rule ++;
+
+ /*
+ * Figure out the function...
+ */
+
+ if (strcmp(name, "match") == 0)
+ op = MIME_MAGIC_MATCH;
+ else if (strcmp(name, "ascii") == 0)
+ op = MIME_MAGIC_ASCII;
+ else if (strcmp(name, "printable") == 0)
+ op = MIME_MAGIC_PRINTABLE;
+ else if (strcmp(name, "string") == 0)
+ op = MIME_MAGIC_STRING;
+ else if (strcmp(name, "char") == 0)
+ op = MIME_MAGIC_CHAR;
+ else if (strcmp(name, "short") == 0)
+ op = MIME_MAGIC_SHORT;
+ else if (strcmp(name, "int") == 0)
+ op = MIME_MAGIC_INT;
+ else if (strcmp(name, "locale") == 0)
+ op = MIME_MAGIC_LOCALE;
+ else
+ return (-1);
+ }
+ else
+ {
+ /*
+ * This is just a filename match on the extension...
+ */
+
+ sprintf(value[0], "*.%s", name);
+ length[0] = strlen(value[0]);
+ num_values = 1;
+ op = MIME_MAGIC_MATCH;
+ }
+
+ /*
+ * Add a rule for this operation.
+ */
+
+ if ((temp = calloc(1, sizeof(mime_magic_t))) == NULL)
+ return (-1);
+
+ temp->invert = invert;
+ if (current != NULL)
+ {
+ temp->parent = current->parent;
+ current->next = temp;
+ }
+ else
+ mt->rules = temp;
+
+ temp->prev = current;
+
+ if (logic == MIME_MAGIC_NOP)
+ {
+ /*
+ * Add parenthetical grouping...
+ */
+
+ temp->op = MIME_MAGIC_OR;
+
+ if ((temp->child = calloc(1, sizeof(mime_magic_t))) == NULL)
+ return (-1);
+
+ temp->child->parent = temp;
+
+ temp = temp->child;
+ logic = MIME_MAGIC_OR;
+ }
+
+ /*
+ * Fill in data for the rule...
+ */
+
+ current = temp;
+ temp->op = op;
+ invert = 0;
+
+ switch (op)
+ {
+ case MIME_MAGIC_MATCH :
+ if (length[0] > (sizeof(temp->value.matchv) - 1))
+ return (-1);
+ strcpy(temp->value.matchv, value[0]);
+ break;
+ case MIME_MAGIC_ASCII :
+ case MIME_MAGIC_PRINTABLE :
+ temp->offset = atoi(value[0]);
+ temp->length = atoi(value[1]);
+ if (temp->length > MIME_MAX_BUFFER)
+ temp->length = MIME_MAX_BUFFER;
+ break;
+ case MIME_MAGIC_STRING :
+ temp->offset = atoi(value[0]);
+ if (length[1] > sizeof(temp->value.stringv))
+ return (-1);
+ temp->length = length[1];
+ memcpy(temp->value.stringv, value[1], length[1]);
+ break;
+ case MIME_MAGIC_CHAR :
+ temp->offset = atoi(value[0]);
+ if (length[1] == 1)
+ temp->value.charv = value[1][0];
+ else
+ temp->value.charv = atoi(value[1]);
+ break;
+ case MIME_MAGIC_SHORT :
+ temp->offset = atoi(value[0]);
+ temp->value.shortv = atoi(value[1]);
+ break;
+ case MIME_MAGIC_INT :
+ temp->offset = atoi(value[0]);
+ temp->value.intv = atoi(value[1]);
+ break;
+ case MIME_MAGIC_LOCALE :
+ if (length[0] > (sizeof(temp->value.localev) - 1))
+ return (-1);
+
+ strcpy(temp->value.localev, value[0]);
+ break;
+ }
+ }
+ else
+ break;
+ }
+
+ return (0);
+}
+
+
+/*
+ * 'mimeFileType()' - Determine the type of a file.
+ */
+
+mime_type_t * /* O - Type of file */
+mimeFileType(mime_t *mime, /* I - MIME database */
+ const char *pathname) /* I - Name of file to check */
+{
+ int i; /* Looping var */
+ FILE *fp; /* File pointer */
+ mime_type_t **types; /* File types */
+ const char *filename; /* Base filename of file */
+
+
+ /*
+ * Range check input parameters...
+ */
+
+ if (mime == NULL || pathname == NULL)
+ return (NULL);
+
+ /*
+ * Try to open the file...
+ */
+
+ if ((fp = fopen(pathname, "r")) == NULL)
+ return (NULL);
+
+ /*
+ * Figure out the filename (without directory portion)...
+ */
+
+ if ((filename = strrchr(pathname, '/')) != NULL)
+ filename ++;
+ else
+ filename = pathname;
+
+ /*
+ * Then check it against all known types...
+ */
+
+ for (i = mime->num_types, types = mime->types; i > 0; i --, types ++)
+ if (checkrules(filename, fp, (*types)->rules))
+ break;
+
+ /*
+ * Finally, close the file and return a match (if any)...
+ */
+
+ fclose(fp);
+
+ if (i > 0)
+ return (*types);
+ else
+ return (NULL);
+}
+
+
+/*
+ * 'mimeType()' - Lookup a file type.
+ */
+
+mime_type_t * /* O - Matching file type definition */
+mimeType(mime_t *mime, /* I - MIME database */
+ const char *super, /* I - Super-type name */
+ const char *type) /* I - Type name */
+{
+ mime_type_t key, /* MIME type search key*/
+ *keyptr, /* Key pointer... */
+ **match; /* Matching pointer */
+
+ /*
+ * Range check input...
+ */
+
+ if (mime == NULL || super == NULL || type == NULL)
+ return (NULL);
+
+ if (strlen(super) > (MIME_MAX_SUPER - 1) ||
+ strlen(type) > (MIME_MAX_TYPE - 1))
+ return (NULL);
+
+ if (mime->num_types == 0)
+ return (NULL);
+
+ /*
+ * Lookup the type in the array...
+ */
+
+ strcpy(key.super, super);
+ strcpy(key.type, type);
+ keyptr = &key;
+
+ match = (mime_type_t **)bsearch(&keyptr, mime->types, mime->num_types,
+ sizeof(mime_type_t *),
+ (int (*)(const void *, const void *))compare);
+
+ if (match == NULL)
+ return (NULL);
+ else
+ return (*match);
+}
+
+
+/*
+ * 'compare()' - Compare two MIME super/type names.
+ */
+
+static int /* O - Result of comparison */
+compare(mime_type_t **t0, /* I - First type */
+ mime_type_t **t1) /* I - Second type */
+{
+ int i; /* Result of comparison */
+
+
+ if ((i = strcasecmp((*t0)->super, (*t1)->super)) == 0)
+ i = strcasecmp((*t0)->type, (*t1)->type);
+
+ return (i);
+}
+
+
+/*
+ * 'checkrules()' - Check each rule in a list.
+ */
+
+static int /* O - 1 if match, 0 if no match */
+checkrules(const char *filename, /* I - Filename */
+ FILE *fp, /* I - File to check */
+ mime_magic_t *rules) /* I - Rules to check */
+{
+ int n; /* Looping var */
+ int logic, /* Logic to apply */
+ result, /* Result of test */
+ intv; /* Integer value */
+ short shortv; /* Short value */
+ unsigned char buffer[MIME_MAX_BUFFER],/* Input buffer */
+ *bufptr; /* Current buffer position */
+ int bufoffset, /* Offset in file for buffer */
+ buflength; /* Length of data in buffer */
+
+
+ if (rules == NULL)
+ return (0);
+
+ if (rules->parent == NULL)
+ logic = MIME_MAGIC_OR;
+ else
+ logic = rules->parent->op;
+
+ bufoffset = -1;
+
+ while (rules != NULL)
+ {
+ /*
+ * Compute the result of this rule...
+ */
+
+ switch (rules->op)
+ {
+ case MIME_MAGIC_MATCH :
+ result = patmatch(filename, rules->value.matchv);
+ break;
+
+ case MIME_MAGIC_ASCII :
+ /*
+ * Load the buffer if necessary...
+ */
+
+ if (bufoffset < 0 || rules->offset < bufoffset ||
+ (rules->offset + rules->length) > (bufoffset + buflength))
+ {
+ /*
+ * Reload file buffer...
+ */
+
+ fseek(fp, rules->offset, SEEK_SET);
+ buflength = fread(buffer, 1, sizeof(buffer), fp);
+ bufoffset = rules->offset;
+ }
+
+ /*
+ * Test for ASCII printable characters plus standard control chars.
+ */
+
+ if ((rules->offset + rules->length) > (bufoffset + buflength))
+ n = bufoffset + buflength - rules->offset;
+ else
+ n = rules->length;
+
+ bufptr = buffer + rules->offset - bufoffset;
+ while (n > 0)
+ if ((*bufptr >= 32 && *bufptr <= 126) ||
+ (*bufptr >= 8 && *bufptr <= 13) ||
+ *bufptr == 26 || *bufptr == 27)
+ {
+ n --;
+ bufptr ++;
+ }
+ else
+ break;
+
+ result = (n == 0);
+ break;
+
+ case MIME_MAGIC_PRINTABLE :
+ /*
+ * Load the buffer if necessary...
+ */
+
+ if (bufoffset < 0 || rules->offset < bufoffset ||
+ (rules->offset + rules->length) > (bufoffset + buflength))
+ {
+ /*
+ * Reload file buffer...
+ */
+
+ fseek(fp, rules->offset, SEEK_SET);
+ buflength = fread(buffer, 1, sizeof(buffer), fp);
+ bufoffset = rules->offset;
+ }
+
+ /*
+ * Test for ASCII printable characters plus standard control chars.
+ */
+
+ if ((rules->offset + rules->length) > (bufoffset + buflength))
+ n = bufoffset + buflength - rules->offset;
+ else
+ n = rules->length;
+
+ bufptr = buffer + rules->offset - bufoffset;
+
+ while (n > 0)
+ if ((*bufptr >= 160 && *bufptr <= 254) ||
+ (*bufptr >= 32 && *bufptr <= 126) ||
+ (*bufptr >= 8 && *bufptr <= 13) ||
+ *bufptr == 26 || *bufptr == 27)
+ {
+ n --;
+ bufptr ++;
+ }
+ else
+ break;
+
+ result = (n == 0);
+ break;
+
+ case MIME_MAGIC_STRING :
+ /*
+ * Load the buffer if necessary...
+ */
+
+ if (bufoffset < 0 || rules->offset < bufoffset ||
+ (rules->offset + rules->length) > (bufoffset + buflength))
+ {
+ /*
+ * Reload file buffer...
+ */
+
+ fseek(fp, rules->offset, SEEK_SET);
+ buflength = fread(buffer, 1, sizeof(buffer), fp);
+ bufoffset = rules->offset;
+ }
+
+ /*
+ * Compare the buffer against the string. If the file is too
+ * short then don't compare - it can't match...
+ */
+
+ if ((rules->offset + rules->length) > (bufoffset + buflength))
+ result = 0;
+ else
+ result = (memcmp(buffer + rules->offset - bufoffset,
+ rules->value.stringv, rules->length) == 0);
+ break;
+
+ case MIME_MAGIC_CHAR :
+ /*
+ * Load the buffer if necessary...
+ */
+
+ if (bufoffset < 0 || rules->offset < bufoffset)
+ {
+ /*
+ * Reload file buffer...
+ */
+
+ fseek(fp, rules->offset, SEEK_SET);
+ buflength = fread(buffer, 1, sizeof(buffer), fp);
+ bufoffset = rules->offset;
+ }
+
+ /*
+ * Compare the character values; if the file is too short, it
+ * can't match...
+ */
+
+ if (buflength < 1)
+ result = 0;
+ else
+ result = (buffer[rules->offset - bufoffset] == rules->value.charv);
+ break;
+
+ case MIME_MAGIC_SHORT :
+ /*
+ * Load the buffer if necessary...
+ */
+
+ if (bufoffset < 0 || rules->offset < bufoffset ||
+ (rules->offset + 2) > (bufoffset + buflength))
+ {
+ /*
+ * Reload file buffer...
+ */
+
+ fseek(fp, rules->offset, SEEK_SET);
+ buflength = fread(buffer, 1, sizeof(buffer), fp);
+ bufoffset = rules->offset;
+ }
+
+ /*
+ * Compare the short values; if the file is too short, it
+ * can't match...
+ */
+
+ if (buflength < 2)
+ result = 0;
+ else
+ {
+ bufptr = buffer + rules->offset - bufoffset;
+ shortv = (bufptr[0] << 8) | bufptr[1];
+ result = (shortv == rules->value.shortv);
+ }
+ break;
+
+ case MIME_MAGIC_INT :
+ /*
+ * Load the buffer if necessary...
+ */
+
+ if (bufoffset < 0 || rules->offset < bufoffset ||
+ (rules->offset + 4) > (bufoffset + buflength))
+ {
+ /*
+ * Reload file buffer...
+ */
+
+ fseek(fp, rules->offset, SEEK_SET);
+ buflength = fread(buffer, 1, sizeof(buffer), fp);
+ bufoffset = rules->offset;
+ }
+
+ /*
+ * Compare the int values; if the file is too short, it
+ * can't match...
+ */
+
+ if (buflength < 4)
+ result = 0;
+ else
+ {
+ bufptr = buffer + rules->offset - bufoffset;
+ intv = (((((bufptr[0] << 8) | bufptr[1]) << 8) | bufptr[2]) << 8) |
+ bufptr[3];;
+ result = (intv == rules->value.intv);
+ }
+ break;
+
+ case MIME_MAGIC_LOCALE :
+ result = (strcmp(rules->value.localev, setlocale(LC_ALL, NULL)) == 0);
+ break;
+
+ default :
+ if (rules->child != NULL)
+ result = checkrules(filename, fp, rules->child);
+ else
+ result = 0;
+ break;
+ }
+
+ /*
+ * If the logic is inverted, invert the result...
+ */
+
+ if (rules->invert)
+ result = !result;
+
+ /*
+ * OK, now if the current logic is OR and this result is true, the this
+ * rule set is true. If the current logic is AND and this result is false,
+ * the the rule set is false...
+ */
+
+ if ((result && logic == MIME_MAGIC_OR) ||
+ (!result && logic == MIME_MAGIC_AND))
+ return (result);
+
+ /*
+ * Otherwise the jury is still out on this one, so move to the next rule.
+ */
+
+ rules = rules->next;
+ }
+
+ return (result);
+}
+
+
+/*
+ * 'patmatch()' - Pattern matching...
+ */
+
+static int /* O - 1 if match, 0 if no match */
+patmatch(const char *s, /* I - String to match against */
+ const char *pat) /* I - Pattern to match against */
+{
+ /*
+ * Range check the input...
+ */
+
+ if (s == NULL || pat == NULL)
+ return (0);
+
+ /*
+ * Loop through the pattern and match strings, and stop if we come to a
+ * point where the strings don't match or we find a complete match.
+ */
+
+ while (*s != '\0' && *pat != '\0')
+ {
+ if (*pat == '*')
+ {
+ /*
+ * Wildcard - 0 or more characters...
+ */
+
+ pat ++;
+ if (*pat == '\0')
+ return (1); /* Last pattern char is *, so everything matches now... */
+
+ /*
+ * Test all remaining combinations until we get to the end of the string.
+ */
+
+ while (*s != '\0')
+ {
+ if (patmatch(s, pat))
+ return (1);
+
+ s ++;
+ }
+ }
+ else if (*pat == '?')
+ {
+ /*
+ * Wildcard - 1 character...
+ */
+
+ pat ++;
+ s ++;
+ continue;
+ }
+ else if (*pat == '[')
+ {
+ /*
+ * Match a character from the input set [chars]...
+ */
+
+ pat ++;
+ while (*pat != ']' && *pat != '\0')
+ if (*s == *pat)
+ break;
+ else
+ pat ++;
+
+ if (*pat == ']' || *pat == '\0')
+ return (0);
+
+ while (*pat != ']' && *pat != '\0')
+ pat ++;
+
+ if (*pat == ']')
+ pat ++;
+
+ continue;
+ }
+ else if (*pat == '\\')
+ {
+ /*
+ * Handle quoted characters...
+ */
+
+ pat ++;
+ }
+
+ /*
+ * Stop if the pattern and string don't match...
+ */
+
+ if (*pat++ != *s++)
+ return (0);
+ }
+
+ /*
+ * Done parsing the pattern and string; return 1 if the last character matches
+ * and 0 otherwise...
+ */
+
+ return (*s == *pat);
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/cups/usersys.c b/cups/usersys.c
new file mode 100644
index 000000000..d837f2e9e
--- /dev/null
+++ b/cups/usersys.c
@@ -0,0 +1,175 @@
+/*
+ * "$Id$"
+ *
+ * User, system, and password routines for the Common UNIX Printing
+ * System (CUPS).
+ *
+ * Copyright 1997-1999 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * Contents:
+ *
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "cups.h"
+#include <config.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+
+#if defined(WIN32) || defined(__EMX__)
+/*
+ * WIN32 and OS/2 username and password stuff...
+ */
+
+/*
+ * 'cupsUser()' - Return the current user's name.
+ */
+
+const char * /* O - User name */
+cupsUser(void)
+{
+ return ("WindowsUser");
+}
+
+
+/*
+ * 'cupsGetPassword()' - Get a password from the user...
+ */
+
+const char * /* O - Password */
+cupsGetPassword(const char *prompt) /* I - Prompt string */
+{
+ return (NULL);
+}
+#else
+/*
+ * UNIX username and password stuff...
+ */
+
+# include <pwd.h>
+
+/*
+ * 'cupsUser()' - Return the current user's name.
+ */
+
+const char * /* O - User name */
+cupsUser(void)
+{
+ struct passwd *pwd; /* User/password entry */
+
+
+ /*
+ * Rewind the password file...
+ */
+
+ setpwent();
+
+ /*
+ * Lookup the password entry for the current user.
+ */
+
+ if ((pwd = getpwuid(getuid())) == NULL)
+ return ("unknown"); /* Unknown user! */
+
+ /*
+ * Rewind the password file again and return the username...
+ */
+
+ setpwent();
+
+ return (pwd->pw_name);
+}
+
+
+/*
+ * 'cupsGetPassword()' - Get a password from the user...
+ */
+
+const char * /* O - Password */
+cupsGetPassword(const char *prompt) /* I - Prompt string */
+{
+ return (getpass(prompt));
+}
+#endif /* WIN32 || __EMX__ */
+
+
+/*
+ * 'cupsServer()' - Return the hostname of the default server...
+ */
+
+const char * /* O - Server name */
+cupsServer(void)
+{
+ FILE *fp; /* cupsd.conf file */
+ char *server; /* Pointer to server name */
+ static char line[1024]; /* Line from file */
+
+
+ /*
+ * First see if the CUPS_SERVER environment variable is set...
+ */
+
+ if ((server = getenv("CUPS_SERVER")) != NULL)
+ return (server);
+
+ /*
+ * Next check to see if we have a cupsd.conf file...
+ */
+
+ if ((fp = fopen(CUPS_SERVERROOT "/conf/cupsd.conf", "r")) == NULL)
+ return ("localhost");
+
+ /*
+ * Read the cupsd.conf file and look for a ServerName line...
+ */
+
+ while (fgets(line, sizeof(line), fp) != NULL)
+ if (strncmp(line, "ServerName ", 11) == 0)
+ {
+ /*
+ * Got it! Drop any trailing newline and find the name...
+ */
+
+ server = line + strlen(line) - 1;
+ if (*server == '\n')
+ *server = '\0';
+
+ for (server = line + 11; isspace(*server); server ++);
+
+ if (*server)
+ return (server);
+ }
+
+ /*
+ * Didn't see a ServerName line, so return "localhost"...
+ */
+
+ fclose(fp);
+
+ return ("localhost");
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/cups/util.c b/cups/util.c
new file mode 100644
index 000000000..e298499cf
--- /dev/null
+++ b/cups/util.c
@@ -0,0 +1,986 @@
+/*
+ * "$Id$"
+ *
+ * Printing utilities for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-1999 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * Contents:
+ *
+ * cupsCancelJob() - Cancel a print job.
+ * cupsDoFileRequest() - Do an IPP request...
+ * cupsGetClasses() - Get a list of printer classes.
+ * cupsGetDefault() - Get the default printer or class.
+ * cupsGetPPD() - Get the PPD file for a printer.
+ * cupsGetPrinters() - Get a list of printers.
+ * cupsPrintFile() - Print a file to a printer or class.
+ * cups_connect() - Connect to the specified host...
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "cups.h"
+#include "ipp.h"
+#include "language.h"
+#include "string.h"
+#include "debug.h"
+#include <stdlib.h>
+#include <ctype.h>
+#include <errno.h>
+#include <sys/stat.h>
+#if defined(WIN32) || defined(__EMX__)
+# include <io.h>
+#else
+# include <unistd.h>
+#endif /* WIN32 || __EMX__ */
+
+
+/*
+ * Local globals...
+ */
+
+static http_t *cups_server = NULL;
+
+
+/*
+ * Local functions...
+ */
+
+static char *cups_connect(const char *name, char *printer, char *hostname);
+
+
+/*
+ * 'cupsCancelJob()' - Cancel a print job.
+ */
+
+int /* O - 1 on success, 0 on failure */
+cupsCancelJob(const char *name, /* I - Name of printer or class */
+ int job) /* I - Job ID */
+{
+ char printer[HTTP_MAX_URI], /* Printer name */
+ hostname[HTTP_MAX_URI], /* Hostname */
+ uri[HTTP_MAX_URI]; /* Printer URI */
+ ipp_t *request, /* IPP request */
+ *response; /* IPP response */
+ cups_lang_t *language; /* Language info */
+
+
+ /*
+ * See if we can connect to the server...
+ */
+
+ if (!cups_connect(name, printer, hostname))
+ return (0);
+
+ /*
+ * Build an IPP_CANCEL_JOB request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * printer-uri
+ * job-id
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = IPP_CANCEL_JOB;
+ request->request.op.request_id = 1;
+
+ language = cupsLangDefault();
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, cupsLangEncoding(language));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL,
+ language != NULL ? language->language : "C");
+
+ sprintf(uri, "ipp://%s:%d/printers/%s", hostname, ippPort(), printer);
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
+ NULL, uri);
+
+ ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", job);
+
+ /*
+ * Do the request...
+ */
+
+ if ((response = cupsDoRequest(cups_server, request, "/jobs/")) == NULL)
+ return (0);
+
+ ippDelete(response);
+ return (1);
+}
+
+
+/*
+ * 'cupsDoFileRequest()' - Do an IPP request...
+ */
+
+ipp_t * /* O - Response data */
+cupsDoFileRequest(http_t *http, /* I - HTTP connection to server */
+ ipp_t *request, /* I - IPP request */
+ const char *resource, /* I - HTTP resource for POST */
+ const char *filename) /* I - File to send or NULL */
+{
+ ipp_t *response; /* IPP response data */
+ char length[255]; /* Content-Length field */
+ http_status_t status; /* Status of HTTP request */
+ FILE *file; /* File to send */
+ struct stat fileinfo; /* File information */
+ int bytes; /* Number of bytes read/written */
+ char buffer[8192]; /* Output buffer */
+ const char *password; /* Password string */
+ char plain[255], /* Plaintext username:password */
+ encode[255]; /* Encoded username:password */
+ static char authstring[255] = "";
+ /* Authorization string */
+
+
+ DEBUG_printf(("cupsDoFileRequest(%08x, %08s, \'%s\', \'%s\')\n",
+ http, request, resource, filename ? filename : "(null)"));
+
+ /*
+ * See if we have a file to send...
+ */
+
+ if (filename != NULL)
+ {
+ if (stat(filename, &fileinfo))
+ {
+ /*
+ * Can't get file information!
+ */
+
+ ippDelete(request);
+ return (NULL);
+ }
+
+ if ((file = fopen(filename, "rb")) == NULL)
+ {
+ /*
+ * Can't open file!
+ */
+
+ ippDelete(request);
+ return (NULL);
+ }
+ }
+
+ /*
+ * Loop until we can send the request without authorization problems.
+ */
+
+ response = NULL;
+
+ while (response == NULL)
+ {
+ DEBUG_puts("cupsDoFileRequest: setup...");
+
+ /*
+ * Setup the HTTP variables needed...
+ */
+
+ if (filename != NULL)
+ sprintf(length, "%u", ippLength(request) + (size_t)fileinfo.st_size);
+ else
+ sprintf(length, "%u", ippLength(request));
+
+ httpClearFields(http);
+ httpSetField(http, HTTP_FIELD_CONTENT_LENGTH, length);
+ httpSetField(http, HTTP_FIELD_CONTENT_TYPE, "application/ipp");
+ httpSetField(http, HTTP_FIELD_AUTHORIZATION, authstring);
+
+ /*
+ * Try the request...
+ */
+
+ DEBUG_puts("cupsDoFileRequest: post...");
+
+ if (httpPost(http, resource))
+ {
+ httpReconnect(http);
+ continue;
+ }
+
+ /*
+ * Send the IPP data and wait for the response...
+ */
+
+ DEBUG_puts("cupsDoFileRequest: ipp write...");
+
+ request->state = IPP_IDLE;
+ if (ippWrite(http, request) != IPP_ERROR)
+ if (filename != NULL)
+ {
+ DEBUG_puts("cupsDoFileRequest: file write...");
+
+ /*
+ * Send the file...
+ */
+
+ rewind(file);
+
+ while ((bytes = fread(buffer, 1, sizeof(buffer), file)) > 0)
+ if (httpWrite(http, buffer, bytes) < bytes)
+ break;
+ }
+
+ /*
+ * Get the server's return status...
+ */
+
+ DEBUG_puts("cupsDoFileRequest: update...");
+
+ while ((status = httpUpdate(http)) == HTTP_CONTINUE);
+
+ if (status == HTTP_UNAUTHORIZED)
+ {
+ DEBUG_puts("cupsDoFileRequest: unauthorized...");
+
+ /*
+ * Flush any error message...
+ */
+
+ httpFlush(http);
+
+ if ((password = cupsGetPassword("Password:")) != NULL)
+ {
+ /*
+ * Got a password; send it to the server...
+ */
+
+ if (!password[0])
+ break;
+ sprintf(plain, "%s:%s", cupsUser(), password);
+ httpEncode64(encode, plain);
+ sprintf(authstring, "Basic %s", encode);
+
+ continue;
+ }
+ else
+ break;
+ }
+
+ if (status != HTTP_OK)
+ {
+ DEBUG_printf(("cupsDoFileRequest: error %d...\n", status));
+
+ /*
+ * Flush any error message...
+ */
+
+ httpFlush(http);
+ break;
+ }
+ else
+ {
+ /*
+ * Read the response...
+ */
+
+ DEBUG_puts("cupsDoFileRequest: response...");
+
+ response = ippNew();
+
+ if (ippRead(http, response) == IPP_ERROR)
+ {
+ /*
+ * Delete the response...
+ */
+
+ ippDelete(response);
+ response = NULL;
+
+ /*
+ * Flush any remaining data...
+ */
+
+ httpFlush(http);
+ break;
+ }
+ }
+ }
+
+ /*
+ * Close the file if needed...
+ */
+
+ if (filename != NULL)
+ fclose(file);
+
+ /*
+ * Delete the original request and return the response...
+ */
+
+ ippDelete(request);
+
+ return (response);
+}
+
+
+/*
+ * 'cupsGetClasses()' - Get a list of printer classes.
+ */
+
+int /* O - Number of classes */
+cupsGetClasses(char ***classes) /* O - Classes */
+{
+ int n; /* Number of classes */
+ ipp_t *request, /* IPP Request */
+ *response; /* IPP Response */
+ ipp_attribute_t *attr; /* Current attribute */
+ cups_lang_t *language; /* Default language */
+
+
+ /*
+ * Try to connect to the server...
+ */
+
+ if (!cups_connect("default", NULL, NULL))
+ return (0);
+
+ /*
+ * Build a CUPS_GET_CLASSES request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = CUPS_GET_CLASSES;
+ request->request.op.request_id = 1;
+
+ language = cupsLangDefault();
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, cupsLangEncoding(language));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ n = 0;
+ *classes = NULL;
+
+ if ((response = cupsDoRequest(cups_server, request, "/classes/")) != NULL)
+ {
+ for (attr = response->attrs; attr != NULL; attr = attr->next)
+ if (strcmp(attr->name, "printer-name") == 0 &&
+ attr->value_tag == IPP_TAG_NAME)
+ {
+ if (n == 0)
+ *classes = malloc(sizeof(char *));
+ else
+ *classes = realloc(*classes, sizeof(char *) * (n + 1));
+
+ if (*classes == NULL)
+ {
+ ippDelete(response);
+ return (0);
+ }
+
+ (*classes)[n] = strdup(attr->values[0].string.text);
+ n ++;
+ }
+
+ ippDelete(response);
+ }
+
+ return (n);
+}
+
+
+/*
+ * 'cupsGetDefault()' - Get the default printer or class.
+ */
+
+const char * /* O - Default printer or NULL */
+cupsGetDefault(void)
+{
+ ipp_t *request, /* IPP Request */
+ *response; /* IPP Response */
+ ipp_attribute_t *attr; /* Current attribute */
+ cups_lang_t *language; /* Default language */
+ static char def_printer[64];/* Default printer */
+
+
+ /*
+ * First see if the LPDEST or PRINTER environment variables are
+ * set...
+ */
+
+ if (getenv("LPDEST") != NULL)
+ return (getenv("LPDEST"));
+ else if (getenv("PRINTER") != NULL)
+ return (getenv("PRINTER"));
+
+ /*
+ * Try to connect to the server...
+ */
+
+ if (!cups_connect("default", NULL, NULL))
+ return (NULL);
+
+ /*
+ * Build a CUPS_GET_DEFAULT request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = CUPS_GET_DEFAULT;
+ request->request.op.request_id = 1;
+
+ language = cupsLangDefault();
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, cupsLangEncoding(language));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ if ((response = cupsDoRequest(cups_server, request, "/printers/")) != NULL)
+ {
+ if ((attr = ippFindAttribute(response, "printer-name", IPP_TAG_NAME)) != NULL)
+ {
+ strcpy(def_printer, attr->values[0].string.text);
+ ippDelete(response);
+ return (def_printer);
+ }
+
+ ippDelete(response);
+ }
+
+ return (NULL);
+}
+
+
+/*
+ * 'cupsGetPPD()' - Get the PPD file for a printer.
+ */
+
+const char * /* O - Filename for PPD file */
+cupsGetPPD(const char *name) /* I - Printer name */
+{
+ FILE *fp; /* PPD file */
+ int bytes; /* Number of bytes read */
+ char buffer[8192]; /* Buffer for file */
+ char printer[HTTP_MAX_URI], /* Printer name */
+ hostname[HTTP_MAX_URI], /* Hostname */
+ resource[HTTP_MAX_URI]; /* Resource name */
+ static char filename[HTTP_MAX_URI]; /* Local filename */
+ char *tempdir; /* Temporary file directory */
+
+
+ /*
+ * See if we can connect to the server...
+ */
+
+ if (!cups_connect(name, printer, hostname))
+ return (NULL);
+
+ /*
+ * Then check for the cache file...
+ */
+
+#if defined(WIN32) || defined(__EMX__)
+ tempdir = "C:/WINDOWS/TEMP";
+
+ sprintf(filename, "%s/%s.ppd", tempdir, printer);
+#else
+ if ((tempdir = getenv("TMPDIR")) == NULL)
+ tempdir = "/tmp";
+
+ sprintf(filename, "%s/%d.%s.ppd", tempdir, getuid(), printer);
+#endif /* WIN32 || __EMX__ */
+
+ /*
+ * And send a request to the HTTP server...
+ */
+
+ sprintf(resource, "/printers/%s.ppd", printer);
+
+ httpClearFields(cups_server);
+ httpSetField(cups_server, HTTP_FIELD_HOST, hostname);
+ httpGet(cups_server, resource);
+
+ switch (httpUpdate(cups_server))
+ {
+ case HTTP_OK : /* New file - get it! */
+ break;
+ default :
+ return (NULL);
+ }
+
+ /*
+ * OK, we need to copy the file; open the file and copy it...
+ */
+
+ unlink(filename);
+ if ((fp = fopen(filename, "w")) == NULL)
+ {
+ /*
+ * Can't open file; close the server connection and return NULL...
+ */
+
+ httpClose(cups_server);
+ cups_server = NULL;
+ return (NULL);
+ }
+
+ while ((bytes = httpRead(cups_server, buffer, sizeof(buffer))) > 0)
+ fwrite(buffer, bytes, 1, fp);
+
+ fclose(fp);
+
+ return (filename);
+}
+
+
+/*
+ * 'cupsGetPrinters()' - Get a list of printers.
+ */
+
+int /* O - Number of printers */
+cupsGetPrinters(char ***printers) /* O - Printers */
+{
+ int n; /* Number of printers */
+ ipp_t *request, /* IPP Request */
+ *response; /* IPP Response */
+ ipp_attribute_t *attr; /* Current attribute */
+ cups_lang_t *language; /* Default language */
+
+
+ /*
+ * Try to connect to the server...
+ */
+
+ if (!cups_connect("default", NULL, NULL))
+ return (0);
+
+ /*
+ * Build a CUPS_GET_PRINTERS request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = CUPS_GET_PRINTERS;
+ request->request.op.request_id = 1;
+
+ language = cupsLangDefault();
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, cupsLangEncoding(language));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ n = 0;
+ *printers = NULL;
+
+ if ((response = cupsDoRequest(cups_server, request, "/printers/")) != NULL)
+ {
+ for (attr = response->attrs; attr != NULL; attr = attr->next)
+ if (strcmp(attr->name, "printer-name") == 0 &&
+ attr->value_tag == IPP_TAG_NAME)
+ {
+ if (n == 0)
+ *printers = malloc(sizeof(char *));
+ else
+ *printers = realloc(*printers, sizeof(char *) * (n + 1));
+
+ if (*printers == NULL)
+ {
+ ippDelete(response);
+ return (0);
+ }
+
+ (*printers)[n] = strdup(attr->values[0].string.text);
+ n ++;
+ }
+
+ ippDelete(response);
+ }
+
+ return (n);
+}
+
+
+/*
+ * 'cupsPrintFile()' - Print a file to a printer or class.
+ */
+
+int /* O - Job ID */
+cupsPrintFile(const char *name, /* I - Printer or class name */
+ const char *filename, /* I - File to print */
+ const char *title, /* I - Title of job */
+ int num_options,/* I - Number of options */
+ cups_option_t *options) /* I - Options */
+{
+ int i; /* Looping var */
+ int n, n2; /* Attribute values */
+ char *option, /* Name of option */
+ *val, /* Pointer to option value */
+ *s; /* Pointer into option value */
+ ipp_t *request; /* IPP request */
+ ipp_t *response; /* IPP response */
+ ipp_attribute_t *attr; /* IPP job-id attribute */
+ char hostname[HTTP_MAX_URI], /* Hostname */
+ printer[HTTP_MAX_URI], /* Printer or class name */
+ uri[HTTP_MAX_URI]; /* Printer URI */
+ cups_lang_t *language; /* Language to use */
+ int jobid; /* New job ID */
+
+
+ DEBUG_printf(("cupsPrintFile(\'%s\', \'%s\', %d, %08x)\n",
+ printer, filename, num_options, options));
+
+ if (name == NULL || filename == NULL)
+ return (0);
+
+ /*
+ * Setup a connection and request data...
+ */
+
+ if ((request = ippNew()) == NULL)
+ return (0);
+
+ if (!cups_connect(name, printer, hostname))
+ {
+ DEBUG_printf(("cupsPrintFile: Unable to open connection - %s.\n",
+ strerror(errno)));
+ ippDelete(request);
+ return (0);
+ }
+
+ /*
+ * Build a standard CUPS URI for the printer and fill the standard IPP
+ * attributes...
+ */
+
+ request->request.op.operation_id = IPP_PRINT_JOB;
+ request->request.op.request_id = 1;
+
+ sprintf(uri, "ipp://%s:%d/printers/%s", hostname, ippPort(), printer);
+
+ language = cupsLangDefault();
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, cupsLangEncoding(language));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL,
+ language != NULL ? language->language : "C");
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
+ NULL, uri);
+
+ /*
+ * Handle raw print files...
+ */
+
+ if (cupsGetOption("raw", num_options, options))
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE, "document-format",
+ NULL, "application/vnd.cups-raw");
+ else
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE, "document-format",
+ NULL, "application/octet-stream");
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
+ NULL, cupsUser());
+
+ if (title)
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL, title);
+
+ /*
+ * Then add all options on the command-line...
+ */
+
+ for (i = 0; i < num_options; i ++)
+ {
+ /*
+ * Skip the "raw" option - handled above...
+ */
+
+ if (strcmp(options[i].name, "raw") == 0)
+ continue;
+
+ /*
+ * See what the option value is; for compatibility with older interface
+ * scripts, we have to support single-argument options as well as
+ * option=value, option=low-high, and option=MxN.
+ */
+
+ option = options[i].name;
+ val = options[i].value;
+
+ if (*val == '\0')
+ val = NULL;
+
+ if (val != NULL)
+ {
+ if (strcasecmp(val, "true") == 0 ||
+ strcasecmp(val, "on") == 0 ||
+ strcasecmp(val, "yes") == 0)
+ {
+ /*
+ * Boolean value - true...
+ */
+
+ n = 1;
+ val = "";
+ }
+ else if (strcasecmp(val, "false") == 0 ||
+ strcasecmp(val, "off") == 0 ||
+ strcasecmp(val, "no") == 0)
+ {
+ /*
+ * Boolean value - false...
+ */
+
+ n = 0;
+ val = "";
+ }
+
+ n = strtol(val, &s, 0);
+ }
+ else
+ {
+ if (strncmp(option, "no", 2) == 0)
+ {
+ option += 2;
+ n = 0;
+ }
+ else
+ n = 1;
+
+ s = "";
+ }
+
+ if (*s != '\0' && *s != '-' && (*s != 'x' || s == val))
+ {
+ /*
+ * String value(s)...
+ */
+
+ DEBUG_printf(("cupsPrintFile: Adding string option \'%s\' with value \'%s\'...\n",
+ option, val));
+
+ ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, option, NULL, val);
+ }
+ else if (val != NULL)
+ {
+ /*
+ * Numeric value, range, or resolution...
+ */
+
+ if (*s == '-')
+ {
+ n2 = strtol(s + 1, NULL, 0);
+ ippAddRange(request, IPP_TAG_JOB, option, n, n2);
+
+ DEBUG_printf(("cupsPrintFile: Adding range option \'%s\' with value %d-%d...\n",
+ option, n, n2));
+ }
+ else if (*s == 'x')
+ {
+ n2 = strtol(s + 1, &s, 0);
+
+ if (strcmp(s, "dpc") == 0)
+ ippAddResolution(request, IPP_TAG_JOB, option, IPP_RES_PER_CM, n, n2);
+ else if (strcmp(s, "dpi") == 0)
+ ippAddResolution(request, IPP_TAG_JOB, option, IPP_RES_PER_INCH, n, n2);
+ else
+ ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, option, NULL, val);
+
+ DEBUG_printf(("cupsPrintFile: Adding resolution option \'%s\' with value %s...\n",
+ option, val));
+ }
+ else
+ {
+ ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_INTEGER, option, n);
+
+ DEBUG_printf(("cupsPrintFile: Adding integer option \'%s\' with value %d...\n",
+ option, n));
+ }
+ }
+ else
+ {
+ /*
+ * Boolean value...
+ */
+
+ DEBUG_printf(("cupsPrintFile: Adding boolean option \'%s\' with value %d...\n",
+ option, n));
+ ippAddBoolean(request, IPP_TAG_JOB, option, (char)n);
+ }
+ }
+
+ /*
+ * Try printing the file...
+ */
+
+ sprintf(uri, "/printers/%s", printer);
+
+ if ((response = cupsDoFileRequest(cups_server, request, uri, filename)) == NULL)
+ jobid = 0;
+ else if (response->request.status.status_code > IPP_OK_CONFLICT)
+ {
+ DEBUG_printf(("IPP response code was 0x%x!\n",
+ response->request.status.status_code));
+ jobid = 0;
+ }
+ else if ((attr = ippFindAttribute(response, "job-id", IPP_TAG_INTEGER)) == NULL)
+ {
+ DEBUG_puts("No job ID!");
+ jobid = 0;
+ }
+ else
+ jobid = attr->values[0].integer;
+
+ if (response != NULL)
+ ippDelete(response);
+
+ return (jobid);
+}
+
+
+/*
+ * 'cupsTempFile()' - Generate a temporary filename.
+ */
+
+char * /* O - Filename */
+cupsTempFile(char *filename, /* I - Pointer to buffer */
+ int len) /* I - Size of buffer */
+{
+ char *tmpdir; /* TMPDIR environment var */
+ static char buf[1024] = ""; /* Buffer if you pass in NULL and 0 */
+
+
+ /*
+ * See if a filename was specified...
+ */
+
+ if (filename == NULL)
+ {
+ filename = buf;
+ len = sizeof(buf);
+ }
+
+ /*
+ * See if TMPDIR is defined...
+ */
+
+ if ((tmpdir = getenv("TMPDIR")) == NULL)
+ tmpdir = "/var/tmp";
+
+ if ((strlen(tmpdir) + 8) > len)
+ {
+ /*
+ * The specified directory exceeds the size of the buffer; default it...
+ */
+
+ strcpy(buf, "/var/tmp/XXXXXX");
+ return (mktemp(buf));
+ }
+ else
+ {
+ /*
+ * Make the temporary name using the specified directory...
+ */
+
+ sprintf(filename, "%s/XXXXXX", tmpdir);
+ return (mktemp(filename));
+ }
+}
+
+
+/*
+ * 'cups_connect()' - Connect to the specified host...
+ */
+
+static char * /* I - Printer name or NULL */
+cups_connect(const char *name, /* I - Destination (printer[@host]) */
+ char *printer, /* O - Printer name */
+ char *hostname) /* O - Hostname */
+{
+ char hostbuf[HTTP_MAX_URI];
+ /* Name of host */
+ static char printerbuf[HTTP_MAX_URI];
+ /* Name of printer or class */
+
+
+ if (name == NULL)
+ return (NULL);
+
+ if (sscanf(name, "%[^@]@%s", printerbuf, hostbuf) == 1)
+ strcpy(hostbuf, cupsServer());
+
+ if (hostname != NULL)
+ strcpy(hostname, hostbuf);
+ else
+ hostname = hostbuf;
+
+ if (printer != NULL)
+ strcpy(printer, printerbuf);
+ else
+ printer = printerbuf;
+
+ if (cups_server != NULL)
+ {
+ if (strcasecmp(cups_server->hostname, hostname) == 0)
+ return (printer);
+
+ httpClose(cups_server);
+ }
+
+ if ((cups_server = httpConnect(hostname, ippPort())) == NULL)
+ return (NULL);
+ else
+ return (printer);
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/data/Makefile b/data/Makefile
new file mode 100644
index 000000000..b4c96b0e5
--- /dev/null
+++ b/data/Makefile
@@ -0,0 +1,56 @@
+#
+# "$Id$"
+#
+# Datafile makefile for the Common UNIX Printing System (CUPS).
+#
+# Copyright 1993-1999 by Easy Software Products.
+#
+# These coded instructions, statements, and computer programs are the
+# property of Easy Software Products and are protected by Federal
+# copyright law. Distribution and use rights are outlined in the file
+# "LICENSE.txt" which should have been included with this file. If this
+# file is missing or damaged please contact Easy Software Products
+# at:
+#
+# Attn: CUPS Licensing Information
+# Easy Software Products
+# 44141 Airport View Drive, Suite 204
+# Hollywood, Maryland 20636-3111 USA
+#
+# Voice: (301) 373-9603
+# EMail: cups-info@cups.org
+# WWW: http://www.cups.org
+#
+
+include ../Makedefs
+
+#
+# Data files...
+#
+
+FILES = 8859-1 8859-14 8859-15 8859-2 8859-3 8859-4 8859-5 \
+ 8859-6 8859-7 8859-8 8859-9 HPGLprolog
+
+#
+# Make everything...
+#
+
+all:
+
+#
+# Clean all config and object files...
+#
+
+clean:
+
+#
+# Install files...
+#
+
+install:
+ -$(MKDIR) $(DATADIR)/data
+ $(CP) $(FILES) $(DATADIR)/data
+
+#
+# End of "$Id$".
+#
diff --git a/doc/Makefile b/doc/Makefile
new file mode 100644
index 000000000..7a59f9bcc
--- /dev/null
+++ b/doc/Makefile
@@ -0,0 +1,109 @@
+#
+# "$Id$"
+#
+# Documentation makefile for the Common UNIX Printing System (CUPS).
+#
+# Copyright 1993-1999 by Easy Software Products.
+#
+# These coded instructions, statements, and computer programs are the
+# property of Easy Software Products and are protected by Federal
+# copyright law. Distribution and use rights are outlined in the file
+# "LICENSE.txt" which should have been included with this file. If this
+# file is missing or damaged please contact Easy Software Products
+# at:
+#
+# Attn: CUPS Licensing Information
+# Easy Software Products
+# 44141 Airport View Drive, Suite 204
+# Hollywood, Maryland 20636-3111 USA
+#
+# Voice: (301) 373-9603
+# EMail: cups-info@cups.org
+# WWW: http://www.cups.org
+#
+
+include ../Makedefs
+
+#
+# HTMLDOC generation rules...
+#
+
+.SUFFIXES: .html .pdf .shtml
+.shtml.html:
+ echo Formatting $@...
+ htmldoc --title images/cups-large.gif --numbered -f $@ $<
+.shtml.pdf:
+ echo Formatting $@...
+ htmldoc --title images/cups-large.gif --duplex --compression=9 \
+ --numbered --jpeg -f $@ $<
+
+#
+# Document files...
+#
+
+DOCUMENTS = cmp.shtml idd.shtml sam.shtml sdd.shtml ssr.shtml \
+ stp.shtml sum.shtml svd.shtml
+DOCIMAGES = images/cups-block-diagram.gif images/cups-large.gif \
+ images/cups-medium.gif images/cups-small.gif
+WEBPAGES = cups.css cupsdoc.css index.html documentation.html
+WEBIMAGES = images/classes.gif images/logo.gif images/navbar.gif \
+ images/printer-idle.gif images/printer-processing.gif \
+ images/printer-stopped.gif
+
+#
+#
+# Make all documents...
+#
+
+all: $(DOCUMENTS:.shtml=.pdf) $(DOCUMENTS:.shtml=.html) overview.pdf
+
+#
+# Remove all generated files...
+#
+
+clean:
+ $(RM) $(DOCUMENTS:.shtml=.pdf)
+ $(RM) $(DOCUMENTS:.shtml=.html)
+ $(RM) overview.pdf
+
+#
+# Install all documentation files...
+#
+
+install:
+ -$(MKDIR) $(DATADIR)/doc
+ $(CP) $(WEBPAGES) $(DATADIR)/doc
+ $(CP) overview.html overview.pdf $(DATADIR)/doc
+ $(CP) $(DOCUMENTS:.shtml=.html) $(DATADIR)/doc
+ $(CP) $(DOCUMENTS:.shtml=.pdf) $(DATADIR)/doc
+ -$(MKDIR) $(DATADIR)/doc/images
+ $(CP) $(WEBIMAGES) $(DATADIR)/doc/images
+ $(CP) $(DOCIMAGES) $(DATADIR)/doc/images
+
+#
+# The overview, admin guide, and user's guide get special attention...
+#
+
+overview.pdf: overview.html
+ echo Formatting $@...
+ htmldoc --duplex --compression=9 --jpeg --webpage -f $@ $<
+
+sam.html: sam.shtml
+ echo Formatting $@...
+ htmldoc --title images/cups-large.gif -f $@ $<
+sam.pdf: sam.shtml
+ echo Formatting $@...
+ htmldoc --title images/cups-large.gif --duplex --compression=9 \
+ --jpeg -f $@ $<
+
+sum.html: sum.shtml
+ echo Formatting $@...
+ htmldoc --title images/cups-large.gif -f $@ $<
+sum.pdf: sum.shtml
+ echo Formatting $@...
+ htmldoc --title images/cups-large.gif --duplex --compression=9 \
+ --jpeg -f $@ $<
+
+#
+# End of Makefile.
+#
diff --git a/doc/cmp.html b/doc/cmp.html
new file mode 100644
index 000000000..2ed695324
--- /dev/null
+++ b/doc/cmp.html
@@ -0,0 +1,651 @@
+<HTML>
+<HEAD>
+<TITLE>CUPS Configuration Management Plan</TITLE>
+<META NAME="AUTHOR" CONTENT="Easy Software Products">
+<META NAME="COPYRIGHT" CONTENT="Copyright 1997-1999, All Rights Reserved">
+<META NAME="DOCNUMBER" CONTENT="CUPS-CMP-1.0">
+</HEAD>
+<BODY>
+<CENTER><A HREF=#contents><IMG SRC="images/cups-large.gif" BORDER=0><BR>
+<H1>CUPS Configuration Management Plan</H1></A><BR>
+CUPS-CMP-1.0<BR>
+Easy Software Products<BR>
+Copyright 1997-1999, All Rights Reserved<BR>
+</CENTER>
+<HR>
+<H1 ALIGN=CENTER><A NAME=CONTENTS>Table of Contents</A></H1>
+<BR>
+<BR><B><A HREF=#1>1 Scope</A></B>
+<UL>
+<LI><A HREF=#1_1>1.1 Identification</A></LI>
+<LI><A HREF=#1_2>1.2 System Overview</A></LI>
+<LI><A HREF=#1_3>1.3 Document Overview</A></LI>
+</UL>
+<B><A HREF=#2>2 References</A></B>
+<UL>
+<LI><A HREF=#2_1>2.1 CUPS Documentation</A></LI>
+<LI><A HREF=#2_2>2.2 Other Documents</A></LI>
+</UL>
+<B><A HREF=#3>3 File Management</A></B>
+<UL>
+<LI><A HREF=#3_1>3.1 Directory Structure</A></LI>
+<LI><A HREF=#3_2>3.2 Source Files</A></LI>
+<LI><A HREF=#3_3>3.3 Configuration Management</A></LI>
+</UL>
+<B><A HREF=#4>4 Trouble Report Processing</A></B>
+<UL>
+<LI><A HREF=#4_1>4.1 Classification</A></LI>
+<LI><A HREF=#4_2>4.2 Identification</A></LI>
+<LI><A HREF=#4_3>4.3 Correction</A></LI>
+<LI><A HREF=#4_4>4.4 Notification</A></LI>
+</UL>
+<B><A HREF=#5>5 Software Releases</A></B>
+<UL>
+<LI><A HREF=#5_1>5.1 Version Numbering</A></LI>
+<LI><A HREF=#5_2>5.2 Generation</A></LI>
+<LI><A HREF=#5_3>5.3 Testing</A></LI>
+<LI><A HREF=#5_4>5.4 Release</A></LI>
+</UL>
+<B><A HREF=#6>A Glossary</A></B>
+<UL>
+<LI><A HREF=#6_1>A.1 Terms</A></LI>
+<LI><A HREF=#6_2>A.2 Acronyms</A></LI>
+</UL>
+<B><A HREF=#7>B Coding Requirements</A></B>
+<UL>
+<LI><A HREF=#7_1>B.1 Source Files</A></LI>
+<UL>
+<LI><A HREF=#7_1_1>B.1.1 Naming</A></LI>
+<LI><A HREF=#7_1_2>B.1.2 Documentation</A></LI>
+</UL>
+<LI><A HREF=#7_2>B.2 Functions</A></LI>
+<UL>
+<LI><A HREF=#7_2_1>B.2.1 Naming</A></LI>
+<LI><A HREF=#7_2_2>B.2.2 Documentation</A></LI>
+</UL>
+<LI><A HREF=#7_3>B.3 Methods</A></LI>
+<UL>
+<LI><A HREF=#7_3_1>B.3.1 Naming</A></LI>
+<LI><A HREF=#7_3_2>B.3.2 Documentation</A></LI>
+</UL>
+<LI><A HREF=#7_4>B.4 Variables</A></LI>
+<UL>
+<LI><A HREF=#7_4_1>B.4.1 Naming</A></LI>
+<LI><A HREF=#7_4_2>B.4.2 Documentation</A></LI>
+</UL>
+<LI><A HREF=#7_5>B.5 Types</A></LI>
+<UL>
+<LI><A HREF=#7_5_1>B.5.1 Naming</A></LI>
+<LI><A HREF=#7_5_2>B.5.2 Documentation</A></LI>
+</UL>
+<LI><A HREF=#7_6>B.6 Structures</A></LI>
+<UL>
+<LI><A HREF=#7_6_1>B.6.1 Naming</A></LI>
+<LI><A HREF=#7_6_2>B.6.2 Documentation</A></LI>
+</UL>
+<LI><A HREF=#7_7>B.7 Classes</A></LI>
+<UL>
+<LI><A HREF=#7_7_1>B.7.1 Naming</A></LI>
+<LI><A HREF=#7_7_2>B.7.2 Documentation</A></LI>
+</UL>
+<LI><A HREF=#7_8>B.8 Constants</A></LI>
+<UL>
+<LI><A HREF=#7_8_1>B.8.1 Naming</A></LI>
+<LI><A HREF=#7_8_2>B.8.2 Documentation</A></LI>
+</UL>
+<LI><A HREF=#7_9>B.9 Code</A></LI>
+<UL>
+<LI><A HREF=#7_9_1>B.9.1 Documentation</A></LI>
+<LI><A HREF=#7_9_2>B.9.2 Style</A></LI>
+</UL>
+</UL>
+<B><A HREF=#8>C Software Trouble Report Form</A></B><HR>
+<H1><A NAME=1>1 Scope</A></H1>
+<H2><A NAME=1_1>1.1 Identification</A></H2>
+ This configuration management plan document provides the guidelines
+for development and maintainance of the Common UNIX Printing System
+(&quot;CUPS&quot;) Version 1.0 software.
+<H2><A NAME=1_2>1.2 System Overview</A></H2>
+ The Common UNIX Printing System provides a portable printing layer for
+ UNIX&reg; operating systems. It has been developed by Easy Software
+ Products to promote a standard printing solution for all UNIX vendors
+ and users. CUPS provides the System V and Berkeley command-line
+interfaces.
+<P>CUPS uses the Internet Printing Protocol (IETF-IPP) as the basis for
+managing print jobs and queues. The Line Printer Daemon (LPD, RFC1179),
+Server Message Block (SMB), and AppSocket protocols are also supported
+with reduced functionality. </P>
+<P>CUPS adds network printer browsing and PostScript Printer
+Description (&quot;PPD&quot;)-based printing options to support real world
+applications under UNIX. </P>
+<P>CUPS also includes a customized version of GNU GhostScript
+(currently based off GNU GhostScript 4.03) and an image file RIP that
+can be used to support non-PostScript printers. </P>
+<H2><A NAME=1_3>1.3 Document Overview</A></H2>
+ This configuration management document is organized into the following
+sections:
+<UL>
+<LI>1 - Scope</LI>
+<LI>2 - References</LI>
+<LI>3 - File Management</LI>
+<LI>4 - Trouble Report Processing</LI>
+<LI>5 - Software Releases</LI>
+<LI>A - Glossary</LI>
+<LI>B - Coding Requirements</LI>
+</UL>
+<H1><A NAME=2>2 References</A></H1>
+<H2><A NAME=2_1>2.1 CUPS Documentation</A></H2>
+ The following CUPS documentation is referenced by this document:
+<UL>
+<LI>CUPS-CMP-1.0: CUPS Configuration Management Plan </LI>
+<LI>CUPS-IDD-1.0: CUPS System Interface Design Description </LI>
+<LI>CUPS-SAM-1.0.x: CUPS Software Administrators Manual </LI>
+<LI>CUPS-SDD-1.0: CUPS Software Design Description </LI>
+<LI>CUPS-SPM-1.0: CUPS Software Programming Manual </LI>
+<LI>CUPS-SSR-1.0: CUPS Software Security Report </LI>
+<LI>CUPS-STP-1.0: CUPS Software Test Plan </LI>
+<LI>CUPS-SUM-1.0.x: CUPS Software Users Manual </LI>
+<LI>CUPS-SVD-1.0.x: CUPS Software Version Description </LI>
+</UL>
+<H2><A NAME=2_2>2.2 Other Documents</A></H2>
+ The following non-CUPS documents are referenced by this document:
+<UL>
+<LI>IEEE 1387.4, System Administration: Printing (draft) </LI>
+<LI>IPP/1.0: Additional Optional Operations - Set 1 </LI>
+<LI>IPP/1.0: Encoding and Transport </LI>
+<LI>IPP/1.0: Implementers Guide </LI>
+<LI>IPP/1.0: Model and Semantics </LI>
+<LI>RFC 1179, Line Printer Daemon Protocol </LI>
+</UL>
+<H1><A NAME=3>3 File Management</A></H1>
+<H2><A NAME=3_1>3.1 Directory Structure</A></H2>
+ Each source file shall be placed a sub-directory corresponding to the
+software sub-system it belongs to (&quot;scheduler&quot;, &quot;libcups&quot;, etc.) To
+remain compatible with older UNIX filesystems, directory names shall
+not exceed 16 characters in length.
+<H2><A NAME=3_2>3.2 Source Files</A></H2>
+ Source files shall be documented and formatted as described in
+Appendix B, Coding Requirements.
+<H2><A NAME=3_3>3.3 Configuration Management</A></H2>
+ Source files shall be placed under the control of the Concurrent
+Versions System (&quot;CVS&quot;) software. Source files shall be &quot;checked in&quot;
+with each change so that modifications can be tracked.
+<P>Documentation on the CVS software is included with the whitepaper,
+&quot;CVS II: Parallelizing Software Development&quot;. </P>
+<H1><A NAME=4>4 Trouble Report Processing</A></H1>
+ A Software Trouble Report (&quot;STR&quot;) shall be submitted every time a user
+or vendor experiences a problem with the CUPS software. Trouble reports
+are maintained in a database with one of the following states:
+<OL>
+<LI>STR is closed with complete resolution</LI>
+<LI>STR is closed without resolution</LI>
+<LI>STR is active</LI>
+<LI>STR is pending (new STR or additional information available)</LI>
+</OL>
+ Trouble reports shall be processed using the following steps.
+<H2><A NAME=4_1>4.1 Classification</A></H2>
+ When a trouble report is received it must be classified at one of the
+following levels:
+<OL>
+<LI>Request for enhancement</LI>
+<LI>Documentation error</LI>
+<LI>Unable to print a file</LI>
+<LI>Unable to print to a printer</LI>
+<LI>Unable to print at all</LI>
+</OL>
+ The scope of the problem should also be determined as:
+<OL>
+<LI>Specific to a machine</LI>
+<LI>Specific to an operating system</LI>
+<LI>Applies to all machines and operating systems</LI>
+</OL>
+<H2><A NAME=4_2>4.2 Identification</A></H2>
+ Once the level and scope of the trouble report is determined the
+software sub-system(s) involved with the problem are determined. This
+may involve additional communication with the user or vendor to isolate
+the problem to a specific cause.
+<P>When the sub-system(s) involved have been identified, an engineer
+will then determine the change(s) needed and estimate the time required
+for the change(s). </P>
+<H2><A NAME=4_3>4.3 Correction</A></H2>
+ Corrections are scheduled based upon the severity and complexity of
+the problem. Once all changes have been made, documented, and tested
+successfully a new software release snapshot is generated. Additional
+tests are added as necessary for proper testing of the changes.
+<H2><A NAME=4_4>4.4 Notification</A></H2>
+ The user or vendor is notified when the fix is available or if the
+problem was caused by user error.
+<H1><A NAME=5>5 Software Releases</A></H1>
+<H2><A NAME=5_1>5.1 Version Numbering</A></H2>
+ CUPS uses a three-part version number separated by periods to
+represent the major, minor, and patch release numbers:
+<UL>
+<PRE>
+major.minor.patch
+1.0.0
+</PRE>
+</UL>
+ Beta-test releases are indentified by appending the letter B followed
+by the build number:
+<UL>
+<PRE>
+major.minor.patchbbuild
+1.0.0b1
+</PRE>
+</UL>
+ A CVS snapshot is generated for every beta and final release and uses
+the version number preceded by the letter &quot;v&quot; and with the decimal
+points replaced by underscores:
+<UL>
+<PRE>
+v1_0_0b1
+v1_0_0
+</PRE>
+</UL>
+ Each change that corrects a fault in a software sub-system increments
+the patch release number. If a change affects the software design of
+CUPS then the minor release number will be incremented and the patch
+release number reset to 0. If CUPS is completely redesigned the major
+release number will be incremented and the minor and patch release
+numbers reset to 0:
+<UL>
+<PRE>
+1.0.0b1 First beta release
+1.0.0b2 Second beta release
+1.0.0 First production release
+1.0.1b1 First beta of 1.0.1
+1.0.1 Production release of 1.0.1
+1.1.0b1 First beta of 1.1.0
+1.1.0 Production release of 1.1.0
+2.0.0b1 First beta of 2.0.0
+2.0.0 Production release of 2.0.0
+</PRE>
+</UL>
+<H2><A NAME=5_2>5.2 Generation</A></H2>
+ Software releases shall be generated for each successfully completed
+software trouble report. All object and executable files shall be
+deleted prior to performing a full build to ensure that source files
+are recompiled.
+<H2><A NAME=5_3>5.3 Testing</A></H2>
+ Software testing shall be conducted according to the CUPS Software
+Test Plan, CUPS-STP-1.0. Failed tests cause STRs to be generated to
+correct the problems found.
+<H2><A NAME=5_4>5.4 Release</A></H2>
+ When testing has been completed successfully a new distribution image
+is created from the current CVS code &quot;snapshot&quot;. No production release
+shall contain software that has not passed the appropriate software
+tests.
+<H1 TYPE=A VALUE=1><A NAME=6>A Glossary</A></H1>
+<H2><A NAME=6_1>A.1 Terms</A></H2>
+<DL>
+<DT>C </DT>
+<DD>A computer language. </DD>
+<DT>parallel </DT>
+<DD>Sending or receiving data more than 1 bit at a time. </DD>
+<DT>pipe </DT>
+<DD>A one-way communications channel between two programs. </DD>
+<DT>serial </DT>
+<DD>Sending or receiving data 1 bit at a time. </DD>
+<DT>socket </DT>
+<DD>A two-way network communications channel. </DD>
+</DL>
+<H2><A NAME=6_2>A.2 Acronyms</A></H2>
+<DL>
+<DT>ASCII </DT>
+<DD>American Standard Code for Information Interchange </DD>
+<DT>CUPS </DT>
+<DD>Common UNIX Printing System </DD>
+<DT>ESC/P </DT>
+<DD>EPSON Standard Code for Printers </DD>
+<DT>FTP </DT>
+<DD>File Transfer Protocol </DD>
+<DT>HP-GL </DT>
+<DD>Hewlett-Packard Graphics Language </DD>
+<DT>HP-PCL </DT>
+<DD>Hewlett-Packard Printer Control Language </DD>
+<DT>HP-PJL </DT>
+<DD>Hewlett-Packard Printer Job Language </DD>
+<DT>IETF </DT>
+<DD>Internet Engineering Task Force </DD>
+<DT>IPP </DT>
+<DD>Internet Printing Protocol </DD>
+<DT>ISO </DT>
+<DD>International Standards Organization </DD>
+<DT>LPD </DT>
+<DD>Line Printer Daemon </DD>
+<DT>MIME </DT>
+<DD>Multimedia Internet Mail Exchange </DD>
+<DT>PCL </DT>
+<DD>Page Control Language </DD>
+<DT>PPD </DT>
+<DD>PostScript Printer Description </DD>
+<DT>SMB </DT>
+<DD>Server Message Block </DD>
+<DT>TFTP </DT>
+<DD>Trivial File Transfer Protocol </DD>
+</DL>
+<H1><A NAME=7>B Coding Requirements</A></H1>
+ These coding requirements provide detailed information on source file
+formatting and documentation content. These guidelines shall be applied
+to all C and C++ source files provided with CUPS.
+<H2><A NAME=7_1>B.1 Source Files</A></H2>
+<H3><A NAME=7_1_1>B.1.1 Naming</A></H3>
+ All source files names shall be 16 characters or less in length to
+ensure compatibility with older UNIX filesystems. Source files
+containing functions shall have an extension of &quot;.c&quot; for ANSI C and
+&quot;.cpp&quot; for C++ source files. All other &quot;include&quot; files shall have an
+extension of &quot;.h&quot;.
+<H3><A NAME=7_1_2>B.1.2 Documentation</A></H3>
+ The top of each source file shall contain a header giving the name of
+the file, the purpose or nature of the source file, the copyright and
+licensing notice, and the functions contained in the file. The file
+name and revision information is provided by the CVS &quot;$Id: cmp.shtml,v
+1.3 1999/05/21 20:54:04 mike Exp $&quot; tag:
+<UL>
+<PRE>
+/*
+ * &quot;$Id$&quot;
+ *
+ * Description of file contents.
+ *
+ * Copyright 1997-1999 by Easy Software Products, all rights
+ * reserved.
+ *
+ * These coded instructions, statements, and computer programs are
+ * the property of Easy Software Products and are protected by
+ * Federal copyright law. Distribution and use rights are outlined
+ * in the file &quot;LICENSE.txt&quot; which should have been included with
+ * this file. If this file is missing or damaged please contact
+ * Easy Software Products at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44145 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * Contents:
+ *
+ * function1() - Description 1.
+ * function2() - Description 2.
+ * function3() - Description 3.
+ */
+</PRE>
+</UL>
+ The bottom of each source file shall contain a trailer giving the name
+of the file using the CVS &quot;$Id: cmp.shtml,v 1.3 1999/05/21 20:54:04
+mike Exp $&quot; tag. The primary purpose of this is to mark the end of a
+source file; if the trailer is missing it is possible that code has
+been lost near the end of the file:
+<UL>
+<PRE>
+/*
+ * End of &quot;$Id$&quot;.
+ */
+</PRE>
+</UL>
+<H2><A NAME=7_2>B.2 Functions</A></H2>
+<H3><A NAME=7_2_1>B.2.1 Naming</A></H3>
+ Functions with a global scope shall be capitalized (&quot;DoThis&quot;,
+&quot;DoThat&quot;, &quot;DoSomethingElse&quot;, etc.) The only exception to this rule
+shall be the CUPS interface library functions which may begin with a
+prefix word in lowercase (&quot;cupsDoThis&quot;, &quot;cupsDoThat&quot;, etc.)
+<P>Functions with a local scope shall be declared &quot;static&quot; and be
+lowercase with underscores between words (&quot;do_this&quot;, &quot;do_that&quot;,
+&quot;do_something_else&quot;, etc.) </P>
+<H3><A NAME=7_2_2>B.2.2 Documentation</A></H3>
+ Each function shall begin with a comment header describing what the
+function does, the possible input limits (if any), and the possible
+output values (if any), and any special information needed:
+<UL>
+<PRE>
+/*
+ * 'do_this()' - Compute y = this(x).
+ *
+ * Notes: none.
+ */
+
+static float /* O - Inverse power value, 0.0 &lt;= y &lt;= 1.0 */
+do_this(float x) /* I - Power value (0.0 &lt;= x &lt;= 1.0) */
+{
+ ...
+ return (y);
+}
+</PRE>
+</UL>
+<H2><A NAME=7_3>B.3 Methods</A></H2>
+<H3><A NAME=7_3_1>B.3.1 Naming</A></H3>
+ Methods shall be in lowercase with underscores between words
+(&quot;do_this&quot;, &quot;do_that&quot;, &quot;do_something_else&quot;, etc.)
+<H3><A NAME=7_3_2>B.3.2 Documentation</A></H3>
+ Each method shall begin with a comment header describing what the
+method does, the possible input limits (if any), and the possible
+output values (if any), and any special information needed:
+<UL>
+<PRE>
+/*
+ * 'class::do_this()' - Compute y = this(x).
+ *
+ * Notes: none.
+ */
+
+float /* O - Inverse power value, 0.0 &lt;= y &lt;= 1.0 */
+class::do_this(float x) /* I - Power value (0.0 &lt;= x &lt;= 1.0) */
+{
+ ...
+ return (y);
+}
+</PRE>
+</UL>
+<H2><A NAME=7_4>B.4 Variables</A></H2>
+<H3><A NAME=7_4_1>B.4.1 Naming</A></H3>
+ Variables with a global scope shall be capitalized (&quot;ThisVariable&quot;,
+&quot;ThatVariable&quot;, &quot;ThisStateVariable&quot;, etc.) The only exception to this
+rule shall be the CUPS interface library global variables which must
+begin with the prefix &quot;cups&quot; (&quot;cupsThisVariable&quot;, &quot;cupsThatVariable&quot;,
+etc.) Global variables shall be replaced by function arguments whenever
+possible.
+<P>Variables with a local scope shall be lowercase with underscores
+between words (&quot;this_variable&quot;, &quot;that_variable&quot;, etc.) Any local
+variables shared by functions within a source file shall be declared
+&quot;static&quot;. </P>
+<H3><A NAME=7_4_2>B.4.2 Documentation</A></H3>
+ Each variable shall be declared on a separate line and shall be
+immediately followed by a comment block describing the variable:
+<UL>
+<PRE>
+int this_variable; /* The current state of this */
+int that_variable; /* The current state of that */
+</PRE>
+</UL>
+<H2><A NAME=7_5>B.5 Types</A></H2>
+<H3><A NAME=7_5_1>B.5.1 Naming</A></H3>
+ All type names shall be lowercase with underscores between words and
+&quot;_t&quot; appended to the end of the name (&quot;this_type_t&quot;, &quot;that_type_t&quot;,
+etc.)
+<H3><A NAME=7_5_2>B.5.2 Documentation</A></H3>
+ Each type shall have a comment block immediately before the typedef:
+<UL>
+<PRE>
+/*
+ * This type is for CUPS foobar options.
+ */
+typedef int cups_this_type_t;
+</PRE>
+</UL>
+<H2><A NAME=7_6>B.6 Structures</A></H2>
+<H3><A NAME=7_6_1>B.6.1 Naming</A></H3>
+ All structure names shall be lowercase with underscores between words
+and &quot;_str&quot; appended to the end of the name (&quot;this_struct_str&quot;,
+&quot;that_struct_str&quot;, etc.)
+<H3><A NAME=7_6_2>B.6.2 Documentation</A></H3>
+ Each structure shall have a comment block immediately before the
+struct and each member shall be documented in accordance with the
+variable naming policy above:
+<UL>
+<PRE>
+/*
+ * This structure is for CUPS foobar options.
+ */
+struct cups_this_struct_str
+{
+ int this_member; /* Current state for this */
+ int that_member; /* Current state for that */
+};
+</PRE>
+</UL>
+<H2><A NAME=7_7>B.7 Classes</A></H2>
+<H3><A NAME=7_7_1>B.7.1 Naming</A></H3>
+ All class names shall be lowercase with underscores between words
+(&quot;this_class&quot;, &quot;that_class&quot;, etc.)
+<H3><A NAME=7_7_2>B.7.2 Documentation</A></H3>
+ Each class shall have a comment block immediately before the class and
+each member shall be documented in accordance with the variable naming
+policy above:
+<UL>
+<PRE>
+/*
+ * This class is for CUPS foobar options.
+ */
+class cups_this_class
+{
+ int this_member; /* Current state for this */
+ int that_member; /* Current state for that */
+};
+</PRE>
+</UL>
+<H2><A NAME=7_8>B.8 Constants</A></H2>
+<H3><A NAME=7_8_1>B.8.1 Naming</A></H3>
+ All constant names shall be uppercase with underscored between words
+(&quot;THIS_CONSTANT&quot;, &quot;THAT_CONSTANT&quot;, etc.) Constants defined for the CUPS
+interface library must begin with an uppercase prefix
+(&quot;CUPS_THIS_CONSTANT&quot;, &quot;CUPS_THAT_CONSTANT&quot;, etc.)
+<P>Typed enumerations shall be used whenever possible to allow for type
+checking by the compiler. </P>
+<H3><A NAME=7_8_2>B.8.2 Documentation</A></H3>
+ Comment blocks shall immediately follow each constant:
+<UL>
+<PRE>
+enum
+{
+ CUPS_THIS_TRAY, /* This tray */
+ CUPS_THAT_TRAY /* That tray */
+};
+</PRE>
+</UL>
+<H2><A NAME=7_9>B.9 Code</A></H2>
+<H3><A NAME=7_9_1>B.9.1 Documentation</A></H3>
+ All source code shall utilize block comments within functions to
+describe the operations being performed by a group of statements:
+<UL>
+<PRE>
+/*
+ * Clear the state array before we begin...
+ */
+
+for (i = 0; i &lt; (sizeof(array) / sizeof(sizeof(array[0])); i ++)
+ array[i] = STATE_IDLE;
+
+/*
+ * Wait for state changes...
+ */
+
+do
+{
+ for (i = 0; i &lt; (sizeof(array) / sizeof(sizeof(array[0])); i ++)
+ if (array[i] != STATE_IDLE)
+ break;
+
+ if (i == (sizeof(array) / sizeof(array[0])))
+ sleep(1);
+} while (i == (sizeof(array) / sizeof(array[0])));
+</PRE>
+</UL>
+<H3><A NAME=7_9_2>B.9.2 Style</A></H3>
+<H4 TYPE=a>B.9.2.a Indentation</H4>
+ All code blocks enclosed by brackets shall begin with the opening
+brace on a new line. The code then follows starting on a new line after
+the brace and is indented 2 spaces. The closing brace is then placed on
+a new line following the code at the original indentation:
+<UL>
+<PRE>
+{
+ int i; /* Looping var */
+
+ /*
+ * Process foobar values from 0 to 999...
+ */
+
+ for (i = 0; i &lt; 1000; i ++)
+ {
+ do_this(i);
+ do_that(i);
+ }
+}
+</PRE>
+</UL>
+ Single-line statements following &quot;do&quot;, &quot;else&quot;, &quot;for&quot;, &quot;if&quot;, and
+&quot;while&quot; shall be indented 2 spaces as well. Blocks of code in a
+&quot;switch&quot; block shall be indented 4 spaces after each &quot;case&quot; and
+&quot;default&quot; case:
+<UL>
+<PRE>
+switch (array[i])
+{
+ case STATE_IDLE :
+ do_this(i);
+ do_that(i);
+ break;
+ default :
+ do_nothing(i);
+ break;
+}
+</PRE>
+</UL>
+<H4>B.9.2.b Spacing</H4>
+ A space shall follow each reserved word (&quot;if&quot;, &quot;while&quot;, etc.) Spaces
+shall not be inserted between a function name and the arguments in
+parenthesis.
+<H4>B.9.2.c Return Values</H4>
+ Parenthesis shall surround values returned from a function using
+&quot;return&quot;:
+<UL>
+<PRE>
+return (STATE_IDLE);
+</PRE>
+</UL>
+<H4>B.9.2.d Loops</H4>
+ Whenever convenient loops should count downward to zero to improve
+program performance:
+<UL>
+<PRE>
+for (i = sizeof(array) / sizeof(array[0]) - 1; i &gt;= 0; i --)
+ array[i] = STATE_IDLE;
+</PRE>
+</UL>
+<H1 ALIGN=RIGHT><A NAME=8>C Software Trouble Report Form</A></H1>
+<CENTER>
+<TABLE WIDTH=80%>
+<TR><TH ALIGN=RIGHT>Summary of Problem:</TH><TD ALIGN=LEFT>
+________________________________________</TD></TR>
+<TR><TH ALIGN=RIGHT>Problem Severity:</TH><TD ALIGN=LEFT>__1=RFE
+<BR>__2=Documentation-Error
+<BR>__3=Unable-to-Print-a-File
+<BR>__4=Unable-to-Print-to-a-Printer
+<BR>__5=Unable-to-Print-at-All</TD></TR>
+<TR><TH ALIGN=RIGHT>Problem Scope:</TH><TD ALIGN=LEFT>__1=Machine
+__2=Operating-System __3=All</TD></TR>
+<TR><TH ALIGN=RIGHT VALIGN=TOP>Detailed Description of Problem:</TH><TD ALIGN=LEFT>
+________________________________________
+<BR>________________________________________
+<BR>________________________________________
+<BR>________________________________________
+<BR>________________________________________
+<BR>________________________________________</TD></TR>
+</TABLE>
+</CENTER>
+</BODY>
+</HTML>
diff --git a/doc/cmp.pdf b/doc/cmp.pdf
new file mode 100644
index 000000000..95d7b965d
--- /dev/null
+++ b/doc/cmp.pdf
@@ -0,0 +1,963 @@
+%PDF-1.2
+%âãÏÓ
+1 0 obj<</Producer(htmldoc 2.0b1 Copyright 1997-1999 Michael Sweet, All Rights Reserved.)/CreationDate(D:19990521210307Z)/Title(CUPS Configuration Management Plan)/Author(Easy Software Products)>>endobj
+2 0 obj<</Type/Encoding/BaseEncoding/WinAnsiEncoding>>endobj
+3 0 obj<</Type/Font/Subtype/Type1/BaseFont/Courier/Encoding 2 0 R>>endobj
+4 0 obj<</Type/Font/Subtype/Type1/BaseFont/Times-Roman/Encoding 2 0 R>>endobj
+5 0 obj<</Type/Font/Subtype/Type1/BaseFont/Times-Bold/Encoding 2 0 R>>endobj
+6 0 obj<</Type/Font/Subtype/Type1/BaseFont/Times-Italic/Encoding 2 0 R>>endobj
+7 0 obj<</Type/Font/Subtype/Type1/BaseFont/Helvetica/Encoding 2 0 R>>endobj
+8 0 obj<</Type/Font/Subtype/Type1/BaseFont/Helvetica-Bold/Encoding 2 0 R>>endobj
+9 0 obj<</Type/Font/Subtype/Type1/BaseFont/Symbol>>endobj
+10 0 obj<</Subtype/Link/Rect[72.0 673.2 80.2 686.2]/Border[0 0 0]/Dest[198 0 R/XYZ null 818 0]>>endobj
+11 0 obj<</Subtype/Link/Rect[80.2 673.2 107.8 686.2]/Border[0 0 0]/Dest[198 0 R/XYZ null 818 0]>>endobj
+12 0 obj<</Subtype/Link/Rect[108.0 660.0 124.5 673.0]/Border[0 0 0]/Dest[198 0 R/XYZ null 737 0]>>endobj
+13 0 obj<</Subtype/Link/Rect[124.5 660.0 183.8 673.0]/Border[0 0 0]/Dest[198 0 R/XYZ null 737 0]>>endobj
+14 0 obj<</Subtype/Link/Rect[108.0 646.8 124.5 659.8]/Border[0 0 0]/Dest[198 0 R/XYZ null 658 0]>>endobj
+15 0 obj<</Subtype/Link/Rect[124.5 646.8 159.6 659.8]/Border[0 0 0]/Dest[198 0 R/XYZ null 658 0]>>endobj
+16 0 obj<</Subtype/Link/Rect[159.6 646.8 203.0 659.8]/Border[0 0 0]/Dest[198 0 R/XYZ null 658 0]>>endobj
+17 0 obj<</Subtype/Link/Rect[108.0 633.6 124.5 646.6]/Border[0 0 0]/Dest[198 0 R/XYZ null 434 0]>>endobj
+18 0 obj<</Subtype/Link/Rect[124.5 633.6 173.1 646.6]/Border[0 0 0]/Dest[198 0 R/XYZ null 434 0]>>endobj
+19 0 obj<</Subtype/Link/Rect[173.1 633.6 216.4 646.6]/Border[0 0 0]/Dest[198 0 R/XYZ null 434 0]>>endobj
+20 0 obj<</Subtype/Link/Rect[72.0 607.2 80.2 620.2]/Border[0 0 0]/Dest[204 0 R/XYZ null 818 0]>>endobj
+21 0 obj<</Subtype/Link/Rect[80.2 607.2 131.6 620.2]/Border[0 0 0]/Dest[204 0 R/XYZ null 818 0]>>endobj
+22 0 obj<</Subtype/Link/Rect[108.0 594.0 124.5 607.0]/Border[0 0 0]/Dest[204 0 R/XYZ null 737 0]>>endobj
+23 0 obj<</Subtype/Link/Rect[124.5 594.0 154.8 607.0]/Border[0 0 0]/Dest[204 0 R/XYZ null 737 0]>>endobj
+24 0 obj<</Subtype/Link/Rect[154.8 594.0 222.6 607.0]/Border[0 0 0]/Dest[204 0 R/XYZ null 737 0]>>endobj
+25 0 obj<</Subtype/Link/Rect[108.0 580.8 124.5 593.8]/Border[0 0 0]/Dest[204 0 R/XYZ null 540 0]>>endobj
+26 0 obj<</Subtype/Link/Rect[124.5 580.8 152.3 593.8]/Border[0 0 0]/Dest[204 0 R/XYZ null 540 0]>>endobj
+27 0 obj<</Subtype/Link/Rect[152.3 580.8 202.4 593.8]/Border[0 0 0]/Dest[204 0 R/XYZ null 540 0]>>endobj
+28 0 obj<</Subtype/Link/Rect[72.0 554.4 80.2 567.4]/Border[0 0 0]/Dest[210 0 R/XYZ null 818 0]>>endobj
+29 0 obj<</Subtype/Link/Rect[80.2 554.4 100.7 567.4]/Border[0 0 0]/Dest[210 0 R/XYZ null 818 0]>>endobj
+30 0 obj<</Subtype/Link/Rect[100.7 554.4 162.4 567.4]/Border[0 0 0]/Dest[210 0 R/XYZ null 818 0]>>endobj
+31 0 obj<</Subtype/Link/Rect[108.0 541.2 124.5 554.2]/Border[0 0 0]/Dest[210 0 R/XYZ null 737 0]>>endobj
+32 0 obj<</Subtype/Link/Rect[124.5 541.2 169.4 554.2]/Border[0 0 0]/Dest[210 0 R/XYZ null 737 0]>>endobj
+33 0 obj<</Subtype/Link/Rect[169.4 541.2 209.7 554.2]/Border[0 0 0]/Dest[210 0 R/XYZ null 737 0]>>endobj
+34 0 obj<</Subtype/Link/Rect[108.0 528.0 124.5 541.0]/Border[0 0 0]/Dest[210 0 R/XYZ null 645 0]>>endobj
+35 0 obj<</Subtype/Link/Rect[124.5 528.0 157.8 541.0]/Border[0 0 0]/Dest[210 0 R/XYZ null 645 0]>>endobj
+36 0 obj<</Subtype/Link/Rect[157.8 528.0 179.2 541.0]/Border[0 0 0]/Dest[210 0 R/XYZ null 645 0]>>endobj
+37 0 obj<</Subtype/Link/Rect[108.0 514.8 124.5 527.8]/Border[0 0 0]/Dest[210 0 R/XYZ null 579 0]>>endobj
+38 0 obj<</Subtype/Link/Rect[124.5 514.8 189.0 527.8]/Border[0 0 0]/Dest[210 0 R/XYZ null 579 0]>>endobj
+39 0 obj<</Subtype/Link/Rect[189.0 514.8 246.4 527.8]/Border[0 0 0]/Dest[210 0 R/XYZ null 579 0]>>endobj
+40 0 obj<</Subtype/Link/Rect[72.0 488.4 80.2 501.4]/Border[0 0 0]/Dest[216 0 R/XYZ null 818 0]>>endobj
+41 0 obj<</Subtype/Link/Rect[80.2 488.4 120.9 501.4]/Border[0 0 0]/Dest[216 0 R/XYZ null 818 0]>>endobj
+42 0 obj<</Subtype/Link/Rect[120.9 488.4 156.6 501.4]/Border[0 0 0]/Dest[216 0 R/XYZ null 818 0]>>endobj
+43 0 obj<</Subtype/Link/Rect[156.6 488.4 206.7 501.4]/Border[0 0 0]/Dest[216 0 R/XYZ null 818 0]>>endobj
+44 0 obj<</Subtype/Link/Rect[108.0 475.2 124.5 488.2]/Border[0 0 0]/Dest[216 0 R/XYZ null 624 0]>>endobj
+45 0 obj<</Subtype/Link/Rect[124.5 475.2 185.0 488.2]/Border[0 0 0]/Dest[216 0 R/XYZ null 624 0]>>endobj
+46 0 obj<</Subtype/Link/Rect[108.0 462.0 124.5 475.0]/Border[0 0 0]/Dest[216 0 R/XYZ null 400 0]>>endobj
+47 0 obj<</Subtype/Link/Rect[124.5 462.0 183.8 475.0]/Border[0 0 0]/Dest[216 0 R/XYZ null 400 0]>>endobj
+48 0 obj<</Subtype/Link/Rect[108.0 448.8 124.5 461.8]/Border[0 0 0]/Dest[216 0 R/XYZ null 268 0]>>endobj
+49 0 obj<</Subtype/Link/Rect[124.5 448.8 171.5 461.8]/Border[0 0 0]/Dest[216 0 R/XYZ null 268 0]>>endobj
+50 0 obj<</Subtype/Link/Rect[108.0 435.6 124.5 448.6]/Border[0 0 0]/Dest[219 0 R/XYZ null 800 0]>>endobj
+51 0 obj<</Subtype/Link/Rect[124.5 435.6 177.7 448.6]/Border[0 0 0]/Dest[219 0 R/XYZ null 800 0]>>endobj
+52 0 obj<</Subtype/Link/Rect[72.0 409.2 80.2 422.2]/Border[0 0 0]/Dest[222 0 R/XYZ null 818 0]>>endobj
+53 0 obj<</Subtype/Link/Rect[80.2 409.2 125.2 422.2]/Border[0 0 0]/Dest[222 0 R/XYZ null 818 0]>>endobj
+54 0 obj<</Subtype/Link/Rect[125.2 409.2 164.9 422.2]/Border[0 0 0]/Dest[222 0 R/XYZ null 818 0]>>endobj
+55 0 obj<</Subtype/Link/Rect[108.0 396.0 124.5 409.0]/Border[0 0 0]/Dest[222 0 R/XYZ null 737 0]>>endobj
+56 0 obj<</Subtype/Link/Rect[124.5 396.0 162.1 409.0]/Border[0 0 0]/Dest[222 0 R/XYZ null 737 0]>>endobj
+57 0 obj<</Subtype/Link/Rect[162.1 396.0 212.2 409.0]/Border[0 0 0]/Dest[222 0 R/XYZ null 737 0]>>endobj
+58 0 obj<</Subtype/Link/Rect[108.0 382.8 124.5 395.8]/Border[0 0 0]/Dest[222 0 R/XYZ null 309 0]>>endobj
+59 0 obj<</Subtype/Link/Rect[124.5 382.8 173.4 395.8]/Border[0 0 0]/Dest[222 0 R/XYZ null 309 0]>>endobj
+60 0 obj<</Subtype/Link/Rect[108.0 369.6 124.5 382.6]/Border[0 0 0]/Dest[222 0 R/XYZ null 230 0]>>endobj
+61 0 obj<</Subtype/Link/Rect[124.5 369.6 157.5 382.6]/Border[0 0 0]/Dest[222 0 R/XYZ null 230 0]>>endobj
+62 0 obj<</Subtype/Link/Rect[108.0 356.4 124.5 369.4]/Border[0 0 0]/Dest[225 0 R/XYZ null 800 0]>>endobj
+63 0 obj<</Subtype/Link/Rect[124.5 356.4 158.7 369.4]/Border[0 0 0]/Dest[225 0 R/XYZ null 800 0]>>endobj
+64 0 obj<</Subtype/Link/Rect[72.0 330.0 82.7 343.0]/Border[0 0 0]/Dest[228 0 R/XYZ null 818 0]>>endobj
+65 0 obj<</Subtype/Link/Rect[82.7 330.0 124.2 343.0]/Border[0 0 0]/Dest[228 0 R/XYZ null 818 0]>>endobj
+66 0 obj<</Subtype/Link/Rect[108.0 316.8 126.9 329.8]/Border[0 0 0]/Dest[228 0 R/XYZ null 737 0]>>endobj
+67 0 obj<</Subtype/Link/Rect[126.9 316.8 155.0 329.8]/Border[0 0 0]/Dest[228 0 R/XYZ null 737 0]>>endobj
+68 0 obj<</Subtype/Link/Rect[108.0 303.6 126.9 316.6]/Border[0 0 0]/Dest[228 0 R/XYZ null 434 0]>>endobj
+69 0 obj<</Subtype/Link/Rect[126.9 303.6 172.8 316.6]/Border[0 0 0]/Dest[228 0 R/XYZ null 434 0]>>endobj
+70 0 obj<</Subtype/Link/Rect[72.0 277.2 82.1 290.2]/Border[0 0 0]/Dest[234 0 R/XYZ null 818 0]>>endobj
+71 0 obj<</Subtype/Link/Rect[82.1 277.2 119.1 290.2]/Border[0 0 0]/Dest[234 0 R/XYZ null 818 0]>>endobj
+72 0 obj<</Subtype/Link/Rect[119.1 277.2 185.1 290.2]/Border[0 0 0]/Dest[234 0 R/XYZ null 818 0]>>endobj
+73 0 obj<</Subtype/Link/Rect[108.0 264.0 126.3 277.0]/Border[0 0 0]/Dest[234 0 R/XYZ null 717 0]>>endobj
+74 0 obj<</Subtype/Link/Rect[126.3 264.0 159.6 277.0]/Border[0 0 0]/Dest[234 0 R/XYZ null 717 0]>>endobj
+75 0 obj<</Subtype/Link/Rect[159.6 264.0 181.0 277.0]/Border[0 0 0]/Dest[234 0 R/XYZ null 717 0]>>endobj
+76 0 obj<</Subtype/Link/Rect[144.0 250.8 170.6 263.8]/Border[0 0 0]/Dest[234 0 R/XYZ null 647 0]>>endobj
+77 0 obj<</Subtype/Link/Rect[170.6 250.8 206.0 263.8]/Border[0 0 0]/Dest[234 0 R/XYZ null 647 0]>>endobj
+78 0 obj<</Subtype/Link/Rect[144.0 237.6 170.6 250.6]/Border[0 0 0]/Dest[234 0 R/XYZ null 561 0]>>endobj
+79 0 obj<</Subtype/Link/Rect[170.6 237.6 238.4 250.6]/Border[0 0 0]/Dest[234 0 R/XYZ null 561 0]>>endobj
+80 0 obj<</Subtype/Link/Rect[108.0 224.4 126.3 237.4]/Border[0 0 0]/Dest[237 0 R/XYZ null 721 0]>>endobj
+81 0 obj<</Subtype/Link/Rect[126.3 224.4 169.7 237.4]/Border[0 0 0]/Dest[237 0 R/XYZ null 721 0]>>endobj
+82 0 obj<</Subtype/Link/Rect[144.0 211.2 170.6 224.2]/Border[0 0 0]/Dest[237 0 R/XYZ null 651 0]>>endobj
+83 0 obj<</Subtype/Link/Rect[170.6 211.2 206.0 224.2]/Border[0 0 0]/Dest[237 0 R/XYZ null 651 0]>>endobj
+84 0 obj<</Subtype/Link/Rect[144.0 198.0 170.6 211.0]/Border[0 0 0]/Dest[237 0 R/XYZ null 526 0]>>endobj
+85 0 obj<</Subtype/Link/Rect[170.6 198.0 238.4 211.0]/Border[0 0 0]/Dest[237 0 R/XYZ null 526 0]>>endobj
+86 0 obj<</Subtype/Link/Rect[108.0 184.8 126.3 197.8]/Border[0 0 0]/Dest[237 0 R/XYZ null 323 0]>>endobj
+87 0 obj<</Subtype/Link/Rect[126.3 184.8 164.8 197.8]/Border[0 0 0]/Dest[237 0 R/XYZ null 323 0]>>endobj
+88 0 obj<</Subtype/Link/Rect[144.0 171.6 170.6 184.6]/Border[0 0 0]/Dest[237 0 R/XYZ null 253 0]>>endobj
+89 0 obj<</Subtype/Link/Rect[170.6 171.6 206.0 184.6]/Border[0 0 0]/Dest[237 0 R/XYZ null 253 0]>>endobj
+90 0 obj<</Subtype/Link/Rect[144.0 158.4 170.6 171.4]/Border[0 0 0]/Dest[237 0 R/XYZ null 181 0]>>endobj
+91 0 obj<</Subtype/Link/Rect[170.6 158.4 238.4 171.4]/Border[0 0 0]/Dest[237 0 R/XYZ null 181 0]>>endobj
+92 0 obj<</Subtype/Link/Rect[108.0 145.2 126.3 158.2]/Border[0 0 0]/Dest[240 0 R/XYZ null 648 0]>>endobj
+93 0 obj<</Subtype/Link/Rect[126.3 145.2 168.5 158.2]/Border[0 0 0]/Dest[240 0 R/XYZ null 648 0]>>endobj
+94 0 obj<</Subtype/Link/Rect[144.0 132.0 170.6 145.0]/Border[0 0 0]/Dest[240 0 R/XYZ null 578 0]>>endobj
+95 0 obj<</Subtype/Link/Rect[170.6 132.0 206.0 145.0]/Border[0 0 0]/Dest[240 0 R/XYZ null 578 0]>>endobj
+96 0 obj<</Subtype/Link/Rect[144.0 118.8 170.6 131.8]/Border[0 0 0]/Dest[240 0 R/XYZ null 440 0]>>endobj
+97 0 obj<</Subtype/Link/Rect[170.6 118.8 238.4 131.8]/Border[0 0 0]/Dest[240 0 R/XYZ null 440 0]>>endobj
+98 0 obj<</Subtype/Link/Rect[108.0 105.6 126.3 118.6]/Border[0 0 0]/Dest[240 0 R/XYZ null 348 0]>>endobj
+99 0 obj<</Subtype/Link/Rect[126.3 105.6 153.2 118.6]/Border[0 0 0]/Dest[240 0 R/XYZ null 348 0]>>endobj
+100 0 obj<</Subtype/Link/Rect[144.0 92.4 170.6 105.4]/Border[0 0 0]/Dest[240 0 R/XYZ null 277 0]>>endobj
+101 0 obj<</Subtype/Link/Rect[170.6 92.4 206.0 105.4]/Border[0 0 0]/Dest[240 0 R/XYZ null 277 0]>>endobj
+102 0 obj<</Subtype/Link/Rect[144.0 79.2 170.6 92.2]/Border[0 0 0]/Dest[240 0 R/XYZ null 205 0]>>endobj
+103 0 obj<</Subtype/Link/Rect[170.6 79.2 238.4 92.2]/Border[0 0 0]/Dest[240 0 R/XYZ null 205 0]>>endobj
+104 0 obj<</Subtype/Link/Rect[108.0 66.0 126.3 79.0]/Border[0 0 0]/Dest[243 0 R/XYZ null 769 0]>>endobj
+105 0 obj<</Subtype/Link/Rect[126.3 66.0 170.9 79.0]/Border[0 0 0]/Dest[243 0 R/XYZ null 769 0]>>endobj
+106 0 obj[10 0 R
+11 0 R
+12 0 R
+13 0 R
+14 0 R
+15 0 R
+16 0 R
+17 0 R
+18 0 R
+19 0 R
+20 0 R
+21 0 R
+22 0 R
+23 0 R
+24 0 R
+25 0 R
+26 0 R
+27 0 R
+28 0 R
+29 0 R
+30 0 R
+31 0 R
+32 0 R
+33 0 R
+34 0 R
+35 0 R
+36 0 R
+37 0 R
+38 0 R
+39 0 R
+40 0 R
+41 0 R
+42 0 R
+43 0 R
+44 0 R
+45 0 R
+46 0 R
+47 0 R
+48 0 R
+49 0 R
+50 0 R
+51 0 R
+52 0 R
+53 0 R
+54 0 R
+55 0 R
+56 0 R
+57 0 R
+58 0 R
+59 0 R
+60 0 R
+61 0 R
+62 0 R
+63 0 R
+64 0 R
+65 0 R
+66 0 R
+67 0 R
+68 0 R
+69 0 R
+70 0 R
+71 0 R
+72 0 R
+73 0 R
+74 0 R
+75 0 R
+76 0 R
+77 0 R
+78 0 R
+79 0 R
+80 0 R
+81 0 R
+82 0 R
+83 0 R
+84 0 R
+85 0 R
+86 0 R
+87 0 R
+88 0 R
+89 0 R
+90 0 R
+91 0 R
+92 0 R
+93 0 R
+94 0 R
+95 0 R
+96 0 R
+97 0 R
+98 0 R
+99 0 R
+100 0 R
+101 0 R
+102 0 R
+103 0 R
+104 0 R
+105 0 R
+]endobj
+107 0 obj<</Subtype/Link/Rect[108.0 673.2 134.6 686.2]/Border[0 0 0]/Dest[243 0 R/XYZ null 699 0]>>endobj
+108 0 obj<</Subtype/Link/Rect[134.6 673.2 170.0 686.2]/Border[0 0 0]/Dest[243 0 R/XYZ null 699 0]>>endobj
+109 0 obj<</Subtype/Link/Rect[108.0 660.0 134.6 673.0]/Border[0 0 0]/Dest[243 0 R/XYZ null 627 0]>>endobj
+110 0 obj<</Subtype/Link/Rect[134.6 660.0 202.4 673.0]/Border[0 0 0]/Dest[243 0 R/XYZ null 627 0]>>endobj
+111 0 obj<</Subtype/Link/Rect[72.0 646.8 90.3 659.8]/Border[0 0 0]/Dest[243 0 R/XYZ null 469 0]>>endobj
+112 0 obj<</Subtype/Link/Rect[90.3 646.8 123.3 659.8]/Border[0 0 0]/Dest[243 0 R/XYZ null 469 0]>>endobj
+113 0 obj<</Subtype/Link/Rect[108.0 633.6 134.6 646.6]/Border[0 0 0]/Dest[243 0 R/XYZ null 398 0]>>endobj
+114 0 obj<</Subtype/Link/Rect[134.6 633.6 170.0 646.6]/Border[0 0 0]/Dest[243 0 R/XYZ null 398 0]>>endobj
+115 0 obj<</Subtype/Link/Rect[108.0 620.4 134.6 633.4]/Border[0 0 0]/Dest[243 0 R/XYZ null 339 0]>>endobj
+116 0 obj<</Subtype/Link/Rect[134.6 620.4 202.4 633.4]/Border[0 0 0]/Dest[243 0 R/XYZ null 339 0]>>endobj
+117 0 obj<</Subtype/Link/Rect[72.0 607.2 90.3 620.2]/Border[0 0 0]/Dest[246 0 R/XYZ null 800 0]>>endobj
+118 0 obj<</Subtype/Link/Rect[90.3 607.2 133.7 620.2]/Border[0 0 0]/Dest[246 0 R/XYZ null 800 0]>>endobj
+119 0 obj<</Subtype/Link/Rect[108.0 594.0 134.6 607.0]/Border[0 0 0]/Dest[246 0 R/XYZ null 729 0]>>endobj
+120 0 obj<</Subtype/Link/Rect[134.6 594.0 170.0 607.0]/Border[0 0 0]/Dest[246 0 R/XYZ null 729 0]>>endobj
+121 0 obj<</Subtype/Link/Rect[108.0 580.8 134.6 593.8]/Border[0 0 0]/Dest[246 0 R/XYZ null 618 0]>>endobj
+122 0 obj<</Subtype/Link/Rect[134.6 580.8 202.4 593.8]/Border[0 0 0]/Dest[246 0 R/XYZ null 618 0]>>endobj
+123 0 obj<</Subtype/Link/Rect[72.0 567.6 90.3 580.6]/Border[0 0 0]/Dest[246 0 R/XYZ null 505 0]>>endobj
+124 0 obj<</Subtype/Link/Rect[90.3 567.6 113.6 580.6]/Border[0 0 0]/Dest[246 0 R/XYZ null 505 0]>>endobj
+125 0 obj<</Subtype/Link/Rect[108.0 554.4 134.6 567.4]/Border[0 0 0]/Dest[246 0 R/XYZ null 435 0]>>endobj
+126 0 obj<</Subtype/Link/Rect[134.6 554.4 202.4 567.4]/Border[0 0 0]/Dest[246 0 R/XYZ null 435 0]>>endobj
+127 0 obj<</Subtype/Link/Rect[108.0 541.2 134.6 554.2]/Border[0 0 0]/Dest[249 0 R/XYZ null 782 0]>>endobj
+128 0 obj<</Subtype/Link/Rect[134.6 541.2 157.2 554.2]/Border[0 0 0]/Dest[249 0 R/XYZ null 782 0]>>endobj
+129 0 obj<</Subtype/Link/Rect[36.0 514.8 46.7 527.8]/Border[0 0 0]/Dest[252 0 R/XYZ null 818 0]>>endobj
+130 0 obj<</Subtype/Link/Rect[46.7 514.8 91.6 527.8]/Border[0 0 0]/Dest[252 0 R/XYZ null 818 0]>>endobj
+131 0 obj<</Subtype/Link/Rect[91.6 514.8 132.2 527.8]/Border[0 0 0]/Dest[252 0 R/XYZ null 818 0]>>endobj
+132 0 obj<</Subtype/Link/Rect[132.2 514.8 168.0 527.8]/Border[0 0 0]/Dest[252 0 R/XYZ null 818 0]>>endobj
+133 0 obj<</Subtype/Link/Rect[168.0 514.8 194.2 527.8]/Border[0 0 0]/Dest[252 0 R/XYZ null 818 0]>>endobj
+134 0 obj[107 0 R
+108 0 R
+109 0 R
+110 0 R
+111 0 R
+112 0 R
+113 0 R
+114 0 R
+115 0 R
+116 0 R
+117 0 R
+118 0 R
+119 0 R
+120 0 R
+121 0 R
+122 0 R
+123 0 R
+124 0 R
+125 0 R
+126 0 R
+127 0 R
+128 0 R
+129 0 R
+130 0 R
+131 0 R
+132 0 R
+133 0 R
+]endobj
+135 0 obj<</Dests 136 0 R>>endobj
+136 0 obj<</Kids[137 0 R]>>endobj
+137 0 obj<</Limits[(1)(8)]/Names[(1)138 0 R(1_1)139 0 R(1_2)140 0 R(1_3)141 0 R(2)142 0 R(2_1)143 0 R(2_2)144 0 R(3)145 0 R(3_1)146 0 R(3_2)147 0 R(3_3)148 0 R(4)149 0 R(4_1)150 0 R(4_2)151 0 R(4_3)152 0 R(4_4)153 0 R(5)154 0 R(5_1)155 0 R(5_2)156 0 R(5_3)157 0 R(5_4)158 0 R(6)159 0 R(6_1)160 0 R(6_2)161 0 R(7)162 0 R(7_1)163 0 R(7_1_1)164 0 R(7_1_2)165 0 R(7_2)166 0 R(7_2_1)167 0 R(7_2_2)168 0 R(7_3)169 0 R(7_3_1)170 0 R(7_3_2)171 0 R(7_4)172 0 R(7_4_1)173 0 R(7_4_2)174 0 R(7_5)175 0 R(7_5_1)176 0 R(7_5_2)177 0 R(7_6)178 0 R(7_6_1)179 0 R(7_6_2)180 0 R(7_7)181 0 R(7_7_1)182 0 R(7_7_2)183 0 R(7_8)184 0 R(7_8_1)185 0 R(7_8_2)186 0 R(7_9)187 0 R(7_9_1)188 0 R(7_9_2)189 0 R(8)190 0 R]>>endobj
+138 0 obj<</D[198 0 R/XYZ null 818 null]>>endobj
+139 0 obj<</D[198 0 R/XYZ null 737 null]>>endobj
+140 0 obj<</D[198 0 R/XYZ null 658 null]>>endobj
+141 0 obj<</D[198 0 R/XYZ null 434 null]>>endobj
+142 0 obj<</D[204 0 R/XYZ null 818 null]>>endobj
+143 0 obj<</D[204 0 R/XYZ null 737 null]>>endobj
+144 0 obj<</D[204 0 R/XYZ null 540 null]>>endobj
+145 0 obj<</D[210 0 R/XYZ null 818 null]>>endobj
+146 0 obj<</D[210 0 R/XYZ null 737 null]>>endobj
+147 0 obj<</D[210 0 R/XYZ null 645 null]>>endobj
+148 0 obj<</D[210 0 R/XYZ null 579 null]>>endobj
+149 0 obj<</D[216 0 R/XYZ null 818 null]>>endobj
+150 0 obj<</D[216 0 R/XYZ null 624 null]>>endobj
+151 0 obj<</D[216 0 R/XYZ null 400 null]>>endobj
+152 0 obj<</D[216 0 R/XYZ null 268 null]>>endobj
+153 0 obj<</D[219 0 R/XYZ null 800 null]>>endobj
+154 0 obj<</D[222 0 R/XYZ null 818 null]>>endobj
+155 0 obj<</D[222 0 R/XYZ null 737 null]>>endobj
+156 0 obj<</D[222 0 R/XYZ null 309 null]>>endobj
+157 0 obj<</D[222 0 R/XYZ null 230 null]>>endobj
+158 0 obj<</D[225 0 R/XYZ null 800 null]>>endobj
+159 0 obj<</D[228 0 R/XYZ null 818 null]>>endobj
+160 0 obj<</D[228 0 R/XYZ null 737 null]>>endobj
+161 0 obj<</D[228 0 R/XYZ null 434 null]>>endobj
+162 0 obj<</D[234 0 R/XYZ null 818 null]>>endobj
+163 0 obj<</D[234 0 R/XYZ null 717 null]>>endobj
+164 0 obj<</D[234 0 R/XYZ null 647 null]>>endobj
+165 0 obj<</D[234 0 R/XYZ null 561 null]>>endobj
+166 0 obj<</D[237 0 R/XYZ null 721 null]>>endobj
+167 0 obj<</D[237 0 R/XYZ null 651 null]>>endobj
+168 0 obj<</D[237 0 R/XYZ null 526 null]>>endobj
+169 0 obj<</D[237 0 R/XYZ null 323 null]>>endobj
+170 0 obj<</D[237 0 R/XYZ null 253 null]>>endobj
+171 0 obj<</D[237 0 R/XYZ null 181 null]>>endobj
+172 0 obj<</D[240 0 R/XYZ null 648 null]>>endobj
+173 0 obj<</D[240 0 R/XYZ null 578 null]>>endobj
+174 0 obj<</D[240 0 R/XYZ null 440 null]>>endobj
+175 0 obj<</D[240 0 R/XYZ null 348 null]>>endobj
+176 0 obj<</D[240 0 R/XYZ null 277 null]>>endobj
+177 0 obj<</D[240 0 R/XYZ null 205 null]>>endobj
+178 0 obj<</D[243 0 R/XYZ null 769 null]>>endobj
+179 0 obj<</D[243 0 R/XYZ null 699 null]>>endobj
+180 0 obj<</D[243 0 R/XYZ null 627 null]>>endobj
+181 0 obj<</D[243 0 R/XYZ null 469 null]>>endobj
+182 0 obj<</D[243 0 R/XYZ null 398 null]>>endobj
+183 0 obj<</D[243 0 R/XYZ null 339 null]>>endobj
+184 0 obj<</D[246 0 R/XYZ null 800 null]>>endobj
+185 0 obj<</D[246 0 R/XYZ null 729 null]>>endobj
+186 0 obj<</D[246 0 R/XYZ null 618 null]>>endobj
+187 0 obj<</D[246 0 R/XYZ null 505 null]>>endobj
+188 0 obj<</D[246 0 R/XYZ null 435 null]>>endobj
+189 0 obj<</D[249 0 R/XYZ null 782 null]>>endobj
+190 0 obj<</D[252 0 R/XYZ null 818 null]>>endobj
+191 0 obj<</Type/Pages/MediaBox[0 0 595 792]/Count 24/Kids[192 0 R
+195 0 R
+258 0 R
+261 0 R
+198 0 R
+201 0 R
+204 0 R
+207 0 R
+210 0 R
+213 0 R
+216 0 R
+219 0 R
+222 0 R
+225 0 R
+228 0 R
+231 0 R
+234 0 R
+237 0 R
+240 0 R
+243 0 R
+246 0 R
+249 0 R
+252 0 R
+255 0 R
+]>>endobj
+192 0 obj<</Type/Page/Parent 191 0 R/Contents 193 0 R/Resources<</ProcSet[/PDF/Text/ImageB/ImageC/ImageI]/Font<</F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+193 0 obj<</Length 194 0 R/Filter/FlateDecode>>stream
+xÚíßoÜ8’Ç¥nõKždOún§;àâ´wfŠmí](IîápÁNfn³3·™Ìö¿¿þåný ¥ª"%Q^s€Aân~Äâ·ŠdQý××bµùïZ|{#Þ¾þË‹;ù"–/þ*Þ®¾»úv÷oß½»Z‰ïÞ_݈››ë«Û]­oî³ÿ|óñMúowbý¯«]y»+ïvåý®ÜïÊwåÿõæ£øúå÷ÏoþCܾ½~óïb}}ýæîÅ­øøð"šž¹ÌŠ|¸Y,gÛ²ùÃâöaì\ò~qzªrv~ù0N.ys¡F*À-nGÆ%oÚ˜Ëùe<.<Ôív \÷¹ø‹Øq®»Ðã•óµÃ\7\*›æÎŒ•Ý1³Ìu/<óòMìWváY)þ¥S\7žµ2Y;Ã%…g³\:Âe¬v‡Ì—¼ð¬ÿÕà\YèuQ¾˜ëÎë¨Lâ!¹®½ÎŠ¿ŒË²Z·±6åÕ \2ô<ÁÀI!,——½sõ‚Ń`qÀ` X 0pZ2øâãÀò¼eO\û-3Íæêk÷Áµòz/“¸>x”iç\©7HyÙ1WÃEEp_áYÚcB†vÀH4ƒ:Åè\™7hYwÅË…œb0‡Ìñb0ÏE{"—Ëó;àpyu®Ôs¢¬-sÉÐ ®‰e®•çH™[åJ=gJl‘Ë+D91Wä9TÖÖ¸2—°Z¥Æ'¨¨F(˜¨FiàµË•¸†Õ2`0>G ŒPã1ÎÇ%]ÄjŒëaŒ˜a0>—ŒšaÐßp/¯¶ån{ñ¡Ûƒ^†Ë?UŸäÛ{ t?\~CÒ¿¼¿èfÀ ëá:o]*Ý - ¸VÝ UqÔÖÃzèp¸éÇ’›¼fs±C Zº8“lÊåâ†ôôÜŒµdˆ™\Ìáb¥Ss†,`r±yn6$cÈ|WÒ¶[²%‹+ìËÞ,´"õ`WÃ$í“l°fp‰Þ¦?ó* se`‘Á|:Yä};W$…±r4s „E›R¹’ŒcŠ1‘K …E›Ó¸2+’ ¹0°·ðz™[-©‘!‚µHÞ2éö՜•XgŒÊÊàÃÁ’jø±}.Bb<—4ÒLKÆ7D= :¹öåÛÁÊ
+e’wT×Á†Ìv2¹h“aŽåŠ†rÈ<Qž`¹f8Í;,‚gˆ`n†ÝY!E—8®•VHèI€ãr@ iÒ᣸Ò=2CÁÖ.¼yç'a3 Wè†hP´~‚àJùÙp·sE®ˆeÀ–í\¡#OêOÐÊ•º5\È›´rEŽ Ò‡Åm\¡cÃ…|Òó®Ì—LëRЕ87\¸(qÒÂ%›]h)‹¹¤ƒÃ…›óËF®ÔÁáÂm#\H•÷{ÅBYѤ‘+t(2$*GVå{ÆBMuR僾¹r’ Wå×½s! qÚÀåÀÞÛ'z®ÔE‘G Z¬åE퀖Z.á¨jàLi¦ã’®ªnêOu\)-4,{²Óß*Æþøs8<Ó¤¤Ê)ÖϯðV5Gq͵\²ôp£¦„V¢k5\‚k4rMµ\‡)êÆqHüÎô
+p\žž
+ê•áÃM¶$àL¯%’k©åÊ
+Ï°fˆ™%š ˆÎ«™+Ðrímor„‰%¥Ÿ¨¹Ñy5sMô\'­”Uú™#8\b¹KÁuÒöo†¥U\5†jášk¹N"QöJìCúÚ+ÀsMõ\Ñã#
+)‹ï +ˆ@^k—Bù.OÏ•~WF1CÄD T\!Ñ [¹–Z®ýgÍö?ÂoÙµ
+ÛTÁ%©fØÊè¹V{ Q?C5Únó\)Õ [¹|=×þÈ˃)ˆ@öÊ1–kz¬­äÚÇuç!Õ+Or,×,|”'%WA¤(«T³' ÊÆÏ=>5WÂZ¥¶†ˆ³WF6Ãf®´„S㒬͒§k@”IŽç’f¡æ:ši³$Ãõˆkå€Àµëx çJX{v8¡¢l,)\pø —díÙµö²ÆÅH_læÊ¢ á:t‘¸g¡º4Ù˜ä®ü ô:.à8%(“šlÌh\¥×qEœñJQžh²±¤q”^Çræ—D={ࢸJ¯áJyg!F²mLˆ\¥×pE¼Íþ¶ç?-sIÆôjåÚ+½†+äÎD˜‡$ÙXR¹öJ¯æʘ§ Æ1io#§rí•^Íz30‹¤Ê\czµsí”^Í%˜™;ã˜Et®uGúuå‚aˆm·.q±•Ë±•‚k×{¡âÚÛ½d„ÑM DQ±úÑÅEñË\§NT¹Ä^“ÝWˆ€XYe®eñÑT¹@Ãõ8P„Óì6̬ȅس™jLb^TÔI…+Óp%žŒnˆ B€ ‡sILë©îÖ¹V}Z‰ö´%.Á‘ÒÑ°,ø¢W¤ä’ǬȆ(v„èPkA`YåJ•\ɱrB}:
+¿Qc_Ñ >:œc|ɬΕ(¸Š©(ÙWíðr¸ÆhS\çÊ\E½IÉ‹0Às­X²Q³‰i^ç:Øj‰+,NXò"L>4——`ÉFuboï)ÖÅE®rFÔªƒŒF §ŠUÊ•‡Sìc¹¢’a'¤þZµ‹¿Ó{M^åJ.Yã
+K–—uª è¨Wo'ÙîŧþyWH©\ SË~²ºÇÕÍûù¨
+`e~2J.Á‹¢œç
+yQ”ë\ÒDæÊ åÐU®”:Ε<59<pEìèÐm®?:tšK<5™?p…OM\œ­ùpÉ''ó{®ôÉÉ<–k2_\Î-»ÍËý·•œ¯ \[æ‹uÎתŸzg—¥m¿Ónoí:Ÿ2Únk¤Ùdœ[^"¸Nû6ÕÉ+¹æ®¹š«8“^¹Û}©ñ5ªSéâ”Ã5Ureêóåí¾Ô
+8\‚+¬?FË\{ƒÕåÎUº8ápMê\¢áŸ…G»”[Ð\K5—¯âª^çCqÕî
+Ë53âÊtñü1¨D®r£¤:UÂ=—ì˜Kjnq(®ó!¹ÊÒŠdKË57âʹ€ÃUj$+¡yâWÆá*7
+ËL¸Ús—r•®óa¹Ê"E2þ†+–+âp•%§SåÓ&ïà\)‡«ÔH{™ã “e¥.Éá*7§ó¯;ÀwÉU¼Î‡æ*7Jjç•.p%®r£J$(v\&ËeWØÌ•q¸ÊÊ+ã`åWá:ž«Ò¨4`qÔ#×ZßňÃUnTN#‚Wëö†oÈ%¼âíLðS¹*>‹I?\Ç7hº(9\ÕFw…­ \î”û]þá7§÷Ï·r%Vï Bû6Ûèr{÷\â™ë™k \Ó'ÊŒ“+|æzæzæzæzæÂqÍž¹ž¹ºçò̹6+ô½RJÝa¹îvï0ó/1Ý¥Ô˜ë¢z}­ü²¡ÂæuÕ§ê“*OqQJÛ;c.QKß(çé§êu\ëòfhG\ê+šò˼²ÓaÇ̪V®eùvÃ%'Jå&Ç-~‰ºyÔÊ5ïƒ TKkQœ`•ôJ]×,/MÍn¸BÕQçAXJ“©ÖepeKé„+{<x:+Ö-¾ºötZ¯¬Ëàš”>º®äxP(¯ ZÀSv…².Ÿ+í’+*œX¯NÞ¥à²NÉ皺ªm~õ§–w’.¹DA äiV4°ðBfM]—<>ÝLâIg\aqNT”þd<qC]×äø8¢6.“x¾ôÏpR‹ãKËIÚªº$®éñqlžLÐWrúÛ1ÊIÚªº$® øèff\-é‡ÇÎÎ6e^«¸pGJ_—ĵ½ÿu|LóN¹&ºØò’Š›¶úñ\â0I·SxÙW¨KÇ9œ)g˜°!uϵ:ü¾ío^wƵך¸qRšFÚº$®è C[K;ã:œuž¯ÕÖ.“âQεuI\pÊ~õóθŽ!z­·û/~ ¾X_WÉUOìÜQ$‡ùºu_ÝqòA*½½ÍÑëëR¸Òƒ¬¶¿ØŒ«é\¯X¯ô^⤞E«­KáÊݺeC®¦sØHy£´8Ž[ëR¸¤w|!ú¬™Ëè|Yj3KE-«E_—À•óî§ÝqU²hý¸6’¢.…kï˜wn¹K®Â‹¶Êª/™ÓÕ¥písºóñ]r=^„­åR©’Mëâ¸öŽ9ÝÕ7ãjÍûº U³F(Šº.Þ/ïÜ`¼ý¿ßÂe#Ÿíþ¢¾¨I4‹U]W²³À[îžëÔÛi%”Z£ê¸öŽyç–ûàz…‰vÒ4Õ%píóÎ-raß-š6‹AS]—|ôb³¾¸N‰×í\•º®½cÞ‹d#—I>ööU2˲+Ör5Õ¥pmLp’öO;á*»WhÝìÔÕ¥pm$ÃO÷ŽÑ+Æí¶p5Õ¥pmG:ñŽ/ªëŒkR²-¿‘KW—ÂûË°~—ɽQúgѨóMu)\ÉþfÍÄœkÙ¼üš×!S-WS]
+Wzrêq%Åñ\U>¦ÒÙ¦º®ì´hä2¹‡¸oëßžµ–Z®¦º-ñ|ém[²|*hÀ5o=‡=Bê‹Mu)\…÷ÂvÇ5¬Õª\ uI\Çet/;k¨Zåj¨ËàŠÍ¹f¸t›jè_‹UôuI\Qí'pez…©qeÜý¨2ŸJ#—Ñû7Ê{1/óF.}]—îý¬v¹
+½Ì[¸´uI\éѧ7smdo½ÑE¨=(1©kT ßÛs(”¯×ëç«ø ß³äl1|/Ö3Wÿ\í¯['WôË’«}ƒc=J.“ó3Wÿ\F ËQsÍFÉ%ÿa¹‚Qrµ/À¦O”k2N®ð ¾†ßÇñÌ5
+€âw͵‡}€²WÇXÖ®€ÒÇV–i»zÊ8æÀ’öÎê8æÀ¢vã”Á:¶[µG}€Š#s`¢]ä
+ã”Ð"H\U§„>BtCë’Ð „ir*ºчíîë‘KŽHè%Fº!Ç
+ýl4r¸,p‰ñb‚™1€ ¹Ä£pc…Þ‹úE®t<‚ˆ2,ÈÑBïH„˜¡">ÈÑBŒDgE.„Ð;"€²+ÈÑBïˆp¬P:
+—‡p„8
+È)‚8sz-k\Ñ&ú.(öCéÑw­!' ÇÐ ýŽÈIÂ1ðkôS—p~‚áï€d¼CO°z¢@NŽa'>r
+g C\Ñž7ЭxC”ÄÐÈKœa 1!>nÈl€Q§äœ ¸h†¥•pLÿ‹•„ú°!çL°ÞWÍ‚ªf³&XÏI–)Ùû@Κ`=»°ˆ<7€5ä=+‡¤÷XS´gå`D­ÀYn÷súÌ
+Õ;c`<k»ñΙ¡µ€±¯ïdŠÉÐP¶ÀƳ¿bY™>R°0qí{±;ÂçT®„4`6µƒ¢Y™KzÉÐ|€#· &……Kæ`1 "Íë9ƒ‹ö ›òMÏRØꀅ݅‚U˜«âµëU‹`4,}<
+îdÝ'–^ªÀV”v{Õ“6ƒEm2U2VÃm4°çùOÖÁ³Å,¤~PÀæÊCXõ›ÎQå†þ1k>WÂâòü[ê`1,¾)hãb˜çÇVóÚ¨•+ò¸ÿ¥ó÷‚5s.éñË9ʳ Þo_q Øvž]¶™ã=“ªeùÐÎe2`{´ýï¾ Ù¿wnÈE]†) òò¡6nò~š<®Ü”+ó씳óÅbñêaSî‹‹Ðð·Í¹l ˜õÒ–ÿˆá’rÍ-p™Iâ Ã…ãroÀ–V¸œ°ö­/—Ûp!¹§6}Ć’+]âZÛãJÇ5\h.ÖNGGÛäÊœášç6¹œÑzÜñžK†cÑxw g
+—Ò¸°DôÑ!‰kpKÄgи²‘X!•kàø—p€MäÔ)¹/T®!½3åø‰Ê5``OJš#s 6Åhi/t®¦1±ŒÁ5Ì#ží2¸™bÔ,%=pÍ`sõ®ôT9WÏÚÁÈ
+endobj
+194 0 obj
+6320
+endobj
+195 0 obj<</Type/Page/Parent 191 0 R/Contents 196 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F8 7 0 R>>>>>>endobj
+196 0 obj<</Length 197 0 R/Filter/FlateDecode>>stream
+xÚ+ä2T0
+ár á
+ä
+endobj
+197 0 obj
+31
+endobj
+198 0 obj<</Type/Page/Parent 191 0 R/Contents 199 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F8 7 0 R/F9 8 0 R/Fc 9 0 R>>>>>>endobj
+199 0 obj<</Length 200 0 R/Filter/FlateDecode>>stream
+xÚ•UMo"9½ó+J9%ÒЛB`oC¾„4™aŒöÀŸ«Á·«c»Aì¯ß²» E‹¢Hi»ªÞ{õÊ•·V
+×ü“Âmº}Ek4oýñ8„tóœoú·=˜g—)Ì$•x5ÿUßÞ$ƒú¾}3Hêˆ$…q†Æ«\Iá™:¸iÚ„vúuè|­H2¹ZU6†B!ŒXaÁéPja #YÕ_–6*C~°ªøO­ æd!à j*cœ0QÆó¯0ò€Ï¨i7éÔPàŽŠ‚Ñ^¾ÿ†‰åheV0Û9,./î^&³‹ÅüDë«4¹G¹ß
+‹É íÝá^{g_åÇíFáöñŸÓx, $ëÅR#Ÿ51ZìÐFí!wѹé»zÈ—.p Œ=¬…;’¿D4ûŽaË<·ƒY#yPVIϦÀ¡ LÁyn¬°Ù‚#]EË ¡u­bƒ&#ë‚ G¸•ãf&Zû»™ÞŸÑºÚWÔ¸ã±(x²vpm.$º¤©ºïb,Ç¥ëRãgКÉj<IÒìêøaþØO&쫨שz‚âØ…ø(~Ñ2J€·
++6ëHηÀ+°÷ƒ‹Ëo“û/0}¼KÓÛáâê Ìxøþ㱆‘&ùÊa³çQ¸ _ËrƇ'<Red¶AhGG®*Ã$°i[å×`‘}â¼22!´ò»“ýYæ€Û²%ûZ+dNKK[$r~&­*ýAºx ¢°‹Éäž_D›[††€â}˜•Ó\™¤ÐÀ¸:Q–ºÙ*“a=¼§³zv]êª~²rž
+õcoš7I9<}§õúâRVÖòÐ;¨‰Rþߨ^rÝ]\ñ Màu£Š`S®ø­MÇáAòùÃeáMì•2ímkÚê>_]¸ß/´ÿ± >ÛŽï‹‘ƒÈ®„‰ÍaG;'­iß)ÆépÖ@²ê Xø;Ö¢Ó¿…ß™ðªo\öòÀpß´SY\¦˜#ÛÀÏõ¬Ô.§>†Ö?¿Ë<+¿ÇùsKUØ”SŒ&ñógaÌϪt¤ïâ”÷ÓyZ¾r…'Müìíî¬Ä'ÞQœ›â[¥lìC>h¢Úµ{éà:9þ¯Üã³!ƒ„Óðý0oýÕú‰ßksendstream
+endobj
+200 0 obj
+869
+endobj
+201 0 obj<</Type/Page/Parent 191 0 R/Contents 202 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F8 7 0 R>>>>>>endobj
+202 0 obj<</Length 203 0 R/Filter/FlateDecode>>stream
+xÚ+ä2T0
+áÒw³P04TIS041׳P072PIÑp VpÎÏKËL/-J,ÉÌÏSðMÌKLOÍMÍ+QÈIÌÓ ÉâÒ…èÑ…j2‰™˜˜é™)€ù†
+ÁÉù© Q×®@.
+endobj
+203 0 obj
+117
+endobj
+204 0 obj<</Type/Page/Parent 191 0 R/Contents 205 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F8 7 0 R/F9 8 0 R/Fc 9 0 R>>>>>>endobj
+205 0 obj<</Length 206 0 R/Filter/FlateDecode>>stream
+xÚ­•OÚ0Åï|Š9²Ò&K 7
+l…T´) {ââÆp•ØÔ6Úòíkc•V%]å@Æ¿7ö¼?ZtÌÀ „nòªõ)k=½ÄÄæMЃŒ¶CX`yŽê!ûîJžýÈyÏ‘ïÊü
+f\£,HŽ0AÅÖÜÞrɶ糹G"Í­„ÿ³…~'aD+Æ™Òf/B*»—)ð¯·PÓ?¤õd~ H±–¤ª¬ w.n¡SÌw’é½±ïVHÝ€›%·¸ª¦I—˜ßRáÿŒímrûf¨ÖÛ¿Mîœ`ÏE§×)áUoPžb¬þ1Â\pï*Æ
+l ™áÙt:…  üÞc° ç›Ý ¡׶ŸU›JRèÕÃ]ç9K’§ÃÌG”2K$%¼nOèdxÆ^‚fì)ϵ=N!“„«» zbͪmyøÕ²ú¼c›qæ‚byh(ÅŠ˜#ÌÕ] ÅËØ<âGøÂ8º1X¬ŒM̵ÈÅÑÛÑqÑÑ{aßúŸéE?6²æU×~žf­¯­_Ð&Ùûendstream
+endobj
+206 0 obj
+522
+endobj
+207 0 obj<</Type/Page/Parent 191 0 R/Contents 208 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F8 7 0 R>>>>>>endobj
+208 0 obj<</Length 209 0 R/Filter/FlateDecode>>stream
+xÚ+ä2T0
+áÒw³P04TIS041׳P072PIÑp VpÎÏKËL/-J,ÉÌÏSðMÌKLOÍMÍ+QÈIÌÓ ÉâÒ…èÑ…j2‰™ê™)€ùF
+A©i©E©yÉ©Å )×®@.
+endobj
+209 0 obj
+122
+endobj
+210 0 obj<</Type/Page/Parent 191 0 R/Contents 211 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+211 0 obj<</Length 212 0 R/Filter/FlateDecode>>stream
+xÚ“Mo›@†ïþ#N‰äÐ`üÙ['’­ÚB­rY/l»ì’ý¨“þúÎ.’›T•9xÅ;ûÎ<ïð4Iàš~ ¬f.7“›|òî~Éò’Þ,WsÈ‹‹î…DøÈ«°Aå.óï½n¯{åÕb÷Ú8­0È6/9ã¹óûŠ9$É Ÿ-{ýã5Xí G(ƒ‹­™”p@h%ãX
+endobj
+212 0 obj
+542
+endobj
+213 0 obj<</Type/Page/Parent 191 0 R/Contents 214 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F8 7 0 R>>>>>>endobj
+214 0 obj<</Length 215 0 R/Filter/FlateDecode>>stream
+xÚ+ä2T0
+áÒw³P04TIS041׳P072PIÑp VpÎÏKËL/-J,ÉÌÏSðMÌKLOÍMÍ+QÈIÌÓ ÉâÒ…èÑ…j2‰[éY*€ùÆ
+n™9©HAò®!\\
+endstream
+endobj
+215 0 obj
+118
+endobj
+216 0 obj<</Type/Page/Parent 191 0 R/Contents 217 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+217 0 obj<</Length 218 0 R/Filter/FlateDecode>>stream
+xÚ…VMsÛ8 ½ûW`zrfmý‘÷–¦»3=µ›¸³_h ŠØ¡H•¤ìæßï%Û±R¥“ÄÑð
+ùmfº§›‹÷Òr¶O¨Yí5ZèXc‚Ì1nOÜÁÁ°àb§té뾌îøðYíYfv€óöZнóØ#oªádÒ&!¯¸h âÉ‚ظž9Òt|N‚éNƒ_r{>i%y‰š»<CblPiâ¯V¿§¢ß1‹@ØÐæ²ËÖD$ÙóGÙy6,Ç[°ªÁ`'‰>±•q5ÝÅ3ˆ*Ð]‘ÐW}àJ…£ZØE º”‚Ì]_Y_GÏõí‰ÂÕBHÿã׊åí‡l{XÞÈýßëÉ¿“ÿóÇÞendstream
+endobj
+218 0 obj
+873
+endobj
+219 0 obj<</Type/Page/Parent 191 0 R/Contents 220 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+220 0 obj<</Length 221 0 R/Filter/FlateDecode>>stream
+xÚuÁNÃ0DïþŠ9–CLŒ“^©à*Â|€Û­Qbƒ“¶ð÷¬®È+YÚy³;ûÅjzwªT?²ÍnŸ¶÷¼ƒö¤©¶å
+Ún$—xIsð¡7sHñF*!Ä
+V"‚@}r8O.#e\\´ô… q±:‹ëÉEÌÄøð]s1a0‡Á>øEú̉#®fBoh˜ÅágêrN™¯Ë»¿åB¶Wu%³ÝìÞ÷oØ¥èÃñœ—¨x6ÑÝèâŒý`–èÕjªÚ¦.¦®ô¤¼Aýßµš½²_¿Yçendstream
+endobj
+221 0 obj
+216
+endobj
+222 0 obj<</Type/Page/Parent 191 0 R/Contents 223 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+223 0 obj<</Length 224 0 R/Filter/FlateDecode>>stream
+xÚVMSÛ0½çWìpjgˆ'’Þ€N/”¤\Y^'flÉ•d(ÿ¾»’çchÃ!±õôöí7¿1Œè/†«1L¦ ËÁÍjðå~ñVL¯.`•~º„¥ÎÜ«0X °h?¯žò2šìðrtÃ#›k?ê2A“«uÀ_@7èñ4 o-–P#pƒ8¬„qðÒ(O
+tŽœ¿L…~ >Hê¼H›Øü¿þÄÓì{‘Ä'ü¸†ÛÇ%X%*»Ñr kTr“iH™{ƒ„¼õ Èr%ŠÖeÿÆçšè¥˜)1ÝúØKVŠ³—3Oòš»'IQæ%Y¨t®œåz(„ $5…ÖX©©@>£—øiôÔ ÷òDLî™ÜµF%QòÆqMg¢.(LŠ~Ú¶‰l í›uXÒ4X"kgwv굉Lß3ºø{aYæíðÝ-}Š6_+Ðøæ¢3ÚƒSß#§H¥«BÁãï©9Ø=ÀÝç¸G^¦7H5!uYQÊ°x#DЃi×£ÿ ¢g6ø°×Ú ‘}§çÃ9ojèsŸjP_¼=¯ß!ÇŒ\¢Ô$â4 #­ŒNké¸àáã=”B° Ô‹=ÒcWâ.zh¦Ö.ô4{ÿÊø`
+endobj
+224 0 obj
+845
+endobj
+225 0 obj<</Type/Page/Parent 191 0 R/Contents 226 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+226 0 obj<</Length 227 0 R/Filter/FlateDecode>>stream
+xÚmP½R„0îyŠ+- ǶÞh§sz¨õ^XŽ8`6Ìoï,,œ¤Úý~÷+QÉS°-ã×crß$·w vi M'»²ªÒšöz—ðJ!ÓMó)¨”Z1›¼”¥`>z²ˆƒ±gè‘áD2Ðnœ
+ÔÏZs7Ã7 Xº@k8xsšƒq̈gà=a$tÞz={O6Àþý(zíAŒÕ6Í£ñ[œ¸wá*…g“wí¬I¿Fîq„j ìºpAO¢aIj]€ ™Å4úá$“7’âXšqº¶¯Û«¢’SÕKŠýÛá{g;sž=.öOh¥Ò£´‘»Y)›*Ï"IeqXäuº…ìŸC?4ÉKò
+endobj
+227 0 obj
+287
+endobj
+228 0 obj<</Type/Page/Parent 191 0 R/Contents 229 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F6 6 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+229 0 obj<</Length 230 0 R/Filter/FlateDecode>>stream
+xÚTÛn£0}ç+æqû
+
+ÃU(äyx±j’ŸÉ_Ç68Pendstream
+endobj
+230 0 obj
+425
+endobj
+231 0 obj<</Type/Page/Parent 191 0 R/Contents 232 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F6 6 0 R/F8 7 0 R>>>>>>endobj
+232 0 obj<</Length 233 0 R/Filter/FlateDecode>>stream
+xÚ“MSƒ0†ïüŠ=ê,•R<öÆv`‡ø"M16$ÒúñëMPÛéXrÚwöÙÝw—/„ùB¸Ží«oŠ½«,†0¼±¡8‰‚ðúâùh–_â'£GGÝÆAÔéô•S­}Dª-Qk@Š M̤ÐJrȉ¨w¤¦_„Ÿ
+þ ”Xþ£ÄR>œƒ_¤8ë‡/,JP ©¨™ Ô°kÀ¤ÝB&Uåä"t¶kÖ2‘’ZV’»ˆåê7"ÑL
+¡ÔD¬-¬TMûè;Gó~vn&?Ø:'´q¢ŠE‘ö³Š׬¡kFà`BA‡ô­z4ÛrYê¼4dÖü—³B®‰‘luY)ö¬sÓ¶ ¸},‹i?µ¤joHm[Ûì”Ëjë`á ;N+¶gfÕã°"¢Ýöé%ßyã‘ù[GÉMÛÌÙ=*­SVïTwfÂtÔPaæå¤Ð£±ÉòÇÃM
+‡6…I0‚.0 †0©”ïMk¥{wÞ'K­&sendstream
+endobj
+233 0 obj
+381
+endobj
+234 0 obj<</Type/Page/Parent 191 0 R/Contents 235 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+235 0 obj<</Length 236 0 R/Filter/FlateDecode>>stream
+xÚV[sÚ:~çWì0}H[<•2‡™6Ós Iò"luj[®$Cø÷]I†˜[Ò)0ƒlíõÛÝOúUó _úð{¦µ›y­u7
+™RÍEøS¢!ƒ%îÛÐÆÍ"ˆDX#N6™Æbb@ÏžO:¯žWúHxƨ˜& ,Ð<O8:ÔÌ›±µ9þü¹êr_®c?|Ÿ—%"Ò%×ežþÀåyC<˜9õ;£¾µÑXÑàštKQ¾§)¦s·vNFÓjV(¦’†šIBÊ(„ÿ³Æ‹¹±LÒ`ŸæÔ‚'\o]2"‰˜„‡ûé#̬«­Ò,Ud—Žso@¦<3XYh€ßEÓ5bš{Á2([½%ÔIX7eƒÑýlZBŒïòܽE´\W“%`Ò:Æ(ë< “"bõ27}Æurx¿Cz;à;p[mž·ú1ÌYFÃø ]%"@!fÔà¹âkFm‹eTÍÚ¨4ì*/d.°'1ÿŒjS'r §Š|+ù*ÖÅ„‡&eô” ë†}k=í SÆf‡jy¹rñ¡Ú‘gÉÖÜ‚YE^…ÅÖZ?Πþa ‘%r¢b&5xÄG® Zín«ãA§=ìÃv
+endobj
+236 0 obj
+1069
+endobj
+237 0 obj<</Type/Page/Parent 191 0 R/Contents 238 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+238 0 obj<</Length 239 0 R/Filter/FlateDecode>>stream
+xÚÕVQ“Ú6~çWì0™ÉqÆÂ9Hó’ËÝÌ=$½Nè3!¯±ZYr%ù€vúß»² Î¥×´O†ÑZÚOß÷iWð[ALoÉ,|DÑ{¿ìï¦À,3š™]O£kX¦ÎTV dRá[øÁ[N¡é ÎI½éCTŠÖŠVä܃0)Bά5(ã<h䶆@‚iÐôb°ü…öaMâ4bI§a*¦…`|yÂ%Ü6HýW÷é‚„”‘Ë}¡†À¢Ø|>ÇWã ƒI¼¸š.â)QþávW«~Ô74æÀ®‚ú FH2¦aþ=ñ¹«´ðÒhw\™4Œ ez]µë"ŸxAÆ4ËN¾Ž&³ì[ésà°QfÍ8aJ—s¥È7¼”ž+ù;¦°ºè0Ë\ºþê÷Íè³)Ðç´Û­rHЋh5€%™k´ÚD¶~âN`¶oè
+4vÊD45_|˲6¦ÓÐ\ÿ‚Om¦0’I4;Æ>Q¨?£¿Y·œü=ø}øÄjaŠ€9rÒBÊ°rzqš¯î¬CzjÐ ëGÇ•º¬<l!ý×¢©Ï¹Þ¯ÃÚ¾³<SùøÈUEÞu—Ò\‰BÒÑH[Ô*©ï1Åô?tùëöV«ÁkÁ)ˆÂÞAó|·tÛ¹ òÉxt ÐF㳌ς¦’ S†l ¯ñ%üHîõ#ÕP°†J«1dqÃïˆ}1w€*4âÐî íá„B®¶0»†–u€þ8çEA}e5!ÑѼ=›úó…;.TÖ&ýû.yù†kaNÍ)õ?nCøžÆëÞo/·aò}mXÔJþe¶Éÿ“ß]·&0ƨ>fóð[F“õ/ÂÑ™ÜT¶AüÈ5ß`­ÿAñÚÛ¾¡¬Ñ›I’Ø4<Lè¿Dqc={ÎúÛeï§Þ_Ī‹nendstream
+endobj
+239 0 obj
+835
+endobj
+240 0 obj<</Type/Page/Parent 191 0 R/Contents 241 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+241 0 obj<</Length 242 0 R/Filter/FlateDecode>>stream
+xÚ•UMsÛ6½ëWìèËQ¢>,Ûi‰wrhêNÔž4ãÁ¥„
+œÚSË£
+Æ¤È 3»&âí“à(këèæZ¨H:Î+ƒ…x„>¯+Û÷´üŸ.µ¸×¢™À¯]¿‡` V’"Ì!ÛAQ+îyu|3³®KTÎG‹
+I­¤Uk52ùf‰¤æÏ+$½89#Í«ZåÔ
+endobj
+242 0 obj
+855
+endobj
+243 0 obj<</Type/Page/Parent 191 0 R/Contents 244 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+244 0 obj<</Length 245 0 R/Filter/FlateDecode>>stream
+xÚÍUMoÚ@½ó+žr"¨Ö|7§†¦·V©âÞ"¡e½€[Ûëz× Tõ¿gv×|*I.­@È3Ì{3oö­ü»ÁÐ¥Coh¿"kÜFΗ.&AˆhasÃñQÜ4ÛBÆr$7U¡gf•Ðegææ:úI¨ Ø [X›píÞ$è[äm0ă)+aªRê}iÏwè¢Ýƒº0`øƳ$_ú²>«‹Â¡gû”¦Ð;6ä<“zÅ);—HÕF–‚k‰MbV¨òX–Z(jKÿš”96ªŒ5xãjF<WàE!©.†Q0+
+ öÑÎ@ý¤qÓöÂcóÊ ÷#8† 7§)iDðxýŒØ^Hë¨Å†ø¬D•ÉÜp“¨üEÍw\¬ŽD{¹+¾–à*³˜§JüBBAœp#Ó-)^r§Écji¹2™Íe¹ßۙиŠv’äà‚ó\ÔKµ|k^&|žºýÓa¡Pi"¶àsµ–½Œc í™w˜ :­“-D´Ù#LÜ?Ѓšóª°‹ÒÁ²s×j.=œÌIÝŸSgmWï×s Ó´*K»`M‡$Ý@¶æ¼çM>¸ˆææý÷Ò ašr­/\ŸÑë®°Tï¾:»àXöæßE¯ðýèm¾÷Ó¾Ýó÷’åq0ù™ûÿåý¨ïµ»GÜîâÿÜèãú°cä÷aÈ÷’qÚ§*_$˪tÞÀWžó¥tÇ}Ÿrç•6ëÕ…] bC›ìûA]o±Ás»‹ßOë×vendstream
+endobj
+245 0 obj
+575
+endobj
+246 0 obj<</Type/Page/Parent 191 0 R/Contents 247 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+247 0 obj<</Length 248 0 R/Filter/FlateDecode>>stream
+xÚµUMoÚ@½ó+¦9 á£i„¤j¤6MWUU¢hmñ6¶×Ý]—Òªÿ½³þ @ J§};óæÍ<ó½fC‡~6 »Ð€×ÎœZûõìckN@wƒáЀã×Ï(2‰Ò,Ѫá|+p=«[àZý‘u\â,®XÌ“yëƒm— îÀêÐ$ŠÀ+“AÂbT BFA!KS”S ®CÈ¥ò„DŸnõ1…¾‚YýÀysys7}uãL®œƒ#Sª¬|s=qÖ®µgÍm€O(q $èaúéúx¢QÌCˆ¸+™\Bœ)MÅç<)8±äåVÍTRÊŸ†›Éu·Eªè.^e¦J#g™3L²%Óœ8¯i¤èjb‚?PB*”ân„ @,Š~è=x!z÷4 p—yƒžˆS¡´vL°×]Mšbç£ʉÎK?:È©ˆ ÜHx÷AN1Ÿ3Ñ’˜ä„yájâ/‹tµiõVúUrÚæ`šßüÞ8<hì|œ|9€vœ+Ð’-¡ÙÞ'õ *<Ó;ñNV*•~hª½qÑû™5¦Uòq¯Æd†§Ii<¡D&=3%K-3Í#þ ÍøL"•¯!mc%^±4z•'9m‡´HW[ã¢Y
+endobj
+248 0 obj
+749
+endobj
+249 0 obj<</Type/Page/Parent 191 0 R/Contents 250 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+250 0 obj<</Length 251 0 R/Filter/FlateDecode>>stream
+xÚ•V]OÛ0}ﯸÊ0š6´
+Û$`LBb[»í&ä&7­‡kw¶Ó&þû®í”,ác¬­’ؾ¾_çø¤¿: ôé›À`×ýÒEçxÒé}A2ˆw`’ÓÚî~â³ãxDc{+psò3X%Á¦;Å»61ƒ3™¡´Ìr%ƒí°¶ÝÙ‡ÎöHHU†0*½1€2Ê`Ó[˜j–Þ 5`æŒÌ¦8ãJnç`çj‰’Ë™·¢‘K\b 2ðnÉRB®„P¥qIPèPU¶a,ÓÖyhì–[Ô>BðÌdÜ
+·¸q΂Ê/¸£Qdˆùé<
+å §Ã§žÀÈÒ9D)£ÜB6æ¬67÷j>…¨„ÓšÝ^ò-pÚÐ8ç0žMN¯Ï>œŸÂÁ#pþÞËð¹ÏT#»i/Tå=P*
+)gÿãó¾)p n‰›Â˜ZNnŸ—·
+Ó³"0˜ø´dšçh¸‰éx³È¾ -´„oþÔ?[êEí²Ê×Z«‚ÂWz¡½JÜ G5^B£°½š›ÁžzWó­÷EÔ2/…ÏòWt„R%WôŽ¡ª@¸T–*DFóMeª”%#ìHÿîP+wç‹¥V+ºÎ4[À5©Â‚Éôõ'®FÃïPåÕÑ#2ôš3—}:ŒÐ…Ä©æûJG»ÝGê¹>¹ä±nTÕ¦ýªü$Iâýú-òõb 'Jæ|Vhÿ‚OL²™—@¸Ì3­› ÷hWwo§ï6%ûnr8Ð\ÿ‰ÿ
+§“Îçι¸j2endstream
+endobj
+251 0 obj
+856
+endobj
+252 0 obj<</Type/Page/Parent 191 0 R/Contents 253 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+253 0 obj<</Length 254 0 R/Filter/FlateDecode>>stream
+xÚí“MOƒ@†ïüŠ9êa‘ᣀ µåf¬Ïd¥‹b
+‹Ó­¦ÿÞ]¨6Æ»lB6»ï3oÞxµ½B¼”u[i C^†±0 }È×g7ÉJ½s“Ü=n¬D'IA*©9Ï_4
+endobj
+254 0 obj
+333
+endobj
+255 0 obj<</Type/Page/Parent 191 0 R/Contents 256 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F8 7 0 R>>>>>>endobj
+256 0 obj<</Length 257 0 R/Filter/FlateDecode>>stream
+xÚ-ŒA D÷œb–ºhªÒµÝ™Tû=
+endobj
+257 0 obj
+135
+endobj
+258 0 obj<</Type/Page/Parent 191 0 R/Contents 259 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>/Annots 106 0 R>>endobj
+259 0 obj<</Length 260 0 R/Filter/FlateDecode>>stream
+xÚÝ›KsÛ8 €ïù:îVŸmºéì¡m¼½»ŽÒz'¶ZÙn'ÿ~I”ÄLøy9°>š
+«ÿØ®ïðù¡¸ ü'_ï¿/nL!Dè1<æ 7QÌâGä®P¦lr>H‘r»é¾µ4R*ìUßÿJ¹ø—˜Õ„²AÓÒø÷]adi°A¨Z£ª½½­´QÓ%¡ë tçŸAÛÓ.PAÝùY±½ßnÖÇm·ÿµÖM]ÖKÒ;§Û8â¤ÛؘÕmШþQ§’Ó)’›º´9ÅHº}<Ûá ãóÞ@J ?:¹È‘öþGÛÿض?m-J·ã0V1®ñƒB«@ƒ²Šq®Ä™®«$²¨‚Wdh”#êu·9íütŸçM¨u©s`úÓH?£žÙ4‚ ø¦–CÀ‡Ïg|S+Õ’ø o‚º2>H‘ò±½oûv¿i3ztËŠ?¿x)fò{ áç…{cã¸%îSMY Îõ?n “ÏúÉoª`´) ä@J¾D¤ kŸ×hœM´Ó4È€¤-„cɦ‰Dn*»22Š‘ôþøµí sŒ‡,Q…X4…¡aÉ ó>$áCŠHÂùab@ŠŸÏ H2hÇN1 ñ²ñ%ãGi¢Ülý¢y4yxW¸h–) Ä õv½_iƒafÖV®\äšÖ0ÎSYˆìqBÇÆ™­ª!è+>¤a®*žöb$½ÞöíæØõó¼ "¸RXÏf@”#ðöØŸ6ÇS?³ Qauöü&c,¤S4¨ð¦øÜ· ‘(ã¢8rn»S¿!|hòtX¸Åg,”Þ8Õ„[–ïp6©Ý˜r AÚÄGø¬Â­Yˆzè'3KB]wûûí—Só6±žp<µ áŒê¿0BÙ gŸ)È2Ùh£†dŸÏJ6ÚhˆíšN6 ¯¿CËø EʪïNŸ©|3yÞÿVíƒOC1Ò>¶ßºžØáLž‘PûH˜ÿ4#íCßùUùa»ÿ2³ªXF(,kÆït5f+hœ—­´4›­RB¨¡ÇÌêúaí5JŸ¸ØjQ«N½²ž¤šØ Úl@¥lªA.ae`”7—z„ÅiÔO¢1QÄ©Q¡KÔ)›(ìÜS2ŠuÊ}XUÍ*4n;¶nå”ZUã 4H¥VyB,d”ŠdQ™O§h”#ê]ÇNT¹}ÚðjȪj=œ Áç³r¨J
+CçЄ7ê)¤H¹íî?×=‘D'
+endobj
+260 0 obj
+1984
+endobj
+261 0 obj<</Type/Page/Parent 191 0 R/Contents 262 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>/Annots 134 0 R>>endobj
+262 0 obj<</Length 263 0 R/Filter/FlateDecode>>stream
+xÚÕ˜Ár›0†ï<…ŽíÁ
+HB׸uOí¤1}
+à©=ÙN2Ìì,|kýÚ_kò+a$…_F2iþžšä¾LîVš0MÊ a2£œH%àZ>¿+«ïûš´²l}}èïËÉÇ2Ii
+saæòø‰(NS"³žkˆ.¨Ä`OÖ¦ÂÙÝ–Ü­r˜©© `¦]Á{*)#³UFà†°,©‰e0°/U³;l l ¥X>˨à.zÁ&#‚rW/¨ f52réEä1‘̤¤ùy‹úÐ>Øøªßµ‡·•À…” ¨—I£c´€5Šœf̪^ŒÃV?5¯ÞÖB“1¤å¾:ëãL 2Ø¥‹·à÷¤°ý=é‚PO
+­)v¥ŠZÛ³µ'pÌ#ìö­-
+Æó2Ú (ca|Š2ÆÌíÙÎÜ8ævÓö #÷övAÈÞBe~Æ{{²V°ö ÓH‚©xì+‹¯¥ËùÕœ©HW
+= »R¦¨hÜÚHFkÑ.ïP7`옄¹°³Á­ÊA sé§N76²ÑØc8ævÝÆVcgÜNg6 }FÇŒd¥ŒXc2¦‘´lŸë·UãŠ\ßÐþ—¾dz4plìKžú£ãæF6öåîòv«}éLåðzã‚ ‚©òGÇlÆÍ·Â ó[÷ö3½ ²_Ko²bv¹\iwŽ¥°×.¼(
+|Q„犿XÏË9bÂ"@»I—öœu»éW]87ì¼Ù– Î¥=®ìÚ“ycžßë`F!ÑÂïµÍ{Ücý³íúÀRG
+endobj
+263 0 obj
+710
+endobj
+264 0 obj<</Count 9/First 265 0 R/Last 318 0 R>>endobj
+265 0 obj<</Parent 264 0 R/Title(Table of Contents)/Dest[258 0 R/XYZ null 756 null]/Next 266 0 R>>endobj
+266 0 obj<</Parent 264 0 R/Count -3/First 267 0 R/Last 269 0 R/Title(1 Scope)/Dest[198 0 R/XYZ null 743 null]/Prev 265 0 R/Next 270 0 R>>endobj
+267 0 obj<</Parent 266 0 R/Title(1.1 Identification)/Dest[198 0 R/XYZ null 693 null]/Next 268 0 R>>endobj
+268 0 obj<</Parent 266 0 R/Title(1.2 System Overview)/Dest[198 0 R/XYZ null 613 null]/Prev 267 0 R/Next 269 0 R>>endobj
+269 0 obj<</Parent 266 0 R/Title(1.3 Document Overview)/Dest[198 0 R/XYZ null 389 null]/Prev 268 0 R>>endobj
+270 0 obj<</Parent 264 0 R/Count -2/First 271 0 R/Last 272 0 R/Title(2 References)/Dest[204 0 R/XYZ null 743 null]/Prev 266 0 R/Next 273 0 R>>endobj
+271 0 obj<</Parent 270 0 R/Title(2.1 CUPS Documentation)/Dest[204 0 R/XYZ null 693 null]/Next 272 0 R>>endobj
+272 0 obj<</Parent 270 0 R/Title(2.2 Other Documents)/Dest[204 0 R/XYZ null 495 null]/Prev 271 0 R>>endobj
+273 0 obj<</Parent 264 0 R/Count -3/First 274 0 R/Last 276 0 R/Title(3 File Management)/Dest[210 0 R/XYZ null 743 null]/Prev 270 0 R/Next 277 0 R>>endobj
+274 0 obj<</Parent 273 0 R/Title(3.1 Directory Structure)/Dest[210 0 R/XYZ null 693 null]/Next 275 0 R>>endobj
+275 0 obj<</Parent 273 0 R/Title(3.2 Source Files)/Dest[210 0 R/XYZ null 600 null]/Prev 274 0 R/Next 276 0 R>>endobj
+276 0 obj<</Parent 273 0 R/Title(3.3 Configuration Management)/Dest[210 0 R/XYZ null 534 null]/Prev 275 0 R>>endobj
+277 0 obj<</Parent 264 0 R/Count -4/First 278 0 R/Last 281 0 R/Title(4 Trouble Report Processing)/Dest[216 0 R/XYZ null 743 null]/Prev 273 0 R/Next 282 0 R>>endobj
+278 0 obj<</Parent 277 0 R/Title(4.1 Classification)/Dest[216 0 R/XYZ null 580 null]/Next 279 0 R>>endobj
+279 0 obj<</Parent 277 0 R/Title(4.2 Identification)/Dest[216 0 R/XYZ null 355 null]/Prev 278 0 R/Next 280 0 R>>endobj
+280 0 obj<</Parent 277 0 R/Title(4.3 Correction)/Dest[216 0 R/XYZ null 223 null]/Prev 279 0 R/Next 281 0 R>>endobj
+281 0 obj<</Parent 277 0 R/Title(4.4 Notification)/Dest[219 0 R/XYZ null 736 null]/Prev 280 0 R>>endobj
+282 0 obj<</Parent 264 0 R/Count -4/First 283 0 R/Last 286 0 R/Title(5 Software Releases)/Dest[222 0 R/XYZ null 743 null]/Prev 277 0 R/Next 287 0 R>>endobj
+283 0 obj<</Parent 282 0 R/Title(5.1 Version Numbering)/Dest[222 0 R/XYZ null 693 null]/Next 284 0 R>>endobj
+284 0 obj<</Parent 282 0 R/Title(5.2 Generation)/Dest[222 0 R/XYZ null 264 null]/Prev 283 0 R/Next 285 0 R>>endobj
+285 0 obj<</Parent 282 0 R/Title(5.3 Testing)/Dest[222 0 R/XYZ null 185 null]/Prev 284 0 R/Next 286 0 R>>endobj
+286 0 obj<</Parent 282 0 R/Title(5.4 Release)/Dest[225 0 R/XYZ null 736 null]/Prev 285 0 R>>endobj
+287 0 obj<</Parent 264 0 R/Count -2/First 288 0 R/Last 289 0 R/Title(A Glossary)/Dest[228 0 R/XYZ null 743 null]/Prev 282 0 R/Next 290 0 R>>endobj
+288 0 obj<</Parent 287 0 R/Title(A.1 Terms)/Dest[228 0 R/XYZ null 693 null]/Next 289 0 R>>endobj
+289 0 obj<</Parent 287 0 R/Title(A.2 Acronyms)/Dest[228 0 R/XYZ null 389 null]/Prev 288 0 R>>endobj
+290 0 obj<</Parent 264 0 R/Count -9/First 291 0 R/Last 315 0 R/Title(B Coding Requirements)/Dest[234 0 R/XYZ null 743 null]/Prev 287 0 R/Next 318 0 R>>endobj
+291 0 obj<</Parent 290 0 R/Count -2/First 292 0 R/Last 293 0 R/Title(B.1 Source Files)/Dest[234 0 R/XYZ null 672 null]/Next 294 0 R>>endobj
+292 0 obj<</Parent 291 0 R/Title(B.1.1 Naming)/Dest[234 0 R/XYZ null 610 null]/Next 293 0 R>>endobj
+293 0 obj<</Parent 291 0 R/Title(B.1.2 Documentation)/Dest[234 0 R/XYZ null 525 null]/Prev 292 0 R>>endobj
+294 0 obj<</Parent 290 0 R/Count -2/First 295 0 R/Last 296 0 R/Title(B.2 Functions)/Dest[237 0 R/XYZ null 676 null]/Prev 291 0 R/Next 297 0 R>>endobj
+295 0 obj<</Parent 294 0 R/Title(B.2.1 Naming)/Dest[237 0 R/XYZ null 615 null]/Next 296 0 R>>endobj
+296 0 obj<</Parent 294 0 R/Title(B.2.2 Documentation)/Dest[237 0 R/XYZ null 489 null]/Prev 295 0 R>>endobj
+297 0 obj<</Parent 290 0 R/Count -2/First 298 0 R/Last 299 0 R/Title(B.3 Methods)/Dest[237 0 R/XYZ null 278 null]/Prev 294 0 R/Next 300 0 R>>endobj
+298 0 obj<</Parent 297 0 R/Title(B.3.1 Naming)/Dest[237 0 R/XYZ null 217 null]/Next 299 0 R>>endobj
+299 0 obj<</Parent 297 0 R/Title(B.3.2 Documentation)/Dest[237 0 R/XYZ null 145 null]/Prev 298 0 R>>endobj
+300 0 obj<</Parent 290 0 R/Count -2/First 301 0 R/Last 302 0 R/Title(B.4 Variables)/Dest[240 0 R/XYZ null 604 null]/Prev 297 0 R/Next 303 0 R>>endobj
+301 0 obj<</Parent 300 0 R/Title(B.4.1 Naming)/Dest[240 0 R/XYZ null 542 null]/Next 302 0 R>>endobj
+302 0 obj<</Parent 300 0 R/Title(B.4.2 Documentation)/Dest[240 0 R/XYZ null 404 null]/Prev 301 0 R>>endobj
+303 0 obj<</Parent 290 0 R/Count -2/First 304 0 R/Last 305 0 R/Title(B.5 Types)/Dest[240 0 R/XYZ null 303 null]/Prev 300 0 R/Next 306 0 R>>endobj
+304 0 obj<</Parent 303 0 R/Title(B.5.1 Naming)/Dest[240 0 R/XYZ null 241 null]/Next 305 0 R>>endobj
+305 0 obj<</Parent 303 0 R/Title(B.5.2 Documentation)/Dest[240 0 R/XYZ null 169 null]/Prev 304 0 R>>endobj
+306 0 obj<</Parent 290 0 R/Count -2/First 307 0 R/Last 308 0 R/Title(B.6 Structures)/Dest[243 0 R/XYZ null 725 null]/Prev 303 0 R/Next 309 0 R>>endobj
+307 0 obj<</Parent 306 0 R/Title(B.6.1 Naming)/Dest[243 0 R/XYZ null 663 null]/Next 308 0 R>>endobj
+308 0 obj<</Parent 306 0 R/Title(B.6.2 Documentation)/Dest[243 0 R/XYZ null 591 null]/Prev 307 0 R>>endobj
+309 0 obj<</Parent 290 0 R/Count -2/First 310 0 R/Last 311 0 R/Title(B.7 Classes)/Dest[243 0 R/XYZ null 424 null]/Prev 306 0 R/Next 312 0 R>>endobj
+310 0 obj<</Parent 309 0 R/Title(B.7.1 Naming)/Dest[243 0 R/XYZ null 362 null]/Next 311 0 R>>endobj
+311 0 obj<</Parent 309 0 R/Title(B.7.2 Documentation)/Dest[243 0 R/XYZ null 303 null]/Prev 310 0 R>>endobj
+312 0 obj<</Parent 290 0 R/Count -2/First 313 0 R/Last 314 0 R/Title(B.8 Constants)/Dest[246 0 R/XYZ null 736 null]/Prev 309 0 R/Next 315 0 R>>endobj
+313 0 obj<</Parent 312 0 R/Title(B.8.1 Naming)/Dest[246 0 R/XYZ null 693 null]/Next 314 0 R>>endobj
+314 0 obj<</Parent 312 0 R/Title(B.8.2 Documentation)/Dest[246 0 R/XYZ null 581 null]/Prev 313 0 R>>endobj
+315 0 obj<</Parent 290 0 R/Count -2/First 316 0 R/Last 317 0 R/Title(B.9 Code)/Dest[246 0 R/XYZ null 460 null]/Prev 312 0 R>>endobj
+316 0 obj<</Parent 315 0 R/Title(B.9.1 Documentation)/Dest[246 0 R/XYZ null 399 null]/Next 317 0 R>>endobj
+317 0 obj<</Parent 315 0 R/Title(B.9.2 Style)/Dest[249 0 R/XYZ null 729 null]/Prev 316 0 R>>endobj
+318 0 obj<</Parent 264 0 R/Title(C Software Trouble Report Form)/Dest[252 0 R/XYZ null 743 null]/Prev 290 0 R>>endobj
+319 0 obj<</Type/Catalog/Pages 191 0 R/Names 135 0 R/ViewerPreferences<</PageLayout/TwoColumnRight>>/Outlines 264 0 R/PageMode/UseOutlines/OpenAction[198 0 R/XYZ null null null]>>endobj
+xref
+0 320
+0000000000 65535 f
+0000000015 00000 n
+0000000218 00000 n
+0000000279 00000 n
+0000000353 00000 n
+0000000431 00000 n
+0000000508 00000 n
+0000000587 00000 n
+0000000663 00000 n
+0000000744 00000 n
+0000000802 00000 n
+0000000905 00000 n
+0000001009 00000 n
+0000001114 00000 n
+0000001219 00000 n
+0000001324 00000 n
+0000001429 00000 n
+0000001534 00000 n
+0000001639 00000 n
+0000001744 00000 n
+0000001849 00000 n
+0000001952 00000 n
+0000002056 00000 n
+0000002161 00000 n
+0000002266 00000 n
+0000002371 00000 n
+0000002476 00000 n
+0000002581 00000 n
+0000002686 00000 n
+0000002789 00000 n
+0000002893 00000 n
+0000002998 00000 n
+0000003103 00000 n
+0000003208 00000 n
+0000003313 00000 n
+0000003418 00000 n
+0000003523 00000 n
+0000003628 00000 n
+0000003733 00000 n
+0000003838 00000 n
+0000003943 00000 n
+0000004046 00000 n
+0000004150 00000 n
+0000004255 00000 n
+0000004360 00000 n
+0000004465 00000 n
+0000004570 00000 n
+0000004675 00000 n
+0000004780 00000 n
+0000004885 00000 n
+0000004990 00000 n
+0000005095 00000 n
+0000005200 00000 n
+0000005303 00000 n
+0000005407 00000 n
+0000005512 00000 n
+0000005617 00000 n
+0000005722 00000 n
+0000005827 00000 n
+0000005932 00000 n
+0000006037 00000 n
+0000006142 00000 n
+0000006247 00000 n
+0000006352 00000 n
+0000006457 00000 n
+0000006560 00000 n
+0000006664 00000 n
+0000006769 00000 n
+0000006874 00000 n
+0000006979 00000 n
+0000007084 00000 n
+0000007187 00000 n
+0000007291 00000 n
+0000007396 00000 n
+0000007501 00000 n
+0000007606 00000 n
+0000007711 00000 n
+0000007816 00000 n
+0000007921 00000 n
+0000008026 00000 n
+0000008131 00000 n
+0000008236 00000 n
+0000008341 00000 n
+0000008446 00000 n
+0000008551 00000 n
+0000008656 00000 n
+0000008761 00000 n
+0000008866 00000 n
+0000008971 00000 n
+0000009076 00000 n
+0000009181 00000 n
+0000009286 00000 n
+0000009391 00000 n
+0000009496 00000 n
+0000009601 00000 n
+0000009706 00000 n
+0000009811 00000 n
+0000009916 00000 n
+0000010021 00000 n
+0000010126 00000 n
+0000010231 00000 n
+0000010336 00000 n
+0000010441 00000 n
+0000010545 00000 n
+0000010649 00000 n
+0000010753 00000 n
+0000010857 00000 n
+0000011553 00000 n
+0000011659 00000 n
+0000011765 00000 n
+0000011871 00000 n
+0000011977 00000 n
+0000012081 00000 n
+0000012186 00000 n
+0000012292 00000 n
+0000012398 00000 n
+0000012504 00000 n
+0000012610 00000 n
+0000012714 00000 n
+0000012819 00000 n
+0000012925 00000 n
+0000013031 00000 n
+0000013137 00000 n
+0000013243 00000 n
+0000013347 00000 n
+0000013452 00000 n
+0000013558 00000 n
+0000013664 00000 n
+0000013770 00000 n
+0000013876 00000 n
+0000013980 00000 n
+0000014084 00000 n
+0000014189 00000 n
+0000014295 00000 n
+0000014401 00000 n
+0000014635 00000 n
+0000014669 00000 n
+0000014703 00000 n
+0000015402 00000 n
+0000015451 00000 n
+0000015500 00000 n
+0000015549 00000 n
+0000015598 00000 n
+0000015647 00000 n
+0000015696 00000 n
+0000015745 00000 n
+0000015794 00000 n
+0000015843 00000 n
+0000015892 00000 n
+0000015941 00000 n
+0000015990 00000 n
+0000016039 00000 n
+0000016088 00000 n
+0000016137 00000 n
+0000016186 00000 n
+0000016235 00000 n
+0000016284 00000 n
+0000016333 00000 n
+0000016382 00000 n
+0000016431 00000 n
+0000016480 00000 n
+0000016529 00000 n
+0000016578 00000 n
+0000016627 00000 n
+0000016676 00000 n
+0000016725 00000 n
+0000016774 00000 n
+0000016823 00000 n
+0000016872 00000 n
+0000016921 00000 n
+0000016970 00000 n
+0000017019 00000 n
+0000017068 00000 n
+0000017117 00000 n
+0000017166 00000 n
+0000017215 00000 n
+0000017264 00000 n
+0000017313 00000 n
+0000017362 00000 n
+0000017411 00000 n
+0000017460 00000 n
+0000017509 00000 n
+0000017558 00000 n
+0000017607 00000 n
+0000017656 00000 n
+0000017705 00000 n
+0000017754 00000 n
+0000017803 00000 n
+0000017852 00000 n
+0000017901 00000 n
+0000017950 00000 n
+0000017999 00000 n
+0000018260 00000 n
+0000018412 00000 n
+0000024803 00000 n
+0000024825 00000 n
+0000024938 00000 n
+0000025040 00000 n
+0000025060 00000 n
+0000025200 00000 n
+0000026140 00000 n
+0000026161 00000 n
+0000026274 00000 n
+0000026462 00000 n
+0000026483 00000 n
+0000026623 00000 n
+0000027216 00000 n
+0000027237 00000 n
+0000027350 00000 n
+0000027543 00000 n
+0000027564 00000 n
+0000027695 00000 n
+0000028308 00000 n
+0000028329 00000 n
+0000028442 00000 n
+0000028631 00000 n
+0000028652 00000 n
+0000028783 00000 n
+0000029727 00000 n
+0000029748 00000 n
+0000029879 00000 n
+0000030166 00000 n
+0000030187 00000 n
+0000030327 00000 n
+0000031243 00000 n
+0000031264 00000 n
+0000031395 00000 n
+0000031753 00000 n
+0000031774 00000 n
+0000031914 00000 n
+0000032410 00000 n
+0000032431 00000 n
+0000032562 00000 n
+0000033014 00000 n
+0000033035 00000 n
+0000033175 00000 n
+0000034315 00000 n
+0000034337 00000 n
+0000034477 00000 n
+0000035383 00000 n
+0000035404 00000 n
+0000035544 00000 n
+0000036470 00000 n
+0000036491 00000 n
+0000036631 00000 n
+0000037277 00000 n
+0000037298 00000 n
+0000037438 00000 n
+0000038258 00000 n
+0000038279 00000 n
+0000038419 00000 n
+0000039346 00000 n
+0000039367 00000 n
+0000039507 00000 n
+0000039911 00000 n
+0000039932 00000 n
+0000040045 00000 n
+0000040251 00000 n
+0000040272 00000 n
+0000040427 00000 n
+0000042482 00000 n
+0000042504 00000 n
+0000042659 00000 n
+0000043440 00000 n
+0000043461 00000 n
+0000043516 00000 n
+0000043621 00000 n
+0000043765 00000 n
+0000043871 00000 n
+0000043991 00000 n
+0000044100 00000 n
+0000044249 00000 n
+0000044359 00000 n
+0000044466 00000 n
+0000044620 00000 n
+0000044731 00000 n
+0000044848 00000 n
+0000044964 00000 n
+0000045128 00000 n
+0000045234 00000 n
+0000045353 00000 n
+0000045468 00000 n
+0000045572 00000 n
+0000045728 00000 n
+0000045837 00000 n
+0000045952 00000 n
+0000046064 00000 n
+0000046163 00000 n
+0000046310 00000 n
+0000046407 00000 n
+0000046507 00000 n
+0000046665 00000 n
+0000046805 00000 n
+0000046905 00000 n
+0000047012 00000 n
+0000047162 00000 n
+0000047262 00000 n
+0000047369 00000 n
+0000047517 00000 n
+0000047617 00000 n
+0000047724 00000 n
+0000047874 00000 n
+0000047974 00000 n
+0000048081 00000 n
+0000048227 00000 n
+0000048327 00000 n
+0000048434 00000 n
+0000048585 00000 n
+0000048685 00000 n
+0000048792 00000 n
+0000048940 00000 n
+0000049040 00000 n
+0000049147 00000 n
+0000049297 00000 n
+0000049397 00000 n
+0000049504 00000 n
+0000049636 00000 n
+0000049743 00000 n
+0000049842 00000 n
+0000049960 00000 n
+trailer
+<</Size 320/Root 319 0 R/Info 1 0 R>>
+startxref
+50146
+%%EOF
diff --git a/doc/cmp.shtml b/doc/cmp.shtml
new file mode 100644
index 000000000..cc6315d3f
--- /dev/null
+++ b/doc/cmp.shtml
@@ -0,0 +1,717 @@
+<HTML>
+<HEAD>
+ <META NAME="DOCNUMBER" CONTENT="CUPS-CMP-1.0">
+ <META NAME="COPYRIGHT" CONTENT="Copyright 1997-1999, All Rights Reserved">
+ <META NAME="Author" CONTENT="Easy Software Products">
+ <TITLE>CUPS Configuration Management Plan</TITLE>
+</HEAD>
+<BODY>
+
+<H1>Scope</H1>
+
+<H2>Identification</H2>
+
+This configuration management plan document provides the guidelines for
+development and maintainance of the Common UNIX Printing System ("CUPS")
+Version 1.0 software.
+
+<H2>System Overview</H2>
+
+The Common UNIX Printing System provides a portable printing layer for
+UNIX&reg; operating systems. It has been developed by Easy Software
+Products to promote a standard printing solution for all UNIX vendors
+and users. CUPS provides the System V and Berkeley command-line interfaces.
+
+<P>CUPS uses the Internet Printing Protocol (IETF-IPP) as the basis
+for managing print jobs and queues. The Line Printer Daemon (LPD,
+RFC1179), Server Message Block (SMB), and AppSocket protocols are also
+supported with reduced functionality.
+
+<P>CUPS adds network printer browsing and PostScript Printer
+Description (&quot;PPD&quot;)-based printing options to support real
+world applications under UNIX.
+
+<P>CUPS also includes a customized version of GNU GhostScript
+(currently based off GNU GhostScript 4.03) and an image file RIP that
+can be used to support non-PostScript printers.
+
+<H2>Document Overview</H2>
+
+This configuration management document is organized into the following
+sections:
+
+<UL>
+ <LI>1 - Scope</LI>
+ <LI>2 - References</LI>
+ <LI>3 - File Management</LI>
+ <LI>4 - Trouble Report Processing</LI>
+ <LI>5 - Software Releases</LI>
+ <LI>A - Glossary</LI>
+ <LI>B - Coding Requirements</LI>
+</UL>
+
+<H1>References</H1>
+
+<H2>CUPS Documentation</H2>
+
+The following CUPS documentation is referenced by this document:
+
+<UL>
+ <LI>CUPS-CMP-1.0: CUPS Configuration Management Plan
+ <LI>CUPS-IDD-1.0: CUPS System Interface Design Description
+ <LI>CUPS-SAM-1.0.x: CUPS Software Administrators Manual
+ <LI>CUPS-SDD-1.0: CUPS Software Design Description
+ <LI>CUPS-SPM-1.0: CUPS Software Programming Manual
+ <LI>CUPS-SSR-1.0: CUPS Software Security Report
+ <LI>CUPS-STP-1.0: CUPS Software Test Plan
+ <LI>CUPS-SUM-1.0.x: CUPS Software Users Manual
+ <LI>CUPS-SVD-1.0.x: CUPS Software Version Description
+</UL>
+
+<H2>Other Documents</H2>
+
+The following non-CUPS documents are referenced by this document:
+
+<UL>
+ <LI>IEEE 1387.4, System Administration: Printing (draft)
+ <LI>IPP/1.0: Additional Optional Operations - Set 1
+ <LI>IPP/1.0: Encoding and Transport
+ <LI>IPP/1.0: Implementers Guide
+ <LI>IPP/1.0: Model and Semantics
+ <LI>RFC 1179, Line Printer Daemon Protocol
+</UL>
+
+<H1>File Management</H1>
+
+<H2>Directory Structure</H2>
+
+Each source file shall be placed a sub-directory corresponding to the software
+sub-system it belongs to ("scheduler", "libcups", etc.) To remain compatible
+with older UNIX filesystems, directory names shall not exceed 16 characters
+in length.
+
+<H2>Source Files</H2>
+
+Source files shall be documented and formatted as described in Appendix
+B, Coding Requirements.
+
+<H2>Configuration Management</H2>
+
+Source files shall be placed under the control of the Concurrent Versions
+System ("CVS") software. Source files shall be "checked in" with each change
+so that modifications can be tracked.
+
+<P>Documentation on the CVS software is included with the whitepaper, "CVS
+II: Parallelizing Software Development".
+
+<H1>Trouble Report Processing</H1>
+
+A Software Trouble Report ("STR") shall be submitted every time a user
+or vendor experiences a problem with the CUPS software. Trouble reports
+are maintained in a database with one of the following states:
+
+<OL>
+ <LI>STR is closed with complete resolution</LI>
+ <LI>STR is closed without resolution</LI>
+ <LI>STR is active</LI>
+ <LI>STR is pending (new STR or additional information available)</LI>
+</OL>
+
+Trouble reports shall be processed using the following steps.
+
+<H2>Classification</H2>
+
+When a trouble report is received it must be classified at one of the following
+levels:
+
+<OL>
+ <LI>Request for enhancement</LI>
+ <LI>Documentation error</LI>
+ <LI>Unable to print a file</LI>
+ <LI>Unable to print to a printer</LI>
+ <LI>Unable to print at all</LI>
+</OL>
+
+The scope of the problem should also be determined as:
+
+<OL>
+ <LI>Specific to a machine</LI>
+ <LI>Specific to an operating system</LI>
+ <LI>Applies to all machines and operating systems</LI>
+</OL>
+
+<H2>Identification</H2>
+
+Once the level and scope of the trouble report is determined the software
+sub-system(s) involved with the problem are determined. This may involve
+additional communication with the user or vendor to isolate the problem
+to a specific cause.
+
+<P>When the sub-system(s) involved have been identified, an engineer will
+then determine the change(s) needed and estimate the time required for
+the change(s).
+
+<H2>Correction</H2>
+
+Corrections are scheduled based upon the severity and complexity of the
+problem. Once all changes have been made, documented, and tested successfully
+a new software release snapshot is generated. Additional tests are added
+as necessary for proper testing of the changes.
+
+<H2>Notification</H2>
+
+The user or vendor is notified when the fix is available or if the problem
+was caused by user error.
+
+<H1>Software Releases</H1>
+
+<H2>Version Numbering</H2>
+
+CUPS uses a three-part version number separated by periods to represent
+the major, minor, and patch release numbers:
+
+<UL>
+<PRE>
+major.minor.patch
+1.0.0
+</PRE>
+</UL>
+
+Beta-test releases are indentified by appending the letter B followed by
+the build number:
+
+<UL>
+<PRE>
+major.minor.patchbbuild
+1.0.0b1
+</PRE>
+</UL>
+
+A CVS snapshot is generated for every beta and final release and uses
+the version number preceded by the letter "v" and with the decimal
+points replaced by underscores:
+
+<UL>
+<PRE>
+v1_0_0b1
+v1_0_0
+</PRE>
+</UL>
+
+Each change that corrects a fault in a software sub-system increments the
+patch release number. If a change affects the software design of CUPS then
+the minor release number will be incremented and the patch release number
+reset to 0. If CUPS is completely redesigned the major release number will
+be incremented and the minor and patch release numbers reset to 0:
+
+<UL>
+<PRE>
+1.0.0b1 First beta release
+1.0.0b2 Second beta release
+1.0.0 First production release
+1.0.1b1 First beta of 1.0.1
+1.0.1 Production release of 1.0.1
+1.1.0b1 First beta of 1.1.0
+1.1.0 Production release of 1.1.0
+2.0.0b1 First beta of 2.0.0
+2.0.0 Production release of 2.0.0
+</PRE>
+</UL>
+
+<H2>Generation</H2>
+
+Software releases shall be generated for each successfully completed software
+trouble report. All object and executable files shall be deleted prior
+to performing a full build to ensure that source files are recompiled.
+
+<H2>Testing</H2>
+
+Software testing shall be conducted according to the CUPS Software Test
+Plan, CUPS-STP-1.0. Failed tests cause STRs to be generated to correct
+the problems found.
+
+<H2>Release</H2>
+
+When testing has been completed successfully a new distribution image is
+created from the current CVS code "snapshot". No production release shall
+contain software that has not passed the appropriate software tests.
+
+<H1 TYPE=A VALUE=1>Glossary</H1>
+
+<H2>Terms</H2>
+
+<DL>
+
+ <DT>C
+ <DD>A computer language.
+
+ <DT>parallel
+ <DD>Sending or receiving data more than 1 bit at a time.
+
+ <DT>pipe
+ <DD>A one-way communications channel between two programs.
+
+ <DT>serial
+ <DD>Sending or receiving data 1 bit at a time.
+
+ <DT>socket
+ <DD>A two-way network communications channel.
+
+</DL>
+
+<H2>Acronyms</H2>
+
+<DL>
+
+ <DT>ASCII
+ <DD>American Standard Code for Information Interchange
+
+ <DT>CUPS
+ <DD>Common UNIX Printing System
+
+ <DT>ESC/P
+ <DD>EPSON Standard Code for Printers
+
+ <DT>FTP
+ <DD>File Transfer Protocol
+
+ <DT>HP-GL
+ <DD>Hewlett-Packard Graphics Language
+
+ <DT>HP-PCL
+ <DD>Hewlett-Packard Printer Control Language
+
+ <DT>HP-PJL
+ <DD>Hewlett-Packard Printer Job Language
+
+ <DT>IETF
+ <DD>Internet Engineering Task Force
+
+ <DT>IPP
+ <DD>Internet Printing Protocol
+
+ <DT>ISO
+ <DD>International Standards Organization
+
+ <DT>LPD
+ <DD>Line Printer Daemon
+
+ <DT>MIME
+ <DD>Multimedia Internet Mail Exchange
+
+ <DT>PCL
+ <DD>Page Control Language
+
+ <DT>PPD
+ <DD>PostScript Printer Description
+
+ <DT>SMB
+ <DD>Server Message Block
+
+ <DT>TFTP
+ <DD>Trivial File Transfer Protocol
+
+</DL>
+
+<H1>Coding Requirements</H1>
+
+These coding requirements provide detailed information on source file
+formatting and documentation content. These guidelines shall be applied
+to all C and C++ source files provided with CUPS.
+
+<H2>Source Files</H2>
+
+<H3>Naming</H3>
+
+All source files names shall be 16 characters or less in length to
+ensure compatibility with older UNIX filesystems. Source files
+containing functions shall have an extension of ".c" for ANSI C and
+".cpp" for C++ source files. All other "include" files shall have an
+extension of ".h".
+
+<H3>Documentation</H3>
+
+The top of each source file shall contain a header giving the name of the
+file, the purpose or nature of the source file, the copyright and licensing
+notice, and the functions contained in the file. The file name and revision
+information is provided by the CVS "$Id: cmp.shtml 351 1999-05-21 20:54:09Z mike $" tag:
+
+<UL>
+<PRE>
+/*
+ * "$Id: cmp.shtml 351 1999-05-21 20:54:09Z mike $"
+ *
+ * Description of file contents.
+ *
+ * Copyright 1997-1999 by Easy Software Products, all rights
+ * reserved.
+ *
+ * These coded instructions, statements, and computer programs are
+ * the property of Easy Software Products and are protected by
+ * Federal copyright law. Distribution and use rights are outlined
+ * in the file "LICENSE.txt" which should have been included with
+ * this file. If this file is missing or damaged please contact
+ * Easy Software Products at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44145 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * Contents:
+ *
+ * function1() - Description 1.
+ * function2() - Description 2.
+ * function3() - Description 3.
+ */
+</PRE>
+</UL>
+
+The bottom of each source file shall contain a trailer giving the name
+of the file using the CVS "$Id: cmp.shtml 351 1999-05-21 20:54:09Z mike $" tag. The primary purpose of this is to
+mark the end of a source file; if the trailer is missing it is possible
+that code has been lost near the end of the file:
+
+<UL>
+<PRE>
+/*
+ * End of "$Id: cmp.shtml 351 1999-05-21 20:54:09Z mike $".
+ */
+</PRE>
+</UL>
+
+<H2>Functions</H2>
+
+<H3>Naming</H3>
+
+Functions with a global scope shall be capitalized ("DoThis", "DoThat",
+"DoSomethingElse", etc.) The only exception to this rule shall be the
+CUPS interface library functions which may begin with a prefix word in
+lowercase ("cupsDoThis", "cupsDoThat", etc.)
+
+<P>Functions with a local scope shall be declared "static" and be lowercase
+with underscores between words ("do_this", "do_that", "do_something_else",
+etc.)
+
+<H3>Documentation</H3>
+
+Each function shall begin with a comment header describing what the function
+does, the possible input limits (if any), and the possible output values
+(if any), and any special information needed:
+
+<UL>
+<PRE>
+/*
+ * 'do_this()' - Compute y = this(x).
+ *
+ * Notes: none.
+ */
+
+static float /* O - Inverse power value, 0.0 &lt;= y &lt;= 1.0 */
+do_this(float x) /* I - Power value (0.0 &lt;= x &lt;= 1.0) */
+{
+ ...
+ return (y);
+}
+</PRE>
+</UL>
+
+<H2>Methods</H2>
+
+<H3>Naming</H3>
+
+Methods shall be in lowercase with underscores between words ("do_this",
+"do_that", "do_something_else", etc.)
+
+<H3>Documentation</H3>
+
+Each method shall begin with a comment header describing what the method
+does, the possible input limits (if any), and the possible output values
+(if any), and any special information needed:
+
+<UL>
+<PRE>
+/*
+ * 'class::do_this()' - Compute y = this(x).
+ *
+ * Notes: none.
+ */
+
+float /* O - Inverse power value, 0.0 &lt;= y &lt;= 1.0 */
+class::do_this(float x) /* I - Power value (0.0 &lt;= x &lt;= 1.0) */
+{
+ ...
+ return (y);
+}
+</PRE>
+</UL>
+
+<H2>Variables</H2>
+
+<H3>Naming</H3>
+
+Variables with a global scope shall be capitalized ("ThisVariable",
+"ThatVariable", "ThisStateVariable", etc.) The only exception to this
+rule shall be the CUPS interface library global variables which must
+begin with the prefix "cups" ("cupsThisVariable", "cupsThatVariable",
+etc.) Global variables shall be replaced by function arguments whenever
+possible.
+
+<P>Variables with a local scope shall be lowercase with underscores between
+words ("this_variable", "that_variable", etc.) Any local variables shared
+by functions within a source file shall be declared "static".
+
+<H3>Documentation</H3>
+
+Each variable shall be declared on a separate line and shall be immediately
+followed by a comment block describing the variable:
+
+<UL><PRE>
+int this_variable; /* The current state of this */
+int that_variable; /* The current state of that */
+</PRE></UL>
+
+<H2>Types</H2>
+
+<H3>Naming</H3>
+
+All type names shall be lowercase with underscores between words and
+"_t" appended to the end of the name ("this_type_t", "that_type_t",
+etc.)
+
+<H3>Documentation</H3>
+
+Each type shall have a comment block immediately before the typedef:
+
+<UL>
+<PRE>
+/*
+ * This type is for CUPS foobar options.
+ */
+typedef int cups_this_type_t;
+</PRE>
+</UL>
+
+<H2>Structures</H2>
+
+<H3>Naming</H3>
+
+All structure names shall be lowercase with underscores between words and
+"_str" appended to the end of the name ("this_struct_str", "that_struct_str",
+etc.)
+
+<H3>Documentation</H3>
+
+Each structure shall have a comment block immediately before the struct
+and each member shall be documented in accordance with the variable naming
+policy above:
+
+<UL>
+<PRE>
+/*
+ * This structure is for CUPS foobar options.
+ */
+struct cups_this_struct_str
+{
+ int this_member; /* Current state for this */
+ int that_member; /* Current state for that */
+};
+</PRE>
+</UL>
+
+<H2>Classes</H2>
+
+<H3>Naming</H3>
+
+All class names shall be lowercase with underscores between words
+("this_class", "that_class", etc.)
+
+<H3>Documentation</H3>
+
+Each class shall have a comment block immediately before the class
+and each member shall be documented in accordance with the variable naming
+policy above:
+
+<UL>
+<PRE>
+/*
+ * This class is for CUPS foobar options.
+ */
+class cups_this_class
+{
+ int this_member; /* Current state for this */
+ int that_member; /* Current state for that */
+};
+</PRE>
+</UL>
+
+<H2>Constants</H2>
+
+<H3>Naming</H3>
+
+All constant names shall be uppercase with underscored between words
+("THIS_CONSTANT", "THAT_CONSTANT", etc.) Constants defined for the CUPS
+interface library must begin with an uppercase prefix
+("CUPS_THIS_CONSTANT", "CUPS_THAT_CONSTANT", etc.)
+
+<P>Typed enumerations shall be used whenever possible to allow for type
+checking by the compiler.
+
+<H3>Documentation</H3>
+
+Comment blocks shall immediately follow each constant:
+
+<UL>
+<PRE>
+enum
+{
+ CUPS_THIS_TRAY, /* This tray */
+ CUPS_THAT_TRAY /* That tray */
+};
+</PRE>
+</UL>
+
+<H2>Code</H2>
+
+<H3>Documentation</H3>
+
+All source code shall utilize block comments within functions to describe
+the operations being performed by a group of statements:
+
+<UL>
+<PRE>
+/*
+ * Clear the state array before we begin...
+ */
+
+for (i = 0; i &lt; (sizeof(array) / sizeof(sizeof(array[0])); i ++)
+ array[i] = STATE_IDLE;
+
+/*
+ * Wait for state changes...
+ */
+
+do
+{
+ for (i = 0; i &lt; (sizeof(array) / sizeof(sizeof(array[0])); i ++)
+ if (array[i] != STATE_IDLE)
+ break;
+
+ if (i == (sizeof(array) / sizeof(array[0])))
+ sleep(1);
+} while (i == (sizeof(array) / sizeof(array[0])));
+</PRE>
+</UL>
+
+<H3>Style</H3>
+
+<H4 TYPE="a">Indentation</H4>
+
+All code blocks enclosed by brackets shall begin with the opening brace
+on a new line. The code then follows starting on a new line after the brace
+and is indented 2 spaces. The closing brace is then placed on a new line
+following the code at the original indentation:
+
+<UL>
+<PRE>
+{
+ int i; /* Looping var */
+
+ /*
+ * Process foobar values from 0 to 999...
+ */
+
+ for (i = 0; i &lt; 1000; i ++)
+ {
+ do_this(i);
+ do_that(i);
+ }
+}
+</PRE>
+</UL>
+
+Single-line statements following "do", "else", "for", "if", and "while"
+shall be indented 2 spaces as well. Blocks of code in a "switch" block
+shall be indented 4 spaces after each "case" and "default" case:
+
+<UL>
+<PRE>
+switch (array[i])
+{
+ case STATE_IDLE :
+ do_this(i);
+ do_that(i);
+ break;
+ default :
+ do_nothing(i);
+ break;
+}
+</PRE>
+</UL>
+
+<H4>Spacing</H4>
+
+A space shall follow each reserved word ("if", "while", etc.) Spaces shall
+not be inserted between a function name and the arguments in parenthesis.
+
+<H4>Return Values</H4>
+
+Parenthesis shall surround values returned from a function using "return":
+
+<UL>
+<PRE>
+return (STATE_IDLE);
+</PRE>
+</UL>
+
+<H4>Loops</H4>
+
+Whenever convenient loops should count downward to zero to improve program
+performance:
+
+<UL>
+<PRE>
+for (i = sizeof(array) / sizeof(array[0]) - 1; i >= 0; i --)
+ array[i] = STATE_IDLE;
+</PRE>
+</UL>
+
+<H1 ALIGN=RIGHT>Software Trouble Report Form</H1>
+
+<CENTER><TABLE WIDTH="80%">
+<TR>
+ <TH ALIGN=RIGHT>Summary of Problem:</TH>
+ <TD ALIGN=LEFT>________________________________________</TD>
+</TR>
+
+<TR>
+ <TH ALIGN=RIGHT>Problem Severity:</TH>
+ <TD ALIGN=LEFT>__1=RFE
+ <BR>__2=Documentation-Error
+ <BR>__3=Unable-to-Print-a-File
+ <BR>__4=Unable-to-Print-to-a-Printer
+ <BR>__5=Unable-to-Print-at-All</TD>
+</TR>
+
+<TR>
+ <TH ALIGN=RIGHT>Problem Scope:</TH>
+ <TD ALIGN=LEFT>__1=Machine __2=Operating-System __3=All</TD>
+</TR>
+
+<TR>
+ <TH ALIGN=RIGHT VALIGN=TOP>Detailed Description of Problem:</TH>
+ <TD ALIGN=LEFT>________________________________________
+ <BR>________________________________________
+ <BR>________________________________________
+ <BR>________________________________________
+ <BR>________________________________________
+ <BR>________________________________________</TD>
+</TR>
+</TABLE></CENTER>
+
+</BODY>
+</HTML>
diff --git a/doc/cups.css b/doc/cups.css
new file mode 100644
index 000000000..d5f37fa43
--- /dev/null
+++ b/doc/cups.css
@@ -0,0 +1,4 @@
+BODY { background-color: #cccc99 }
+H1 { font-family: sans-serif; }
+H2 { font-family: sans-serif; }
+TH { background-color: #999966 }
diff --git a/doc/cupsdoc.css b/doc/cupsdoc.css
new file mode 100644
index 000000000..333f20144
--- /dev/null
+++ b/doc/cupsdoc.css
@@ -0,0 +1,9 @@
+H1 { font-family: sans-serif }
+H2 { font-family: sans-serif }
+H3 { font-family: sans-serif }
+H4 { font-family: sans-serif }
+H5 { font-family: sans-serif }
+H6 { font-family: sans-serif }
+SUP { font-family: sans-serif; font-size: 6pt }
+PRE { margin-left: 2em }
+CODE { font-weight: bold }
diff --git a/doc/documentation.html b/doc/documentation.html
new file mode 100644
index 000000000..e8803fcb5
--- /dev/null
+++ b/doc/documentation.html
@@ -0,0 +1,70 @@
+<HTML>
+<HEAD>
+ <TITLE>Documentation - Common UNIX Printing System</TITLE>
+ <LINK REL=STYLESHEET TYPE="text/css" HREF="cups.css">
+ <MAP NAME="navbar">
+ <AREA SHAPE="RECT" COORDS="10,10,85,30" HREF="printers" ALT="Current Printer Status">
+ <AREA SHAPE="RECT" COORDS="95,10,175,30" HREF="classes" ALT="Current Printer Classes Status">
+ <AREA SHAPE="RECT" COORDS="185,10,235,30" HREF="jobs" ALT="Current Jobs Status">
+ <AREA SHAPE="RECT" COORDS="245,10,395,30" HREF="documentation.html" ALT="Read CUPS Documentation On-Line">
+ <AREA SHAPE="RECT" COORDS="405,10,490,30" HREF="http://www.cups.org" ALT="Download the Current CUPS Software">
+ </MAP>
+</HEAD>
+
+<BODY>
+<P ALIGN=CENTER>
+<A HREF="http://www.easysw.com" ALT="Easy Software Products Home Page">
+<IMG SRC="images/logo.gif" WIDTH="71" HEIGHT="40" BORDER=0 ALT="Easy Software Products Home Page"></A>
+<IMG SRC="images/navbar.gif" WIDTH="540" HEIGHT="40" USEMAP="#navbar" BORDER=0>
+
+<H1>Documentation</H1>
+
+The following documentation for CUPS is available on this server:
+
+<UL>
+
+ <LI>Whitepaper - An Overview of the Common UNIX Printing System (
+ <A HREF="overview.html">HTML</A> |
+ <A HREF="overview.pdf">PDF</A> )
+
+ <LI>Software Users Manual (
+ <A HREF="sum.html">HTML</A> |
+ <A HREF="sum.pdf">PDF</A> )
+
+ <LI>Software Administrators Manual (
+ <A HREF="sam.html">HTML</A> |
+ <A HREF="sam.pdf">PDF</A> )
+
+ <LI>Configuration Management Plan (
+ <A HREF="cmp.html">HTML</A> |
+ <A HREF="cmp.pdf">PDF</A> )
+
+ <LI>Interface Design Description (
+ <A HREF="idd.html">HTML</A> |
+ <A HREF="idd.pdf">PDF</A> )
+
+ <LI>Software Design Description (
+ <A HREF="sdd.html">HTML</A> |
+ <A HREF="sdd.pdf">PDF</A> )
+
+ <LI>Software Version Description (
+ <A HREF="svd.html">HTML</A> |
+ <A HREF="svd.pdf">PDF</A> )
+
+ <LI>Software Security Report (
+ <A HREF="ssr.html">HTML</A> |
+ <A HREF="ssr.pdf">PDF</A> )
+
+ <LI>Software Test Plan (Not Yet Available)
+
+</UL>
+
+<HR>
+
+<P>The Common UNIX Printing System, CUPS, and the CUPS logo are the
+trademark property of <A HREF="http://www.easysw.com">Easy Software
+Products</A>. CUPS is copyright 1997-1999 by Easy Software Products,
+All Rights Reserved.
+
+</BODY>
+</HTML>
diff --git a/doc/figures.sc b/doc/figures.sc
new file mode 100644
index 000000000..44c439ffd
--- /dev/null
+++ b/doc/figures.sc
Binary files differ
diff --git a/doc/idd.html b/doc/idd.html
new file mode 100644
index 000000000..d003eeb5d
--- /dev/null
+++ b/doc/idd.html
@@ -0,0 +1,746 @@
+<HTML>
+<HEAD>
+<TITLE>DRAFT - CUPS Interface Design Description</TITLE>
+<META NAME="AUTHOR" CONTENT="Easy Software Products">
+<META NAME="COPYRIGHT" CONTENT="Copyright 1997-1999, All Rights Reserved">
+<META NAME="DOCNUMBER" CONTENT="CUPS-IDD-1.0">
+</HEAD>
+<BODY>
+<CENTER><A HREF=#contents><IMG SRC="images/cups-large.gif" BORDER=0><BR>
+<H1>DRAFT - CUPS Interface Design Description</H1></A><BR>
+CUPS-IDD-1.0<BR>
+Easy Software Products<BR>
+Copyright 1997-1999, All Rights Reserved<BR>
+</CENTER>
+<HR>
+<H1 ALIGN=CENTER><A NAME=CONTENTS>Table of Contents</A></H1>
+<BR>
+<BR><B><A HREF=#1>1 Scope</A></B>
+<UL>
+<LI><A HREF=#1_1>1.1 Identification</A></LI>
+<LI><A HREF=#1_2>1.2 System Overview</A></LI>
+<LI><A HREF=#1_3>1.3 Document Overview</A></LI>
+</UL>
+<B><A HREF=#2>2 References</A></B>
+<UL>
+<LI><A HREF=#2_1>2.1 CUPS Documentation</A></LI>
+<LI><A HREF=#2_2>2.2 Other Documents</A></LI>
+</UL>
+<B><A HREF=#3>3 Internal Interfaces</A></B>
+<UL>
+<LI><A HREF=#3_1>3.1 Character Set Files</A></LI>
+<LI><A HREF=#3_2>3.2 Language Files</A></LI>
+<LI><A HREF=#3_3>3.3 MIME Files</A></LI>
+<UL>
+<LI><A HREF=#3_3_1>3.3.1 mime.types</A></LI>
+<LI><A HREF=#3_3_2>3.3.2 mime.convs</A></LI>
+</UL>
+<LI><A HREF=#3_4>3.4 PostScript Printer Description Files</A></LI>
+<UL>
+<LI><A HREF=#3_4_1>3.4.1 CUPS Extensions to PPD Files</A></LI>
+</UL>
+<LI><A HREF=#3_5>3.5 Scheduler Configuration Files</A></LI>
+<UL>
+<LI><A HREF=#3_5_1>3.5.1 classes.conf</A></LI>
+<LI><A HREF=#3_5_2>3.5.2 cupsd.conf</A></LI>
+<LI><A HREF=#3_5_3>3.5.3 printers.conf</A></LI>
+</UL>
+</UL>
+<B><A HREF=#4>4 External Interfaces</A></B>
+<UL>
+<LI><A HREF=#4_1>4.1 AppSocket Protocol</A></LI>
+<LI><A HREF=#4_2>4.2 CUPS Browsing Protocol</A></LI>
+<LI><A HREF=#4_3>4.3 CUPS PostScript File</A></LI>
+<LI><A HREF=#4_4>4.4 CUPS Raster File</A></LI>
+<LI><A HREF=#4_5>4.5 CUPS Raw Files</A></LI>
+<LI><A HREF=#4_6>4.6 File Transfer Protocol</A></LI>
+<LI><A HREF=#4_7>4.7 Internet Printing Protocol</A></LI>
+<UL>
+<LI><A HREF=#4_7_1>4.7.1 Get Default Destination (CUPS_GET_DEFAULT =
+0x4001)</A></LI>
+<LI><A HREF=#4_7_2>4.7.2 Get Printers (CUPS_GET_PRINTERS = 0x4002)</A></LI>
+<LI><A HREF=#4_7_3>4.7.3 Add Printer (CUPS_ADD_PRINTER = 0x4003)</A></LI>
+<LI><A HREF=#4_7_4>4.7.4 Delete Printer (CUPS_DELETE_PRINTER = 0x4004)</A>
+</LI>
+<LI><A HREF=#4_7_5>4.7.5 Get Classes (CUPS_GET_CLASSES = 0x4005)</A></LI>
+<LI><A HREF=#4_7_6>4.7.6 Add Class (CUPS_ADD_CLASS = 0x4006)</A></LI>
+<LI><A HREF=#4_7_7>4.7.7 Delete Class (CUPS_DELETE_CLASS = 0x4007)</A></LI>
+<LI><A HREF=#4_7_8>4.7.8 Accept Jobs (CUPS_ACCEPT_JOBS = 0x4008)</A></LI>
+<LI><A HREF=#4_7_9>4.7.9 Reject Jobs (CUPS_REJECT_JOBS = 0x4009)</A></LI>
+<LI><A HREF=#4_7_10>4.7.10 Set Default Destination (CUPS_SET_DEFAULT =
+0x400A)</A></LI>
+</UL>
+<LI><A HREF=#4_8>4.8 Line Printer Daemon Protocol</A></LI>
+<LI><A HREF=#4_9>4.9 Server Message Block Protocol</A></LI>
+<LI><A HREF=#4_10>4.10 Trivial File Transfer Protocol</A></LI>
+</UL>
+<B><A HREF=#5>5 5 - Directories</A></B>
+<BR>
+<BR><B><A HREF=#6>A Glossary</A></B>
+<UL>
+<LI><A HREF=#6_1>A.1 Terms</A></LI>
+<LI><A HREF=#6_2>A.2 Acronyms</A></LI>
+</UL>
+<HR>
+<H1><A NAME=1>1 Scope</A></H1>
+<H2><A NAME=1_1>1.1 Identification</A></H2>
+<P>This interface design description document provides detailed file
+formats, message formats, and program conventions for the Common UNIX
+Printing System (&quot;CUPS&quot;) Version 1.0. </P>
+<H2><A NAME=1_2>1.2 System Overview</A></H2>
+<P>The Common UNIX Printing System provides a portable printing layer
+for UNIX&reg; operating systems. It has been developed by Easy Software
+ Products to promote a standard printing solution for all UNIX vendors
+ and users. CUPS provides the System V and Berkeley command-line
+interfaces. </P>
+<P>CUPS uses the Internet Printing Protocol (IETF-IPP) as the basis for
+managing print jobs and queues. The Line Printer Daemon (LPD, RFC1179),
+Server Message Block (SMB), and AppSocket protocols are also supported
+with reduced functionality. </P>
+<P>CUPS adds network printer browsing and PostScript Printer
+Description (&quot;PPD&quot;)-based printing options to support real world
+applications under UNIX. </P>
+<P>CUPS also includes a customized version of GNU GhostScript
+(currently based off GNU GhostScript 4.03) and an image file RIP that
+can be used to support non-PostScript printers. </P>
+<H2><A NAME=1_3>1.3 Document Overview</A></H2>
+<P>This interface design description document is organized into the
+following sections: </P>
+<UL>
+<LI>1 - Scope </LI>
+<LI>2 - References </LI>
+<LI>3 - Internal Interfaces </LI>
+<LI>4 - External Interfaces </LI>
+<LI>5 - Directories </LI>
+<LI>A - Glossary </LI>
+</UL>
+<H1><A NAME=2>2 References</A></H1>
+<H2><A NAME=2_1>2.1 CUPS Documentation</A></H2>
+<P>The following CUPS documentation is referenced by this document: </P>
+<UL>
+<LI>CUPS-CMP-1.0: CUPS Configuration Management Plan </LI>
+<LI>CUPS-IDD-1.0: CUPS System Interface Design Description </LI>
+<LI>CUPS-SAM-1.0.x: CUPS Software Administrators Manual </LI>
+<LI>CUPS-SDD-1.0: CUPS Software Design Description </LI>
+<LI>CUPS-SPM-1.0: CUPS Software Programming Manual </LI>
+<LI>CUPS-SSR-1.0: CUPS Software Security Report </LI>
+<LI>CUPS-STP-1.0: CUPS Software Test Plan </LI>
+<LI>CUPS-SUM-1.0.x: CUPS Software Users Manual </LI>
+<LI>CUPS-SVD-1.0.x: CUPS Software Version Description </LI>
+</UL>
+<H2><A NAME=2_2>2.2 Other Documents</A></H2>
+<P>The following non-CUPS documents are referenced by this document: </P>
+<UL>
+<LI>IEEE 1387.4, System Administration: Printing (draft) </LI>
+<LI>IPP/1.0: Additional Optional Operations - Set 1 </LI>
+<LI>IPP/1.0: Encoding and Transport </LI>
+<LI>IPP/1.0: Implementers Guide </LI>
+<LI>IPP/1.0: Model and Semantics </LI>
+<LI>RFC 1179, Line Printer Daemon Protocol </LI>
+</UL>
+<H1><A NAME=3>3 Internal Interfaces</A></H1>
+<H2><A NAME=3_1>3.1 Character Set Files</A></H2>
+<P>The character set files define a mapping between 8-bit characters
+and the Unicode character set. They are named using the ISO standard
+number defined for the character set. Each file consists of up to 256
+lines of ASCII text. Each line consists of two hexadecimal numbers; the
+first number is the character number in the character set (0x00 to
+0xff), and the second number is the Unicode character number (0x0000 to
+0xffff). </P>
+<H2><A NAME=3_2>3.2 Language Files</A></H2>
+<P>The language files define the default character set and a collection
+of text messages in that language. They are named by prefixing the
+string &quot;cups_&quot; to the front of the language specifier (e.g. &quot;cups_en&quot;,
+&quot;cups_fr&quot;, etc.) Each file consists of two or more lines of ASCII text. </P>
+<P>The first line identifies the character set to be used for the
+messages. The currently recognized values are: </P>
+<UL>
+<LI>us-ascii </LI>
+<LI>utf-8 </LI>
+<LI>iso-8859-1 </LI>
+<LI>iso-8859-2 </LI>
+<LI>iso-8859-3 </LI>
+<LI>iso-8859-4 </LI>
+<LI>iso-8859-5 </LI>
+<LI>iso-8859-6 </LI>
+<LI>iso-8859-7 </LI>
+<LI>iso-8859-8 </LI>
+<LI>iso-8859-9 </LI>
+<LI>iso-8859-14 </LI>
+<LI>iso-8859-15 </LI>
+</UL>
+<P>The second and succeeding lines define text messages. If the message
+text is preceded by a number, then the current message number is
+updated and the text after the number is used. </P>
+<H2><A NAME=3_3>3.3 MIME Files</A></H2>
+<P>CUPS uses two MIME files in its standard configuration. </P>
+<H3><A NAME=3_3_1>3.3.1 mime.types</A></H3>
+<P>The mime.types file defines the recognized file types and consists
+of 1 or more lines of ASCII text. Comment lines start with the pound
+(&quot;#&quot;) character. The backslash (&quot;\&quot;) character can be used at the end
+of a line to continue that line to the next. </P>
+<P>Each non-blank line starts with a MIME type identifier
+(&quot;super/type&quot;) as registered with the IANA. All text following the MIME
+type is treated as a series of type recognition rules: </P>
+<UL>
+<PRE>
+mime-type := super &quot;/&quot; type { SP rule }*
+super := { &quot;a-z&quot; | &quot;A-Z&quot; }*
+type := { &quot;a-z&quot; | &quot;A-Z&quot; | &quot;-&quot; | &quot;.&quot; | &quot;0-9&quot; }*
+rule := { extension | match | operator | &quot;(&quot; rule &quot;)&quot; }*
+extension := { &quot;a-z&quot; | &quot;A-Z&quot; | &quot;0-9&quot; }*
+match := &quot;match(&quot; regexp &quot;)&quot; |
+ &quot;ascii(&quot; offset &quot;,&quot; length &quot;)&quot; |
+ &quot;printable(&quot; offset &quot;,&quot; length &quot;)&quot; |
+ &quot;string(&quot; offset &quot;,&quot; string &quot;)&quot; |
+ &quot;char(&quot; offset &quot;,&quot; value &quot;)&quot; |
+ &quot;short(&quot; offset &quot;,&quot; value &quot;)&quot; |
+ &quot;int(&quot; offset &quot;,&quot; value &quot;)&quot; |
+ &quot;locale(&quot; string &quot;)&quot;
+operator := &quot;+&quot; | [ logical AND ]
+ &quot;,&quot; | SP [ logical OR ]
+ &quot;!&quot; [ unary NOT ]
+</PRE>
+</UL>
+<P>The <CODE>int</CODE> and <CODE>short</CODE> rules match look for
+integers in network byte order (a.k.a. big-endian) with the
+most-significant byte first. </P>
+<H3><A NAME=3_3_2>3.3.2 mime.convs</A></H3>
+<P>The mime.types file defines the recognized file filters and consists
+of 1 or more lines of ASCII text. Comment lines start with the pound
+(&quot;#&quot;) character. </P>
+<P>Each non-blank line starts with two MIME type identifiers
+(&quot;super/type&quot;) representing the source and destination types. Following
+the MIME types are a cost value (0 to 100) and the filter program to
+use. If the filter program is not specified using the full path then it
+must reside in the CUPS filter directory. </P>
+<H2><A NAME=3_4>3.4 PostScript Printer Description Files</A></H2>
+<P>The PostScript Printer Description (PPD) file format is described in <A
+HREF=http://partners.adobe.com/supportservice/devrelations/PDFS/TN/5003.PPD_Spec_v4.3.pdf>
+Adobe TechNote #5003: PostScript Printer Description File Format
+Specification Version 4.3</A>. </P>
+<H3><A NAME=3_4_1>3.4.1 CUPS Extensions to PPD Files</A></H3>
+<P>CUPS adds several new attributes that are described below. </P>
+<H4>3.4.1.1 cupsFilter</H4>
+<P>This string attribute provides a conversion rule of the form: </P>
+<UL>
+<PRE>
+source/type cost program
+</PRE>
+</UL>
+<P>The destination type is assumed to the printer's type. If a printer
+supports the source type directly the special filter program &quot;-&quot; may be
+specified. </P>
+<H4>3.4.1.2 cupsManualCopies</H4>
+<P>This boolean attribute notifies the RIP filters that the destination
+printer does not support copy generation in hardware. The default value
+is false. </P>
+<H4>3.4.1.3 cupsModelNumber</H4>
+<P>This integer attribute specifies a printer-specific model number.
+This number can be used by a filter program to adjust the output for a
+specific model of printer. </P>
+<H4>3.4.1.4 cupsProfile</H4>
+<P>This string attribute specifies a color profile of the form: </P>
+<UL>
+<PRE>
+resolution/type density gamma m00 m01 m02 m10 m11 m12 m20 m21 m22
+</PRE>
+</UL>
+<P>The <I>resolution</I> and <I>type</I> values may be &quot;-&quot; to act as a
+wildcard. Otherwise they must match one of the <CODE>Resolution</CODE>
+ or <CODE>MediaType</CODE> attributes defined in the PPD file. </P>
+<P>The <I>density</I> and <I>gamma</I> values define gamma and density
+adjustment function such that: </P>
+<UL>
+<PRE>
+f(x) = density * x<SUP>gamma</SUP>
+</PRE>
+</UL>
+<P>The <I>m00</I> through <I>m22</I> values define a 3x3 transformation
+matrix for the CMY color values. The density function is applied <I>
+after</I> the CMY transformation. </P>
+<H4>3.4.1.5 cupsVersion</H4>
+<P>This required attribute describes which version of the CUPS IDD was
+used for the PPD file extensions. Currently it must be the string
+&quot;1.0&quot;. </P>
+<H2><A NAME=3_5>3.5 Scheduler Configuration Files</A></H2>
+<P>The scheduler reads three configuration files that define the
+available printers, classes, and services: </P>
+<DL>
+<DT>classes.conf </DT>
+<DD>This file defines all of the printer classes known to the system. </DD>
+<DT>cupsd.conf </DT>
+<DD>This file defines the files, directories, passwords, etc. used by
+the scheduler. </DD>
+<DT>printers.conf </DT>
+<DD>This file defines all of the printers known to the system. </DD>
+</DL>
+<H3><A NAME=3_5_1>3.5.1 classes.conf</A></H3>
+<P>The classes.conf file consists of 1 or more lines of ASCII text.
+ Comment lines start with the pound (&quot;#&quot;) character. </P>
+<P>Each non-blank line starts with the name of a configuration
+directive followed by its value. The following directives are
+understood:
+<CENTER>
+<TABLE BORDER=1 WIDTH=90%>
+<TR><TH WIDTH=25%>Directive</TH><TH>Description</TH></TR>
+<TR><TD>&lt;Class name&gt;
+<BR> &lt;/Class&gt;</TD><TD></TR>
+<TR><TD>&lt;DefaultClass name&gt;
+<BR> &lt;/Class&gt;</TD><TD></TR>
+<TR><TD>Info</TD><TD></TR>
+<TR><TD>Location</TD><TD></TR>
+<TR><TD>MoreInfo</TD><TD></TR>
+<TR><TD>Printer</TD><TD></TR>
+</TABLE>
+</CENTER>
+</P>
+<H3><A NAME=3_5_2>3.5.2 cupsd.conf</A></H3>
+<P>The cupsd.conf file consists of 1 or more lines of ASCII text.
+ Comment lines start with the pound (&quot;#&quot;) character. </P>
+<P>Each non-blank line starts with the name of a configuration
+directive followed by its value. The following directives are
+understood:
+<CENTER>
+<TABLE BORDER=1 WIDTH=90%>
+<TR><TH WIDTH=25%>Directive</TH><TH>Default</TH><TH>Description</TH></TR>
+<TR><TD>AccessLog</TD><TD><TD>Specifies the location of the access log
+file (default &quot;logs/access_log&quot;).</TD></TR>
+<TR><TD>Allow</TD><TD><TD></TR>
+<TR><TD>AuthClass</TD><TD><TD></TR>
+<TR><TD>AuthType</TD><TD><TD></TR>
+<TR><TD>BrowseAddress</TD><TD><TD></TR>
+<TR><TD>BrowseInterval</TD><TD><TD></TR>
+<TR><TD>BrowsePort</TD><TD><TD></TR>
+<TR><TD>BrowseTimeout</TD><TD><TD></TR>
+<TR><TD>Browsing</TD><TD><TD></TR>
+<TR><TD>DefaultCharset</TD><TD><TD></TR>
+<TR><TD>DefaultLanguage</TD><TD><TD></TR>
+<TR><TD>Deny</TD><TD><TD></TR>
+<TR><TD>DocumentRoot</TD><TD><TD></TR>
+<TR><TD>ErrorLog</TD><TD><TD></TR>
+<TR><TD>Group</TD><TD><TD></TR>
+<TR><TD>HostNameLookups</TD><TD><TD></TR>
+<TR><TD>ImplicitClasses</TD><TD><TD></TR>
+<TR><TD>KeepAlive</TD><TD><TD></TR>
+<TR><TD>KeepAliveTimeout</TD><TD><TD></TR>
+<TR><TD>&lt;Location path&gt;
+<BR> &lt;/Location&gt;</TD><TD><TD></TR>
+<TR><TD>LogLevel</TD><TD><TD></TR>
+<TR><TD>MaxClients</TD><TD><TD></TR>
+<TR><TD>MaxLogSize</TD><TD><TD></TR>
+<TR><TD>MaxRequestSize</TD><TD><TD></TR>
+<TR><TD>Order</TD><TD><TD></TR>
+<TR><TD>PageLog</TD><TD><TD></TR>
+<TR><TD>Port</TD><TD><TD></TR>
+<TR><TD>RIPCache</TD><TD><TD></TR>
+<TR><TD>ServerAdmin</TD><TD><TD></TR>
+<TR><TD>ServerName</TD><TD><TD></TR>
+<TR><TD>ServerRoot</TD><TD><TD></TR>
+<TR><TD>SystemGroup</TD><TD><TD></TR>
+<TR><TD>TempDir</TD><TD><TD></TR>
+<TR><TD>Timeout</TD><TD><TD></TR>
+<TR><TD>User</TD><TD><TD></TR>
+</TABLE>
+</CENTER>
+</P>
+<H3><A NAME=3_5_3>3.5.3 printers.conf</A></H3>
+<P>The printers.conf file consists of 1 or more lines of ASCII text.
+ Comment lines start with the pound (&quot;#&quot;) character. </P>
+<P>Each non-blank line starts with the name of a configuration
+directive followed by its value. The following directives are
+understood:
+<CENTER>
+<TABLE BORDER=1 WIDTH=90%>
+<TR><TH WIDTH=25%>Directive</TH><TH>Description</TH></TR>
+<TR><TD>&lt;DefaultPrinter name&gt;
+<BR> &lt;/Printer&gt;</TD><TD></TR>
+<TR><TD>DeviceURI</TD><TD></TR>
+<TR><TD>Info</TD><TD></TR>
+<TR><TD>Location</TD><TD></TR>
+<TR><TD>MoreInfo</TD><TD></TR>
+<TR><TD>&lt;Printer name&gt;
+<BR> &lt;/Printer&gt;</TD><TD></TR>
+<TR><TD>State</TD><TD></TR>
+</TABLE>
+</CENTER>
+</P>
+<H1><A NAME=4>4 External Interfaces</A></H1>
+<H2><A NAME=4_1>4.1 AppSocket Protocol</A></H2>
+<P>The AppSocket protocol is an 8-bit clean TCP/IP socket connection.
+The default IP service port is 9100. </P>
+<H2><A NAME=4_2>4.2 CUPS Browsing Protocol</A></H2>
+<P>The CUPS Browsing Protocol is a UDP/IP-based broadcast service. By
+default this service operates on IP service port 631. </P>
+<P>Each broadcast packet describes the state of a single printer or
+class and is an ASCII text string of up to 1450 bytes ending with a
+newline (0x0a). The string is formatted as follows: </P>
+<UL>
+<PRE>
+type SP state SP uri NL
+</PRE>
+</UL>
+<P>The <I>state</I> and <I>uri</I> values correspond to the IPP <CODE>
+printer-state</CODE> and <CODE>printer-uri-supported</CODE> attributes. </P>
+<P>The <I>type</I> value is a hexadecimal number string representing
+capability/type bits:
+<CENTER>
+<TABLE BORDER=1 WIDTH=40%>
+<TR><TH WIDTH=8%>Bit</TH><TH>Description</TH></TR>
+<TR><TD>0</TD><TD>0 = printer
+<BR> 1 = class</TD></TR>
+<TR><TD>1</TD><TD>0 = local
+<BR> 1 = remote
+<BR> (always 1)</TD></TR>
+<TR><TD>2</TD><TD>1 = can print B</TD></TR>
+<TR><TD>3</TD><TD>1 = can print color</TD></TR>
+<TR><TD>4</TD><TD>1 = can duplex</TD></TR>
+<TR><TD>5</TD><TD>1 = can staple</TD></TR>
+<TR><TD>6</TD><TD>1 = can do fast copies</TD></TR>
+<TR><TD>7</TD><TD>1 = can do fast collating</TD></TR>
+<TR><TD>8</TD><TD>1 = can punch holes</TD></TR>
+<TR><TD>9</TD><TD>1 = can cover</TD></TR>
+<TR><TD>10</TD><TD>1 = can bind</TD></TR>
+<TR><TD>11</TD><TD>1 = can sort</TD></TR>
+<TR><TD>12</TD><TD>1 = can print up to 9x14 inches</TD></TR>
+<TR><TD>13</TD><TD>1 = can print up to 18x24 inches</TD></TR>
+<TR><TD>14</TD><TD>1 = can print up to 36x48 inches</TD></TR>
+<TR><TD>15</TD><TD>1 = can print variable sizes</TD></TR>
+</TABLE>
+</CENTER>
+</P>
+<H2><A NAME=4_3>4.3 CUPS PostScript File</A></H2>
+<P>CUPS PostScript files are device-dependent Adobe PostScript program
+files. The PostScript language is described in the <A HREF=http://partners.adobe.com/supportservice/devrelations/PDFS/TN/PLRM.pdf>
+Adobe PostScript Language Reference Manual, Third Edition</A>. </P>
+<P>The MIME type for CUPS PostScript files is <CODE>
+application/vnd.cups-postscript</CODE>. </P>
+<H2><A NAME=4_4>4.4 CUPS Raster File</A></H2>
+<P>CUPS raster files are device-dependent raster image files that
+contain a PostScript page device dictionary and device-dependent raster
+imagery for each page in the document. These files are used to
+transfer raster data from the PostScript and image file RIPs to
+device-dependent filters that convert the raster data to a printable
+format. </P>
+<P>A raster file begins with a four byte synchronization word:
+0x52615374 (&quot;RaSt&quot;) for big-endian architectures and 0x74536152
+(&quot;tSaR&quot;) for little-endian architectures. The writer of the raster
+file will use the native word order, and the reader is responsible for
+detecting a reversed word order file and swapping bytes as needed. The
+CUPS Interface Library raster functions perform this function
+automatically. </P>
+<P>Following the synchronization word are a series of raster pages.
+ Each page starts with a page device dictionary header and is followed
+immediately by the raster data for that page.
+<CENTER>
+<TABLE BORDER=1 WIDTH=80%>
+<TR><TH WIDTH=10%>Bytes</TH><TH WIDTH=20%>Description</TH><TH>Values</TH>
+</TR>
+<TR><TD>0-63</TD><TD>MediaClass</TD><TD>Nul-terminated ASCII string</TD></TR>
+<TR><TD>64-127</TD><TD>MediaColor</TD><TD>Nul-terminated ASCII string</TD>
+</TR>
+<TR><TD>128-191</TD><TD>MediaType</TD><TD>Nul-terminated ASCII string</TD>
+</TR>
+<TR><TD>192-255</TD><TD>OutputType</TD><TD>Nul-terminated ASCII string</TD>
+</TR>
+<TR><TD>256-259</TD><TD>AdvanceDistance</TD><TD>0 to 2<SUP>32</SUP> - 1
+points</TD></TR>
+<TR><TD>260-263</TD><TD>AdvanceMedia</TD><TD>0 = Never advance roll
+<BR> 1 = Advance roll after file
+<BR> 2 = Advance roll after job
+<BR> 3 = Advance roll after set
+<BR> 4 = Advance roll after page</TD></TR>
+<TR><TD>264-267</TD><TD>Collate</TD><TD>0 = do not collate copies
+<BR> 1 = collate copies</TD></TR>
+<TR><TD>268-271</TD><TD>CutMedia</TD><TD>0 = Never cut media
+<BR> 1 = Cut roll after file
+<BR> 2 = Cut roll after job
+<BR> 3 = Cut roll after set
+<BR> 4 = Cut roll after page</TD></TR>
+<TR><TD>272-275</TD><TD>Duplex</TD><TD>0 = Print single-sided
+<BR> 1 = Print double-sided</TD></TR>
+<TR><TD>276-283</TD><TD>HWResolution</TD><TD>Horizontal and vertical
+resolution in dots-per-inch.</TD></TR>
+<TR><TD>284-299</TD><TD>ImagingBoundingBox</TD><TD>Four integers giving
+the left, bottom, right, and top positions of the page bounding box in
+points</TD></TR>
+<TR><TD>300-303</TD><TD>InsertSheet</TD><TD>0 = Do not insert separator
+sheets
+<BR> 1 = Insert separator sheets</TD></TR>
+<TR><TD>304-307</TD><TD>Jog</TD><TD>0 = Do no jog pages
+<BR> 1 = Jog pages after file
+<BR> 2 = Jog pages after job
+<BR> 3 = Jog pages after set</TD></TR>
+<TR><TD>308-311</TD><TD>LeadingEdge</TD><TD>0 = Top edge is first
+<BR> 1 = Right edge is first
+<BR> 2 = Bottom edge is first
+<BR> 3 = Left edge is first</TD></TR>
+<TR><TD>312-319</TD><TD>Margins</TD><TD>Left and bottom origin of image
+in points</TD></TR>
+<TR><TD>320-323</TD><TD>ManualFeed</TD><TD>0 = Do not manually feed
+media
+<BR> 1 = Manually feed media</TD></TR>
+<TR><TD>324-327</TD><TD>MediaPosition</TD><TD>Input slot position from
+0 to N</TD></TR>
+<TR><TD>328-331</TD><TD>MediaWeight</TD><TD>Media weight in grams per
+meter squared</TD></TR>
+<TR><TD>332-335</TD><TD>MirrorPrint</TD><TD>0 = Do not mirror prints
+<BR> 1 = Mirror prints</TD></TR>
+<TR><TD>336-339</TD><TD>NegativePrint</TD><TD>0 = Do not invert prints
+<BR> 1 = Invert prints</TD></TR>
+<TR><TD>340-343</TD><TD>NumCopies</TD><TD>1 to 2<SUP>32</SUP> - 1</TD></TR>
+<TR><TD>344-347</TD><TD>Orientation</TD><TD>0 = Do not rotate page
+<BR> 1 = Rotate page counter-clockwise
+<BR> 2 = Turn page upside down
+<BR> 3 = Rotate page clockwise</TD></TR>
+<TR><TD>348-351</TD><TD>OutputFaceUp</TD><TD>0 = Output face down
+<BR> 1 = Output face up</TD></TR>
+<TR><TD>352-359</TD><TD>PageSize</TD><TD>Width and length in points</TD></TR>
+<TR><TD>360-363</TD><TD>Separations</TD><TD>0 = Print composite image
+<BR> 1 = Print color separations</TD></TR>
+<TR><TD>364-367</TD><TD>TraySwitch</TD><TD>0 = Do not change trays if
+selected tray is empty
+<BR> 1 = Change trays if selected tray is empty</TD></TR>
+<TR><TD>368-371</TD><TD>Tumble</TD><TD>0 = Do not rotate even pages
+when duplexing
+<BR> 1 = Rotate even pages when duplexing</TD></TR>
+<TR><TD>372-375</TD><TD>cupsWidth</TD><TD>Width of page image in pixels</TD>
+</TR>
+<TR><TD>376-379</TD><TD>cupsHeight</TD><TD>Height of page image in
+pixels</TD></TR>
+<TR><TD>380-383</TD><TD>cupsMediaType</TD><TD>Driver-specific 0 to 2<SUP>
+32</SUP> - 1</TD></TR>
+<TR><TD>384-387</TD><TD>cupsBitsPerColor</TD><TD>1, 2, 4, 8 bits</TD></TR>
+<TR><TD>388-391</TD><TD>cupsBitsPerPixel</TD><TD>1 to 32 bits</TD></TR>
+<TR><TD>392-395</TD><TD>cupsBytesPerLine</TD><TD>1 to 2<SUP>32</SUP> -
+1 bytes</TD></TR>
+<TR><TD>396-399</TD><TD>cupsColorOrder</TD><TD>0 = chunky pixels (CMYK
+CMYK CMYK)
+<BR> 1 = banded pixels (CCC MMM YYY KKK)
+<BR> 2 = planar pixels (CCC... MMM... YYY... KKK...)</TD></TR>
+<TR><TD>400-403</TD><TD>cupsColorSpace</TD><TD>0 = white
+<BR> 1 = RGB
+<BR> 2 = RGBA
+<BR> 3 = black
+<BR> 4 = CMY
+<BR> 5 = YMC
+<BR> 6 = CMYK
+<BR> 7 = YMCK
+<BR> 8 = KCMY
+<BR> 9 = KCMYcm</TD></TR>
+<TR><TD>404-407</TD><TD>cupsCompression</TD><TD>Driver-specific 0 to 2<SUP>
+32</SUP> - 1</TD></TR>
+<TR><TD>408-411</TD><TD>cupsRowCount</TD><TD>Driver-specific 0 to 2<SUP>
+32</SUP> - 1</TD></TR>
+<TR><TD>412-415</TD><TD>cupsRowFeed</TD><TD>Driver-specific 0 to 2<SUP>
+32</SUP> - 1</TD></TR>
+<TR><TD>416-419</TD><TD>cupsRowStep</TD><TD>Driver-specific 0 to 2<SUP>
+32</SUP> - 1</TD></TR>
+</TABLE>
+</CENTER>
+</P>
+<P>The MIME type for CUPS Raster files is <CODE>
+application/vnd.cups-raster</CODE>. </P>
+<H2><A NAME=4_5>4.5 CUPS Raw Files</A></H2>
+<P>Raw files are printer-dependent print files that are in a format
+suitable to the destination printer (e.g. HP-PCL, HP-RTL, etc.) The
+MIME type for CUPS Raw files is <CODE>application/vnd.cups-raw</CODE>. </P>
+<H2><A NAME=4_6>4.6 File Transfer Protocol</A></H2>
+<P>The File Transfer Protocol (FTP) is described by <A HREF=http://www.ietf.org/rfc/rfc959.txt>
+RFC 959: File Transfer Protocol</A>. </P>
+<H2><A NAME=4_7>4.7 Internet Printing Protocol</A></H2>
+<P>The Internet Printing Protocol is described by the following RFCs: </P>
+<UL>
+<LI><A HREF=http://www.ietf.org/rfc/rfc2565.txt>RFC 2565: Internet
+Printing Protocol/1.0: Encoding and Transport</A></LI>
+<LI><A HREF=http://www.ietf.org/rfc/rfc2566.txt>RFC 2566: Internet
+Printing Protocol/1.0: Model and Semantics</A></LI>
+<LI><A HREF=http://www.ietf.org/rfc/rfc2567.txt>RFC 2567: Design Goals
+for an Internet Printing Protocol</A></LI>
+<LI><A HREF=http://www.ietf.org/rfc/rfc2568.txt>RFC 2568: Rationale for
+the Structure of the Model and Protocol for the Internet Printing
+Protocol</A></LI>
+<LI><A HREF=http://www.ietf.org/rfc/rfc2569.txt>RFC 2569: Mapping
+between LPD and IPP Protocols</A></LI>
+</UL>
+<P>CUPS defines the following extension operations to IPP. </P>
+<H3><A NAME=4_7_1>4.7.1 Get Default Destination (CUPS_GET_DEFAULT =
+0x4001)</A></H3>
+<P>The get default destination operation returns the printer attributes
+for the system default printer or class. The only required attributes
+are <CODE>attributes-charset</CODE> and <CODE>
+attributes-natural-language</CODE>. </P>
+<P>Get default destination will only return <CODE>ipp-ok</CODE>. </P>
+<H3><A NAME=4_7_2>4.7.2 Get Printers (CUPS_GET_PRINTERS = 0x4002)</A></H3>
+<P>The get printers operation returns the printer attributes for all
+printers known to the system. The only required attributes are <CODE>
+attributes-charset</CODE> and <CODE>attributes-natural-language</CODE>. </P>
+<P>Get printers will only return <CODE>ipp-ok</CODE>. </P>
+<H3><A NAME=4_7_3>4.7.3 Add Printer (CUPS_ADD_PRINTER = 0x4003)</A></H3>
+<P>The add printer operation adds or replaces the specified printer.
+The <CODE>attributes-charset</CODE>, <CODE>attributes-natural-language</CODE>
+ and <CODE>printer-uri</CODE> attributes are required. </P>
+<P>The <CODE>printer-location</CODE>, <CODE>printer-info</CODE>, <CODE>
+printer-more-info</CODE>, and <CODE>device-uri</CODE> attributes are
+required when initially adding a printer and optional when modifying a
+printer. </P>
+<P>A PPD file or System V interface script may follow the IPP request
+body. If a valid interface script or PPD file is not provided then the
+printer is treated as a generic PostScript device. </P>
+<P>Add printer will return <CODE>ipp-ok</CODE>, <CODE>ipp-not-authorized</CODE>
+, <CODE>ipp-bad-request</CODE>, or <CODE>ipp-attributes</CODE>. </P>
+<H3><A NAME=4_7_4>4.7.4 Delete Printer (CUPS_DELETE_PRINTER = 0x4004)</A>
+</H3>
+<P>The delete printer operation removes the specified printer. The only
+required attributes are <CODE>attributes-charset</CODE>, <CODE>
+attributes-natural-language</CODE>, and <CODE>printer-uri</CODE>. </P>
+<P>Delete printer will return <CODE>ipp-ok</CODE>, <CODE>ipp-not-found</CODE>
+, or <CODE>ipp-not-authorized</CODE>. </P>
+<H3><A NAME=4_7_5>4.7.5 Get Classes (CUPS_GET_CLASSES = 0x4005)</A></H3>
+<P>The get classes operation returns the printer attributes for all
+classes known to the system. The only required attributes are <CODE>
+attributes-charset</CODE> and <CODE>attributes-natural-language</CODE>. </P>
+<P>Get classes will only return <CODE>ipp-ok</CODE>. </P>
+<H3><A NAME=4_7_6>4.7.6 Add Class (CUPS_ADD_CLASS = 0x4006)</A></H3>
+<P>The add class operation adds or replaces the specified class. The <CODE>
+attributes-charset</CODE>, <CODE>attributes-natural-language</CODE>,
+and <CODE>printer-uri</CODE> attributes are required. </P>
+<P>The <CODE>printer-location</CODE>, <CODE>printer-info</CODE>, <CODE>
+printer-more-info</CODE>, and <CODE>member-uris</CODE> attributes are
+required when initially adding a printer and optional when modifying a
+printer. </P>
+<P>Add class will return <CODE>ipp-ok</CODE>, <CODE>ipp-not-authorized</CODE>
+, <CODE>ipp-bad-request</CODE>, or <CODE>ipp-attributes</CODE>. </P>
+<H3><A NAME=4_7_7>4.7.7 Delete Class (CUPS_DELETE_CLASS = 0x4007)</A></H3>
+<P>The delete class operation removes the specified class. The only
+required attributes are <CODE>attributes-charset</CODE>, <CODE>
+attributes-natural-language</CODE>, and <CODE>printer-uri</CODE>. </P>
+<P>Delete class will return <CODE>ipp-ok</CODE>, <CODE>ipp-not-found</CODE>
+, or <CODE>ipp-not-authorized</CODE>. </P>
+<H3><A NAME=4_7_8>4.7.8 Accept Jobs (CUPS_ACCEPT_JOBS = 0x4008)</A></H3>
+<P>The accept jobs operation allows jobs to be accepted by the
+specified destination. The only required attributes are <CODE>
+attributes-charset</CODE>, <CODE>attributes-natural-language</CODE>,
+and <CODE>printer-uri</CODE>. </P>
+<P>Accept jobs will return <CODE>ipp-ok</CODE>, <CODE>ipp-not-found</CODE>
+, or <CODE>ipp-not-authorized</CODE>. </P>
+<H3><A NAME=4_7_9>4.7.9 Reject Jobs (CUPS_REJECT_JOBS = 0x4009)</A></H3>
+<P>The reject jobs operation prevents jobs from being accepted by the
+specified destination. The only required attributes are <CODE>
+attributes-charset</CODE>, <CODE>attributes-natural-language</CODE>,
+and <CODE>printer-uri</CODE>. </P>
+<P>Reject jobs will return <CODE>ipp-ok</CODE>, <CODE>ipp-not-found</CODE>
+, or <CODE>ipp-not-authorized</CODE>. </P>
+<H3><A NAME=4_7_10>4.7.10 Set Default Destination (CUPS_SET_DEFAULT =
+0x400A)</A></H3>
+<P>The set default destination operation returns the printer attributes
+for the system default printer or class. The only required attributes
+are <CODE>attributes-charset</CODE>, <CODE>attributes-natural-language</CODE>
+, and <CODE>printer-uri</CODE>. </P>
+<P>Set default destination will return <CODE>ipp-ok</CODE>, <CODE>
+ipp-not-authorized</CODE>, <CODE>ipp-bad-request</CODE>, or <CODE>
+ipp-not-found</CODE>. </P>
+<H2><A NAME=4_8>4.8 Line Printer Daemon Protocol</A></H2>
+<P>The Line Printer Daemon (LPD) protocol is described by <A HREF=http://www.ietf.org/rfc/rfc1179.txt>
+RFC 1179: Line Printer Daemon Protocol</A>. </P>
+<H2><A NAME=4_9>4.9 Server Message Block Protocol</A></H2>
+<P>The Server Message Block (SMB) and related Common Internet File
+System (CIFS) protocols are described at <A HREF=http://anu.samba.org/cifs>
+http://anu.samba.org/cifs</A>. </P>
+<H2><A NAME=4_10>4.10 Trivial File Transfer Protocol</A></H2>
+<P>The Trivial File Transfer Protocol (TFTP) is described by <A HREF=http://www.ietf.org/rfc/rfc1350.txt>
+RFC 1350: The TFTP Protocol (Revision 2)</A>. </P>
+<H1><A NAME=5>5 5 - Directories</A></H1>
+<DL>
+<DT>/usr/bin </DT>
+<DD>The <CODE>cancel</CODE>, <CODE>lp</CODE>, <CODE>lpq</CODE>, <CODE>
+lpr</CODE>, <CODE>lprm</CODE>, and <CODE>lpstat</CODE> commands reside
+here. </DD>
+<DT>/usr/lib </DT>
+<DD>The <CODE>accept</CODE>, <CODE>disable</CODE>, <CODE>enable</CODE>, <CODE>
+lpadmin</CODE>, and <CODE>reject</CODE> commands reside here. </DD>
+<DT>/usr/sbin </DT>
+<DD>The <CODE>lpc</CODE> and <CODE>cupsd</CODE> commands resize here. </DD>
+<DT>/usr/share/cups </DT>
+<DD>This is the root directory of the CUPS static data. </DD>
+<DT>/usr/share/cups/data </DT>
+<DD>The character set and filter data files reside here. </DD>
+<DT>/usr/share/cups/fonts </DT>
+<DD>The <CODE>pstoraster</CODE> font files reside here. </DD>
+<DT>/usr/share/cups/model </DT>
+<DD>The sample PPD files reside here. </DD>
+<DT>/usr/share/cups/pstoraster </DT>
+<DD>The <CODE>pstoraster</CODE> data files reside here. </DD>
+<DT>/var/cups </DT>
+<DD>This is the root directory of the CUPS scheduler. </DD>
+<DT>/var/cups/backend </DT>
+<DD>The backend filters reside here. </DD>
+<DT>/var/cups/cgi-bin </DT>
+<DD>The CGI programs reside here. </DD>
+<DT>/var/cups/conf </DT>
+<DD>The scheduler configuration and MIME files reside here. </DD>
+<DT>/var/cups/doc </DT>
+<DD>The scheduler documentation files reside here. </DD>
+<DT>/var/cups/filter </DT>
+<DD>The file filters reside here. </DD>
+<DT>/var/cups/interfaces </DT>
+<DD>System V interface scripts reside here. </DD>
+<DT>/var/cups/logs </DT>
+<DD>The <CODE>access_log</CODE>, <CODE>error_log</CODE>, and <CODE>
+page_log</CODE> files reside here. </DD>
+<DT>/var/cups/ppd </DT>
+<DD>This directory contains PPD files for each printer. </DD>
+<DT>/var/cups/requests </DT>
+<DD>This directory contains pending print job files. </DD>
+</DL>
+<H1 TYPE=A VALUE=1><A NAME=6>A Glossary</A></H1>
+<H2><A NAME=6_1>A.1 Terms</A></H2>
+<DL>
+<DT>C </DT>
+<DD>A computer language. </DD>
+<DT>parallel </DT>
+<DD>Sending or receiving data more than 1 bit at a time. </DD>
+<DT>pipe </DT>
+<DD>A one-way communications channel between two programs. </DD>
+<DT>serial </DT>
+<DD>Sending or receiving data 1 bit at a time. </DD>
+<DT>socket </DT>
+<DD>A two-way network communications channel. </DD>
+</DL>
+<H2><A NAME=6_2>A.2 Acronyms</A></H2>
+<DL>
+<DT>ASCII </DT>
+<DD>American Standard Code for Information Interchange </DD>
+<DT>CUPS </DT>
+<DD>Common UNIX Printing System </DD>
+<DT>ESC/P </DT>
+<DD>EPSON Standard Code for Printers </DD>
+<DT>FTP </DT>
+<DD>File Transfer Protocol </DD>
+<DT>HP-GL </DT>
+<DD>Hewlett-Packard Graphics Language </DD>
+<DT>HP-PCL </DT>
+<DD>Hewlett-Packard Printer Control Language </DD>
+<DT>HP-PJL </DT>
+<DD>Hewlett-Packard Printer Job Language </DD>
+<DT>IETF </DT>
+<DD>Internet Engineering Task Force </DD>
+<DT>IPP </DT>
+<DD>Internet Printing Protocol </DD>
+<DT>ISO </DT>
+<DD>International Standards Organization </DD>
+<DT>LPD </DT>
+<DD>Line Printer Daemon </DD>
+<DT>MIME </DT>
+<DD>Multimedia Internet Mail Exchange </DD>
+<DT>PCL </DT>
+<DD>Page Control Language </DD>
+<DT>PPD </DT>
+<DD>PostScript Printer Description </DD>
+<DT>SMB </DT>
+<DD>Server Message Block </DD>
+<DT>TFTP </DT>
+<DD>Trivial File Transfer Protocol </DD>
+</DL>
+</BODY>
+</HTML>
diff --git a/doc/idd.pdf b/doc/idd.pdf
new file mode 100644
index 000000000..00c4bcb26
--- /dev/null
+++ b/doc/idd.pdf
@@ -0,0 +1,1577 @@
+%PDF-1.2
+%âãÏÓ
+1 0 obj<</Producer(htmldoc 2.0b1 Copyright 1997-1999 Michael Sweet, All Rights Reserved.)/CreationDate(D:19990730151722Z)/Title(DRAFT - CUPS Interface Design Description)/Author(Easy Software Products)>>endobj
+2 0 obj<</Type/Encoding/BaseEncoding/WinAnsiEncoding>>endobj
+3 0 obj<</Type/Font/Subtype/Type1/BaseFont/Courier/Encoding 2 0 R>>endobj
+4 0 obj<</Type/Font/Subtype/Type1/BaseFont/Times-Roman/Encoding 2 0 R>>endobj
+5 0 obj<</Type/Font/Subtype/Type1/BaseFont/Times-Bold/Encoding 2 0 R>>endobj
+6 0 obj<</Type/Font/Subtype/Type1/BaseFont/Times-Italic/Encoding 2 0 R>>endobj
+7 0 obj<</Type/Font/Subtype/Type1/BaseFont/Helvetica/Encoding 2 0 R>>endobj
+8 0 obj<</Type/Font/Subtype/Type1/BaseFont/Helvetica-Bold/Encoding 2 0 R>>endobj
+9 0 obj<</Type/Font/Subtype/Type1/BaseFont/Symbol>>endobj
+10 0 obj<</S/URI/URI(http://partners.adobe.com/supportservice/devrelations/PDFS/TN/5003.PPD_Spec_v4.3.pdf)>>endobj
+11 0 obj<</Subtype/Link/Rect[336.6 152.4 368.7 163.4]/Border[0 0 0]/A 10 0 R>>endobj
+12 0 obj<</S/URI/URI(http://partners.adobe.com/supportservice/devrelations/PDFS/TN/5003.PPD_Spec_v4.3.pdf)>>endobj
+13 0 obj<</Subtype/Link/Rect[368.7 152.4 414.8 163.4]/Border[0 0 0]/A 12 0 R>>endobj
+14 0 obj<</S/URI/URI(http://partners.adobe.com/supportservice/devrelations/PDFS/TN/5003.PPD_Spec_v4.3.pdf)>>endobj
+15 0 obj<</Subtype/Link/Rect[414.8 152.4 448.1 163.4]/Border[0 0 0]/A 14 0 R>>endobj
+16 0 obj<</S/URI/URI(http://partners.adobe.com/supportservice/devrelations/PDFS/TN/5003.PPD_Spec_v4.3.pdf)>>endobj
+17 0 obj<</Subtype/Link/Rect[448.1 152.4 493.4 163.4]/Border[0 0 0]/A 16 0 R>>endobj
+18 0 obj<</S/URI/URI(http://partners.adobe.com/supportservice/devrelations/PDFS/TN/5003.PPD_Spec_v4.3.pdf)>>endobj
+19 0 obj<</Subtype/Link/Rect[36.0 139.2 68.7 150.2]/Border[0 0 0]/A 18 0 R>>endobj
+20 0 obj<</S/URI/URI(http://partners.adobe.com/supportservice/devrelations/PDFS/TN/5003.PPD_Spec_v4.3.pdf)>>endobj
+21 0 obj<</Subtype/Link/Rect[68.7 139.2 122.8 150.2]/Border[0 0 0]/A 20 0 R>>endobj
+22 0 obj<</S/URI/URI(http://partners.adobe.com/supportservice/devrelations/PDFS/TN/5003.PPD_Spec_v4.3.pdf)>>endobj
+23 0 obj<</Subtype/Link/Rect[122.8 139.2 142.6 150.2]/Border[0 0 0]/A 22 0 R>>endobj
+24 0 obj<</S/URI/URI(http://partners.adobe.com/supportservice/devrelations/PDFS/TN/5003.PPD_Spec_v4.3.pdf)>>endobj
+25 0 obj<</Subtype/Link/Rect[142.6 139.2 177.2 150.2]/Border[0 0 0]/A 24 0 R>>endobj
+26 0 obj<</S/URI/URI(http://partners.adobe.com/supportservice/devrelations/PDFS/TN/5003.PPD_Spec_v4.3.pdf)>>endobj
+27 0 obj<</Subtype/Link/Rect[177.2 139.2 238.0 150.2]/Border[0 0 0]/A 26 0 R>>endobj
+28 0 obj<</S/URI/URI(http://partners.adobe.com/supportservice/devrelations/PDFS/TN/5003.PPD_Spec_v4.3.pdf)>>endobj
+29 0 obj<</Subtype/Link/Rect[238.0 139.2 275.5 150.2]/Border[0 0 0]/A 28 0 R>>endobj
+30 0 obj<</S/URI/URI(http://partners.adobe.com/supportservice/devrelations/PDFS/TN/5003.PPD_Spec_v4.3.pdf)>>endobj
+31 0 obj<</Subtype/Link/Rect[275.5 139.2 289.3 150.2]/Border[0 0 0]/A 30 0 R>>endobj
+32 0 obj[11 0 R
+13 0 R
+15 0 R
+17 0 R
+19 0 R
+21 0 R
+23 0 R
+25 0 R
+27 0 R
+29 0 R
+31 0 R
+]endobj
+33 0 obj<</S/URI/URI(http://partners.adobe.com/supportservice/devrelations/PDFS/TN/PLRM.pdf)>>endobj
+34 0 obj<</Subtype/Link/Rect[108.4 537.6 140.5 548.6]/Border[0 0 0]/A 33 0 R>>endobj
+35 0 obj<</S/URI/URI(http://partners.adobe.com/supportservice/devrelations/PDFS/TN/PLRM.pdf)>>endobj
+36 0 obj<</Subtype/Link/Rect[140.5 537.6 188.5 548.6]/Border[0 0 0]/A 35 0 R>>endobj
+37 0 obj<</S/URI/URI(http://partners.adobe.com/supportservice/devrelations/PDFS/TN/PLRM.pdf)>>endobj
+38 0 obj<</Subtype/Link/Rect[188.5 537.6 234.6 548.6]/Border[0 0 0]/A 37 0 R>>endobj
+39 0 obj<</S/URI/URI(http://partners.adobe.com/supportservice/devrelations/PDFS/TN/PLRM.pdf)>>endobj
+40 0 obj<</Subtype/Link/Rect[234.6 537.6 281.9 548.6]/Border[0 0 0]/A 39 0 R>>endobj
+41 0 obj<</S/URI/URI(http://partners.adobe.com/supportservice/devrelations/PDFS/TN/PLRM.pdf)>>endobj
+42 0 obj<</Subtype/Link/Rect[281.9 537.6 321.0 548.6]/Border[0 0 0]/A 41 0 R>>endobj
+43 0 obj<</S/URI/URI(http://partners.adobe.com/supportservice/devrelations/PDFS/TN/PLRM.pdf)>>endobj
+44 0 obj<</Subtype/Link/Rect[321.0 537.6 348.2 548.6]/Border[0 0 0]/A 43 0 R>>endobj
+45 0 obj<</S/URI/URI(http://partners.adobe.com/supportservice/devrelations/PDFS/TN/PLRM.pdf)>>endobj
+46 0 obj<</Subtype/Link/Rect[348.2 537.6 380.6 548.6]/Border[0 0 0]/A 45 0 R>>endobj
+47 0 obj[34 0 R
+36 0 R
+38 0 R
+40 0 R
+42 0 R
+44 0 R
+46 0 R
+]endobj
+48 0 obj<</S/URI/URI(http://www.ietf.org/rfc/rfc959.txt)>>endobj
+49 0 obj<</Subtype/Link/Rect[289.5 672.0 313.1 685.0]/Border[0 0 0]/A 48 0 R>>endobj
+50 0 obj<</S/URI/URI(http://www.ietf.org/rfc/rfc959.txt)>>endobj
+51 0 obj<</Subtype/Link/Rect[313.1 672.0 335.4 685.0]/Border[0 0 0]/A 50 0 R>>endobj
+52 0 obj<</S/URI/URI(http://www.ietf.org/rfc/rfc959.txt)>>endobj
+53 0 obj<</Subtype/Link/Rect[335.4 672.0 355.2 685.0]/Border[0 0 0]/A 52 0 R>>endobj
+54 0 obj<</S/URI/URI(http://www.ietf.org/rfc/rfc959.txt)>>endobj
+55 0 obj<</Subtype/Link/Rect[355.2 672.0 395.2 685.0]/Border[0 0 0]/A 54 0 R>>endobj
+56 0 obj<</S/URI/URI(http://www.ietf.org/rfc/rfc959.txt)>>endobj
+57 0 obj<</Subtype/Link/Rect[395.2 672.0 432.5 685.0]/Border[0 0 0]/A 56 0 R>>endobj
+58 0 obj<</S/URI/URI(http://www.ietf.org/rfc/rfc2565.txt)>>endobj
+59 0 obj<</Subtype/Link/Rect[108.0 566.6 131.5 579.6]/Border[0 0 0]/A 58 0 R>>endobj
+60 0 obj<</S/URI/URI(http://www.ietf.org/rfc/rfc2565.txt)>>endobj
+61 0 obj<</Subtype/Link/Rect[131.5 566.6 159.3 579.6]/Border[0 0 0]/A 60 0 R>>endobj
+62 0 obj<</S/URI/URI(http://www.ietf.org/rfc/rfc2565.txt)>>endobj
+63 0 obj<</Subtype/Link/Rect[159.3 566.6 196.3 579.6]/Border[0 0 0]/A 62 0 R>>endobj
+64 0 obj<</S/URI/URI(http://www.ietf.org/rfc/rfc2565.txt)>>endobj
+65 0 obj<</Subtype/Link/Rect[196.3 566.6 234.5 579.6]/Border[0 0 0]/A 64 0 R>>endobj
+66 0 obj<</S/URI/URI(http://www.ietf.org/rfc/rfc2565.txt)>>endobj
+67 0 obj<</Subtype/Link/Rect[234.5 566.6 294.4 579.6]/Border[0 0 0]/A 66 0 R>>endobj
+68 0 obj<</S/URI/URI(http://www.ietf.org/rfc/rfc2565.txt)>>endobj
+69 0 obj<</Subtype/Link/Rect[294.4 566.6 339.3 579.6]/Border[0 0 0]/A 68 0 R>>endobj
+70 0 obj<</S/URI/URI(http://www.ietf.org/rfc/rfc2565.txt)>>endobj
+71 0 obj<</Subtype/Link/Rect[339.3 566.6 358.0 579.6]/Border[0 0 0]/A 70 0 R>>endobj
+72 0 obj<</S/URI/URI(http://www.ietf.org/rfc/rfc2565.txt)>>endobj
+73 0 obj<</Subtype/Link/Rect[358.0 566.6 400.7 579.6]/Border[0 0 0]/A 72 0 R>>endobj
+74 0 obj<</S/URI/URI(http://www.ietf.org/rfc/rfc2566.txt)>>endobj
+75 0 obj<</Subtype/Link/Rect[108.0 553.4 131.5 566.4]/Border[0 0 0]/A 74 0 R>>endobj
+76 0 obj<</S/URI/URI(http://www.ietf.org/rfc/rfc2566.txt)>>endobj
+77 0 obj<</Subtype/Link/Rect[131.5 553.4 159.3 566.4]/Border[0 0 0]/A 76 0 R>>endobj
+78 0 obj<</S/URI/URI(http://www.ietf.org/rfc/rfc2566.txt)>>endobj
+79 0 obj<</Subtype/Link/Rect[159.3 553.4 196.3 566.4]/Border[0 0 0]/A 78 0 R>>endobj
+80 0 obj<</S/URI/URI(http://www.ietf.org/rfc/rfc2566.txt)>>endobj
+81 0 obj<</Subtype/Link/Rect[196.3 553.4 234.5 566.4]/Border[0 0 0]/A 80 0 R>>endobj
+82 0 obj<</S/URI/URI(http://www.ietf.org/rfc/rfc2566.txt)>>endobj
+83 0 obj<</Subtype/Link/Rect[234.5 553.4 294.4 566.4]/Border[0 0 0]/A 82 0 R>>endobj
+84 0 obj<</S/URI/URI(http://www.ietf.org/rfc/rfc2566.txt)>>endobj
+85 0 obj<</Subtype/Link/Rect[294.4 553.4 325.9 566.4]/Border[0 0 0]/A 84 0 R>>endobj
+86 0 obj<</S/URI/URI(http://www.ietf.org/rfc/rfc2566.txt)>>endobj
+87 0 obj<</Subtype/Link/Rect[325.9 553.4 344.5 566.4]/Border[0 0 0]/A 86 0 R>>endobj
+88 0 obj<</S/URI/URI(http://www.ietf.org/rfc/rfc2566.txt)>>endobj
+89 0 obj<</Subtype/Link/Rect[344.5 553.4 389.7 566.4]/Border[0 0 0]/A 88 0 R>>endobj
+90 0 obj<</S/URI/URI(http://www.ietf.org/rfc/rfc2567.txt)>>endobj
+91 0 obj<</Subtype/Link/Rect[108.0 540.2 131.5 553.2]/Border[0 0 0]/A 90 0 R>>endobj
+92 0 obj<</S/URI/URI(http://www.ietf.org/rfc/rfc2567.txt)>>endobj
+93 0 obj<</Subtype/Link/Rect[131.5 540.2 159.3 553.2]/Border[0 0 0]/A 92 0 R>>endobj
+94 0 obj<</S/URI/URI(http://www.ietf.org/rfc/rfc2567.txt)>>endobj
+95 0 obj<</Subtype/Link/Rect[159.3 540.2 193.3 553.2]/Border[0 0 0]/A 94 0 R>>endobj
+96 0 obj<</S/URI/URI(http://www.ietf.org/rfc/rfc2567.txt)>>endobj
+97 0 obj<</Subtype/Link/Rect[193.3 540.2 221.7 553.2]/Border[0 0 0]/A 96 0 R>>endobj
+98 0 obj<</S/URI/URI(http://www.ietf.org/rfc/rfc2567.txt)>>endobj
+99 0 obj<</Subtype/Link/Rect[221.7 540.2 237.2 553.2]/Border[0 0 0]/A 98 0 R>>endobj
+100 0 obj<</S/URI/URI(http://www.ietf.org/rfc/rfc2567.txt)>>endobj
+101 0 obj<</Subtype/Link/Rect[237.2 540.2 250.4 553.2]/Border[0 0 0]/A 100 0 R>>endobj
+102 0 obj<</S/URI/URI(http://www.ietf.org/rfc/rfc2567.txt)>>endobj
+103 0 obj<</Subtype/Link/Rect[250.4 540.2 287.3 553.2]/Border[0 0 0]/A 102 0 R>>endobj
+104 0 obj<</S/URI/URI(http://www.ietf.org/rfc/rfc2567.txt)>>endobj
+105 0 obj<</Subtype/Link/Rect[287.3 540.2 325.5 553.2]/Border[0 0 0]/A 104 0 R>>endobj
+106 0 obj<</S/URI/URI(http://www.ietf.org/rfc/rfc2567.txt)>>endobj
+107 0 obj<</Subtype/Link/Rect[325.5 540.2 362.8 553.2]/Border[0 0 0]/A 106 0 R>>endobj
+108 0 obj<</S/URI/URI(http://www.ietf.org/rfc/rfc2568.txt)>>endobj
+109 0 obj<</Subtype/Link/Rect[108.0 527.0 131.5 540.0]/Border[0 0 0]/A 108 0 R>>endobj
+110 0 obj<</S/URI/URI(http://www.ietf.org/rfc/rfc2568.txt)>>endobj
+111 0 obj<</Subtype/Link/Rect[131.5 527.0 159.3 540.0]/Border[0 0 0]/A 110 0 R>>endobj
+112 0 obj<</S/URI/URI(http://www.ietf.org/rfc/rfc2568.txt)>>endobj
+113 0 obj<</Subtype/Link/Rect[159.3 527.0 204.3 540.0]/Border[0 0 0]/A 112 0 R>>endobj
+114 0 obj<</S/URI/URI(http://www.ietf.org/rfc/rfc2568.txt)>>endobj
+115 0 obj<</Subtype/Link/Rect[204.3 527.0 219.8 540.0]/Border[0 0 0]/A 114 0 R>>endobj
+116 0 obj<</S/URI/URI(http://www.ietf.org/rfc/rfc2568.txt)>>endobj
+117 0 obj<</Subtype/Link/Rect[219.8 527.0 236.0 540.0]/Border[0 0 0]/A 116 0 R>>endobj
+118 0 obj<</S/URI/URI(http://www.ietf.org/rfc/rfc2568.txt)>>endobj
+119 0 obj<</Subtype/Link/Rect[236.0 527.0 279.1 540.0]/Border[0 0 0]/A 118 0 R>>endobj
+120 0 obj<</S/URI/URI(http://www.ietf.org/rfc/rfc2568.txt)>>endobj
+121 0 obj<</Subtype/Link/Rect[279.1 527.0 291.0 540.0]/Border[0 0 0]/A 120 0 R>>endobj
+122 0 obj<</S/URI/URI(http://www.ietf.org/rfc/rfc2568.txt)>>endobj
+123 0 obj<</Subtype/Link/Rect[291.0 527.0 307.2 540.0]/Border[0 0 0]/A 122 0 R>>endobj
+124 0 obj<</S/URI/URI(http://www.ietf.org/rfc/rfc2568.txt)>>endobj
+125 0 obj<</Subtype/Link/Rect[307.2 527.0 338.7 540.0]/Border[0 0 0]/A 124 0 R>>endobj
+126 0 obj<</S/URI/URI(http://www.ietf.org/rfc/rfc2568.txt)>>endobj
+127 0 obj<</Subtype/Link/Rect[338.7 527.0 357.3 540.0]/Border[0 0 0]/A 126 0 R>>endobj
+128 0 obj<</S/URI/URI(http://www.ietf.org/rfc/rfc2568.txt)>>endobj
+129 0 obj<</Subtype/Link/Rect[357.3 527.0 397.3 540.0]/Border[0 0 0]/A 128 0 R>>endobj
+130 0 obj<</S/URI/URI(http://www.ietf.org/rfc/rfc2568.txt)>>endobj
+131 0 obj<</Subtype/Link/Rect[397.3 527.0 415.7 540.0]/Border[0 0 0]/A 130 0 R>>endobj
+132 0 obj<</S/URI/URI(http://www.ietf.org/rfc/rfc2568.txt)>>endobj
+133 0 obj<</Subtype/Link/Rect[412.9 527.0 429.1 540.0]/Border[0 0 0]/A 132 0 R>>endobj
+134 0 obj<</S/URI/URI(http://www.ietf.org/rfc/rfc2568.txt)>>endobj
+135 0 obj<</Subtype/Link/Rect[429.1 527.0 466.1 540.0]/Border[0 0 0]/A 134 0 R>>endobj
+136 0 obj<</S/URI/URI(http://www.ietf.org/rfc/rfc2568.txt)>>endobj
+137 0 obj<</Subtype/Link/Rect[466.1 527.0 504.3 540.0]/Border[0 0 0]/A 136 0 R>>endobj
+138 0 obj<</S/URI/URI(http://www.ietf.org/rfc/rfc2568.txt)>>endobj
+139 0 obj<</Subtype/Link/Rect[504.3 527.0 541.6 540.0]/Border[0 0 0]/A 138 0 R>>endobj
+140 0 obj<</S/URI/URI(http://www.ietf.org/rfc/rfc2569.txt)>>endobj
+141 0 obj<</Subtype/Link/Rect[108.0 513.8 131.5 526.8]/Border[0 0 0]/A 140 0 R>>endobj
+142 0 obj<</S/URI/URI(http://www.ietf.org/rfc/rfc2569.txt)>>endobj
+143 0 obj<</Subtype/Link/Rect[131.5 513.8 159.3 526.8]/Border[0 0 0]/A 142 0 R>>endobj
+144 0 obj<</S/URI/URI(http://www.ietf.org/rfc/rfc2569.txt)>>endobj
+145 0 obj<</Subtype/Link/Rect[159.3 513.8 201.8 526.8]/Border[0 0 0]/A 144 0 R>>endobj
+146 0 obj<</S/URI/URI(http://www.ietf.org/rfc/rfc2569.txt)>>endobj
+147 0 obj<</Subtype/Link/Rect[201.8 513.8 241.2 526.8]/Border[0 0 0]/A 146 0 R>>endobj
+148 0 obj<</S/URI/URI(http://www.ietf.org/rfc/rfc2569.txt)>>endobj
+149 0 obj<</Subtype/Link/Rect[241.2 513.8 264.8 526.8]/Border[0 0 0]/A 148 0 R>>endobj
+150 0 obj<</S/URI/URI(http://www.ietf.org/rfc/rfc2569.txt)>>endobj
+151 0 obj<</Subtype/Link/Rect[264.8 513.8 283.4 526.8]/Border[0 0 0]/A 150 0 R>>endobj
+152 0 obj<</S/URI/URI(http://www.ietf.org/rfc/rfc2569.txt)>>endobj
+153 0 obj<</Subtype/Link/Rect[283.4 513.8 302.0 526.8]/Border[0 0 0]/A 152 0 R>>endobj
+154 0 obj<</S/URI/URI(http://www.ietf.org/rfc/rfc2569.txt)>>endobj
+155 0 obj<</Subtype/Link/Rect[302.0 513.8 343.6 526.8]/Border[0 0 0]/A 154 0 R>>endobj
+156 0 obj[49 0 R
+51 0 R
+53 0 R
+55 0 R
+57 0 R
+59 0 R
+61 0 R
+63 0 R
+65 0 R
+67 0 R
+69 0 R
+71 0 R
+73 0 R
+75 0 R
+77 0 R
+79 0 R
+81 0 R
+83 0 R
+85 0 R
+87 0 R
+89 0 R
+91 0 R
+93 0 R
+95 0 R
+97 0 R
+99 0 R
+101 0 R
+103 0 R
+105 0 R
+107 0 R
+109 0 R
+111 0 R
+113 0 R
+115 0 R
+117 0 R
+119 0 R
+121 0 R
+123 0 R
+125 0 R
+127 0 R
+129 0 R
+131 0 R
+133 0 R
+135 0 R
+137 0 R
+139 0 R
+141 0 R
+143 0 R
+145 0 R
+147 0 R
+149 0 R
+151 0 R
+153 0 R
+155 0 R
+]endobj
+157 0 obj<</S/URI/URI(http://www.ietf.org/rfc/rfc1179.txt)>>endobj
+158 0 obj<</Subtype/Link/Rect[326.5 501.6 350.0 514.6]/Border[0 0 0]/A 157 0 R>>endobj
+159 0 obj<</S/URI/URI(http://www.ietf.org/rfc/rfc1179.txt)>>endobj
+160 0 obj<</Subtype/Link/Rect[350.0 501.6 377.8 514.6]/Border[0 0 0]/A 159 0 R>>endobj
+161 0 obj<</S/URI/URI(http://www.ietf.org/rfc/rfc1179.txt)>>endobj
+162 0 obj<</Subtype/Link/Rect[377.8 501.6 400.7 514.6]/Border[0 0 0]/A 161 0 R>>endobj
+163 0 obj<</S/URI/URI(http://www.ietf.org/rfc/rfc1179.txt)>>endobj
+164 0 obj<</Subtype/Link/Rect[400.7 501.6 433.4 514.6]/Border[0 0 0]/A 163 0 R>>endobj
+165 0 obj<</S/URI/URI(http://www.ietf.org/rfc/rfc1179.txt)>>endobj
+166 0 obj<</Subtype/Link/Rect[433.4 501.6 473.5 514.6]/Border[0 0 0]/A 165 0 R>>endobj
+167 0 obj<</S/URI/URI(http://www.ietf.org/rfc/rfc1179.txt)>>endobj
+168 0 obj<</Subtype/Link/Rect[473.5 501.6 510.7 514.6]/Border[0 0 0]/A 167 0 R>>endobj
+169 0 obj<</S/URI/URI(http://anu.samba.org/cifs)>>endobj
+170 0 obj<</Subtype/Link/Rect[82.7 409.3 192.1 422.3]/Border[0 0 0]/A 169 0 R>>endobj
+171 0 obj<</S/URI/URI(http://www.ietf.org/rfc/rfc1350.txt)>>endobj
+172 0 obj<</Subtype/Link/Rect[328.9 330.3 352.5 343.3]/Border[0 0 0]/A 171 0 R>>endobj
+173 0 obj<</S/URI/URI(http://www.ietf.org/rfc/rfc1350.txt)>>endobj
+174 0 obj<</Subtype/Link/Rect[352.5 330.3 380.3 343.3]/Border[0 0 0]/A 173 0 R>>endobj
+175 0 obj<</S/URI/URI(http://www.ietf.org/rfc/rfc1350.txt)>>endobj
+176 0 obj<</Subtype/Link/Rect[380.3 330.3 400.1 343.3]/Border[0 0 0]/A 175 0 R>>endobj
+177 0 obj<</S/URI/URI(http://www.ietf.org/rfc/rfc1350.txt)>>endobj
+178 0 obj<</Subtype/Link/Rect[400.1 330.3 428.6 343.3]/Border[0 0 0]/A 177 0 R>>endobj
+179 0 obj<</S/URI/URI(http://www.ietf.org/rfc/rfc1350.txt)>>endobj
+180 0 obj<</Subtype/Link/Rect[428.6 330.3 468.6 343.3]/Border[0 0 0]/A 179 0 R>>endobj
+181 0 obj<</S/URI/URI(http://www.ietf.org/rfc/rfc1350.txt)>>endobj
+182 0 obj<</Subtype/Link/Rect[468.6 330.3 514.1 343.3]/Border[0 0 0]/A 181 0 R>>endobj
+183 0 obj<</S/URI/URI(http://www.ietf.org/rfc/rfc1350.txt)>>endobj
+184 0 obj<</Subtype/Link/Rect[514.1 330.3 523.3 343.3]/Border[0 0 0]/A 183 0 R>>endobj
+185 0 obj[158 0 R
+160 0 R
+162 0 R
+164 0 R
+166 0 R
+168 0 R
+170 0 R
+172 0 R
+174 0 R
+176 0 R
+178 0 R
+180 0 R
+182 0 R
+184 0 R
+]endobj
+186 0 obj<</Subtype/Link/Rect[72.0 673.2 80.2 686.2]/Border[0 0 0]/Dest[415 0 R/XYZ null 818 0]>>endobj
+187 0 obj<</Subtype/Link/Rect[80.2 673.2 107.8 686.2]/Border[0 0 0]/Dest[415 0 R/XYZ null 818 0]>>endobj
+188 0 obj<</Subtype/Link/Rect[108.0 660.0 124.5 673.0]/Border[0 0 0]/Dest[415 0 R/XYZ null 737 0]>>endobj
+189 0 obj<</Subtype/Link/Rect[124.5 660.0 183.8 673.0]/Border[0 0 0]/Dest[415 0 R/XYZ null 737 0]>>endobj
+190 0 obj<</Subtype/Link/Rect[108.0 646.8 124.5 659.8]/Border[0 0 0]/Dest[415 0 R/XYZ null 645 0]>>endobj
+191 0 obj<</Subtype/Link/Rect[124.5 646.8 159.6 659.8]/Border[0 0 0]/Dest[415 0 R/XYZ null 645 0]>>endobj
+192 0 obj<</Subtype/Link/Rect[159.6 646.8 203.0 659.8]/Border[0 0 0]/Dest[415 0 R/XYZ null 645 0]>>endobj
+193 0 obj<</Subtype/Link/Rect[108.0 633.6 124.5 646.6]/Border[0 0 0]/Dest[415 0 R/XYZ null 408 0]>>endobj
+194 0 obj<</Subtype/Link/Rect[124.5 633.6 173.1 646.6]/Border[0 0 0]/Dest[415 0 R/XYZ null 408 0]>>endobj
+195 0 obj<</Subtype/Link/Rect[173.1 633.6 216.4 646.6]/Border[0 0 0]/Dest[415 0 R/XYZ null 408 0]>>endobj
+196 0 obj<</Subtype/Link/Rect[72.0 607.2 80.2 620.2]/Border[0 0 0]/Dest[421 0 R/XYZ null 818 0]>>endobj
+197 0 obj<</Subtype/Link/Rect[80.2 607.2 131.6 620.2]/Border[0 0 0]/Dest[421 0 R/XYZ null 818 0]>>endobj
+198 0 obj<</Subtype/Link/Rect[108.0 594.0 124.5 607.0]/Border[0 0 0]/Dest[421 0 R/XYZ null 737 0]>>endobj
+199 0 obj<</Subtype/Link/Rect[124.5 594.0 154.8 607.0]/Border[0 0 0]/Dest[421 0 R/XYZ null 737 0]>>endobj
+200 0 obj<</Subtype/Link/Rect[154.8 594.0 222.6 607.0]/Border[0 0 0]/Dest[421 0 R/XYZ null 737 0]>>endobj
+201 0 obj<</Subtype/Link/Rect[108.0 580.8 124.5 593.8]/Border[0 0 0]/Dest[421 0 R/XYZ null 526 0]>>endobj
+202 0 obj<</Subtype/Link/Rect[124.5 580.8 152.3 593.8]/Border[0 0 0]/Dest[421 0 R/XYZ null 526 0]>>endobj
+203 0 obj<</Subtype/Link/Rect[152.3 580.8 202.4 593.8]/Border[0 0 0]/Dest[421 0 R/XYZ null 526 0]>>endobj
+204 0 obj<</Subtype/Link/Rect[72.0 554.4 80.2 567.4]/Border[0 0 0]/Dest[427 0 R/XYZ null 818 0]>>endobj
+205 0 obj<</Subtype/Link/Rect[80.2 554.4 121.5 567.4]/Border[0 0 0]/Dest[427 0 R/XYZ null 818 0]>>endobj
+206 0 obj<</Subtype/Link/Rect[121.5 554.4 168.5 567.4]/Border[0 0 0]/Dest[427 0 R/XYZ null 818 0]>>endobj
+207 0 obj<</Subtype/Link/Rect[108.0 541.2 124.5 554.2]/Border[0 0 0]/Dest[427 0 R/XYZ null 737 0]>>endobj
+208 0 obj<</Subtype/Link/Rect[124.5 541.2 170.0 554.2]/Border[0 0 0]/Dest[427 0 R/XYZ null 737 0]>>endobj
+209 0 obj<</Subtype/Link/Rect[170.0 541.2 186.8 554.2]/Border[0 0 0]/Dest[427 0 R/XYZ null 737 0]>>endobj
+210 0 obj<</Subtype/Link/Rect[186.8 541.2 208.2 554.2]/Border[0 0 0]/Dest[427 0 R/XYZ null 737 0]>>endobj
+211 0 obj<</Subtype/Link/Rect[108.0 528.0 124.5 541.0]/Border[0 0 0]/Dest[427 0 R/XYZ null 619 0]>>endobj
+212 0 obj<</Subtype/Link/Rect[124.5 528.0 170.6 541.0]/Border[0 0 0]/Dest[427 0 R/XYZ null 619 0]>>endobj
+213 0 obj<</Subtype/Link/Rect[170.6 528.0 192.0 541.0]/Border[0 0 0]/Dest[427 0 R/XYZ null 619 0]>>endobj
+214 0 obj<</Subtype/Link/Rect[108.0 514.8 124.5 527.8]/Border[0 0 0]/Dest[427 0 R/XYZ null 249 0]>>endobj
+215 0 obj<</Subtype/Link/Rect[124.5 514.8 157.2 527.8]/Border[0 0 0]/Dest[427 0 R/XYZ null 249 0]>>endobj
+216 0 obj<</Subtype/Link/Rect[157.2 514.8 178.6 527.8]/Border[0 0 0]/Dest[427 0 R/XYZ null 249 0]>>endobj
+217 0 obj<</Subtype/Link/Rect[144.0 501.6 168.8 514.6]/Border[0 0 0]/Dest[430 0 R/XYZ null 782 0]>>endobj
+218 0 obj<</Subtype/Link/Rect[168.8 501.6 219.8 514.6]/Border[0 0 0]/Dest[430 0 R/XYZ null 782 0]>>endobj
+219 0 obj<</Subtype/Link/Rect[144.0 488.4 168.8 501.4]/Border[0 0 0]/Dest[430 0 R/XYZ null 401 0]>>endobj
+220 0 obj<</Subtype/Link/Rect[168.8 488.4 222.2 501.4]/Border[0 0 0]/Dest[430 0 R/XYZ null 401 0]>>endobj
+221 0 obj<</Subtype/Link/Rect[108.0 475.2 124.5 488.2]/Border[0 0 0]/Dest[430 0 R/XYZ null 278 0]>>endobj
+222 0 obj<</Subtype/Link/Rect[124.5 475.2 172.5 488.2]/Border[0 0 0]/Dest[430 0 R/XYZ null 278 0]>>endobj
+223 0 obj<</Subtype/Link/Rect[172.5 475.2 205.2 488.2]/Border[0 0 0]/Dest[430 0 R/XYZ null 278 0]>>endobj
+224 0 obj<</Subtype/Link/Rect[205.2 475.2 259.2 488.2]/Border[0 0 0]/Dest[430 0 R/XYZ null 278 0]>>endobj
+225 0 obj<</Subtype/Link/Rect[259.2 475.2 280.6 488.2]/Border[0 0 0]/Dest[430 0 R/XYZ null 278 0]>>endobj
+226 0 obj<</Subtype/Link/Rect[144.0 462.0 168.8 475.0]/Border[0 0 0]/Dest[430 0 R/XYZ null 171 0]>>endobj
+227 0 obj<</Subtype/Link/Rect[168.8 462.0 199.0 475.0]/Border[0 0 0]/Dest[430 0 R/XYZ null 171 0]>>endobj
+228 0 obj<</Subtype/Link/Rect[199.0 462.0 250.0 475.0]/Border[0 0 0]/Dest[430 0 R/XYZ null 171 0]>>endobj
+229 0 obj<</Subtype/Link/Rect[250.0 462.0 261.3 475.0]/Border[0 0 0]/Dest[430 0 R/XYZ null 171 0]>>endobj
+230 0 obj<</Subtype/Link/Rect[261.3 462.0 284.3 475.0]/Border[0 0 0]/Dest[430 0 R/XYZ null 171 0]>>endobj
+231 0 obj<</Subtype/Link/Rect[284.3 462.0 305.7 475.0]/Border[0 0 0]/Dest[430 0 R/XYZ null 171 0]>>endobj
+232 0 obj<</Subtype/Link/Rect[108.0 448.8 124.5 461.8]/Border[0 0 0]/Dest[436 0 R/XYZ null 800 0]>>endobj
+233 0 obj<</Subtype/Link/Rect[124.5 448.8 171.2 461.8]/Border[0 0 0]/Dest[436 0 R/XYZ null 800 0]>>endobj
+234 0 obj<</Subtype/Link/Rect[171.2 448.8 235.7 461.8]/Border[0 0 0]/Dest[436 0 R/XYZ null 800 0]>>endobj
+235 0 obj<</Subtype/Link/Rect[235.7 448.8 257.1 461.8]/Border[0 0 0]/Dest[436 0 R/XYZ null 800 0]>>endobj
+236 0 obj<</Subtype/Link/Rect[144.0 435.6 168.8 448.6]/Border[0 0 0]/Dest[436 0 R/XYZ null 547 0]>>endobj
+237 0 obj<</Subtype/Link/Rect[168.8 435.6 221.6 448.6]/Border[0 0 0]/Dest[436 0 R/XYZ null 547 0]>>endobj
+238 0 obj<</Subtype/Link/Rect[144.0 422.4 168.8 435.4]/Border[0 0 0]/Dest[436 0 R/XYZ null 215 0]>>endobj
+239 0 obj<</Subtype/Link/Rect[168.8 422.4 216.7 435.4]/Border[0 0 0]/Dest[436 0 R/XYZ null 215 0]>>endobj
+240 0 obj<</Subtype/Link/Rect[144.0 409.2 168.8 422.2]/Border[0 0 0]/Dest[442 0 R/XYZ null 641 0]>>endobj
+241 0 obj<</Subtype/Link/Rect[168.8 409.2 224.7 422.2]/Border[0 0 0]/Dest[442 0 R/XYZ null 641 0]>>endobj
+242 0 obj<</Subtype/Link/Rect[72.0 382.8 80.2 395.8]/Border[0 0 0]/Dest[445 0 R/XYZ null 818 0]>>endobj
+243 0 obj<</Subtype/Link/Rect[80.2 382.8 123.9 395.8]/Border[0 0 0]/Dest[445 0 R/XYZ null 818 0]>>endobj
+244 0 obj<</Subtype/Link/Rect[123.9 382.8 171.0 395.8]/Border[0 0 0]/Dest[445 0 R/XYZ null 818 0]>>endobj
+245 0 obj<</Subtype/Link/Rect[108.0 369.6 124.5 382.6]/Border[0 0 0]/Dest[445 0 R/XYZ null 737 0]>>endobj
+246 0 obj<</Subtype/Link/Rect[124.5 369.6 176.1 382.6]/Border[0 0 0]/Dest[445 0 R/XYZ null 737 0]>>endobj
+247 0 obj<</Subtype/Link/Rect[176.1 369.6 213.4 382.6]/Border[0 0 0]/Dest[445 0 R/XYZ null 737 0]>>endobj
+248 0 obj<</Subtype/Link/Rect[108.0 356.4 124.5 369.4]/Border[0 0 0]/Dest[445 0 R/XYZ null 658 0]>>endobj
+249 0 obj<</Subtype/Link/Rect[124.5 356.4 154.8 369.4]/Border[0 0 0]/Dest[445 0 R/XYZ null 658 0]>>endobj
+250 0 obj<</Subtype/Link/Rect[154.8 356.4 200.3 369.4]/Border[0 0 0]/Dest[445 0 R/XYZ null 658 0]>>endobj
+251 0 obj<</Subtype/Link/Rect[200.3 356.4 237.6 369.4]/Border[0 0 0]/Dest[445 0 R/XYZ null 658 0]>>endobj
+252 0 obj<</Subtype/Link/Rect[108.0 343.2 124.5 356.2]/Border[0 0 0]/Dest[448 0 R/XYZ null 676 0]>>endobj
+253 0 obj<</Subtype/Link/Rect[124.5 343.2 154.8 356.2]/Border[0 0 0]/Dest[448 0 R/XYZ null 676 0]>>endobj
+254 0 obj<</Subtype/Link/Rect[154.8 343.2 202.7 356.2]/Border[0 0 0]/Dest[448 0 R/XYZ null 676 0]>>endobj
+255 0 obj<</Subtype/Link/Rect[202.7 343.2 219.9 356.2]/Border[0 0 0]/Dest[448 0 R/XYZ null 676 0]>>endobj
+256 0 obj<</Subtype/Link/Rect[108.0 330.0 124.5 343.0]/Border[0 0 0]/Dest[448 0 R/XYZ null 558 0]>>endobj
+257 0 obj<</Subtype/Link/Rect[124.5 330.0 154.8 343.0]/Border[0 0 0]/Dest[448 0 R/XYZ null 558 0]>>endobj
+258 0 obj<</Subtype/Link/Rect[154.8 330.0 185.6 343.0]/Border[0 0 0]/Dest[448 0 R/XYZ null 558 0]>>endobj
+259 0 obj<</Subtype/Link/Rect[185.6 330.0 202.7 343.0]/Border[0 0 0]/Dest[448 0 R/XYZ null 558 0]>>endobj
+260 0 obj<</Subtype/Link/Rect[108.0 316.8 124.5 329.8]/Border[0 0 0]/Dest[454 0 R/XYZ null 276 0]>>endobj
+261 0 obj<</Subtype/Link/Rect[124.5 316.8 154.8 329.8]/Border[0 0 0]/Dest[454 0 R/XYZ null 276 0]>>endobj
+262 0 obj<</Subtype/Link/Rect[154.8 316.8 177.7 329.8]/Border[0 0 0]/Dest[454 0 R/XYZ null 276 0]>>endobj
+263 0 obj<</Subtype/Link/Rect[177.7 316.8 199.1 329.8]/Border[0 0 0]/Dest[454 0 R/XYZ null 276 0]>>endobj
+264 0 obj<</Subtype/Link/Rect[108.0 303.6 124.5 316.6]/Border[0 0 0]/Dest[457 0 R/XYZ null 800 0]>>endobj
+265 0 obj<</Subtype/Link/Rect[124.5 303.6 144.4 316.6]/Border[0 0 0]/Dest[457 0 R/XYZ null 800 0]>>endobj
+266 0 obj<</Subtype/Link/Rect[144.4 303.6 184.4 316.6]/Border[0 0 0]/Dest[457 0 R/XYZ null 800 0]>>endobj
+267 0 obj<</Subtype/Link/Rect[184.4 303.6 221.7 316.6]/Border[0 0 0]/Dest[457 0 R/XYZ null 800 0]>>endobj
+268 0 obj<</Subtype/Link/Rect[108.0 290.4 124.5 303.4]/Border[0 0 0]/Dest[457 0 R/XYZ null 720 0]>>endobj
+269 0 obj<</Subtype/Link/Rect[124.5 290.4 161.5 303.4]/Border[0 0 0]/Dest[457 0 R/XYZ null 720 0]>>endobj
+270 0 obj<</Subtype/Link/Rect[161.5 290.4 199.7 303.4]/Border[0 0 0]/Dest[457 0 R/XYZ null 720 0]>>endobj
+271 0 obj<</Subtype/Link/Rect[199.7 290.4 236.9 303.4]/Border[0 0 0]/Dest[457 0 R/XYZ null 720 0]>>endobj
+272 0 obj<</Subtype/Link/Rect[144.0 277.2 168.8 290.2]/Border[0 0 0]/Dest[457 0 R/XYZ null 508 0]>>endobj
+273 0 obj<</Subtype/Link/Rect[168.8 277.2 187.4 290.2]/Border[0 0 0]/Dest[457 0 R/XYZ null 508 0]>>endobj
+274 0 obj<</Subtype/Link/Rect[187.4 277.2 223.1 290.2]/Border[0 0 0]/Dest[457 0 R/XYZ null 508 0]>>endobj
+275 0 obj<</Subtype/Link/Rect[223.1 277.2 276.6 290.2]/Border[0 0 0]/Dest[457 0 R/XYZ null 508 0]>>endobj
+276 0 obj<</Subtype/Link/Rect[276.6 277.2 393.0 290.2]/Border[0 0 0]/Dest[457 0 R/XYZ null 508 0]>>endobj
+277 0 obj<</Subtype/Link/Rect[393.0 277.2 402.0 290.2]/Border[0 0 0]/Dest[457 0 R/XYZ null 508 0]>>endobj
+278 0 obj<</Subtype/Link/Rect[402.0 277.2 438.6 290.2]/Border[0 0 0]/Dest[457 0 R/XYZ null 508 0]>>endobj
+279 0 obj<</Subtype/Link/Rect[144.0 264.0 168.8 277.0]/Border[0 0 0]/Dest[457 0 R/XYZ null 396 0]>>endobj
+280 0 obj<</Subtype/Link/Rect[168.8 264.0 187.4 277.0]/Border[0 0 0]/Dest[457 0 R/XYZ null 396 0]>>endobj
+281 0 obj<</Subtype/Link/Rect[187.4 264.0 224.4 277.0]/Border[0 0 0]/Dest[457 0 R/XYZ null 396 0]>>endobj
+282 0 obj<</Subtype/Link/Rect[224.4 264.0 342.6 277.0]/Border[0 0 0]/Dest[457 0 R/XYZ null 396 0]>>endobj
+283 0 obj<</Subtype/Link/Rect[342.6 264.0 351.6 277.0]/Border[0 0 0]/Dest[457 0 R/XYZ null 396 0]>>endobj
+284 0 obj<</Subtype/Link/Rect[351.6 264.0 388.2 277.0]/Border[0 0 0]/Dest[457 0 R/XYZ null 396 0]>>endobj
+285 0 obj<</Subtype/Link/Rect[144.0 250.8 168.8 263.8]/Border[0 0 0]/Dest[457 0 R/XYZ null 284 0]>>endobj
+286 0 obj<</Subtype/Link/Rect[168.8 250.8 190.4 263.8]/Border[0 0 0]/Dest[457 0 R/XYZ null 284 0]>>endobj
+287 0 obj<</Subtype/Link/Rect[190.4 250.8 223.1 263.8]/Border[0 0 0]/Dest[457 0 R/XYZ null 284 0]>>endobj
+288 0 obj<</Subtype/Link/Rect[223.1 250.8 337.7 263.8]/Border[0 0 0]/Dest[457 0 R/XYZ null 284 0]>>endobj
+289 0 obj<</Subtype/Link/Rect[337.7 250.8 346.7 263.8]/Border[0 0 0]/Dest[457 0 R/XYZ null 284 0]>>endobj
+290 0 obj<</Subtype/Link/Rect[346.7 250.8 383.3 263.8]/Border[0 0 0]/Dest[457 0 R/XYZ null 284 0]>>endobj
+291 0 obj<</Subtype/Link/Rect[144.0 237.6 168.8 250.6]/Border[0 0 0]/Dest[460 0 R/XYZ null 782 0]>>endobj
+292 0 obj<</Subtype/Link/Rect[168.8 237.6 200.2 250.6]/Border[0 0 0]/Dest[460 0 R/XYZ null 782 0]>>endobj
+293 0 obj<</Subtype/Link/Rect[200.2 237.6 232.9 250.6]/Border[0 0 0]/Dest[460 0 R/XYZ null 782 0]>>endobj
+294 0 obj<</Subtype/Link/Rect[232.9 237.6 365.2 250.6]/Border[0 0 0]/Dest[460 0 R/XYZ null 782 0]>>endobj
+295 0 obj<</Subtype/Link/Rect[365.2 237.6 374.2 250.6]/Border[0 0 0]/Dest[460 0 R/XYZ null 782 0]>>endobj
+296 0 obj<</Subtype/Link/Rect[374.2 237.6 410.8 250.6]/Border[0 0 0]/Dest[460 0 R/XYZ null 782 0]>>endobj
+297 0 obj<</Subtype/Link/Rect[144.0 224.4 168.8 237.4]/Border[0 0 0]/Dest[460 0 R/XYZ null 670 0]>>endobj
+298 0 obj<</Subtype/Link/Rect[168.8 224.4 187.4 237.4]/Border[0 0 0]/Dest[460 0 R/XYZ null 670 0]>>endobj
+299 0 obj<</Subtype/Link/Rect[187.4 224.4 223.1 237.4]/Border[0 0 0]/Dest[460 0 R/XYZ null 670 0]>>endobj
+300 0 obj<</Subtype/Link/Rect[223.1 224.4 336.5 237.4]/Border[0 0 0]/Dest[460 0 R/XYZ null 670 0]>>endobj
+301 0 obj<</Subtype/Link/Rect[336.5 224.4 345.5 237.4]/Border[0 0 0]/Dest[460 0 R/XYZ null 670 0]>>endobj
+302 0 obj<</Subtype/Link/Rect[345.5 224.4 382.1 237.4]/Border[0 0 0]/Dest[460 0 R/XYZ null 670 0]>>endobj
+303 0 obj<</Subtype/Link/Rect[144.0 211.2 168.8 224.2]/Border[0 0 0]/Dest[460 0 R/XYZ null 558 0]>>endobj
+304 0 obj<</Subtype/Link/Rect[168.8 211.2 190.4 224.2]/Border[0 0 0]/Dest[460 0 R/XYZ null 558 0]>>endobj
+305 0 obj<</Subtype/Link/Rect[190.4 211.2 217.0 224.2]/Border[0 0 0]/Dest[460 0 R/XYZ null 558 0]>>endobj
+306 0 obj<</Subtype/Link/Rect[217.0 211.2 320.0 224.2]/Border[0 0 0]/Dest[460 0 R/XYZ null 558 0]>>endobj
+307 0 obj<</Subtype/Link/Rect[320.0 211.2 329.0 224.2]/Border[0 0 0]/Dest[460 0 R/XYZ null 558 0]>>endobj
+308 0 obj<</Subtype/Link/Rect[329.0 211.2 365.6 224.2]/Border[0 0 0]/Dest[460 0 R/XYZ null 558 0]>>endobj
+309 0 obj<</Subtype/Link/Rect[144.0 198.0 168.8 211.0]/Border[0 0 0]/Dest[460 0 R/XYZ null 406 0]>>endobj
+310 0 obj<</Subtype/Link/Rect[168.8 198.0 200.2 211.0]/Border[0 0 0]/Dest[460 0 R/XYZ null 406 0]>>endobj
+311 0 obj<</Subtype/Link/Rect[200.2 198.0 226.8 211.0]/Border[0 0 0]/Dest[460 0 R/XYZ null 406 0]>>endobj
+312 0 obj<</Subtype/Link/Rect[226.8 198.0 347.5 211.0]/Border[0 0 0]/Dest[460 0 R/XYZ null 406 0]>>endobj
+313 0 obj<</Subtype/Link/Rect[347.5 198.0 356.5 211.0]/Border[0 0 0]/Dest[460 0 R/XYZ null 406 0]>>endobj
+314 0 obj<</Subtype/Link/Rect[356.5 198.0 393.1 211.0]/Border[0 0 0]/Dest[460 0 R/XYZ null 406 0]>>endobj
+315 0 obj<</Subtype/Link/Rect[144.0 184.8 168.8 197.8]/Border[0 0 0]/Dest[460 0 R/XYZ null 295 0]>>endobj
+316 0 obj<</Subtype/Link/Rect[168.8 184.8 202.7 197.8]/Border[0 0 0]/Dest[460 0 R/XYZ null 295 0]>>endobj
+317 0 obj<</Subtype/Link/Rect[202.7 184.8 225.0 197.8]/Border[0 0 0]/Dest[460 0 R/XYZ null 295 0]>>endobj
+318 0 obj<</Subtype/Link/Rect[225.0 184.8 337.7 197.8]/Border[0 0 0]/Dest[460 0 R/XYZ null 295 0]>>endobj
+319 0 obj<</Subtype/Link/Rect[337.7 184.8 346.7 197.8]/Border[0 0 0]/Dest[460 0 R/XYZ null 295 0]>>endobj
+320 0 obj<</Subtype/Link/Rect[346.7 184.8 383.3 197.8]/Border[0 0 0]/Dest[460 0 R/XYZ null 295 0]>>endobj
+321 0 obj<</Subtype/Link/Rect[144.0 171.6 168.8 184.6]/Border[0 0 0]/Dest[460 0 R/XYZ null 183 0]>>endobj
+322 0 obj<</Subtype/Link/Rect[168.8 171.6 199.6 184.6]/Border[0 0 0]/Dest[460 0 R/XYZ null 183 0]>>endobj
+323 0 obj<</Subtype/Link/Rect[199.6 171.6 221.9 184.6]/Border[0 0 0]/Dest[460 0 R/XYZ null 183 0]>>endobj
+324 0 obj<</Subtype/Link/Rect[221.9 171.6 331.6 184.6]/Border[0 0 0]/Dest[460 0 R/XYZ null 183 0]>>endobj
+325 0 obj<</Subtype/Link/Rect[331.6 171.6 340.6 184.6]/Border[0 0 0]/Dest[460 0 R/XYZ null 183 0]>>endobj
+326 0 obj<</Subtype/Link/Rect[340.6 171.6 377.2 184.6]/Border[0 0 0]/Dest[460 0 R/XYZ null 183 0]>>endobj
+327 0 obj<</Subtype/Link/Rect[144.0 158.4 174.2 171.4]/Border[0 0 0]/Dest[463 0 R/XYZ null 752 0]>>endobj
+328 0 obj<</Subtype/Link/Rect[174.2 158.4 191.1 171.4]/Border[0 0 0]/Dest[463 0 R/XYZ null 752 0]>>endobj
+329 0 obj<</Subtype/Link/Rect[191.1 158.4 226.8 171.4]/Border[0 0 0]/Dest[463 0 R/XYZ null 752 0]>>endobj
+330 0 obj<</Subtype/Link/Rect[226.8 158.4 280.3 171.4]/Border[0 0 0]/Dest[463 0 R/XYZ null 752 0]>>endobj
+331 0 obj<</Subtype/Link/Rect[280.3 158.4 394.9 171.4]/Border[0 0 0]/Dest[463 0 R/XYZ null 752 0]>>endobj
+332 0 obj<</Subtype/Link/Rect[394.9 158.4 403.8 171.4]/Border[0 0 0]/Dest[463 0 R/XYZ null 752 0]>>endobj
+333 0 obj<</Subtype/Link/Rect[403.8 158.4 442.9 171.4]/Border[0 0 0]/Dest[463 0 R/XYZ null 752 0]>>endobj
+334 0 obj<</Subtype/Link/Rect[108.0 145.2 124.5 158.2]/Border[0 0 0]/Dest[463 0 R/XYZ null 629 0]>>endobj
+335 0 obj<</Subtype/Link/Rect[124.5 145.2 147.4 158.2]/Border[0 0 0]/Dest[463 0 R/XYZ null 629 0]>>endobj
+336 0 obj<</Subtype/Link/Rect[147.4 145.2 180.1 158.2]/Border[0 0 0]/Dest[463 0 R/XYZ null 629 0]>>endobj
+337 0 obj<</Subtype/Link/Rect[180.1 145.2 220.1 158.2]/Border[0 0 0]/Dest[463 0 R/XYZ null 629 0]>>endobj
+338 0 obj<</Subtype/Link/Rect[220.1 145.2 257.4 158.2]/Border[0 0 0]/Dest[463 0 R/XYZ null 629 0]>>endobj
+339 0 obj<</Subtype/Link/Rect[108.0 132.0 124.5 145.0]/Border[0 0 0]/Dest[463 0 R/XYZ null 550 0]>>endobj
+340 0 obj<</Subtype/Link/Rect[124.5 132.0 156.0 145.0]/Border[0 0 0]/Dest[463 0 R/XYZ null 550 0]>>endobj
+341 0 obj<</Subtype/Link/Rect[156.0 132.0 197.2 145.0]/Border[0 0 0]/Dest[463 0 R/XYZ null 550 0]>>endobj
+342 0 obj<</Subtype/Link/Rect[197.2 132.0 226.2 145.0]/Border[0 0 0]/Dest[463 0 R/XYZ null 550 0]>>endobj
+343 0 obj<</Subtype/Link/Rect[226.2 132.0 263.5 145.0]/Border[0 0 0]/Dest[463 0 R/XYZ null 550 0]>>endobj
+344 0 obj<</Subtype/Link/Rect[108.0 118.8 130.0 131.8]/Border[0 0 0]/Dest[463 0 R/XYZ null 458 0]>>endobj
+345 0 obj<</Subtype/Link/Rect[130.0 118.8 162.7 131.8]/Border[0 0 0]/Dest[463 0 R/XYZ null 458 0]>>endobj
+346 0 obj<</Subtype/Link/Rect[162.7 118.8 182.6 131.8]/Border[0 0 0]/Dest[463 0 R/XYZ null 458 0]>>endobj
+347 0 obj<</Subtype/Link/Rect[182.6 118.8 222.6 131.8]/Border[0 0 0]/Dest[463 0 R/XYZ null 458 0]>>endobj
+348 0 obj<</Subtype/Link/Rect[222.6 118.8 259.8 131.8]/Border[0 0 0]/Dest[463 0 R/XYZ null 458 0]>>endobj
+349 0 obj<</Subtype/Link/Rect[72.0 92.4 80.2 105.4]/Border[0 0 0]/Dest[469 0 R/XYZ null 818 0]>>endobj
+350 0 obj<</Subtype/Link/Rect[80.2 92.4 88.5 105.4]/Border[0 0 0]/Dest[469 0 R/XYZ null 818 0]>>endobj
+351 0 obj<</Subtype/Link/Rect[88.5 92.4 97.5 105.4]/Border[0 0 0]/Dest[469 0 R/XYZ null 818 0]>>endobj
+352 0 obj<</Subtype/Link/Rect[97.5 92.4 149.4 105.4]/Border[0 0 0]/Dest[469 0 R/XYZ null 818 0]>>endobj
+353 0 obj<</Subtype/Link/Rect[72.0 66.0 82.7 79.0]/Border[0 0 0]/Dest[475 0 R/XYZ null 818 0]>>endobj
+354 0 obj<</Subtype/Link/Rect[82.7 66.0 124.2 79.0]/Border[0 0 0]/Dest[475 0 R/XYZ null 818 0]>>endobj
+355 0 obj[186 0 R
+187 0 R
+188 0 R
+189 0 R
+190 0 R
+191 0 R
+192 0 R
+193 0 R
+194 0 R
+195 0 R
+196 0 R
+197 0 R
+198 0 R
+199 0 R
+200 0 R
+201 0 R
+202 0 R
+203 0 R
+204 0 R
+205 0 R
+206 0 R
+207 0 R
+208 0 R
+209 0 R
+210 0 R
+211 0 R
+212 0 R
+213 0 R
+214 0 R
+215 0 R
+216 0 R
+217 0 R
+218 0 R
+219 0 R
+220 0 R
+221 0 R
+222 0 R
+223 0 R
+224 0 R
+225 0 R
+226 0 R
+227 0 R
+228 0 R
+229 0 R
+230 0 R
+231 0 R
+232 0 R
+233 0 R
+234 0 R
+235 0 R
+236 0 R
+237 0 R
+238 0 R
+239 0 R
+240 0 R
+241 0 R
+242 0 R
+243 0 R
+244 0 R
+245 0 R
+246 0 R
+247 0 R
+248 0 R
+249 0 R
+250 0 R
+251 0 R
+252 0 R
+253 0 R
+254 0 R
+255 0 R
+256 0 R
+257 0 R
+258 0 R
+259 0 R
+260 0 R
+261 0 R
+262 0 R
+263 0 R
+264 0 R
+265 0 R
+266 0 R
+267 0 R
+268 0 R
+269 0 R
+270 0 R
+271 0 R
+272 0 R
+273 0 R
+274 0 R
+275 0 R
+276 0 R
+277 0 R
+278 0 R
+279 0 R
+280 0 R
+281 0 R
+282 0 R
+283 0 R
+284 0 R
+285 0 R
+286 0 R
+287 0 R
+288 0 R
+289 0 R
+290 0 R
+291 0 R
+292 0 R
+293 0 R
+294 0 R
+295 0 R
+296 0 R
+297 0 R
+298 0 R
+299 0 R
+300 0 R
+301 0 R
+302 0 R
+303 0 R
+304 0 R
+305 0 R
+306 0 R
+307 0 R
+308 0 R
+309 0 R
+310 0 R
+311 0 R
+312 0 R
+313 0 R
+314 0 R
+315 0 R
+316 0 R
+317 0 R
+318 0 R
+319 0 R
+320 0 R
+321 0 R
+322 0 R
+323 0 R
+324 0 R
+325 0 R
+326 0 R
+327 0 R
+328 0 R
+329 0 R
+330 0 R
+331 0 R
+332 0 R
+333 0 R
+334 0 R
+335 0 R
+336 0 R
+337 0 R
+338 0 R
+339 0 R
+340 0 R
+341 0 R
+342 0 R
+343 0 R
+344 0 R
+345 0 R
+346 0 R
+347 0 R
+348 0 R
+349 0 R
+350 0 R
+351 0 R
+352 0 R
+353 0 R
+354 0 R
+]endobj
+356 0 obj<</Subtype/Link/Rect[72.0 673.2 90.9 686.2]/Border[0 0 0]/Dest[475 0 R/XYZ null 737 0]>>endobj
+357 0 obj<</Subtype/Link/Rect[90.9 673.2 119.0 686.2]/Border[0 0 0]/Dest[475 0 R/XYZ null 737 0]>>endobj
+358 0 obj<</Subtype/Link/Rect[72.0 660.0 90.9 673.0]/Border[0 0 0]/Dest[475 0 R/XYZ null 434 0]>>endobj
+359 0 obj<</Subtype/Link/Rect[90.9 660.0 136.8 673.0]/Border[0 0 0]/Dest[475 0 R/XYZ null 434 0]>>endobj
+360 0 obj[356 0 R
+357 0 R
+358 0 R
+359 0 R
+]endobj
+361 0 obj<</Dests 362 0 R>>endobj
+362 0 obj<</Kids[363 0 R]>>endobj
+363 0 obj<</Limits[(1)(6_2)]/Names[(1)364 0 R(1_1)365 0 R(1_2)366 0 R(1_3)367 0 R(2)368 0 R(2_1)369 0 R(2_2)370 0 R(3)371 0 R(3_1)372 0 R(3_2)373 0 R(3_3)374 0 R(3_3_1)375 0 R(3_3_2)376 0 R(3_4)377 0 R(3_4_1)378 0 R(3_5)379 0 R(3_5_1)380 0 R(3_5_2)381 0 R(3_5_3)382 0 R(4)383 0 R(4_1)384 0 R(4_10)385 0 R(4_2)386 0 R(4_3)387 0 R(4_4)388 0 R(4_5)389 0 R(4_6)390 0 R(4_7)391 0 R(4_7_1)392 0 R(4_7_10)393 0 R(4_7_2)394 0 R(4_7_3)395 0 R(4_7_4)396 0 R(4_7_5)397 0 R(4_7_6)398 0 R(4_7_7)399 0 R(4_7_8)400 0 R(4_7_9)401 0 R(4_8)402 0 R(4_9)403 0 R(5)404 0 R(6)405 0 R(6_1)406 0 R(6_2)407 0 R]>>endobj
+364 0 obj<</D[415 0 R/XYZ null 818 null]>>endobj
+365 0 obj<</D[415 0 R/XYZ null 737 null]>>endobj
+366 0 obj<</D[415 0 R/XYZ null 645 null]>>endobj
+367 0 obj<</D[415 0 R/XYZ null 408 null]>>endobj
+368 0 obj<</D[421 0 R/XYZ null 818 null]>>endobj
+369 0 obj<</D[421 0 R/XYZ null 737 null]>>endobj
+370 0 obj<</D[421 0 R/XYZ null 526 null]>>endobj
+371 0 obj<</D[427 0 R/XYZ null 818 null]>>endobj
+372 0 obj<</D[427 0 R/XYZ null 737 null]>>endobj
+373 0 obj<</D[427 0 R/XYZ null 619 null]>>endobj
+374 0 obj<</D[427 0 R/XYZ null 249 null]>>endobj
+375 0 obj<</D[430 0 R/XYZ null 782 null]>>endobj
+376 0 obj<</D[430 0 R/XYZ null 401 null]>>endobj
+377 0 obj<</D[430 0 R/XYZ null 278 null]>>endobj
+378 0 obj<</D[430 0 R/XYZ null 171 null]>>endobj
+379 0 obj<</D[436 0 R/XYZ null 800 null]>>endobj
+380 0 obj<</D[436 0 R/XYZ null 547 null]>>endobj
+381 0 obj<</D[436 0 R/XYZ null 215 null]>>endobj
+382 0 obj<</D[442 0 R/XYZ null 641 null]>>endobj
+383 0 obj<</D[445 0 R/XYZ null 818 null]>>endobj
+384 0 obj<</D[445 0 R/XYZ null 737 null]>>endobj
+385 0 obj<</D[463 0 R/XYZ null 458 null]>>endobj
+386 0 obj<</D[445 0 R/XYZ null 658 null]>>endobj
+387 0 obj<</D[448 0 R/XYZ null 676 null]>>endobj
+388 0 obj<</D[448 0 R/XYZ null 558 null]>>endobj
+389 0 obj<</D[454 0 R/XYZ null 276 null]>>endobj
+390 0 obj<</D[457 0 R/XYZ null 800 null]>>endobj
+391 0 obj<</D[457 0 R/XYZ null 720 null]>>endobj
+392 0 obj<</D[457 0 R/XYZ null 508 null]>>endobj
+393 0 obj<</D[463 0 R/XYZ null 752 null]>>endobj
+394 0 obj<</D[457 0 R/XYZ null 396 null]>>endobj
+395 0 obj<</D[457 0 R/XYZ null 284 null]>>endobj
+396 0 obj<</D[460 0 R/XYZ null 782 null]>>endobj
+397 0 obj<</D[460 0 R/XYZ null 670 null]>>endobj
+398 0 obj<</D[460 0 R/XYZ null 558 null]>>endobj
+399 0 obj<</D[460 0 R/XYZ null 406 null]>>endobj
+400 0 obj<</D[460 0 R/XYZ null 295 null]>>endobj
+401 0 obj<</D[460 0 R/XYZ null 183 null]>>endobj
+402 0 obj<</D[463 0 R/XYZ null 629 null]>>endobj
+403 0 obj<</D[463 0 R/XYZ null 550 null]>>endobj
+404 0 obj<</D[469 0 R/XYZ null 818 null]>>endobj
+405 0 obj<</D[475 0 R/XYZ null 818 null]>>endobj
+406 0 obj<</D[475 0 R/XYZ null 737 null]>>endobj
+407 0 obj<</D[475 0 R/XYZ null 434 null]>>endobj
+408 0 obj<</Type/Pages/MediaBox[0 0 595 792]/Count 26/Kids[409 0 R
+412 0 R
+481 0 R
+484 0 R
+415 0 R
+418 0 R
+421 0 R
+424 0 R
+427 0 R
+430 0 R
+433 0 R
+436 0 R
+439 0 R
+442 0 R
+445 0 R
+448 0 R
+451 0 R
+454 0 R
+457 0 R
+460 0 R
+463 0 R
+466 0 R
+469 0 R
+472 0 R
+475 0 R
+478 0 R
+]>>endobj
+409 0 obj<</Type/Page/Parent 408 0 R/Contents 410 0 R/Resources<</ProcSet[/PDF/Text/ImageB/ImageC/ImageI]/Font<</F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+410 0 obj<</Length 411 0 R/Filter/FlateDecode>>stream
+xÚíßoä8rÇ¥nõË<ÉÞéwº= $@ÆÓö¸go€ h“y+Iòäa±;{Ùà²{73—àþûô/wë)U)‰ò™ ì®mªÅXüV‘,ªÿòêZ¬6ÿ\‹ïoÄûâ§ÿyu/_ÅòÕ_ÄûÕ®¾ßýñý‡W+ñ‡W7âææúêvWëÓ»‡ì?Þ}z—þë½XÿójWÞïʇ]ù¸+»òîü˾û$¾}ùëçwÿ.nß_¿û7±¾¾~wâV|z|•?Ï/\fE>Þ,‹‹³mÙüÏâöqì\òaqzªrv~ù8N.ys¡F*À-nGÆ%oÚ˜žÊùe<.<Ôív \¹ø‹Øq®ûÐã•óµÃ\7\*›æÎô•Ý>³Ìõ <óò]ìWváY)þ¥S\7žµ2Y;Ã%…g³\:Âe¬v»Ì—¼ð¬ÿÍà\YèuQ¾˜ëÞë¨Lâ!¹®½ÎŠ¿ŒË²Z·±6åÍ \2ô<ÁÀI!,—×½sõ‚Ń`qÀ` X 0pZ2øâãÀò¼eO\û-3Íæêk÷Áµòz/“¸î¼Ê´s®Ô¤¼î˜+ ‡á"ˆ"¸¯ð,í€1H!C;`$šAbt®Ì´¬»â
+‡åB1Cæx1‡ç"‹=‘Kåùp ¸¼À:Wê9QÖ–¹dè×Ä2×Ês¤Ì­r¥ž3%¶È劢œ+ò*kk\™KX­Òã TÔ# LÔ£Š4ðZåJ\Ãjé0ŸÆ£: F¨ñçŒã’.b5Æõ0FGŒ0ŸKF0读Î΋7Ûr¿=øÐm‡A/Ý埿©òí9ˆÎ: ºï.¿!é_>\tÓaÐuw·N•î Ж\«nºªØk ëa=tØ]„ôcÉÍ ^³¹Ø¡-]œI6årqC zznÆš2ÄL.fw±Ò©9]0¹X<7’Ñe>+éFÛ-vÙ’ÅöeƒGoZ‘z°¿ªa’öÎI6X3¸DoC‹Ÿyй²°È`>‹,ò¾#’ÂX9š¹Â"‚M©\É
+ - p\h!M:|W:°Gf(ØÃ…Wà ï¼à$l†á
+Ý ŠÖO\)?0®Ãâv®ÈÑ tز+tDãIí Z¹R·º Ùa“V®È±îBú°¸+t¬»OzÞ•9ã’iM
+Z¸çº %NZ¸„c£ -eq#—t°»pc~ÙÈ•:Ø]¸e¤ ‘ ©ò~¯X(+š4r…E†Dåhâª|ÏX¨á±nàBª|Ð7WN²!àªüºw.„!N¸X[câDÏ•º(òhA‹µ\à¢È£ÐRË%U œ)Ít\ÒUÕÀ ý©Ž+¥…†eOvú©bìO¿‡Ã3MJªœbýü
+ß0`Qs×\Ë%K7jJh%ºÖXÃ%h±F#×TËu¸MQç0ŽCâp†W€ãòô\PP¯ n
+´%gx-‘\K-WVx>€5CÌ( Ô\@t^Í\–ko{“S'L,)ýDÍ%ˆÎ«™k¢ç:i…¤ÌzÐÏÁáËõd\
+®“¶§x3Ä(}¬âʨ1T ×\ËuÁˆ²V’`:Ðç^žkªçŠžQH™|gXAò\»Ê·pyz®ôðYÅ %Pq…D3låZj¹ö÷ší…_²k¶©‚KRÍ°•+Ðs­ö@¢¾‡j´Üæ+¸Rª¶rùz®ý͉Ӄ)ˆ@öÊ1–kz¬­äÚÇuå!Õ+Or,×,|’'%WA¤(³T³' ÊÆÏ==5Wš¥¶†ˆ³WF6Ãf®´„S㒬Œ§k@”IŽç’Of¡æ:ši±$õˆså€Àµkx çJXkv8¡¢l,)\p¸†K²ÖìZ[Yãb¤/6seQÐpšH\³‹PMšlLr
+W~Pzp6œ”IM6f4®ƒÒë¸"N¥(O4ÙXÒ¸J¯ã
+9ãK¢ž=p7Q\¥×p¥¼=Œ#Ù@Š6&D®ƒÒk¸"ÞbÛóŸ–¹$cxµrí•^Ãò6g"ÌÃ’l,©\{¥WseÌ]Œ㘴¶‘S¹öJ¯æ½˜ERe®ˆ1¼Ú¹vJ¯æÌ̉qÌ@‰¢:×κ#ý¼rÁ0Ä6[—¸X›ÊåØJÁµk½Pqíí^2Bh&P¢¨Xýèâ¢øe®S#ª\b¯É‚nˆ+DÀ¬,‚2ײøhª\ ázê(Ân
+vfVäB¬ÙL5&1/*ê¤Â•i¸’OF7Ä!@ùÆ$¦õªëŸu®ÕÓC@ïV¢=m‰Kpd£´5, ¾¨À)¹ä±+²!J„]!:ÔšDP
+8\Õ‹*#eg–@…Q9†ëôƒ"û_Íås¸*UžJ<—ß—â8_;—⢴ê`›r ˜ðpbÎU× Åq¾v.ÝÀ@5_öìpI ×J­óŠã|®úE‘ÒÁ&Œ
+r= 9·å&Öó!¸ê ¥#BqÍP\©Ú¥š8JqœÁU¿HË•Z⊔ñÆÃ) ©4±~œÁU¿(T:"×Ã%½š"7ÎSÇù0\µ‹´\ ?Œ*´áðKô¼RqœÃU»¨XýtG×:7œÍ•M¬çÃpÕ.Òr?ŒÊÕ£YU­5ñ Ú4®êE\9š+PýÖ5M¬çCqU/ÒrE¹Öê^ŒÕM¬çCqU/Òr­øáa®68Íò~­‰Õã|(®êEa%MõÈ%¬qù±†«ª–šX9·ãª\Äçš ¹üeá·¿|Xö˜«›X9·ãª\$
+Š]â
+ípò‹ñÆ~Ù#P7±rœÇU¹¨G%X® ÇÍSrWáýPŠ&îD›ÊU¾hUˆ¢—×-WÚÄU>·äRœ,6q…åšqeºxþT
+"Wù¢¤:TÂ=—ì˜KjNq(Žó!¹Ê¥É––knÄ•7r‡«t‘¬„æ‰\‡«|QX`âÀÕž{¸ì«tœËU¾(R$ão¸Òa¹"Wé¢ä´«|Zäœ+åp•.’ÅV¦ÇxÃdZiKr¸Ê‰Óþ×½GàŠ»ä*çCs•/Jjû•.p%®òE•HPì¸L¦Ë(®°™+ãp•/*ÏŒƒ•\…ã|x®ÊE¥‹£¹Öú&F®òEå4"Øqµ.oø†\Â+.ÑÎÔ?•«rÑ]q²˜ôÃu|€¦‰’ÃU½è¾°aËò°Ë?üîôþùV®±äÀÊâ™Ah_f]nïžK¼p½pkúL¹‚qr…/\/\/\/\/\8®Ù × W÷\ž9×fæƒ>WJ©;,×ýîfþ%¦¹”ºs]T¯•_6TxÃœ¢®z7@½Så)Ji[gÌ%jéå<ýâöT½.ƒk]^ íˆë®>£)¿Ì+;mvÜaNTµr-Ë´.©ØQ*¿Ð09.ñKÔÉ£V®y\ šZ‹â
+e]>WÚ%WTر^¼KÁe’Ï5uUËüê»–·w’.¹DA äiT4°ðBfM]—Ü>Ý âIg\aqNT”þd<qC]×äø8¢6.“x¾ôg8©Åq€¥å$mU]×ôø86O&è‡+9ýtLr’¶ª.‰+(>º™WKúáñÏÙÙ¦Ì bÎHéë’¸¶ç¿ŽiÞ)×D÷‡-O!©¸i©Ï%ƒt;„—q…ºtœÃžrV€ Rwð\«Ãçm?yÝ×>\kâÆIiië’¸¢ƒ m-=îŒë°×y¾VX»LŠ'9×Ö%qÁ)ûÕÏ;ã:†èµÖî¿ø!,øb}]%W=±sG‘ÆëÖ}uÇUÈ©´6zò6G_¬¯KáJ°Ú~°WÓ¾^±^é½ÄI=‹V[—•~ܺeC®¦}ØHyŽ£49Ž[ëR¸¤w|!ú¬™ËhYj3KE-«E_—À•óî·ÝqU²hý¸Ö“¢.…kï˜wn¹K®Â‹¶Êª/™ÓÕ¥písºóñ]r=„­åR©’Mëâ¸öŽ9ÝÕ7ãjÍûºU£F(Šº.Þ/ïÜ`¼ý·ßÂe#Ÿíá¢>©I4“U]W²³À[îžëÔÚi%”Z£ê¸öŽyç–ûàz…‰vÐ4Õ%píóÎ-raß-š6‹AS]—|òb³¾¸N‰×í\•º®½cÞ‹d#—I>ööU2˲+Ör5Õ¥pmLp’ÖO;á*»Wh]ìÔÕ¥pm$ÃO÷ŽÑ+Æ­¶p5Õ¥pm{:ñŽ/ªëŒkR²-¿‘KW—Âûð~—ɹQú³hÔù¦º®d²fbεlž~Í‹ó©–«©.…+=9õθ’b®*·©4¶©.…+;͹LÎ!î¯õoO›ZK-WSÝ–x¾ô¶-YÞ4àš·îÞ6!õ“Ŧº®Â{a»ãŠæjU®†º$.ã2:—5T­r5ÔepÅæ\3\ºM5ô¯Å*úº$®¨ö›¸2½ÂÔ¸2îzT™ŽO¥‘Ëèý嵘×y#—¾.‰K÷~V»\…Æ^æ-\Úº$®ôèÓ›¹Œ²·Þè"Ôn”˜Ô5*†ïí9Ê×ëõóU|†ïYr¶¾ë…«®ö×-“+ú»åŠGÉÕ¾À±%—É„ù…«.£‰å¨¹f£ä’·\Á(¹Ú'`ÓgÊ5'Wø _Ãïãxá€kõ _Ü÷ø,GÉÏ”+y†¢á÷š UÚ^ö:7üº¡J[“—¸ï t.Jìp9HµI]lø½œC•ÅŽ.àh %°ß{ëZÀ!Úì ù=Å®­´…mz€ü^i×Åc 8d«¿E~¿¹cGŠãÝ÷Ñ'­Ý
+È®uIè´
+Á~+r¼Ð£I™ !ô“QÈFPæB,Ýø£Y…KŒC8Bœ
+
+„#B¶r‚ :°˜ÝªÓ*BðÌØå
+È)‚8sx-k\Ñú,(ÖCéÑg­!' ÇÐ ýŽÈIÂ1ð
+lEiG°7=)aóf0XÔ&Sõ c5œF{žÿd<[ÌBê6WrÀªßtŽ*7ôÛ¬ù\ ‹Ëóo©Å°ø¦8 ‹ÙažwwÜYÍs£V®Èãü—Î?Ö0ÎM¸¤Ç/ç(kÌ.xŸ¾4â2è°í8»l3Ç&UËô¡Ë¤ÃöhúϾ ÙŸ;7ä¢NÔyùXë7ù°MWnÊ•yvÊÙùb±xó¸)‹ÅEhøisc.f½´å?b¸¤ƒ\s \f’8Hwá¸Üë°¥.ç:¬}é Ç%ÇÖ]H.®M± ‡äÊC—¸Öö¸Òquš‹µÒÑ‘ÆÇ6¹2g¸æ¹M.g´·½ç’áX4žÆÅ]Â@4h\NHv¿—Â%Gc…4.,½uHâÜñY4®l$VHå8þ%l`¹µDJî •kHïLÙ~¢r Ø“’æÈ\ƒ 1ZÚ k !FL,cp 3Ĉ{» ®A†5Ë€ÃEIB3Ø\½k=UŽÇÕ³v02
+ù‹¸½¹ú nWW·BþüéÝR¼9dâÓoß>ùåÇŸ>‹ÇÏ_ýãoÛÿüôå×?ûõ÷ßþQþ÷æ3nÅõõö3®?Ü^½o¯ß_Ýl?e{õÛOo¯¯VÛŠo¯×WÅÛ›û›Ä?~ý›È~ÿåÛÿýøå³€/¿ÿüן¾}ÝU¼}¿iÌés~ÿóß¾üúÇÿú¶iêÇïßnþõñŸÄÝŸþ$Òí/¿Šôó×Ï_þ÷óÏÛKc¹ûÇó;endstream
+endobj
+411 0 obj
+6325
+endobj
+412 0 obj<</Type/Page/Parent 408 0 R/Contents 413 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F8 7 0 R>>>>>>endobj
+413 0 obj<</Length 414 0 R/Filter/FlateDecode>>stream
+xÚ+ä2T0
+ár á
+ä
+endobj
+414 0 obj
+31
+endobj
+415 0 obj<</Type/Page/Parent 408 0 R/Contents 416 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F8 7 0 R/F9 8 0 R/Fc 9 0 R>>>>>>endobj
+416 0 obj<</Length 417 0 R/Filter/FlateDecode>>stream
+xÚ¥U[OA~çWœø¤‰l]nBßTÔ¨Ý
+š>ð2ÌÎÂèìžufJ}ÏÌ,B©5š†DâœÛ÷}çÂK#†úÄpÚ‚vxÞ8Ÿ4¾\ À$#Kï´“ô0†1ÇRMž‚µõƒ½ÙíGÁ#Ša”ŠÂÊLrf%Á¹q\»¶QϹNÒ€,¬ÐãRaä¼p_\ËÒEBŠ¼Ê)”—’,dµL*‘BF!C3kŽ!Æ°ùÎ+R4×,wõ©jÜŽZ®*Çbéàaaœ;Ø…€ Ìs*÷p7ú‰&H²˜Ãxm¬ÈazxpñŒ¦Gð(´q°âè$zCâUKÐÚ[
+½”bõŽïW%ΠDmÙŒX—ÅÖB{.vÚêö€š£™7ŸÀD0²°`fO…™Nê¥P‘Âl —̬aŒ™]1-¦·,: 9ZAŒ%]™N· ªÊ·Ê¡`J$pŠÚ¸.ìÕ­ iStË͵ æûè;w.ô³Pb œ¤¡‡¦’…ØŽŠ‰ê¬­^Pܧ£Ô!ÕÈùÂnÅ$69*jæèrrÕ% µ“÷32Œ•bsçïéÁÎ<x©DEEšµGçÆáòe¨C&\§‡7Éðî¯.âøt0=:†1 Ùoë!=WÈŸÉm|{ÂYYŽéQøI÷P©2µ)ƒ{%MUºI ¦­¤]€Ô'·UÁ]#˜’vý¦>,M ,+ÔÏ!aši\GÙ¡HÐر_¾-§m¤MH’!-B“$;C€eX'û¬’) º*V–ª> ª"axßFLì©ë\Uaxe,æòÕ^Ö«ˆ\ß=Àõb }zÈ+­iÅÕPÌþöêD'íéÑ^'+@æþ–¸ s?JhH˜Nï3á†,u;±aV`ÑÜ‘­–Õ¼Ú0ܶˆIrD=g…ˆBÐw†JáÊïªðb¾†b¼.ÖêùW᧭Þ)ü‰†®~s÷îó-ÊpoEQR¸™ VÐÊ~*´M¡a‹inF¯kÿ©Êqùóÿrt)ÇPjÒ µüdìÅ^+¤•×ë`êצfP<îîÿ¤vú'Ñ€òºW÷ÿå¤ñ½ñ…KZendstream
+endobj
+417 0 obj
+837
+endobj
+418 0 obj<</Type/Page/Parent 408 0 R/Contents 419 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F8 7 0 R>>>>>>endobj
+419 0 obj<</Length 420 0 R/Filter/FlateDecode>>stream
+xÚ+ä2T0
+áÒw³P04TIS046V072PIÑp rt QÐUp VðÌ+I-JKLNUpI-ÎLÏQÉE™%™ùyš!Y\º ºPF 3=30ßP!89¿ $êÂÈ
+endobj
+420 0 obj
+120
+endobj
+421 0 obj<</Type/Page/Parent 408 0 R/Contents 422 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F8 7 0 R/F9 8 0 R/Fc 9 0 R>>>>>>endobj
+422 0 obj<</Length 423 0 R/Filter/FlateDecode>>stream
+xÚ­•AsÚ0…ïüŠ=’™ØÁ†`Ãé0S&69qQ­5¨cKT“òï+!C ¥âf|°Á«ï­´ïÁ÷V
+ó¦õ £íX Dž£ºË¾¹’G?vEÞcì»2?€ñ2Ia"ò]…\Íw zuywà÷my¶A(DYŠ7Æ×n!=_L<êRøº½1_k†œ×à0öCð¾keö#¸Tn[o<O¼Àï ÞXð‚­wÒéÍ 'k´lHJÂ/ù¦ñ k$þŸM&gðt¯4V0ãeAr„ *¶æö–K¶}?›[$ÒÑÜJø?Ž"¢ÐoD"ŒhÅ8SÚìEHe÷²#eþåŽôi=™_C'R¬%©*k‚Æ]§‹kèódzoì»R7àfÉ5n†ª©EÒåæ·Tø?c{\Ǿªõöo“{O°ç¢cbY§8„g½AyŠ±úÇsÁ½‹+°-|d†gÓé‚nù½ûcÀÎœov74†b\Û~Vm*I¡Ww7ç,I3QÊ,‘”ð¼== “Qà{iš±§<ÔöH8…L®n6è‰5«¶åáWËèóŽQlÆ™ Šå¡¡+bŽ0W7Ocó îá ãèÆ`]D°241×"µ·ãzQí½0
+¡~ý§éÅ`„Í«®ý<ÍZ/­Ÿ²6Úbendstream
+endobj
+423 0 obj
+529
+endobj
+424 0 obj<</Type/Page/Parent 408 0 R/Contents 425 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F8 7 0 R>>>>>>endobj
+425 0 obj<</Length 426 0 R/Filter/FlateDecode>>stream
+xÚ-‹Á
+Â0DïûsÔC´›”êµZ Þ´® q#ŒšöÿшÌÀ03¼71ª¯®)öÚ-û5˜!ìV¶‚\gÝÐöƒíùpÂ>MšÃÅ+:ã-•ð9¾¦øLs¹“) ù“uYjË‹¿n1hЬÉëX®Ð‘>H.#ùendstream
+endobj
+426 0 obj
+123
+endobj
+427 0 obj<</Type/Page/Parent 408 0 R/Contents 428 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F8 7 0 R/F9 8 0 R/Fc 9 0 R>>>>>>endobj
+428 0 obj<</Length 429 0 R/Filter/FlateDecode>>stream
+xÚVÑn›0}ÏW\õ©•
+ $!d{ÚªVŠ´j›Bß"MŽ±O`˜m¶t_¿k „ШÝP_\¸çÜ˹ǧý9 `Š?,C˜E@óɧdòîaÁ
+Žo¢å’ôzki˜’$sN(Ó7ÉW»ðcWí-bßÕû܈"‹aà <ˆ¬EÌ!šúÙÊl}r`@»zõÜÖCʸ ä¤,…ÜÃŽ™ßŒIˆ½0'ˆ"S0Èò$-Ò›ØàˆbvìÌüÐö•$g)TÚR[ôzó´A.¢RU¾C¼›!^¨ºfÀ|Oè¡žh!µÐFCÁ¡*Á."Èl :ÜÜ­×`ر¥°ugæwv$)£"GáÝ8úC=J›vB¡ƒµÏ¥}>h|.óözzœNí¬Ó#çÛ›ÛNHÍp˜tÐ⥺Í{Çsb²\þƒàƒ„ð™È}Eö켑µ¥g¶°á‘T™¸Ç~A5³ŒQ#
+Yë‰ZCδFí´!¦c~Ã"»g(v=¶NÑFÙã­JýýÊ~v½UHS7ë­K\!µLÌßû ŠÉ«ÛæÈ™¡þöfÐþ²½¬7ÐŽy¡Xg0èYª! #§vÒ9¦v™H™4v ýÒÐöKv ¯DÏñ­hµF@+¥Ÿ=ƒB‹ì¥øƒ¥¿HV!Ê÷Þ-’6‹ cüŽnm-á|Óוöˆ¦Bœãz\îÅÿ…ºðâx±ò‚q°pl66[ŒƒEã`Ëq°‘{[\÷H)ƒZKÏù·ŸEM0ڈѥŒ¥6Ü-lƒ©5>¬yÿ¹·¨(”¥.\H¤·¶òbl»[בœr¹*SbXÚwMO¸½Ïö×^!Þé×3yëÇû7òøîéëÆré:…j€KeŒTñÔýýD™¸ØWŠØnÇ'¾8~õ?‹y<õWX‡5õ&î“É·É__ˆ–endstream
+endobj
+429 0 obj
+717
+endobj
+430 0 obj<</Type/Page/Parent 408 0 R/Contents 431 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>/Annots 32 0 R>>endobj
+431 0 obj<</Length 432 0 R/Filter/FlateDecode>>stream
+xÚ­WËrÛ6Ýû+n™Ò†4)Éòc¦ 5¶;^Äqcµ‹Æ]@$(¡& …€ì8qÿ½ç”E1²2îTö"qîëà> O{ ÅøKh0âÿ´Üûe²·~LÉ êÓ$ÇÚè(á¯Yo ¢„JUÊÈ>,¤y=ùÐ!%‰†ƒãhÄÀÉ\¶`”«BR&sUáÁb­–©žUê‹Ìüš‡‰*£TWFkHçðI×TêZRá$ñj|ýö€ül#z«ËRV–€iïnÖóPcEmé^Ù¹³·ÐKè¾é¯‚›×”ÎE-R+ëˆØÑ©HoM!Ìœ77JEESIKW…uÊ$TÁá¼"«; «ª%Væh0N°b·t }&Ò9Uº
+§…¨n=Ü9o¼÷‚Þ]¼;s‘Ê®ÊÜ‚§f¹õ>/°Ç€Ô˜“5<}
+üb|9Žh\Ž³Ž§¹.
+}¯ª™ƒ¶ì`“j),‡Œ]!#kåéwËÍæY¥+ª—…4'>b:öƒ4z²±2™ð§DètœüLÎ
+ö¯õ+]_9uôÏB¯ˆðK@ŒÃ?ƒ.n¥· Ã=t×È]ãðøQgÖ‰‚$‰DdT
+‹½y$ ûÂ"! Þ½“àü5káínl3í
+=S¦ñå)ýµ]?›xÃ2Hñ ™÷v‰ü<}ÿHËJÔtù~â%Öm7ärk7ÞU5úåžò-r-Ðs¶ƒrlwq®º›:(´¾¥@´6ø‚Žfïu}KÓ+Ѭ3×™Dt‰ˆ¦j¢U*Q¡=­R§•ÚØÐ(t“|TÖ+ÊUm|£ÜœCá ï£ä9Ô÷Ýöîÿ›C¸Xíe“¨T3—èE“è…c´o æÛÉPËE- šFoô²N%ÇØq<“ á»#,¢óí# ŽŒmêë¦óœK☧bdÏ'-j=«EÉë¤]ä[r¡ƒÅª´%³)G–AråF¾Ä<[ÏhEÊR¹„ˆDpZ2èíïW×+û™ÂF£xÖIu­seè“jHWˆç:­ÕÂÒwJÈžJã^0)çÈ—lg“½8ŠqnâK—¿Ò Ž‘u T©¤’õ°y*èÚŸªÖ)š$ÍNsŠ~ÇìMïêê¬úÕ5
+’éÉd
+jòó^µü€W‡G|íÕ| QÒ0éãÄ· ßF
+L}_ø=†ïÃhàê÷ÉtÜlnowã"Y\iž­K†Û|]][[¹Y†Ž*ï0‹ Ÿ{É1¯§Këú9œå¶´®‹©Dójü9jT&ƒ~ÐGnÜž~ŸO(ô]0 ¹@k˜ImFXGÈ¢áa?fÉ¿:ãç~!iÛû¥ÿ·ùendstream
+endobj
+432 0 obj
+1236
+endobj
+433 0 obj<</Type/Page/Parent 408 0 R/Contents 434 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F4 4 0 R/F6 6 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+434 0 obj<</Length 435 0 R/Filter/FlateDecode>>stream
+xÚVßÚF~ç¯ÝKI%;Øæàˆ”‡äÈI÷p-½ÒJ•ò²ØkØÈÞuöÇÿ}gvmNr¤B¬½3»ß|óÍ _G Lð“À<…ly=ú¸½}X@’ÀºÄÙÝ4¾ƒu1ÎâiœÄ ä®1¢²\¿YAÓioe‹xF¦ë0`¬r ÌâïÆYV/¢àäJ¾pm„’ ]ÅA•`wJ¥ëwáÔ ,┎EPQ’ѺÓ^C÷c£œÎù[{l8žg,¿Õ¬¾D‘ÿ `…d–îö¾•ãj^€UFƒÀ1¼_Œ7ˆá±DÈíK0®i”¶Æ[ áœBhžÛê6ž Vu€[ü¥§­
+7Ñ Ôì›Ö¾¼ˆüÿQ:‹§'þSÏÿ“ŽU÷ªܼž…Rgò, RYº)ðü¸‚€Šž™õ/Ï)êÂ.z k?RÞaË%×ÁPÈA°;¦‹=ÓÈ_ ½d®²ðÂ*ç9/Yeøµh³­*xõ›«7×$GP·ölÇ«9e0jßåPÓ¹ ýÁ„O#c˜gP›#ú^fn)ʆ_œ ì)ggIËè7¸ uÞ¢¸ùÔG¾Ò
+oæÿ±Ð΃ÎU¥<n:çÕ™æFUŽrj­àÒ‹¹guÍ žLð›à7…:Á5:Õ ®S\§¸NÓ+¶gíöÙu—~c`² ¡™W˜é
+‹jŒò“[¬räc/ª"GeÆð;2¡÷Âp¢ä5å¯f6ß’SG=ñCÏ?D‰l_zŸx!Øú;HûŒ*!QlBú­VK’\¨3m|‡¯6?A–OØØ
+÷·9%ß.ËAØ5—¨h's_êÆ!IÔ-~ZCåçñáóxßû+:ß9I¡„Åý&d|†³?9†½–’[òzMN¨ÏaÈv§•Ûî¾±*uHƒìÕL* ÐöðG‹ƒ¯vJÜýÓ?mÉß®ùõ9:ŸŸ4š¦Âî?ÄÄÊoÆ츿çɵ†rëÊßaê¾ÞP4ÿêp˜g-GBŽK$c¿˜ýnz· åþ¯ÕŸð¸\‹ÌwÍŽNÉÀ–XPÒÄîÖ¨/œœÂ†2Üð0DCc»IâÉMÞ] 9É2˜e·áŸÉòùÃâu×’áT^r#¶’~zÓÕjD®Ñ<ôÔ¿O=>êÿä„é:½›Ä‹ ¿9=Zþý ®æÂÊendstream
+endobj
+435 0 obj
+916
+endobj
+436 0 obj<</Type/Page/Parent 408 0 R/Contents 437 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F5 5 0 R/F6 6 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+437 0 obj<</Length 438 0 R/Filter/FlateDecode>>stream
+xÚí–ßoÚ0Çßù+NÝK+•4ŽCSU©£í„´I]ÉÞúâ&Nñlhÿûí
+ìNÊFí¼7ô48ÒBmQ¨ƪæP9Bœºö0üÕX¨ã1—¦YÔ†ÕæÂŒüÔ èñôäÓÉã™e@eOŒ±šå¦‰c¸
+Ê-ËG •l?UL¾¸ý¾zµ±dcnIØF‰û,Š¦UU•šû´ t±jÊ°çõKB>o-1 x\DÇ襊¦ò;Mø’cצ¡Ç½YxY£÷J1¾vžë¼‹eWn³V„Ø –‡¯ÅA”tQŸÇQˆCÔn1&q¸´¦]³vme‘Þ(:¶Ë¾M­‹ÖÕÆa//ÜÚÕ~¶¨c£òl”â÷²yk϶²F6$ŠÒÐWÜå /Ù´2Ö!A|<š³ÞFS/<¥: JÉrå­·äÊŠ’ÔJ~S9;\"!ø<ZÖYo—%½ÄÞ<Åéw|£Ÿ—ôÂeþ {ëÂiâóïÛÚ]W-Gë:ë­ïĪCÚøÅxÇ6ý1‚]WÆfw\šýï»{c· ¡’^ô\ |¸¾Ë  ýŸ÷Cج—,ç€Q<KØhmëŠ×˜kž];C±bßLús„åô£õXB——endstream
+endobj
+438 0 obj
+741
+endobj
+439 0 obj<</Type/Page/Parent 408 0 R/Contents 440 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F5 5 0 R/F8 7 0 R>>>>>>endobj
+440 0 obj<</Length 441 0 R/Filter/FlateDecode>>stream
+xÚ•˜]oÛ6†ïý+ˆ^µQÄQ"Ppݦ –m©íÝ™¶µÉ–+Ééº_?Jç$)
+Æ<D€À‘)øJ<ü:ã,u?œå‚IͪÃìýzv}“1ÎÙzËtšh¦ ™l½yý¡îl5ÔöÍúïW"ÉÜ©ãq»-ÏÍ0Íx"öUWŸ†º=Žß|\ÏÒ$uÿìé×ò*I™Îsw}εû,RwzgÙÊs•>Óšh™òéiŒ
+Ç(r¦µtãtÎóª²}×îFc‘ç0’ÕÉVõ¶¶=ö–5mUŽƒbívü{$SvÅ¥»¸cËé
+Ú±mÝXöåõæ¹:?p¯Ñ_ý—ûüêË›$P&9–ʤ´ó½\& §_¢±L^Ú•i,ϧÒ4Mû-`ÈÇóÉ7hêôÒ`˜ t~ö‹¦ìûËž™I#&ÒDO?ž¹Æiæ<×ßO6 ™é#kMÕôÒ¨™éDšï»ö[oç›Mgƒ%ÍÒDÑ]¦ºzit•ò®·ÇÁve•éS’²@Se½4Êrý¬zßvC@“§qBšªé¥AS™ â¢ëú`ÛsÀU&"RH]ý4ºæYò\Ôú¸ hj)¤©š^5³ "…¯Êžìzª©2™Bš*ë¥QVf)”½+»s¹ <­”0¡Bšjë¥Ñ–gàzüLMDœ¦
+zi”Fáò©­Î{–m¸ó²("Ò„4QÕO£j® M»®íptASiBšªé¥Q3S¦O]{>U"¤©Ž^¥‚ýÒöÃïåÁÞµí?çSà5*E"¤©¶^mùTÏÛé©«z˜ÖP6äšyBšêê¥ÁUlG~µö4o°yÙS1íÒDO?ž96Ož¤7©ÐyD¨¦êziÔÍ$„êíÝcƒs*‡ý»Ÿú–·×_¿ Œc
+
+°ñiÒSE‘&ŠúiÍ Lb'êJºªÿ ¤O³ž,
+4UÔK£hf`ú:Ñ¥ýz¶ý@æ<Yhª¬—FYi MtÛ¥Œx% MuôÒèÈÍhxï~Á…
+-£¢VQQ‹(`®o
+ÜNt™r‡ÐåüfÍ®ØâÏû›¶&¶eeÙÛ×»#ûiôj<ñ
+Ï”ÉØ‚Un½¸Iªö¸U¤‰ IƒÃÿ<û›iš7endstream
+endobj
+441 0 obj
+1023
+endobj
+442 0 obj<</Type/Page/Parent 408 0 R/Contents 443 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+443 0 obj<</Length 444 0 R/Filter/FlateDecode>>stream
+xÚ­•ßOÛ0ÇßûWœØ <4øG~uBHŒª´I¬ o¼˜Ô¡Þ’¸KÜ2þ{Îqh7H*M­¢ÈþÞÝÇç»Ë¯‚?
+<´ÿ´}IF§×>P
+I,‚0æ^ Éòx!«­¬æZ›“äÇè* íî1¿æ{Â(BJC|gÄcPIXtˆ©Oöêö¨9¡ïªÙ‚†Ü ÐçÚÈâ¦Ò›ui¡Å`R§JÚ©nI}îù–4‘ÅzªªJyþpJ§JÙ©n)ÇK©
+©7}·N#T¦tꡔꖒpËxWËž4“ŸC[õ@ÀnuÓI M3ì²À§–•{Ça]©ÒȪöR]f}ßuÆ|âj9YÉ¿•©\¾Õª65è {WWPh ™«R6K‹ËÙ Œüm<¸ÔE!KÓnÖFTž”Y±®õ¦\Âýñѧ£û ¡i{íéJT"ÅÀ^»ÁBWµW"]A©ËñC.ÊŸcç·Þ;.E!-‰°¨™zÜTÂ(]ÂRU25j+!Óy®ŸäžA¡éVäé=°ÛRåã¢q ‹è˜£õò³Ë_Ðæ/$˜¼1'wújeE }E˜`».ë´Rk‹õqåø”PÚNͱyþ©…?¦*Ÿ43õl*3±ÉÍ­»â&mçoN}vÚɣx7w¹0b:µÃÜ«]OñÐwÕ7•[•Ê»ù¬'r0Gº#A6²ï»K›•™î ÊãÆ¢S¿ý5™þªSÑ_œÆ”ƒS¿˜4gý†½Û^6‰˜_NÝ–½~ÑÏþGÙ±¦X†–Sw— ÚŽ]adOÔ¦Pg£QwödÜö$å"æÃüâ:1\ÞÝ.`fS‰TÎ õX›±1¶†ãÖ’»ä“ç;yõ Òͺ^îæ<êûè-$/Šendstream
+endobj
+444 0 obj
+695
+endobj
+445 0 obj<</Type/Page/Parent 408 0 R/Contents 446 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F4 4 0 R/F5 5 0 R/F6 6 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+446 0 obj<</Length 447 0 R/Filter/FlateDecode>>stream
+xÚ–Ár£8†ï~Š>:I` [µ‡q&³åª­-ïÄsËE€¼f#VˆÄ~ûmIàØTª\ýêþôwKâ¿‚V üÒãl½›=~‹Æ°ÛãH¸
+`—Íx>i¡J^À¦Ä‡=OEý°û×i—^äÔ‹eä9½GáKU½Èô§Ð°URËTnB
+ F!Ô[ž
+¨¤Ò&NL ñz€‘ fðôcûk%ßë¼ügtÿ ?¾ÖEÂk‘A¢$ÏR^ëŽÌƒõù¬8£C–•P\‹d‰+1é1)õ“Þ¬+ô©×Ž³Ð­ã™§‡«d·Fe¢NUž`LеÆð ÷Èh° Œ¦rSU
+}æµ±=kÝÿòò´Ù€'$×ʬ'6h 4X’;¼älÀE™á{®˜£ïE^
+x“ᯮVm0L²—êȵF“¸y)
+tó7ç:ØÄݛ޼äèRRó¢Ï•€—m»&|hTýy[´…™~]67¶Ãs;ùvÊÜZp§ÃØ÷ª7^4¸âT*%êJâ4Ƙ¼Ùn¡[D+n]^ ¥»•_¹ÚMÄü‹º©LíEöK
+ºÊŽó8ñO»Ê}DYSâ4úb*ŒÁíÙ¸ìƒÁã
+iÆ`CL…±â!Ö^Éa¯3öæbKe•‹±H™?ýÐpâ!(»f^}UÜœŒc\ÄŸÞÖN<Àņ#êíê¦ÄOƒ,FmŠüéMmµ0(°-÷á¤òMŒm¯Moh«
+endobj
+447 0 obj
+935
+endobj
+448 0 obj<</Type/Page/Parent 408 0 R/Contents 449 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F4 4 0 R/F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>/Annots 47 0 R>>endobj
+449 0 obj<</Length 450 0 R/Filter/FlateDecode>>stream
+xÚ­XKoÛ8¾çW zJJ©§èÁI“E€¦›u¼{ê…–h›…,i)ÊŽûëwHÊŽ,?¢C éÓÌ÷Í‹#ÿwEÀÃ/~¤¿ÓÕÕíôêæ!
+Mª„Ñ+ @é’×z?½ò\íïLþD“‘ëAÇh”Rü“z.Éáå8Jö`ûh$jèE¾Bþ{ôHòJ‡ò c´:”ŸŸãøn`ïñó£× ÈÏÑêP~|ŽõñŠfžç·fR°YΡ¿ßçFb´8”›Ÿàvó0êÊ›#£0 lׇ»ž_๬ÕK*E¥àAä\sz«[dË¢ž#¸†n2¾)w2^ñ"ã¨rœ•3ÞÅV²\H¶²Ï¸0]ÜÍY±hØ‚ƒ8ŽiEÆi‡ž‡\V@¼À Ûÿr«•yÄÍxvg<Ã
+
+endobj
+450 0 obj
+1407
+endobj
+451 0 obj<</Type/Page/Parent 408 0 R/Contents 452 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F8 7 0 R>>>>>>endobj
+452 0 obj<</Length 453 0 R/Filter/FlateDecode>>stream
+xÚ˜Ks£8…÷ùZÎT5n$ñòb“¤=®N:“¸+kbËD=Ñ<Ò_?Wº@ æ¡JUl£Ì9\]ùû%.üQ2²;^\n/Þo<B)ÙˆOIñUD¶û?Xà9,ÿÜ~»ðB8]©4+¡Q×Çc.ù‹ìÉTEv8 ¯¹¥¦\âP¾bš£À¶îÊ…[êþ=üC¼hå’ ðVñBxËùÊ#… #ìÚïX¸¥˜¹nG3õhðA«÷]8`ÔG iO}]ÝŠ½ŒÏä߉WQ]]‘c;>§’Ä“øPz©Pìœú¦ž?‡JQ ïÊãDÌ»í¯iëvÈáßœÛÈ¢Ûs0º4ºýF£Û~6n‡ ÜöOݾ®óTü<óú¾YEJ™%©pJ¹û»Ú«úùšQïGp²]­!k[kHךï5Ú‡EüTûǧQª´®¤Êz|T…ü­²*†'›í ”]%wð¡èp"3^•N.
+Gf»—Õ‚tskF:sáVg¥¥ÏÁtC£ô7º‘ÎÚ‰ dÖëSñ7Ç8Ç{©êlo^ûE°Qu"+‘ˆ¢$‰|†T/‚¤âP½#ϪªÔñ)dòµK•Ê5’«Rj³J¢æ\=AàTüFxóSÛ˜+øš…”ò]û”BÖºrÜé”òÖMJq×u¸Û«›¬„ªx| ½És9- Ñ‘ÇE\)MöÍ49í‰RëÙ„¬­'HOxâ‡èˆŽôV­O*w61~Lú§vl>³‡Øxh©&µg<¤äý5L•Y ‹ÎÁ‡†Fßhô¯ÃæžÝÈá´·ú}±žöÉùú¿U90@d >e5â烞³”¶óÒLßYLûùæú94m'÷»åmÑNdmíDzÂNÞ,oœ2°³—r·q)Wö¬4ªtbaˆ{`tFÉ£(ËL⼫ÅHGÖ6Ò‘tNÛÚaIŒ÷Åfuœn®Ãc‘t4Dú‹
+)`srm'EP¨JÿÐîH‡MÂÛ(ÙA;àìRµûï‡,Ǻ¯m]dH×¹ÞçÁÖçG6Ò/ô®{z½wiWb‹-²¶-Òã-ú«0T¿¨_ê
+ˆM¼_ó3wq`tÌ:@ê|^¼Ù—ÛM/ƒÚÎ.OüòÒ>.²Õï¥Ë=<¶Gù»ßw>É}õbº¥Td ¼µÝ±…mr.ïwÃ.8-¶»a—›Ã)ãq#,€Ðz¡ñˆû*½ø¡c§Ž¦ØNþÖ±S©Þõ¯7³~pëø4¨õêÁÇÃóý&jŽrNB†¿ð<ü½ÙBÌ]}½„ð‡¹njóZ”2ÉôË®y›%Ž>ÑiÎôàÒ欇¸Ô=æٛy¡ßÉËþ½ø×Àþ[endstream
+endobj
+453 0 obj
+1348
+endobj
+454 0 obj<</Type/Page/Parent 408 0 R/Contents 455 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+455 0 obj<</Length 456 0 R/Filter/FlateDecode>>stream
+xÚÅ—Msâ8†ïüŠ>fªbdÉ_‡=LÈdf*Cm–°µEÕ\#@;`¼¶ÿߖÚ$ x÷’JʆæmÙïÓrKþ§ÇáÙÿ|Ù»õ>ÞIàFS9D‰ðM®D$=ÅF÷dŒ9UÙöq£M>·QÎB
+3ø nWP¬ äó¬˜)0(¬AO¡V •5qÐ5¨ei¶6›Ç…Ø|ŽùýΉŸG=æ3tñr~™ø ¢HúÈ?
+áK¨<¶hÓðE‹.ˆÆ^ÔHöԈΠ°Ä1߶^>-Ô)XÕÊdFzV”ÙLÕ°™ãÇɺ\¨_º˜µ`vH9Hpª# §í È©O
+ý?“aÊðrÝL’¶«IRŸ0GøX“ óD"MÔDg£m¹?Ûo+ý¬*¯.U®§:Ç YA`%¶éÄvÄ) ¾xó½üH)ÁùO¿€ü˜˜ížÿË`œ¶3§>&Œš[O°a&ñ!˜mêUõW‹UµÇ†_Cp ò=>¡æ‚7×e:zsÚÎÞœú„7Qï ö¶”Ÿðö`gí¾7[gt1&Ø®']6æ´9õ cœærŠ=)=êI7[£¬¯ïºPǶ§oú¼Ûô…';ðœuna¤íÌéÛyHZXŠ-,=janöþ^MTu´˜åóuñsÛt.øqÕŒïáåðãCË*ö”\Ú_Sú} 0áþþ8'Àœr‘Yµ—ãû¾M³'Ì´'LÆåŸF,#±{–¤mjç“–ŸbRâWuƒX¦äF2æI&Z?–Y~¼_Ø̵Qm»/7-¤0úé ,,ôE–ÿ<ˆK»óŒ¢!FǃþA4"íýA8&ña8ÁðýñÐiΗçK$¹+)ÏÕˆÄT£³j*É©HoäT%ÁÙ®J«WiYVª®õªx¿…N0Ù¹g’¶k u{RF`Oò£¥`¸ÚôW뼕 ‘;'i»R!õ *QJSò
+º™ M”•.ðRÞD•
+ðÂP¤Q˜yfœ _c2ki‰ßëµ6¾ÛJô<QµÑ…s°×råÏüƒ…âëƒ÷Ðÿ~ xŽð¬LŽë:œä¶ù¯Ð6íÄ’æ;ßù"v;üt7Â)å®õÍÞô—i¸Uµžö”Wº4Í’àÙT/hÃ(mH$'-uOñm©-ÿf¢þÑû5&Dendstream
+endobj
+456 0 obj
+1137
+endobj
+457 0 obj<</Type/Page/Parent 408 0 R/Contents 458 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F4 4 0 R/F8 7 0 R/F9 8 0 R/Fc 9 0 R>>>>/Annots 156 0 R>>endobj
+458 0 obj<</Length 459 0 R/Filter/FlateDecode>>stream
+xÚ͘ÛnÛF†ïýsé
+RQ%eö RxØÃéþYØàšë„gò3×tô<#6à:S<'¬>;m>Žš”}
+Unî®ßCäG†Váܤ﹨FÔp#œ­R6„Û¢e!j –uV<Då:Q³ Sêt³*Õˆ¯ežËgÂp*ÕE:iC;3LŽáÍ î <­¬`Ò|×Æ‘lÀp6ÍÃxÞ°YhºZš°oçò31N Éç«ã9A&ž>'ÈÄC¦N™˜çÒEBÆšr°.¾ë4~ w:~à_h\ñC\ÀU‘È”¾Ž‹´±ñV–õ¸}!Ú×°©„^é{†™ëü¡tþhéF~ Í€Þœ½?t<#zhxN ï»f¤ç|85N ?péÿ
+Øí2Óú#˜öÇG™Š\™c)61IõëÍaùÈvæP:s´t£½†f
+, ¢™Æ–Joq]ãkW‹ª/Ÿj_ÕbÓÇêHü5Éãª2{S™*EùÃÝe%^×XÌ ¶µÕŽú|øÕHžâ²õá´Î•N§á´veœy\<îâGqÔÚl‡ÖÝoN¤å9ËshÇMi9î1Ûn ùe,¸FWGéºh2Vq1w·¬®î–šÎ+ÔÜvq~JÂçÕ·üRÈç‚L9«ôƒÅŽÔüßé×Oç_Í…Ë4íDë4»œÏ;Í:ÉÜ ÉbŒÒK¯~[Qí”b›ÇI»ˆT[‘dëLô Q~6ÓoŒJ8•ï1µÚA»2{Az£sбj#³é‚æ2Q™9žË)>+ÖòµìF–b¼ÁÈ,Sñ-KÄô$ª£_枟p_ÉŠ¬Î°äö$´z)1Ô%v)·ÍA¦72ÍÖûê8q—°ÀjMoèÐ4Ëf)þ »Fÿ
+
+I'¿e)α¦aóźqMË<fשB”Y YÕË&z“×cÕ¡Êø'*øí…ã4â]ý$ËìG³„MµxˆS£MÒ S2Òb0Âøš2kŸm×…
+endobj
+459 0 obj
+1470
+endobj
+460 0 obj<</Type/Page/Parent 408 0 R/Contents 461 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+461 0 obj<</Length 462 0 R/Filter/FlateDecode>>stream
+xÚÝ—_oÚ0Àßù÷H¥†&@<ì!…´jUm ²·JÈ$¸MìÔ1cìÓÏÆIIBZè†:m îÎ÷çwwá¹a)_tlõöãƕ׸¸€ÕiµÁ åovßR_ƒf·Õkua„#,0Œ9¡sxh¿§³‘{ïzîl<¹ýì¹øæ®ivÎμGi¯ –¥­AËVÖ¼%†@ÛJ2[,Á Â(p³ï8!…Òû$$8ÈåZ t6RîyE¸ü ÁÉ|%¤âXjîÕÑ͘á/O±(û×<‡²nQƒ"±â(2"D+´À{ªˆUõÌecÅIE¼¥®¥km[&Uº6*çbM¢H†'O¤U›$I ötÈq%E™0B¶¢Áž0ã¯É£•X2N~â Îá2F§­‹©Ð¸„,`¡4•Uȸ¸q½ÙðÞ™NÝiÅå(ÒŠŸY)¡R¡‰ÈsT(z(B2c¹âek
+‚i‚6©Àq 6Y öéP½AB=;uõ?Ÿ*7…4laÈ<?šˆCõ²Á ]¯¼ZÎh¤«•×Ê>P+$-l,TJÞK_'ò÷šx+®kñÞŸÓקíÒÊ`y!§Z­špr£ó·‰9Ô°¹<¡!;V6f×+ÔDãx®ƒL_²Ú…˜a½Ä%‚ÈVÛ¨Bº
+endobj
+462 0 obj
+771
+endobj
+463 0 obj<</Type/Page/Parent 408 0 R/Contents 464 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>/Annots 185 0 R>>endobj
+464 0 obj<</Length 465 0 R/Filter/FlateDecode>>stream
+xÚ­VËnâ0ÝówI¥Ip$¤Ò,ÊkÔªÕ0î* uê8é|ý\ãBI ‹Q¥7ç¾Î=×ö[Ë‚ø68D›V?luÆ.X„+üâõ\³á²=e/,’ð’.2øÍ㓹Hà*|AR´ùvk¤¯úŸ¥—ö·:T’Jc•æÉòœŠ&<Íås*ø_öÙÈÔë
+P‡ž©fD¨Ùâšó¨DÞ€CT‰‡<NñUâížéŸÇWˆ÷,äí,¾‚Ø€K¬=Kõø*ñÎq>ý“’ÅzO}Áét<@¢ýàúb#®HáêT†ö^H€˜xG,Ëp¡§Ñë‘ãƪÖrÞž=ô±Ù8¿8`1•ØâAºQÙÞªÔœÈ1ÑZïN¸OÞŽguè½f¯FmXÉt|×tZË&¦U¬JjqÙÅ%&K%3T’Ý~–r{ÝéÐ$73ºYP3ëNÄWÙW=š6<BÁß9u‰¡ I¶BŠ¾0p¶Âº»jìÁ.éUÍÀÙ§ëRM8é ÇáéýúÌU‚«™ëáûJÝÌzæ¬óø
+ñ]5 gñ⃋ø Šw]ûB>Uâ»ÖQ½ 3ª»U´a7£N—\î!Èw•ÿ){ç™:sl}î7«ªWtØ°lý»øô•ÿáôf‚êB¡‡hE#¦n|¨vu«Ž5å  ß&å%€âŽu§îXŵd:º §»ŸýYy- tz®¯Nµå"ùÕú%Œ$endstream
+endobj
+465 0 obj
+864
+endobj
+466 0 obj<</Type/Page/Parent 408 0 R/Contents 467 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F8 7 0 R>>>>>>endobj
+467 0 obj<</Length 468 0 R/Filter/FlateDecode>>stream
+xÚ-ŒÁ
+‚@„ïûsÔƒ¶«’zèºB^*ý½ RÛ
+i©‡¿6b>øæÅø7þÖT=XBl“GÔAø>BƒnVVîs‚ƒ´>U8Œ«ž»‹ÒÈôÒßG3jîŸk?6 Ì1¢ó7EdçEnˆÜÐQêA«Åt]ÐXæ¶-e!Sj‹cRaþ8ÛØ’Ø™}
+endstream
+endobj
+468 0 obj
+154
+endobj
+469 0 obj<</Type/Page/Parent 408 0 R/Contents 470 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F4 4 0 R/F6 6 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+470 0 obj<</Length 471 0 R/Filter/FlateDecode>>stream
+xÚ­•ÍnÂ0 Çï}
+™´RÊÊ×uÀ&HLë $.„¥MI¤íé—
+endobj
+471 0 obj
+433
+endobj
+472 0 obj<</Type/Page/Parent 408 0 R/Contents 473 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F4 4 0 R/F6 6 0 R/F8 7 0 R>>>>>>endobj
+473 0 obj<</Length 474 0 R/Filter/FlateDecode>>stream
+xÚ“ßOÂ0Çß÷WÜ#&î'£òªNßP¦¯f¶·­ÚÒv&ü÷¶l‚$JÖdÉõ{ßÏ]{Ý)$îKaLü¢ëà® â4…²ö!2Í£)”lT:¦21“ôª\:Y~…‰r/+[C[dÝ
+58i·Fa+Ë¥€š¯Ð€FÃB‹£Þç ~7:ð\šE}é­¡^@àÂåÕEó;e±5×ð
+{±ëSse/à­dcÎ÷Óï&Ã®6óæò~¦®•¨µÔ'…•`ÇbU5xB{éE)ÅÎuÅ 0®‘Z©·@¥›
+. ÌçÅÀ«¥¬h JïÎøoP›5ÿ'+Œ‹¦ÇÁR¾÷u ØéàDÜãÈ&ñNÅóí¬„î_æ xÜÏAáªþ·7êÞ"LÇco²Ägf‰å qF»À&Ψªâýä=”ÁSð o|ìendstream
+endobj
+474 0 obj
+338
+endobj
+475 0 obj<</Type/Page/Parent 408 0 R/Contents 476 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F6 6 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+476 0 obj<</Length 477 0 R/Filter/FlateDecode>>stream
+xÚTÛn£0}ç+æqû
+
+ÃU(LòðbÕ$?“¿ÇA8Qendstream
+endobj
+477 0 obj
+425
+endobj
+478 0 obj<</Type/Page/Parent 408 0 R/Contents 479 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F6 6 0 R/F8 7 0 R>>>>>>endobj
+479 0 obj<</Length 480 0 R/Filter/FlateDecode>>stream
+xÚ’ÝRƒ0…ïyŠ½Ô °PŠxÙÛ)c”ø
+endobj
+480 0 obj
+376
+endobj
+481 0 obj<</Type/Page/Parent 408 0 R/Contents 482 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>/Annots 355 0 R>>endobj
+482 0 obj<</Length 483 0 R/Filter/FlateDecode>>stream
+xÚÕœKs7€ïúsÜÌ Þ3‡d‰rÙe¯µsK•‹¡F2‘Ô’”ÿûàÑ=
+¬”UTÛNÀÓEÁ*Hfµ›ä€:ß,WÖÝóR„J9‘9€X0 ·õ̦adÀW†÷?ü=*à+#B¨ætÀG¼rÕñƒ(WÝm·íÖ‹nw`ÛãŠ?_ù'
+ÎoG ÷¡1.Ü+íß7/†{|…Æì‚8pÎ~½¼&LûZçWµ3ZJ
+ò@¹D¤ ­Ÿ×h%›HÓDhIêŽy1M ¹©mìÊÈ ÒûýÇnK˜#vwY¢v±(…`hÃsˆÅt@b­}›üßãw£cßœ($ÀÛÑP9ßK‘òÚÖÊÛõüþ0,¬ªViAœánç‡ãÛñE7U˜;µÝû³oŒŒgµ 1_”#¼†M¿uþ ÒÙÇùv¾ØS3)A¸‚Xº0–Û&â®;ª>Húº9é~g$T
+i$7nó #1’Þ½~7%âlìíjrí ²‚Y´QôqÆÖ±µ­µŒÖ~Ýк”-cìhVÖ2T4LpnMb†plµ\u“ý—‡îx ]F%0­6Ð ‡QIÈý2î$¸PÝòr€ùa\lÖŸ £zæAÔ… !똠Cƒ ’aZ–¥ dV×C4Èu¹Ùí¯Ûå¹Þ®S¤p̨€ÌírMfúay
+› ú’&4¨QMƒ%*–4Èv»Õ&‡ƒ`‹Ç‡Ý 1†Òmc<s½]G#Þ€c¾AŽ#ƒºq¥q6³qÅäpì!!¤;Êg˜ÉɦMn² ¿†›láïQ›lB6!„Iz“ ñÊLeü ŠËÙô&[ðñ¿Îi \q“MÛjrÂ
+GfBÈxdãvÙ„P!pÊâ.¾«nö" ÔéÃÃõfñ'¹;–0\<b6îdDT
+íÿC!Å
+Z\©XE…i]¥±Š2%ë¹i‡dëô¼Ÿ*mB˜s&ç\cÝcWˆ…J*"\%%m”Í€¨ GURÏ·Üd…31.y\±‡µbçR`1eŠûGÈfÌ \9À^Ñgú±·[ý»˜˜±PAà‘Áíüñž&w\à¢P…¸³ÎRØfL1+_t×9
+À+¸RÒߥŸvøp¨`²BûjúfzVt¦„¶‚méŒ
+¦Xe§]Áî.Øä(P0I•Ý~ÏM€QþÄá¹_„éP\Æš›ÕÔÉNº:©u:Å£ð
+UŠÝûü–Á@`#¶5#"Ü‚ (ôÀQÛš³òÚŠ
+2«å¯ÇmkFš[ñá ‚,Öòig‹2õð1QA&µü)õ¾éL•ú
+(H6~…Ɔwý{ÒÛåš:‹½Ý¬iœÛ¥,¾ãŸ\¨|ÂC¼ã?ïVä ^™ñš'ïú<²Ó¶’´&^yhMáÆC eT[p €Úš±Î A®dwÛOäœe|8Æ]ðOAÌ»n·#?˜ô ‘.س^ÞoÒ1.’Z607#¼@ý๒4p?¿Á¿)h$ÓTºŠ,[[¹:7媦§Ì¶ËOKêžgì¼òOg¤ MnÏS9¯ïŒ_`’@Ü?Vñ^¥¸Æ-,2œJqŨðƒo»¾ù#~ÎÞŒþ˜=Ü$W¥¯ý€Sm÷1m3øŒ}û¹ÒuÀB€¼ ªÔ1îW
+ B€œ/·¶2ßl—‡nÉ(v¬_?ÂZúž7 qÌßÝcTÜwWYý‚—ʺHñ™„gÄ gí÷®·_̃ö8nQÿ¸¿úóE~߸ONë&|ËÆÕéŬzQùÛ^ý•óÊֺ˻u•|šÐ!„r ä…áþ /ahÿsò73&Ï·endstream
+endobj
+483 0 obj
+3110
+endobj
+484 0 obj<</Type/Page/Parent 408 0 R/Contents 485 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>/Annots 360 0 R>>endobj
+485 0 obj<</Length 486 0 R/Filter/FlateDecode>>stream
+xÚÍ’Mnƒ0F÷>Å,›Æcƒeš”ª»6qPˆˆ‚i6½}ÇàôOBÙÐ ËöûÆOþ`‚^¥ýWuìÞ°¸,
+endobj
+486 0 obj
+273
+endobj
+487 0 obj<</Count 7/First 488 0 R/Last 530 0 R>>endobj
+488 0 obj<</Parent 487 0 R/Title(Table of Contents)/Dest[481 0 R/XYZ null 756 null]/Next 489 0 R>>endobj
+489 0 obj<</Parent 487 0 R/Count -3/First 490 0 R/Last 492 0 R/Title(1 Scope)/Dest[415 0 R/XYZ null 743 null]/Prev 488 0 R/Next 493 0 R>>endobj
+490 0 obj<</Parent 489 0 R/Title(1.1 Identification)/Dest[415 0 R/XYZ null 693 null]/Next 491 0 R>>endobj
+491 0 obj<</Parent 489 0 R/Title(1.2 System Overview)/Dest[415 0 R/XYZ null 600 null]/Prev 490 0 R/Next 492 0 R>>endobj
+492 0 obj<</Parent 489 0 R/Title(1.3 Document Overview)/Dest[415 0 R/XYZ null 363 null]/Prev 491 0 R>>endobj
+493 0 obj<</Parent 487 0 R/Count -2/First 494 0 R/Last 495 0 R/Title(2 References)/Dest[421 0 R/XYZ null 743 null]/Prev 489 0 R/Next 496 0 R>>endobj
+494 0 obj<</Parent 493 0 R/Title(2.1 CUPS Documentation)/Dest[421 0 R/XYZ null 693 null]/Next 495 0 R>>endobj
+495 0 obj<</Parent 493 0 R/Title(2.2 Other Documents)/Dest[421 0 R/XYZ null 481 null]/Prev 494 0 R>>endobj
+496 0 obj<</Parent 487 0 R/Count -5/First 497 0 R/Last 504 0 R/Title(3 Internal Interfaces)/Dest[427 0 R/XYZ null 743 null]/Prev 493 0 R/Next 508 0 R>>endobj
+497 0 obj<</Parent 496 0 R/Title(3.1 Character Set Files)/Dest[427 0 R/XYZ null 693 null]/Next 498 0 R>>endobj
+498 0 obj<</Parent 496 0 R/Title(3.2 Language Files)/Dest[427 0 R/XYZ null 574 null]/Prev 497 0 R/Next 499 0 R>>endobj
+499 0 obj<</Parent 496 0 R/Count -2/First 500 0 R/Last 501 0 R/Title(3.3 MIME Files)/Dest[427 0 R/XYZ null 204 null]/Prev 498 0 R/Next 502 0 R>>endobj
+500 0 obj<</Parent 499 0 R/Title(3.3.1 mime.types)/Dest[430 0 R/XYZ null 729 null]/Next 501 0 R>>endobj
+501 0 obj<</Parent 499 0 R/Title(3.3.2 mime.convs)/Dest[430 0 R/XYZ null 365 null]/Prev 500 0 R>>endobj
+502 0 obj<</Parent 496 0 R/Count -1/First 503 0 R/Last 503 0 R/Title(3.4 PostScript Printer Description Files)/Dest[430 0 R/XYZ null 233 null]/Prev 499 0 R/Next 504 0 R>>endobj
+503 0 obj<</Parent 502 0 R/Title(3.4.1 CUPS Extensions to PPD Files)/Dest[430 0 R/XYZ null 134 null]>>endobj
+504 0 obj<</Parent 496 0 R/Count -3/First 505 0 R/Last 507 0 R/Title(3.5 Scheduler Configuration Files)/Dest[436 0 R/XYZ null 736 null]/Prev 502 0 R>>endobj
+505 0 obj<</Parent 504 0 R/Title(3.5.1 classes.conf)/Dest[436 0 R/XYZ null 511 null]/Next 506 0 R>>endobj
+506 0 obj<</Parent 504 0 R/Title(3.5.2 cupsd.conf)/Dest[436 0 R/XYZ null 178 null]/Prev 505 0 R/Next 507 0 R>>endobj
+507 0 obj<</Parent 504 0 R/Title(3.5.3 printers.conf)/Dest[442 0 R/XYZ null 605 null]/Prev 506 0 R>>endobj
+508 0 obj<</Parent 487 0 R/Count -10/First 509 0 R/Last 528 0 R/Title(4 External Interfaces)/Dest[445 0 R/XYZ null 743 null]/Prev 496 0 R/Next 529 0 R>>endobj
+509 0 obj<</Parent 508 0 R/Title(4.1 AppSocket Protocol)/Dest[445 0 R/XYZ null 693 null]/Next 510 0 R>>endobj
+510 0 obj<</Parent 508 0 R/Title(4.2 CUPS Browsing Protocol)/Dest[445 0 R/XYZ null 613 null]/Prev 509 0 R/Next 511 0 R>>endobj
+511 0 obj<</Parent 508 0 R/Title(4.3 CUPS PostScript File)/Dest[448 0 R/XYZ null 631 null]/Prev 510 0 R/Next 512 0 R>>endobj
+512 0 obj<</Parent 508 0 R/Title(4.4 CUPS Raster File)/Dest[448 0 R/XYZ null 513 null]/Prev 511 0 R/Next 513 0 R>>endobj
+513 0 obj<</Parent 508 0 R/Title(4.5 CUPS Raw Files)/Dest[454 0 R/XYZ null 231 null]/Prev 512 0 R/Next 514 0 R>>endobj
+514 0 obj<</Parent 508 0 R/Title(4.6 File Transfer Protocol)/Dest[457 0 R/XYZ null 736 null]/Prev 513 0 R/Next 515 0 R>>endobj
+515 0 obj<</Parent 508 0 R/Count -10/First 516 0 R/Last 525 0 R/Title(4.7 Internet Printing Protocol)/Dest[457 0 R/XYZ null 676 null]/Prev 514 0 R/Next 526 0 R>>endobj
+516 0 obj<</Parent 515 0 R/Title(4.7.1 Get Default Destination \(CUPS_GET_DEFAULT = 0x4001\))/Dest[457 0 R/XYZ null 471 null]/Next 517 0 R>>endobj
+517 0 obj<</Parent 515 0 R/Title(4.7.2 Get Printers \(CUPS_GET_PRINTERS = 0x4002\))/Dest[457 0 R/XYZ null 360 null]/Prev 516 0 R/Next 518 0 R>>endobj
+518 0 obj<</Parent 515 0 R/Title(4.7.3 Add Printer \(CUPS_ADD_PRINTER = 0x4003\))/Dest[457 0 R/XYZ null 248 null]/Prev 517 0 R/Next 519 0 R>>endobj
+519 0 obj<</Parent 515 0 R/Title(4.7.4 Delete Printer \(CUPS_DELETE_PRINTER = 0x4004\))/Dest[460 0 R/XYZ null 729 null]/Prev 518 0 R/Next 520 0 R>>endobj
+520 0 obj<</Parent 515 0 R/Title(4.7.5 Get Classes \(CUPS_GET_CLASSES = 0x4005\))/Dest[460 0 R/XYZ null 633 null]/Prev 519 0 R/Next 521 0 R>>endobj
+521 0 obj<</Parent 515 0 R/Title(4.7.6 Add Class \(CUPS_ADD_CLASS = 0x4006\))/Dest[460 0 R/XYZ null 521 null]/Prev 520 0 R/Next 522 0 R>>endobj
+522 0 obj<</Parent 515 0 R/Title(4.7.7 Delete Class \(CUPS_DELETE_CLASS = 0x4007\))/Dest[460 0 R/XYZ null 370 null]/Prev 521 0 R/Next 523 0 R>>endobj
+523 0 obj<</Parent 515 0 R/Title(4.7.8 Accept Jobs \(CUPS_ACCEPT_JOBS = 0x4008\))/Dest[460 0 R/XYZ null 258 null]/Prev 522 0 R/Next 524 0 R>>endobj
+524 0 obj<</Parent 515 0 R/Title(4.7.9 Reject Jobs \(CUPS_REJECT_JOBS = 0x4009\))/Dest[460 0 R/XYZ null 146 null]/Prev 523 0 R/Next 525 0 R>>endobj
+525 0 obj<</Parent 515 0 R/Title(4.7.10 Set Default Destination \(CUPS_SET_DEFAULT = 0x400A\))/Dest[463 0 R/XYZ null 716 null]/Prev 524 0 R>>endobj
+526 0 obj<</Parent 508 0 R/Title(4.8 Line Printer Daemon Protocol)/Dest[463 0 R/XYZ null 584 null]/Prev 515 0 R/Next 527 0 R>>endobj
+527 0 obj<</Parent 508 0 R/Title(4.9 Server Message Block Protocol)/Dest[463 0 R/XYZ null 505 null]/Prev 526 0 R/Next 528 0 R>>endobj
+528 0 obj<</Parent 508 0 R/Title(4.10 Trivial File Transfer Protocol)/Dest[463 0 R/XYZ null 413 null]/Prev 527 0 R>>endobj
+529 0 obj<</Parent 487 0 R/Title(5 5 - Directories)/Dest[469 0 R/XYZ null 743 null]/Prev 508 0 R/Next 530 0 R>>endobj
+530 0 obj<</Parent 487 0 R/Count -2/First 531 0 R/Last 532 0 R/Title(A Glossary)/Dest[475 0 R/XYZ null 743 null]/Prev 529 0 R>>endobj
+531 0 obj<</Parent 530 0 R/Title(A.1 Terms)/Dest[475 0 R/XYZ null 693 null]/Next 532 0 R>>endobj
+532 0 obj<</Parent 530 0 R/Title(A.2 Acronyms)/Dest[475 0 R/XYZ null 389 null]/Prev 531 0 R>>endobj
+533 0 obj<</Type/Catalog/Pages 408 0 R/Names 361 0 R/ViewerPreferences<</PageLayout/TwoColumnRight>>/Outlines 487 0 R/PageMode/UseOutlines/OpenAction[415 0 R/XYZ null null null]>>endobj
+xref
+0 534
+0000000000 65535 f
+0000000015 00000 n
+0000000225 00000 n
+0000000286 00000 n
+0000000360 00000 n
+0000000438 00000 n
+0000000515 00000 n
+0000000594 00000 n
+0000000670 00000 n
+0000000751 00000 n
+0000000809 00000 n
+0000000924 00000 n
+0000001009 00000 n
+0000001124 00000 n
+0000001209 00000 n
+0000001324 00000 n
+0000001409 00000 n
+0000001524 00000 n
+0000001609 00000 n
+0000001724 00000 n
+0000001807 00000 n
+0000001922 00000 n
+0000002006 00000 n
+0000002121 00000 n
+0000002206 00000 n
+0000002321 00000 n
+0000002406 00000 n
+0000002521 00000 n
+0000002606 00000 n
+0000002721 00000 n
+0000002806 00000 n
+0000002921 00000 n
+0000003006 00000 n
+0000003100 00000 n
+0000003201 00000 n
+0000003286 00000 n
+0000003387 00000 n
+0000003472 00000 n
+0000003573 00000 n
+0000003658 00000 n
+0000003759 00000 n
+0000003844 00000 n
+0000003945 00000 n
+0000004030 00000 n
+0000004131 00000 n
+0000004216 00000 n
+0000004317 00000 n
+0000004402 00000 n
+0000004468 00000 n
+0000004533 00000 n
+0000004618 00000 n
+0000004683 00000 n
+0000004768 00000 n
+0000004833 00000 n
+0000004918 00000 n
+0000004983 00000 n
+0000005068 00000 n
+0000005133 00000 n
+0000005218 00000 n
+0000005284 00000 n
+0000005369 00000 n
+0000005435 00000 n
+0000005520 00000 n
+0000005586 00000 n
+0000005671 00000 n
+0000005737 00000 n
+0000005822 00000 n
+0000005888 00000 n
+0000005973 00000 n
+0000006039 00000 n
+0000006124 00000 n
+0000006190 00000 n
+0000006275 00000 n
+0000006341 00000 n
+0000006426 00000 n
+0000006492 00000 n
+0000006577 00000 n
+0000006643 00000 n
+0000006728 00000 n
+0000006794 00000 n
+0000006879 00000 n
+0000006945 00000 n
+0000007030 00000 n
+0000007096 00000 n
+0000007181 00000 n
+0000007247 00000 n
+0000007332 00000 n
+0000007398 00000 n
+0000007483 00000 n
+0000007549 00000 n
+0000007634 00000 n
+0000007700 00000 n
+0000007785 00000 n
+0000007851 00000 n
+0000007936 00000 n
+0000008002 00000 n
+0000008087 00000 n
+0000008153 00000 n
+0000008238 00000 n
+0000008304 00000 n
+0000008389 00000 n
+0000008456 00000 n
+0000008543 00000 n
+0000008610 00000 n
+0000008697 00000 n
+0000008764 00000 n
+0000008851 00000 n
+0000008918 00000 n
+0000009005 00000 n
+0000009072 00000 n
+0000009159 00000 n
+0000009226 00000 n
+0000009313 00000 n
+0000009380 00000 n
+0000009467 00000 n
+0000009534 00000 n
+0000009621 00000 n
+0000009688 00000 n
+0000009775 00000 n
+0000009842 00000 n
+0000009929 00000 n
+0000009996 00000 n
+0000010083 00000 n
+0000010150 00000 n
+0000010237 00000 n
+0000010304 00000 n
+0000010391 00000 n
+0000010458 00000 n
+0000010545 00000 n
+0000010612 00000 n
+0000010699 00000 n
+0000010766 00000 n
+0000010853 00000 n
+0000010920 00000 n
+0000011007 00000 n
+0000011074 00000 n
+0000011161 00000 n
+0000011228 00000 n
+0000011315 00000 n
+0000011382 00000 n
+0000011469 00000 n
+0000011536 00000 n
+0000011623 00000 n
+0000011690 00000 n
+0000011777 00000 n
+0000011844 00000 n
+0000011931 00000 n
+0000011998 00000 n
+0000012085 00000 n
+0000012152 00000 n
+0000012239 00000 n
+0000012306 00000 n
+0000012393 00000 n
+0000012460 00000 n
+0000012547 00000 n
+0000012614 00000 n
+0000012701 00000 n
+0000013125 00000 n
+0000013192 00000 n
+0000013279 00000 n
+0000013346 00000 n
+0000013433 00000 n
+0000013500 00000 n
+0000013587 00000 n
+0000013654 00000 n
+0000013741 00000 n
+0000013808 00000 n
+0000013895 00000 n
+0000013962 00000 n
+0000014049 00000 n
+0000014106 00000 n
+0000014192 00000 n
+0000014259 00000 n
+0000014346 00000 n
+0000014413 00000 n
+0000014500 00000 n
+0000014567 00000 n
+0000014654 00000 n
+0000014721 00000 n
+0000014808 00000 n
+0000014875 00000 n
+0000014962 00000 n
+0000015029 00000 n
+0000015116 00000 n
+0000015183 00000 n
+0000015270 00000 n
+0000015400 00000 n
+0000015504 00000 n
+0000015609 00000 n
+0000015715 00000 n
+0000015821 00000 n
+0000015927 00000 n
+0000016033 00000 n
+0000016139 00000 n
+0000016245 00000 n
+0000016351 00000 n
+0000016457 00000 n
+0000016561 00000 n
+0000016666 00000 n
+0000016772 00000 n
+0000016878 00000 n
+0000016984 00000 n
+0000017090 00000 n
+0000017196 00000 n
+0000017302 00000 n
+0000017406 00000 n
+0000017511 00000 n
+0000017617 00000 n
+0000017723 00000 n
+0000017829 00000 n
+0000017935 00000 n
+0000018041 00000 n
+0000018147 00000 n
+0000018253 00000 n
+0000018359 00000 n
+0000018465 00000 n
+0000018571 00000 n
+0000018677 00000 n
+0000018783 00000 n
+0000018889 00000 n
+0000018995 00000 n
+0000019101 00000 n
+0000019207 00000 n
+0000019313 00000 n
+0000019419 00000 n
+0000019525 00000 n
+0000019631 00000 n
+0000019737 00000 n
+0000019843 00000 n
+0000019949 00000 n
+0000020055 00000 n
+0000020161 00000 n
+0000020267 00000 n
+0000020373 00000 n
+0000020479 00000 n
+0000020585 00000 n
+0000020691 00000 n
+0000020797 00000 n
+0000020903 00000 n
+0000021009 00000 n
+0000021115 00000 n
+0000021221 00000 n
+0000021327 00000 n
+0000021431 00000 n
+0000021536 00000 n
+0000021642 00000 n
+0000021748 00000 n
+0000021854 00000 n
+0000021960 00000 n
+0000022066 00000 n
+0000022172 00000 n
+0000022278 00000 n
+0000022384 00000 n
+0000022490 00000 n
+0000022596 00000 n
+0000022702 00000 n
+0000022808 00000 n
+0000022914 00000 n
+0000023020 00000 n
+0000023126 00000 n
+0000023232 00000 n
+0000023338 00000 n
+0000023444 00000 n
+0000023550 00000 n
+0000023656 00000 n
+0000023762 00000 n
+0000023868 00000 n
+0000023974 00000 n
+0000024080 00000 n
+0000024186 00000 n
+0000024292 00000 n
+0000024398 00000 n
+0000024504 00000 n
+0000024610 00000 n
+0000024716 00000 n
+0000024822 00000 n
+0000024928 00000 n
+0000025034 00000 n
+0000025140 00000 n
+0000025246 00000 n
+0000025352 00000 n
+0000025458 00000 n
+0000025564 00000 n
+0000025670 00000 n
+0000025776 00000 n
+0000025882 00000 n
+0000025988 00000 n
+0000026094 00000 n
+0000026200 00000 n
+0000026306 00000 n
+0000026412 00000 n
+0000026518 00000 n
+0000026624 00000 n
+0000026730 00000 n
+0000026836 00000 n
+0000026942 00000 n
+0000027048 00000 n
+0000027154 00000 n
+0000027260 00000 n
+0000027366 00000 n
+0000027472 00000 n
+0000027578 00000 n
+0000027684 00000 n
+0000027790 00000 n
+0000027896 00000 n
+0000028002 00000 n
+0000028108 00000 n
+0000028214 00000 n
+0000028320 00000 n
+0000028426 00000 n
+0000028532 00000 n
+0000028638 00000 n
+0000028744 00000 n
+0000028850 00000 n
+0000028956 00000 n
+0000029062 00000 n
+0000029168 00000 n
+0000029274 00000 n
+0000029380 00000 n
+0000029486 00000 n
+0000029592 00000 n
+0000029698 00000 n
+0000029804 00000 n
+0000029910 00000 n
+0000030016 00000 n
+0000030122 00000 n
+0000030228 00000 n
+0000030334 00000 n
+0000030440 00000 n
+0000030546 00000 n
+0000030652 00000 n
+0000030758 00000 n
+0000030864 00000 n
+0000030970 00000 n
+0000031076 00000 n
+0000031182 00000 n
+0000031288 00000 n
+0000031394 00000 n
+0000031500 00000 n
+0000031606 00000 n
+0000031712 00000 n
+0000031818 00000 n
+0000031924 00000 n
+0000032030 00000 n
+0000032136 00000 n
+0000032242 00000 n
+0000032348 00000 n
+0000032454 00000 n
+0000032560 00000 n
+0000032666 00000 n
+0000032769 00000 n
+0000032872 00000 n
+0000032975 00000 n
+0000033079 00000 n
+0000033181 00000 n
+0000033284 00000 n
+0000034654 00000 n
+0000034758 00000 n
+0000034863 00000 n
+0000034967 00000 n
+0000035072 00000 n
+0000035122 00000 n
+0000035156 00000 n
+0000035190 00000 n
+0000035785 00000 n
+0000035834 00000 n
+0000035883 00000 n
+0000035932 00000 n
+0000035981 00000 n
+0000036030 00000 n
+0000036079 00000 n
+0000036128 00000 n
+0000036177 00000 n
+0000036226 00000 n
+0000036275 00000 n
+0000036324 00000 n
+0000036373 00000 n
+0000036422 00000 n
+0000036471 00000 n
+0000036520 00000 n
+0000036569 00000 n
+0000036618 00000 n
+0000036667 00000 n
+0000036716 00000 n
+0000036765 00000 n
+0000036814 00000 n
+0000036863 00000 n
+0000036912 00000 n
+0000036961 00000 n
+0000037010 00000 n
+0000037059 00000 n
+0000037108 00000 n
+0000037157 00000 n
+0000037206 00000 n
+0000037255 00000 n
+0000037304 00000 n
+0000037353 00000 n
+0000037402 00000 n
+0000037451 00000 n
+0000037500 00000 n
+0000037549 00000 n
+0000037598 00000 n
+0000037647 00000 n
+0000037696 00000 n
+0000037745 00000 n
+0000037794 00000 n
+0000037843 00000 n
+0000037892 00000 n
+0000037941 00000 n
+0000038218 00000 n
+0000038370 00000 n
+0000044766 00000 n
+0000044788 00000 n
+0000044901 00000 n
+0000045003 00000 n
+0000045023 00000 n
+0000045163 00000 n
+0000046071 00000 n
+0000046092 00000 n
+0000046205 00000 n
+0000046396 00000 n
+0000046417 00000 n
+0000046557 00000 n
+0000047157 00000 n
+0000047178 00000 n
+0000047291 00000 n
+0000047485 00000 n
+0000047506 00000 n
+0000047646 00000 n
+0000048434 00000 n
+0000048455 00000 n
+0000048609 00000 n
+0000049916 00000 n
+0000049938 00000 n
+0000050087 00000 n
+0000051074 00000 n
+0000051095 00000 n
+0000051244 00000 n
+0000052056 00000 n
+0000052077 00000 n
+0000052208 00000 n
+0000053302 00000 n
+0000053324 00000 n
+0000053464 00000 n
+0000054230 00000 n
+0000054251 00000 n
+0000054409 00000 n
+0000055415 00000 n
+0000055436 00000 n
+0000055599 00000 n
+0000057077 00000 n
+0000057099 00000 n
+0000057221 00000 n
+0000058640 00000 n
+0000058662 00000 n
+0000058802 00000 n
+0000060010 00000 n
+0000060032 00000 n
+0000060196 00000 n
+0000061737 00000 n
+0000061759 00000 n
+0000061899 00000 n
+0000062741 00000 n
+0000062762 00000 n
+0000062917 00000 n
+0000063852 00000 n
+0000063873 00000 n
+0000063986 00000 n
+0000064211 00000 n
+0000064232 00000 n
+0000064381 00000 n
+0000064885 00000 n
+0000064906 00000 n
+0000065046 00000 n
+0000065455 00000 n
+0000065476 00000 n
+0000065616 00000 n
+0000066112 00000 n
+0000066133 00000 n
+0000066264 00000 n
+0000066711 00000 n
+0000066732 00000 n
+0000066887 00000 n
+0000070068 00000 n
+0000070090 00000 n
+0000070236 00000 n
+0000070580 00000 n
+0000070601 00000 n
+0000070656 00000 n
+0000070761 00000 n
+0000070905 00000 n
+0000071011 00000 n
+0000071131 00000 n
+0000071240 00000 n
+0000071389 00000 n
+0000071499 00000 n
+0000071606 00000 n
+0000071764 00000 n
+0000071875 00000 n
+0000071994 00000 n
+0000072145 00000 n
+0000072249 00000 n
+0000072353 00000 n
+0000072530 00000 n
+0000072639 00000 n
+0000072796 00000 n
+0000072902 00000 n
+0000073019 00000 n
+0000073126 00000 n
+0000073285 00000 n
+0000073395 00000 n
+0000073522 00000 n
+0000073647 00000 n
+0000073768 00000 n
+0000073887 00000 n
+0000074014 00000 n
+0000074182 00000 n
+0000074329 00000 n
+0000074479 00000 n
+0000074627 00000 n
+0000074781 00000 n
+0000074929 00000 n
+0000075073 00000 n
+0000075223 00000 n
+0000075371 00000 n
+0000075519 00000 n
+0000075667 00000 n
+0000075800 00000 n
+0000075934 00000 n
+0000076057 00000 n
+0000076175 00000 n
+0000076309 00000 n
+0000076406 00000 n
+0000076506 00000 n
+trailer
+<</Size 534/Root 533 0 R/Info 1 0 R>>
+startxref
+76692
+%%EOF
diff --git a/doc/idd.shtml b/doc/idd.shtml
new file mode 100644
index 000000000..4da9a9e29
--- /dev/null
+++ b/doc/idd.shtml
@@ -0,0 +1,1219 @@
+<HTML>
+<HEAD>
+ <META NAME="COPYRIGHT" CONTENT="Copyright 1997-1999, All Rights Reserved">
+ <META NAME="DOCNUMBER" CONTENT="CUPS-IDD-1.0">
+ <META NAME="Author" CONTENT="Easy Software Products">
+ <TITLE>DRAFT - CUPS Interface Design Description</TITLE>
+</HEAD>
+<BODY>
+
+<H1>Scope</H1>
+
+<H2>Identification</H2>
+
+<P>This interface design description document provides detailed file
+formats, message formats, and program conventions for the Common UNIX
+Printing System ("CUPS") Version 1.0.
+
+<H2>System Overview</H2>
+
+<P>The Common UNIX Printing System provides a portable printing layer for
+UNIX&reg; operating systems. It has been developed by Easy Software
+Products to promote a standard printing solution for all UNIX vendors
+and users. CUPS provides the System V and Berkeley command-line interfaces.
+
+<P>CUPS uses the Internet Printing Protocol (IETF-IPP) as the basis
+for managing print jobs and queues. The Line Printer Daemon (LPD,
+RFC1179), Server Message Block (SMB), and AppSocket protocols are also
+supported with reduced functionality.
+
+<P>CUPS adds network printer browsing and PostScript Printer
+Description (&quot;PPD&quot;)-based printing options to support real
+world applications under UNIX.
+
+<P>CUPS also includes a customized version of GNU GhostScript
+(currently based off GNU GhostScript 4.03) and an image file RIP that
+can be used to support non-PostScript printers.
+
+<H2>Document Overview</H2>
+
+<P>This interface design description document is organized into the following
+sections:
+
+<UL>
+ <LI>1 - Scope
+ <LI>2 - References
+ <LI>3 - Internal Interfaces
+ <LI>4 - External Interfaces
+ <LI>5 - Directories
+ <LI>A - Glossary
+</UL>
+
+<H1>References</H1>
+
+<H2>CUPS Documentation</H2>
+
+<P>The following CUPS documentation is referenced by this document:
+
+<UL>
+ <LI>CUPS-CMP-1.0: CUPS Configuration Management Plan
+ <LI>CUPS-IDD-1.0: CUPS System Interface Design Description
+ <LI>CUPS-SAM-1.0.x: CUPS Software Administrators Manual
+ <LI>CUPS-SDD-1.0: CUPS Software Design Description
+ <LI>CUPS-SPM-1.0: CUPS Software Programming Manual
+ <LI>CUPS-SSR-1.0: CUPS Software Security Report
+ <LI>CUPS-STP-1.0: CUPS Software Test Plan
+ <LI>CUPS-SUM-1.0.x: CUPS Software Users Manual
+ <LI>CUPS-SVD-1.0.x: CUPS Software Version Description
+</UL>
+
+<H2>Other Documents</H2>
+
+<P>The following non-CUPS documents are referenced by this document:
+
+<UL>
+ <LI>IEEE 1387.4, System Administration: Printing (draft)
+ <LI>IPP/1.0: Additional Optional Operations - Set 1
+ <LI>IPP/1.0: Encoding and Transport
+ <LI>IPP/1.0: Implementers Guide
+ <LI>IPP/1.0: Model and Semantics
+ <LI>RFC 1179, Line Printer Daemon Protocol
+</UL>
+
+<H1>Internal Interfaces</H1>
+
+<H2>Character Set Files</H2>
+
+<P>The character set files define a mapping between 8-bit characters
+and the Unicode character set. They are named using the ISO standard
+number defined for the character set. Each file consists of up to 256
+lines of ASCII text. Each line consists of two hexadecimal numbers; the
+first number is the character number in the character set (0x00 to
+0xff), and the second number is the Unicode character number (0x0000 to
+0xffff).
+
+<H2>Language Files</H2>
+
+<P>The language files define the default character set and a collection of
+text messages in that language. They are named by prefixing the string "cups_"
+to the front of the language specifier (e.g. "cups_en", "cups_fr", etc.) Each
+file consists of two or more lines of ASCII text.
+
+<P>The first line identifies the character set to be used for the messages.
+The currently recognized values are:
+
+<UL>
+ <LI>us-ascii
+ <LI>utf-8
+ <LI>iso-8859-1
+ <LI>iso-8859-2
+ <LI>iso-8859-3
+ <LI>iso-8859-4
+ <LI>iso-8859-5
+ <LI>iso-8859-6
+ <LI>iso-8859-7
+ <LI>iso-8859-8
+ <LI>iso-8859-9
+ <LI>iso-8859-14
+ <LI>iso-8859-15
+</UL>
+
+<P>The second and succeeding lines define text messages. If the message text
+is preceded by a number, then the current message number is updated and the
+text after the number is used.
+
+<H2>MIME Files</H2>
+
+<P>CUPS uses two MIME files in its standard configuration.
+
+<H3>mime.types</H3>
+
+<P>The mime.types file defines the recognized file types and consists
+of 1 or more lines of ASCII text. Comment lines start with the pound
+("#") character. The backslash ("\") character can be used at the end
+of a line to continue that line to the next.
+
+<P>Each non-blank line starts with a MIME type identifier ("super/type")
+as registered with the IANA. All text following the MIME type is treated as
+a series of type recognition rules:
+
+<UL><PRE>
+mime-type := super "/" type { SP rule }*
+super := { "a-z" | "A-Z" }*
+type := { "a-z" | "A-Z" | "-" | "." | "0-9" }*
+rule := { extension | match | operator | "(" rule ")" }*
+extension := { "a-z" | "A-Z" | "0-9" }*
+match := "match(" regexp ")" |
+ "ascii(" offset "," length ")" |
+ "printable(" offset "," length ")" |
+ "string(" offset "," string ")" |
+ "char(" offset "," value ")" |
+ "short(" offset "," value ")" |
+ "int(" offset "," value ")" |
+ "locale(" string ")"
+operator := "+" | [ logical AND ]
+ "," | SP [ logical OR ]
+ "!" [ unary NOT ]
+</PRE></UL>
+
+<P>The <CODE>int</CODE> and <CODE>short</CODE> rules match look for integers
+in network byte order (a.k.a. big-endian) with the most-significant byte first.
+
+<H3>mime.convs</H3>
+
+<P>The mime.types file defines the recognized file filters and consists
+of 1 or more lines of ASCII text. Comment lines start with the pound
+("#") character.
+
+<P>Each non-blank line starts with two MIME type identifiers ("super/type")
+representing the source and destination types. Following the MIME types are
+a cost value (0 to 100) and the filter program to use. If the filter program
+is not specified using the full path then it must reside in the CUPS filter
+directory.
+
+<H2>PostScript Printer Description Files</H2>
+
+<P>The PostScript Printer Description (PPD) file format is described in
+<A HREF="http://partners.adobe.com/supportservice/devrelations/PDFS/TN/5003.PPD_Spec_v4.3.pdf">
+Adobe TechNote #5003: PostScript Printer Description File Format
+Specification Version 4.3</A>.
+
+<H3>CUPS Extensions to PPD Files</H3>
+
+<P>CUPS adds several new attributes that are described below.
+
+<H4>cupsFilter</H4>
+
+<P>This string attribute provides a conversion rule of the form:
+
+<UL><PRE>
+source/type cost program
+</PRE></UL>
+
+<P>The destination type is assumed to the printer's type. If a printer
+supports the source type directly the special filter program "-" may be
+specified.
+
+<H4>cupsManualCopies</H4>
+
+<P>This boolean attribute notifies the RIP filters that the destination printer
+does not support copy generation in hardware. The default value is false.
+
+<H4>cupsModelNumber</H4>
+
+<P>This integer attribute specifies a printer-specific model number. This number
+can be used by a filter program to adjust the output for a specific model of
+printer.
+
+<H4>cupsProfile</H4>
+
+<P>This string attribute specifies a color profile of the form:
+
+<UL><PRE>
+resolution/type density gamma m00 m01 m02 m10 m11 m12 m20 m21 m22
+</PRE></UL>
+
+<P>The <I>resolution</I> and <I>type</I> values may be "-" to act as a
+wildcard. Otherwise they must match one of the <CODE>Resolution</CODE> or
+<CODE>MediaType</CODE> attributes defined in the PPD file.
+
+<P>The <I>density</I> and <I>gamma</I> values define gamma and density
+adjustment function such that:
+
+<UL><PRE>
+f(x) = density * x<SUP>gamma</SUP>
+</PRE></UL>
+
+<P>The <I>m00</I> through <I>m22</I> values define a 3x3 transformation
+matrix for the CMY color values. The density function is applied <I>after</I>
+the CMY transformation.
+
+<H4>cupsVersion</H4>
+
+<P>This required attribute describes which version of the CUPS IDD was used
+for the PPD file extensions. Currently it must be the string "1.0".
+
+<H2>Scheduler Configuration Files</H2>
+
+<P>The scheduler reads three configuration files that define the available
+printers, classes, and services:
+
+<DL>
+
+ <DT>classes.conf
+ <DD>This file defines all of the printer classes known to the
+ system.
+
+ <DT>cupsd.conf
+ <DD>This file defines the files, directories, passwords, etc.
+ used by the scheduler.
+
+ <DT>printers.conf
+ <DD>This file defines all of the printers known to the system.
+
+</DL>
+
+<H3>classes.conf</H3>
+
+<P>The classes.conf file consists of 1 or more lines of ASCII text.
+Comment lines start with the pound ("#") character.
+
+<P>Each non-blank line starts with the name of a configuration directive
+followed by its value. The following directives are understood:
+
+<CENTER><TABLE WIDTH="90%" BORDER="1">
+<TR>
+ <TH WIDTH="25%">Directive</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD>&lt;Class name&gt;<BR>
+ &lt;/Class&gt;</TD>
+ <TD></TD>
+</TR>
+<TR>
+ <TD>&lt;DefaultClass name&gt;<BR>
+ &lt;/Class&gt;</TD>
+ <TD></TD>
+</TR>
+<TR>
+ <TD>Info</TD>
+ <TD></TD>
+</TR>
+<TR>
+ <TD>Location</TD>
+ <TD></TD>
+</TR>
+<TR>
+ <TD>MoreInfo</TD>
+ <TD></TD>
+</TR>
+<TR>
+ <TD>Printer</TD>
+ <TD></TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>cupsd.conf</H3>
+
+<P>The cupsd.conf file consists of 1 or more lines of ASCII text.
+Comment lines start with the pound ("#") character.
+
+<P>Each non-blank line starts with the name of a configuration directive
+followed by its value. The following directives are understood:
+
+<CENTER><TABLE WIDTH="90%" BORDER="1">
+<TR>
+ <TH WIDTH="25%">Directive</TH>
+ <TH>Default</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD>AccessLog</TD>
+ <TD></TD>
+ <TD>Specifies the location of the access log file (default
+ "logs/access_log").</TD>
+</TR>
+<TR>
+ <TD>Allow</TD>
+ <TD></TD>
+ <TD></TD>
+</TR>
+<TR>
+ <TD>AuthClass</TD>
+ <TD></TD>
+ <TD></TD>
+</TR>
+<TR>
+ <TD>AuthType</TD>
+ <TD></TD>
+ <TD></TD>
+</TR>
+<TR>
+ <TD>BrowseAddress</TD>
+ <TD></TD>
+ <TD></TD>
+</TR>
+<TR>
+ <TD>BrowseInterval</TD>
+ <TD></TD>
+ <TD></TD>
+</TR>
+<TR>
+ <TD>BrowsePort</TD>
+ <TD></TD>
+ <TD></TD>
+</TR>
+<TR>
+ <TD>BrowseTimeout</TD>
+ <TD></TD>
+ <TD></TD>
+</TR>
+<TR>
+ <TD>Browsing</TD>
+ <TD></TD>
+ <TD></TD>
+</TR>
+<TR>
+ <TD>DefaultCharset</TD>
+ <TD></TD>
+ <TD></TD>
+</TR>
+<TR>
+ <TD>DefaultLanguage</TD>
+ <TD></TD>
+ <TD></TD>
+</TR>
+<TR>
+ <TD>Deny</TD>
+ <TD></TD>
+ <TD></TD>
+</TR>
+<TR>
+ <TD>DocumentRoot</TD>
+ <TD></TD>
+ <TD></TD>
+</TR>
+<TR>
+ <TD>ErrorLog</TD>
+ <TD></TD>
+ <TD></TD>
+</TR>
+<TR>
+ <TD>Group</TD>
+ <TD></TD>
+ <TD></TD>
+</TR>
+<TR>
+ <TD>HostNameLookups</TD>
+ <TD></TD>
+ <TD></TD>
+</TR>
+<TR>
+ <TD>ImplicitClasses</TD>
+ <TD></TD>
+ <TD></TD>
+</TR>
+<TR>
+ <TD>KeepAlive</TD>
+ <TD></TD>
+ <TD></TD>
+</TR>
+<TR>
+ <TD>KeepAliveTimeout</TD>
+ <TD></TD>
+ <TD></TD>
+</TR>
+<TR>
+ <TD>&lt;Location path&gt;<BR>
+ &lt;/Location&gt;</TD>
+ <TD></TD>
+ <TD></TD>
+</TR>
+<TR>
+ <TD>LogLevel</TD>
+ <TD></TD>
+ <TD></TD>
+</TR>
+<TR>
+ <TD>MaxClients</TD>
+ <TD></TD>
+ <TD></TD>
+</TR>
+<TR>
+ <TD>MaxLogSize</TD>
+ <TD></TD>
+ <TD></TD>
+</TR>
+<TR>
+ <TD>MaxRequestSize</TD>
+ <TD></TD>
+ <TD></TD>
+</TR>
+<TR>
+ <TD>Order</TD>
+ <TD></TD>
+ <TD></TD>
+</TR>
+<TR>
+ <TD>PageLog</TD>
+ <TD></TD>
+ <TD></TD>
+</TR>
+<TR>
+ <TD>Port</TD>
+ <TD></TD>
+ <TD></TD>
+</TR>
+<TR>
+ <TD>RIPCache</TD>
+ <TD></TD>
+ <TD></TD>
+</TR>
+<TR>
+ <TD>ServerAdmin</TD>
+ <TD></TD>
+ <TD></TD>
+</TR>
+<TR>
+ <TD>ServerName</TD>
+ <TD></TD>
+ <TD></TD>
+</TR>
+<TR>
+ <TD>ServerRoot</TD>
+ <TD></TD>
+ <TD></TD>
+</TR>
+<TR>
+ <TD>SystemGroup</TD>
+ <TD></TD>
+ <TD></TD>
+</TR>
+<TR>
+ <TD>TempDir</TD>
+ <TD></TD>
+ <TD></TD>
+</TR>
+<TR>
+ <TD>Timeout</TD>
+ <TD></TD>
+ <TD></TD>
+</TR>
+<TR>
+ <TD>User</TD>
+ <TD></TD>
+ <TD></TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>printers.conf</H3>
+
+<P>The printers.conf file consists of 1 or more lines of ASCII text.
+Comment lines start with the pound ("#") character.
+
+<P>Each non-blank line starts with the name of a configuration directive
+followed by its value. The following directives are understood:
+
+<CENTER><TABLE WIDTH="90%" BORDER="1">
+<TR>
+ <TH WIDTH="25%">Directive</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD>&lt;DefaultPrinter name&gt;<BR>
+ &lt;/Printer&gt;</TD>
+ <TD></TD>
+</TR>
+<TR>
+ <TD>DeviceURI</TD>
+ <TD></TD>
+</TR>
+<TR>
+ <TD>Info</TD>
+ <TD></TD>
+</TR>
+<TR>
+ <TD>Location</TD>
+ <TD></TD>
+</TR>
+<TR>
+ <TD>MoreInfo</TD>
+ <TD></TD>
+</TR>
+<TR>
+ <TD>&lt;Printer name&gt;<BR>
+ &lt;/Printer&gt;</TD>
+ <TD></TD>
+</TR>
+<TR>
+ <TD>State</TD>
+ <TD></TD>
+</TR>
+</TABLE></CENTER>
+
+<H1>External Interfaces</H1>
+
+<H2>AppSocket Protocol</H2>
+
+<P>The AppSocket protocol is an 8-bit clean TCP/IP socket connection.
+The default IP service port is 9100.
+
+<H2>CUPS Browsing Protocol</H2>
+
+<P>The CUPS Browsing Protocol is a UDP/IP-based broadcast service. By default
+this service operates on IP service port 631.
+
+<P>Each broadcast packet describes the state of a single printer or class and
+is an ASCII text string of up to 1450 bytes ending with a newline (0x0a). The
+string is formatted as follows:
+
+<UL><PRE>
+type SP state SP uri NL
+</PRE></UL>
+
+<P>The <I>state</I> and <I>uri</I> values correspond to the IPP
+<CODE>printer-state</CODE> and <CODE>printer-uri-supported</CODE> attributes.
+
+<P>The <I>type</I> value is a hexadecimal number string representing
+capability/type bits:
+
+<CENTER><TABLE WIDTH="40%" BORDER="1">
+<TR>
+ <TH WIDTH="8%">Bit</TH>
+ <TH>Description</TH>
+</TR>
+<TR>
+ <TD>0</TD>
+ <TD>0 = printer<BR>
+ 1 = class</TD>
+</TR>
+<TR>
+ <TD>1</TD>
+ <TD>0 = local<BR>
+ 1 = remote<BR>
+ (always 1)</TD>
+</TR>
+<TR>
+ <TD>2</TD>
+ <TD>1 = can print B&W</TD>
+</TR>
+<TR>
+ <TD>3</TD>
+ <TD>1 = can print color</TD>
+</TR>
+<TR>
+ <TD>4</TD>
+ <TD>1 = can duplex</TD>
+</TR>
+<TR>
+ <TD>5</TD>
+ <TD>1 = can staple</TD>
+</TR>
+<TR>
+ <TD>6</TD>
+ <TD>1 = can do fast copies</TD>
+</TR>
+<TR>
+ <TD>7</TD>
+ <TD>1 = can do fast collating</TD>
+</TR>
+<TR>
+ <TD>8</TD>
+ <TD>1 = can punch holes</TD>
+</TR>
+<TR>
+ <TD>9</TD>
+ <TD>1 = can cover</TD>
+</TR>
+<TR>
+ <TD>10</TD>
+ <TD>1 = can bind</TD>
+</TR>
+<TR>
+ <TD>11</TD>
+ <TD>1 = can sort</TD>
+</TR>
+<TR>
+ <TD>12</TD>
+ <TD>1 = can print up to 9x14 inches</TD>
+</TR>
+<TR>
+ <TD>13</TD>
+ <TD>1 = can print up to 18x24 inches</TD>
+</TR>
+<TR>
+ <TD>14</TD>
+ <TD>1 = can print up to 36x48 inches</TD>
+</TR>
+<TR>
+ <TD>15</TD>
+ <TD>1 = can print variable sizes</TD>
+</TR>
+</TABLE></CENTER>
+
+<H2>CUPS PostScript File</H2>
+
+<P>CUPS PostScript files are device-dependent Adobe PostScript program files.
+The PostScript language is described in the
+<A HREF="http://partners.adobe.com/supportservice/devrelations/PDFS/TN/PLRM.pdf">
+Adobe PostScript Language Reference Manual, Third Edition</A>.
+
+<P>The MIME type for CUPS PostScript files is
+<CODE>application/vnd.cups-postscript</CODE>.
+
+<H2>CUPS Raster File</H2>
+
+<P>CUPS raster files are device-dependent raster image files that contain a
+PostScript page device dictionary and device-dependent raster imagery for
+each page in the document. These files are used to transfer raster data
+from the PostScript and image file RIPs to device-dependent filters that
+convert the raster data to a printable format.
+
+<P>A raster file begins with a four byte synchronization word: 0x52615374
+("RaSt") for big-endian architectures and 0x74536152 ("tSaR") for little-endian
+architectures. The writer of the raster file will use the native word order,
+and the reader is responsible for detecting a reversed word order file and
+swapping bytes as needed. The CUPS Interface Library raster functions perform
+this function automatically.
+
+<P>Following the synchronization word are a series of raster pages. Each page
+starts with a page device dictionary header and is followed immediately by the
+raster data for that page.
+
+<CENTER><TABLE WIDTH="80%" BORDER="1">
+<TR>
+ <TH WIDTH="10%">Bytes</TH>
+ <TH WIDTH="20%">Description</TH>
+ <TH>Values</TH>
+</TR>
+<TR>
+ <TD>0-63</TD>
+ <TD>MediaClass</TD>
+ <TD>Nul-terminated ASCII string</TD>
+</TR>
+<TR>
+ <TD>64-127</TD>
+ <TD>MediaColor</TD>
+ <TD>Nul-terminated ASCII string</TD>
+</TR>
+<TR>
+ <TD>128-191</TD>
+ <TD>MediaType</TD>
+ <TD>Nul-terminated ASCII string</TD>
+</TR>
+<TR>
+ <TD>192-255</TD>
+ <TD>OutputType</TD>
+ <TD>Nul-terminated ASCII string</TD>
+</TR>
+<TR>
+ <TD>256-259</TD>
+ <TD>AdvanceDistance</TD>
+ <TD>0 to 2<SUP>32</SUP> - 1 points</TD>
+</TR>
+<TR>
+ <TD>260-263</TD>
+ <TD>AdvanceMedia</TD>
+ <TD>0 = Never advance roll<BR>
+ 1 = Advance roll after file<BR>
+ 2 = Advance roll after job<BR>
+ 3 = Advance roll after set<BR>
+ 4 = Advance roll after page</TD>
+</TR>
+<TR>
+ <TD>264-267</TD>
+ <TD>Collate</TD>
+ <TD>0 = do not collate copies<BR>
+ 1 = collate copies</TD>
+</TR>
+<TR>
+ <TD>268-271</TD>
+ <TD>CutMedia</TD>
+ <TD>0 = Never cut media<BR>
+ 1 = Cut roll after file<BR>
+ 2 = Cut roll after job<BR>
+ 3 = Cut roll after set<BR>
+ 4 = Cut roll after page</TD>
+</TR>
+<TR>
+ <TD>272-275</TD>
+ <TD>Duplex</TD>
+ <TD>0 = Print single-sided<BR>
+ 1 = Print double-sided</TD>
+</TR>
+<TR>
+ <TD>276-283</TD>
+ <TD>HWResolution</TD>
+ <TD>Horizontal and vertical resolution in dots-per-inch.</TD>
+</TR>
+<TR>
+ <TD>284-299</TD>
+ <TD>ImagingBoundingBox</TD>
+ <TD>Four integers giving the left, bottom, right, and top positions
+ of the page bounding box in points</TD>
+</TR>
+<TR>
+ <TD>300-303</TD>
+ <TD>InsertSheet</TD>
+ <TD>0 = Do not insert separator sheets<BR>
+ 1 = Insert separator sheets</TD>
+</TR>
+<TR>
+ <TD>304-307</TD>
+ <TD>Jog</TD>
+ <TD>0 = Do no jog pages<BR>
+ 1 = Jog pages after file<BR>
+ 2 = Jog pages after job<BR>
+ 3 = Jog pages after set</TD>
+</TR>
+<TR>
+ <TD>308-311</TD>
+ <TD>LeadingEdge</TD>
+ <TD>0 = Top edge is first<BR>
+ 1 = Right edge is first<BR>
+ 2 = Bottom edge is first<BR>
+ 3 = Left edge is first</TD>
+</TR>
+<TR>
+ <TD>312-319</TD>
+ <TD>Margins</TD>
+ <TD>Left and bottom origin of image in points</TD>
+</TR>
+<TR>
+ <TD>320-323</TD>
+ <TD>ManualFeed</TD>
+ <TD>0 = Do not manually feed media<BR>
+ 1 = Manually feed media</TD>
+</TR>
+<TR>
+ <TD>324-327</TD>
+ <TD>MediaPosition</TD>
+ <TD>Input slot position from 0 to N</TD>
+</TR>
+<TR>
+ <TD>328-331</TD>
+ <TD>MediaWeight</TD>
+ <TD>Media weight in grams per meter squared</TD>
+</TR>
+<TR>
+ <TD>332-335</TD>
+ <TD>MirrorPrint</TD>
+ <TD>0 = Do not mirror prints<BR>
+ 1 = Mirror prints</TD>
+</TR>
+<TR>
+ <TD>336-339</TD>
+ <TD>NegativePrint</TD>
+ <TD>0 = Do not invert prints<BR>
+ 1 = Invert prints</TD>
+</TR>
+<TR>
+ <TD>340-343</TD>
+ <TD>NumCopies</TD>
+ <TD>1 to 2<SUP>32</SUP> - 1</TD>
+</TR>
+<TR>
+ <TD>344-347</TD>
+ <TD>Orientation</TD>
+ <TD>0 = Do not rotate page<BR>
+ 1 = Rotate page counter-clockwise<BR>
+ 2 = Turn page upside down<BR>
+ 3 = Rotate page clockwise</TD>
+</TR>
+<TR>
+ <TD>348-351</TD>
+ <TD>OutputFaceUp</TD>
+ <TD>0 = Output face down<BR>
+ 1 = Output face up</TD>
+</TR>
+<TR>
+ <TD>352-359</TD>
+ <TD>PageSize</TD>
+ <TD>Width and length in points</TD>
+</TR>
+<TR>
+ <TD>360-363</TD>
+ <TD>Separations</TD>
+ <TD>0 = Print composite image<BR>
+ 1 = Print color separations</TD>
+</TR>
+<TR>
+ <TD>364-367</TD>
+ <TD>TraySwitch</TD>
+ <TD>0 = Do not change trays if selected tray is empty<BR>
+ 1 = Change trays if selected tray is empty</TD>
+</TR>
+<TR>
+ <TD>368-371</TD>
+ <TD>Tumble</TD>
+ <TD>0 = Do not rotate even pages when duplexing<BR>
+ 1 = Rotate even pages when duplexing</TD>
+</TR>
+<TR>
+ <TD>372-375</TD>
+ <TD>cupsWidth</TD>
+ <TD>Width of page image in pixels</TD>
+</TR>
+<TR>
+ <TD>376-379</TD>
+ <TD>cupsHeight</TD>
+ <TD>Height of page image in pixels</TD>
+</TR>
+<TR>
+ <TD>380-383</TD>
+ <TD>cupsMediaType</TD>
+ <TD>Driver-specific 0 to 2<SUP>32</SUP> - 1</TD>
+</TR>
+<TR>
+ <TD>384-387</TD>
+ <TD>cupsBitsPerColor</TD>
+ <TD>1, 2, 4, 8 bits</TD>
+</TR>
+<TR>
+ <TD>388-391</TD>
+ <TD>cupsBitsPerPixel</TD>
+ <TD>1 to 32 bits</TD>
+</TR>
+<TR>
+ <TD>392-395</TD>
+ <TD>cupsBytesPerLine</TD>
+ <TD>1 to 2<SUP>32</SUP> - 1 bytes</TD>
+</TR>
+<TR>
+ <TD>396-399</TD>
+ <TD>cupsColorOrder</TD>
+ <TD>0 = chunky pixels (CMYK CMYK CMYK)<BR>
+ 1 = banded pixels (CCC MMM YYY KKK)<BR>
+ 2 = planar pixels (CCC... MMM... YYY... KKK...)</TD>
+</TR>
+<TR>
+ <TD>400-403</TD>
+ <TD>cupsColorSpace</TD>
+ <TD>0 = white<BR>
+ 1 = RGB<BR>
+ 2 = RGBA<BR>
+ 3 = black<BR>
+ 4 = CMY<BR>
+ 5 = YMC<BR>
+ 6 = CMYK<BR>
+ 7 = YMCK<BR>
+ 8 = KCMY<BR>
+ 9 = KCMYcm</TD>
+</TR>
+<TR>
+ <TD>404-407</TD>
+ <TD>cupsCompression</TD>
+ <TD>Driver-specific 0 to 2<SUP>32</SUP> - 1</TD>
+</TR>
+<TR>
+ <TD>408-411</TD>
+ <TD>cupsRowCount</TD>
+ <TD>Driver-specific 0 to 2<SUP>32</SUP> - 1</TD>
+</TR>
+<TR>
+ <TD>412-415</TD>
+ <TD>cupsRowFeed</TD>
+ <TD>Driver-specific 0 to 2<SUP>32</SUP> - 1</TD>
+</TR>
+<TR>
+ <TD>416-419</TD>
+ <TD>cupsRowStep</TD>
+ <TD>Driver-specific 0 to 2<SUP>32</SUP> - 1</TD>
+</TR>
+</TABLE></CENTER>
+
+<P>The MIME type for CUPS Raster files is
+<CODE>application/vnd.cups-raster</CODE>.
+
+<H2>CUPS Raw Files</H2>
+
+<P>Raw files are printer-dependent print files that are in a format suitable
+to the destination printer (e.g. HP-PCL, HP-RTL, etc.) The MIME type for CUPS
+Raw files is <CODE>application/vnd.cups-raw</CODE>.
+
+<H2>File Transfer Protocol</H2>
+
+<P>The File Transfer Protocol (FTP) is described by
+<A HREF="http://www.ietf.org/rfc/rfc959.txt">RFC 959: File Transfer
+Protocol</A>.
+
+<H2>Internet Printing Protocol</H2>
+
+<P>The Internet Printing Protocol is described by the following RFCs:
+
+<UL>
+
+ <LI><A HREF="http://www.ietf.org/rfc/rfc2565.txt">
+ RFC 2565: Internet Printing Protocol/1.0: Encoding and Transport</A>
+
+ <LI><A HREF="http://www.ietf.org/rfc/rfc2566.txt">
+ RFC 2566: Internet Printing Protocol/1.0: Model and Semantics</A>
+
+ <LI><A HREF="http://www.ietf.org/rfc/rfc2567.txt">
+ RFC 2567: Design Goals for an Internet Printing Protocol</A>
+
+ <LI><A HREF="http://www.ietf.org/rfc/rfc2568.txt">
+ RFC 2568: Rationale for the Structure of the Model and Protocol
+ for the Internet Printing Protocol</A>
+
+ <LI><A HREF="http://www.ietf.org/rfc/rfc2569.txt">
+ RFC 2569: Mapping between LPD and IPP Protocols</A>
+
+</UL>
+
+<P>CUPS defines the following extension operations to IPP.
+
+<H3>Get Default Destination (CUPS_GET_DEFAULT = 0x4001)</H3>
+
+<P>The get default destination operation returns the printer attributes
+for the system default printer or class. The only required attributes
+are <CODE>attributes-charset</CODE> and
+<CODE>attributes-natural-language</CODE>.
+
+<P>Get default destination will only return <CODE>ipp-ok</CODE>.
+
+<H3>Get Printers (CUPS_GET_PRINTERS = 0x4002)</H3>
+
+<P>The get printers operation returns the printer attributes for all
+printers known to the system. The only required attributes are
+<CODE>attributes-charset</CODE> and
+<CODE>attributes-natural-language</CODE>.
+
+<P>Get printers will only return <CODE>ipp-ok</CODE>.
+
+<H3>Add Printer (CUPS_ADD_PRINTER = 0x4003)</H3>
+
+<P>The add printer operation adds or replaces the specified printer. The
+<CODE>attributes-charset</CODE>,
+<CODE>attributes-natural-language</CODE> and <CODE>printer-uri</CODE>
+attributes are required.
+
+<P>The <CODE>printer-location</CODE>, <CODE>printer-info</CODE>,
+<CODE>printer-more-info</CODE>, and <CODE>device-uri</CODE> attributes
+are required when initially adding a printer and optional when modifying
+a printer.
+
+<P>A PPD file or System V interface script may follow the IPP request
+body. If a valid interface script or PPD file is not provided then the
+printer is treated as a generic PostScript device.
+
+<P>Add printer will return <CODE>ipp-ok</CODE>, <CODE>ipp-not-authorized</CODE>,
+<CODE>ipp-bad-request</CODE>, or <CODE>ipp-attributes</CODE>.
+
+<H3>Delete Printer (CUPS_DELETE_PRINTER = 0x4004)</H3>
+
+<P>The delete printer operation removes the specified printer. The only
+required attributes are <CODE>attributes-charset</CODE>,
+<CODE>attributes-natural-language</CODE>, and <CODE>printer-uri</CODE>.
+
+<P>Delete printer will return <CODE>ipp-ok</CODE>, <CODE>ipp-not-found</CODE>,
+or <CODE>ipp-not-authorized</CODE>.
+
+<H3>Get Classes (CUPS_GET_CLASSES = 0x4005)</H3>
+
+<P>The get classes operation returns the printer attributes for all
+classes known to the system. The only required attributes are
+<CODE>attributes-charset</CODE> and
+<CODE>attributes-natural-language</CODE>.
+
+<P>Get classes will only return <CODE>ipp-ok</CODE>.
+
+<H3>Add Class (CUPS_ADD_CLASS = 0x4006)</H3>
+
+<P>The add class operation adds or replaces the specified class. The
+<CODE>attributes-charset</CODE>,
+<CODE>attributes-natural-language</CODE>, and <CODE>printer-uri</CODE>
+attributes are required.
+
+<P>The <CODE>printer-location</CODE>, <CODE>printer-info</CODE>,
+<CODE>printer-more-info</CODE>, and <CODE>member-uris</CODE> attributes
+are required when initially adding a printer and optional when modifying
+a printer.
+
+<P>Add class will return <CODE>ipp-ok</CODE>, <CODE>ipp-not-authorized</CODE>,
+<CODE>ipp-bad-request</CODE>, or <CODE>ipp-attributes</CODE>.
+
+<H3>Delete Class (CUPS_DELETE_CLASS = 0x4007)</H3>
+
+<P>The delete class operation removes the specified class. The only
+required attributes are <CODE>attributes-charset</CODE>,
+<CODE>attributes-natural-language</CODE>, and <CODE>printer-uri</CODE>.
+
+<P>Delete class will return <CODE>ipp-ok</CODE>, <CODE>ipp-not-found</CODE>,
+or <CODE>ipp-not-authorized</CODE>.
+
+<H3>Accept Jobs (CUPS_ACCEPT_JOBS = 0x4008)</H3>
+
+<P>The accept jobs operation allows jobs to be accepted by the
+specified destination. The only required attributes are
+<CODE>attributes-charset</CODE>,
+<CODE>attributes-natural-language</CODE>, and
+<CODE>printer-uri</CODE>.
+
+<P>Accept jobs will return <CODE>ipp-ok</CODE>, <CODE>ipp-not-found</CODE>,
+or <CODE>ipp-not-authorized</CODE>.
+
+<H3>Reject Jobs (CUPS_REJECT_JOBS = 0x4009)</H3>
+
+<P>The reject jobs operation prevents jobs from being accepted by the
+specified destination. The only required attributes are
+<CODE>attributes-charset</CODE>,
+<CODE>attributes-natural-language</CODE>, and
+<CODE>printer-uri</CODE>.
+
+<P>Reject jobs will return <CODE>ipp-ok</CODE>, <CODE>ipp-not-found</CODE>,
+or <CODE>ipp-not-authorized</CODE>.
+
+<H3>Set Default Destination (CUPS_SET_DEFAULT = 0x400A)</H3>
+
+<P>The set default destination operation returns the printer attributes
+for the system default printer or class. The only required attributes
+are <CODE>attributes-charset</CODE>,
+<CODE>attributes-natural-language</CODE>, and <CODE>printer-uri</CODE>.
+
+<P>Set default destination will return <CODE>ipp-ok</CODE>,
+<CODE>ipp-not-authorized</CODE>, <CODE>ipp-bad-request</CODE>, or
+<CODE>ipp-not-found</CODE>.
+
+<H2>Line Printer Daemon Protocol</H2>
+
+<P>The Line Printer Daemon (LPD) protocol is described by
+<A HREF="http://www.ietf.org/rfc/rfc1179.txt">RFC 1179: Line Printer Daemon
+Protocol</A>.
+
+<H2>Server Message Block Protocol</H2>
+
+<P>The Server Message Block (SMB) and related Common Internet File
+System (CIFS) protocols are described at
+<A HREF="http://anu.samba.org/cifs">http://anu.samba.org/cifs</A>.
+
+<H2>Trivial File Transfer Protocol</H2>
+
+<P>The Trivial File Transfer Protocol (TFTP) is described by
+<A HREF="http://www.ietf.org/rfc/rfc1350.txt">RFC 1350: The TFTP Protocol
+(Revision 2)</A>.
+
+<H1>5 - Directories</H1>
+
+<DL>
+
+ <DT>/usr/bin
+ <DD>The <CODE>cancel</CODE>, <CODE>lp</CODE>, <CODE>lpq</CODE>,
+ <CODE>lpr</CODE>, <CODE>lprm</CODE>, and <CODE>lpstat</CODE> commands
+ reside here.
+
+ <DT>/usr/lib
+ <DD>The <CODE>accept</CODE>, <CODE>disable</CODE>, <CODE>enable</CODE>,
+ <CODE>lpadmin</CODE>, and <CODE>reject</CODE> commands reside here.
+
+ <DT>/usr/sbin
+ <DD>The <CODE>lpc</CODE> and <CODE>cupsd</CODE> commands resize here.
+
+ <DT>/usr/share/cups
+ <DD>This is the root directory of the CUPS static data.
+
+ <DT>/usr/share/cups/data
+ <DD>The character set and filter data files reside here.
+
+ <DT>/usr/share/cups/fonts
+ <DD>The <CODE>pstoraster</CODE> font files reside here.
+
+ <DT>/usr/share/cups/model
+ <DD>The sample PPD files reside here.
+
+ <DT>/usr/share/cups/pstoraster
+ <DD>The <CODE>pstoraster</CODE> data files reside here.
+
+ <DT>/var/cups
+ <DD>This is the root directory of the CUPS scheduler.
+
+ <DT>/var/cups/backend
+ <DD>The backend filters reside here.
+
+ <DT>/var/cups/cgi-bin
+ <DD>The CGI programs reside here.
+
+ <DT>/var/cups/conf
+ <DD>The scheduler configuration and MIME files reside here.
+
+ <DT>/var/cups/doc
+ <DD>The scheduler documentation files reside here.
+
+ <DT>/var/cups/filter
+ <DD>The file filters reside here.
+
+ <DT>/var/cups/interfaces
+ <DD>System V interface scripts reside here.
+
+ <DT>/var/cups/logs
+ <DD>The <CODE>access_log</CODE>, <CODE>error_log</CODE>, and
+ <CODE>page_log</CODE> files reside here.
+
+ <DT>/var/cups/ppd
+ <DD>This directory contains PPD files for each printer.
+
+ <DT>/var/cups/requests
+ <DD>This directory contains pending print job files.
+
+</DL>
+
+<H1 TYPE=A VALUE=1>Glossary</H1>
+
+<H2>Terms</H2>
+
+<DL>
+
+ <DT>C
+ <DD>A computer language.
+
+ <DT>parallel
+ <DD>Sending or receiving data more than 1 bit at a time.
+
+ <DT>pipe
+ <DD>A one-way communications channel between two programs.
+
+ <DT>serial
+ <DD>Sending or receiving data 1 bit at a time.
+
+ <DT>socket
+ <DD>A two-way network communications channel.
+
+</DL>
+
+<H2>Acronyms</H2>
+
+<DL>
+
+ <DT>ASCII
+ <DD>American Standard Code for Information Interchange
+
+ <DT>CUPS
+ <DD>Common UNIX Printing System
+
+ <DT>ESC/P
+ <DD>EPSON Standard Code for Printers
+
+ <DT>FTP
+ <DD>File Transfer Protocol
+
+ <DT>HP-GL
+ <DD>Hewlett-Packard Graphics Language
+
+ <DT>HP-PCL
+ <DD>Hewlett-Packard Printer Control Language
+
+ <DT>HP-PJL
+ <DD>Hewlett-Packard Printer Job Language
+
+ <DT>IETF
+ <DD>Internet Engineering Task Force
+
+ <DT>IPP
+ <DD>Internet Printing Protocol
+
+ <DT>ISO
+ <DD>International Standards Organization
+
+ <DT>LPD
+ <DD>Line Printer Daemon
+
+ <DT>MIME
+ <DD>Multimedia Internet Mail Exchange
+
+ <DT>PCL
+ <DD>Page Control Language
+
+ <DT>PPD
+ <DD>PostScript Printer Description
+
+ <DT>SMB
+ <DD>Server Message Block
+
+ <DT>TFTP
+ <DD>Trivial File Transfer Protocol
+
+</DL>
+
+</BODY>
+</HTML>
diff --git a/doc/images/classes.gif b/doc/images/classes.gif
new file mode 100644
index 000000000..ace15df98
--- /dev/null
+++ b/doc/images/classes.gif
Binary files differ
diff --git a/doc/images/cups-bar.gif b/doc/images/cups-bar.gif
new file mode 100644
index 000000000..b2bdf4b52
--- /dev/null
+++ b/doc/images/cups-bar.gif
Binary files differ
diff --git a/doc/images/cups-block-diagram.gif b/doc/images/cups-block-diagram.gif
new file mode 100644
index 000000000..156a2e78e
--- /dev/null
+++ b/doc/images/cups-block-diagram.gif
Binary files differ
diff --git a/doc/images/cups-large.gif b/doc/images/cups-large.gif
new file mode 100644
index 000000000..fc66ef07c
--- /dev/null
+++ b/doc/images/cups-large.gif
Binary files differ
diff --git a/doc/images/cups-medium.gif b/doc/images/cups-medium.gif
new file mode 100644
index 000000000..c45ed6ab9
--- /dev/null
+++ b/doc/images/cups-medium.gif
Binary files differ
diff --git a/doc/images/cups-small.gif b/doc/images/cups-small.gif
new file mode 100644
index 000000000..6adb4a29f
--- /dev/null
+++ b/doc/images/cups-small.gif
Binary files differ
diff --git a/doc/images/draft.gif b/doc/images/draft.gif
new file mode 100644
index 000000000..77d9716ab
--- /dev/null
+++ b/doc/images/draft.gif
Binary files differ
diff --git a/doc/images/logo.gif b/doc/images/logo.gif
new file mode 100644
index 000000000..9999795ca
--- /dev/null
+++ b/doc/images/logo.gif
Binary files differ
diff --git a/doc/images/navbar.gif b/doc/images/navbar.gif
new file mode 100644
index 000000000..3389d2338
--- /dev/null
+++ b/doc/images/navbar.gif
Binary files differ
diff --git a/doc/images/navbar.xcf.gz b/doc/images/navbar.xcf.gz
new file mode 100644
index 000000000..28438a6bb
--- /dev/null
+++ b/doc/images/navbar.xcf.gz
Binary files differ
diff --git a/doc/images/printer-idle.gif b/doc/images/printer-idle.gif
new file mode 100644
index 000000000..68d990c62
--- /dev/null
+++ b/doc/images/printer-idle.gif
Binary files differ
diff --git a/doc/images/printer-processing.gif b/doc/images/printer-processing.gif
new file mode 100644
index 000000000..a9a23f795
--- /dev/null
+++ b/doc/images/printer-processing.gif
Binary files differ
diff --git a/doc/images/printer-stopped.gif b/doc/images/printer-stopped.gif
new file mode 100644
index 000000000..76f45649b
--- /dev/null
+++ b/doc/images/printer-stopped.gif
Binary files differ
diff --git a/doc/index.html b/doc/index.html
new file mode 100644
index 000000000..99ff5b0b6
--- /dev/null
+++ b/doc/index.html
@@ -0,0 +1,34 @@
+<HTML>
+<HEAD>
+ <TITLE>Common UNIX Printing System</TITLE>
+ <LINK REL=STYLESHEET TYPE="text/css" HREF="cups.css">
+ <MAP NAME="navbar">
+ <AREA SHAPE="RECT" COORDS="10,10,85,30" HREF="printers" ALT="Current Printer Status">
+ <AREA SHAPE="RECT" COORDS="95,10,175,30" HREF="classes" ALT="Current Printer Classes Status">
+ <AREA SHAPE="RECT" COORDS="185,10,235,30" HREF="jobs" ALT="Current Jobs Status">
+ <AREA SHAPE="RECT" COORDS="245,10,395,30" HREF="documentation.html" ALT="Read CUPS Documentation On-Line">
+ <AREA SHAPE="RECT" COORDS="405,10,490,30" HREF="http://www.cups.org" ALT="Download the Current CUPS Software">
+ </MAP>
+</HEAD>
+
+<BODY>
+<P ALIGN=CENTER>
+<A HREF="http://www.easysw.com" ALT="Easy Software Products Home Page">
+<IMG SRC="images/logo.gif" WIDTH="71" HEIGHT="40" BORDER=0 ALT="Easy Software Products Home Page"></A>
+<IMG SRC="images/navbar.gif" WIDTH="540" HEIGHT="40" USEMAP="#navbar" BORDER=0>
+
+<H1><A HREF="printers">Current Printer Status</A></H1>
+<H1><A HREF="classes">Current Printer Classes Status</A></H1>
+<H1><A HREF="jobs">Current Jobs Status</A></H1>
+<H1><A HREF="documentation.html">Read CUPS Documentation On-Line</A></H1>
+<H1><A HREF="http://www.cups.org">Download the Current CUPS Software</A></H1>
+
+<HR>
+
+<P>The Common UNIX Printing System, CUPS, and the CUPS logo are the
+trademark property of <A HREF="http://www.easysw.com">Easy Software
+Products</A>. CUPS is copyright 1997-1999 by Easy Software Products,
+All Rights Reserved.
+
+</BODY>
+</HTML>
diff --git a/doc/overview.html b/doc/overview.html
new file mode 100644
index 000000000..a3979ab1c
--- /dev/null
+++ b/doc/overview.html
@@ -0,0 +1,287 @@
+<HTML>
+<HEAD>
+ <META NAME="Author" CONTENT="Michael Sweet">
+ <TITLE>An Overview of the Common UNIX Printing System</TITLE>
+ <LINK REL=STYLESHEET TYPE="text/css" HREF="cupsdoc.css">
+</HEAD>
+<BODY BGCOLOR=#ffffff>
+<TABLE WIDTH="100%">
+<TR VALIGN=TOP>
+ <TD><IMG SRC="images/cups-large.gif" WIDTH="103" HEIGHT="120"></TD>
+ <TD><H1 ALIGN=right>An Overview of the<BR>
+ Common UNIX Printing System</H1></DIV>
+
+ <P ALIGN=right>May 11, 1999<BR>
+ Michael Sweet, Easy Software Products<BR>
+ Copyright 1998-1999, All Rights Reserved.</P>
+ </TD>
+</TR>
+</TABLE>
+
+<P>This whitepaper describes the Common UNIX Printing
+System<SUP>TM</SUP> ("CUPS<SUP>TM</SUP>"), a portable and extensible
+printing system for UNIX<SUP>&reg;</SUP>. CUPS is being developed by
+Easy Software Products, a software firm located in Hollywood, Maryland
+that has been selling commercial software for Silicon
+Graphics<SUP>&reg;</SUP>, Sun<SUP>&reg;</SUP>, and HP workstations
+since 1993 through more than 40 distributors serving over 80 countries
+worldwide.
+
+<P>Additional information on CUPS is available on the World Wide Web at
+"<A HREF="http://www.cups.org">http://www.cups.org</A>".
+
+<H2>Background</H2>
+
+Printing within UNIX has historically been done using one of two
+printing systems - the Berkeley Line Printer Daemon ("LPD") [RFC1179]
+and the AT&amp;T Line Printer system. Replacements for these printing
+systems have emerged [LPRng, Palladin, PLP], however none of the
+replacements change the fundamental capabilities of these systems.
+
+<P>Over the last few years several attempts at developing a standard
+printing interface have been made, including the draft POSIX Printing
+standard [IEEE-1387.4, last updated in 1994] and Internet Printing
+Protocol [IETF-IPP]. The POSIX printing standard defines a common set
+of command-line tools as well as a C interface for printer
+administration and print jobs. The Internet Printing Protocol defines
+extensions to the HyperText Transport Protocol 1.1 [RFC2068] to provide
+support for remote printing services.
+
+<H2>Weaknesses in Existing Printing Systems</H2>
+
+Easy Software Products has identified several major weaknesses in
+the printing systems currently in use:
+
+<OL>
+
+ <LI>Users must print text or Adobe<SUP>&reg;</SUP>
+ PostScript<SUP>TM</SUP> files; other formats <I>may</I> be
+ supported, but not universally.
+
+ <LI>Lack of a standard command-line interface; each operating
+ system and driver package provides different command-line
+ options, e.g. setting the media size with one driver may
+ involve passing a single option ("letter") while another
+ requires two ("mediasize letter").
+
+ <LI>Lack of a standard application interface; most UNIX
+ applications either do not provide an interface for sending
+ printer options, or restrict the options to those in printer
+ description files supplied with the application.
+
+ <LI>Remote printing problems; no vendor seems to use the same
+ remote printing protocol (many use an "enhanced" version of the
+ LPD protocol with vendor-specific extensions).
+
+ <LI>Client printer administration hassles; most vendors
+ require that you install remote printers on each client by
+ hand, and many do not even provide the ability to browse
+ printers on the "server" system.
+
+ <LI>Drivers typically are hardcoded to handle printing to
+ parallel or serial printers; support for other types of
+ interfaces or networking protocols requires that a driver be
+ rewritten to support them.
+
+ <LI>Security, accounting, and quotas; most printing systems do
+ not support access control lists ("ACLs"), and printer
+ accounting and quotas are not well supported, if at all.
+
+</OL>
+
+<H2>Goals of CUPS</H2>
+
+The basic goals of CUPS are:
+
+<OL>
+
+ <LI>Provide standard support for text (US ASCII, UTF-8, and
+ ISO-8859-x), Adobe PostScript, PDF, HP-GL/2, TIFF, JPEG, PNG,
+ PBM, PGM, PPM, GIF, SGI RGB, Sun Raster, and Kodak
+ PhotoCD<SUP>TM</SUP> files.
+
+ <LI>Provide a standard command-line interface with a standard
+ minimum set of options (media size and so forth).
+
+ <LI>Provide a standard application interface.
+
+ <LI>Provide a common remote printing interface (IPP).
+
+ <LI>Provide a printer browsing interface and allow users to
+ print to remote printers using a "printer@server" notation
+ rather than adding the printer locally.
+
+ <LI>Provide a scheduler extension interface to support
+ different interfaces separate from the printer driver (e.g.
+ serial, parallel, lpd, tftp, ipp, etc.)
+
+ <LI>Provide a standard interface for ACLs, quotas, accounting,
+ and logging.
+
+</OL>
+
+<H2>Design Overview</H2>
+
+Like most printing systems, CUPS is designed around a central print
+scheduling process that dispatches print jobs, processes administrative
+commands, provides printer status information to local and remote
+programs, and informs users as needed. Figure 1 shows the basic
+organization of CUPS.
+
+<P ALIGN=CENTER><IMG SRC="images/cups-block-diagram.gif" WIDTH="540" HEIGHT="200">
+<BR>Figure 1 - CUPS Block Diagram
+
+<H3>Scheduler</H3>
+
+The scheduler is a HTTP/1.1 server application that handles HTTP
+requests. Besides handling printer requests via IPP POST requests, the
+scheduler also acts as a full-featured web server for documentation and
+status monitoring.
+
+<P>The scheduler also monitors the LAN for printer browsing information
+and dispatches print jobs as needed using the appropriate filters and
+backends.
+
+<H3>Configuration Files</H3>
+
+The configuration files consist of:
+
+<UL>
+
+ <LI>A HTTP server configuration file.
+
+ <LI>Printer and class definition files.
+
+ <LI>MIME type and conversion rule files.
+
+ <LI>PostScript Printer Description (PPD) files.
+
+</UL>
+
+The HTTP server configuration file is purposely similar to the
+Apache server configuration file and defines all of the access control
+properties for the server.
+
+<P>The printer and class definition files list the available printer
+queues and classes.
+
+<P>The MIME type files list the supported MIME types (text/plain,
+application/postscript, etc.) and "magic" rules for automatically
+detecting the format of a file. These are used by the HTTP server to
+determine the <I>Content-Type</I> field for <I>GET</I> and <I>HEAD</I>
+requests, and by the IPP request handler to determine the file type
+when a <I>Print-Job</I> request is received with a
+<I>document-format</I> of <I>application/octet-stream</I>.
+
+<P>The MIME conversion rule files list the available filters. These
+files are augmented by <I>AddFilter</I> entries in the printer
+definition files. The filters are used when a job is dispatched so that
+an application can send a convenient file format to the printing system
+which then converts the document into a printable format as needed.
+
+<P>The PPD files describe the capabilities of PostScript printers. There is
+one PPD file for each printer.
+
+<H3>CUPS Interface Library</H3>
+
+The CUPS interface library contains CUPS-specific convenience functions
+for queuing print jobs, etc. It also contains functions to access
+resources via HTTP and IPP, perform MIME typing and conversion, and
+manipulate PPD files.
+
+<H3>Filters</H3>
+
+A filter program reads from the standard input or from a file if a
+filename is supplied. All filters must support a common set of options
+including printer name, job ID, username, job title, number of copies,
+and job options. All output is sent to the standard output.
+
+<H3>Backends</H3>
+
+A backend program is a special filter that writes incoming data to a
+device or network connection. Backends for serial, parallel, LPD, TFTP,
+FTP, IPP, SMB, and AppSocket (JetDirect) connections are provided in
+CUPS 1.0.
+
+<H2>Berkeley and System V Commands</H2>
+
+CUPS provides the System V and Berkeley command-line interfaces
+for submitting jobs and checking the printer status. The "lpstat" and
+"lpc status" commands also show network printers ("printer@hostname")
+when printer browsing is enabled.
+
+<P>The System V administation commands are supplied for managing
+printers local to the system. The Berkeley printer administration tool
+("lpc") is only supported in a "read-only" mode to check the current
+status of the printer queues and scheduler.
+
+<H2>Summary</H2>
+
+The Common UNIX Printing System provides a modern printing interface
+for UNIX applications that is both flexible and user-friendly. The
+software provides System V and Berkeley compatible command-line
+interfaces to ensure compatibility with existing applications.
+
+<H2>Licensing</H2>
+
+CUPS is available under the terms of the Aladdin Free Public
+License, which means that it is basically free except for commercial
+distribution. Vendors wishing to license CUPS for their printing
+solution should contact Easy Software Products at:
+
+<UL>
+<P>Attn: CUPS Licensing<BR>
+Easy Software Products<BR>
+44141 Airport View Drive Suite 204<BR>
+Hollywood, Maryland 20636-3111 USA
+<P>+1.301.373.9600<BR>
+cups-info@cups.org
+</UL>
+
+<H2>References</H2>
+
+<DL>
+
+ <DT>IEEE-1387.4</DT>
+
+ <DD>System Administration - Part 4: Printing Interfaces (draft)</DD>
+
+ <DT><A HREF="http://www.pwg.org/ipp/index.html">IETF-IPP</A></DT>
+
+ <DD>Internet Printing Protocol/1.0</DD>
+
+ <DT><A HREF="http://www.astart.com/lprng.html">LPRng</A></DT>
+
+ <DD>An enhanced, extended, and portable implementation of the Berkeley LPR
+ print spooler functionality</DD>
+
+ <DT>Palladin</DT>
+
+ <DD>A printing system developed at the Massachussetts Institute of Technology</DD>
+
+ <DT><A HREF="http://www-usa.iona.com//hyplan/jmason/plp.html">PLP</A></DT>
+
+ <DD>The Portable Line Printer spooler system</DD>
+
+ <DT><A HREF="http://www.cis.ohio-state.edu/rfc/rfc1179.txt">RFC1179</A></DT>
+
+ <DD>Line Printer Daemon Protocol</DD>
+
+ <DT><A HREF="http://www.cis.ohio-state.edu/rfc/rfc2046.txt">RFC2046</A></DT>
+
+ <DD>Multipurpose Internet Mail Extensions (MIME) Part Two: Media Types</DD>
+
+ <DT><A HREF="http://www.cis.ohio-state.edu/rfc/rfc2068.txt">RFC2068</A></DT>
+
+ <DD>Hypertext Transfer Protocol -- HTTP/1.1</DD>
+
+</DL>
+
+<H2>Trademarks</H2>
+
+The Common UNIX Printing System, CUPS, and the CUPS logo are
+trademarks of Easy Software Products. All other trademarks are the
+property of their respective owners.
+
+</BODY>
+</HTML>
diff --git a/doc/overview.pdf b/doc/overview.pdf
new file mode 100644
index 000000000..34e13ba32
--- /dev/null
+++ b/doc/overview.pdf
Binary files differ
diff --git a/doc/sam.html b/doc/sam.html
new file mode 100644
index 000000000..3a0a0a661
--- /dev/null
+++ b/doc/sam.html
@@ -0,0 +1,873 @@
+<HTML>
+<HEAD>
+<TITLE>DRAFT - CUPS Software Administrators Manual</TITLE>
+<META NAME="AUTHOR" CONTENT="Easy Software Products">
+<META NAME="COPYRIGHT" CONTENT="Copyright 1997-1999, All Rights Reserved">
+<META NAME="DOCNUMBER" CONTENT="CUPS-SAM-1.0.0">
+</HEAD>
+<BODY>
+<CENTER><A HREF=#contents><IMG SRC="images/cups-large.gif" BORDER=0><BR>
+<H1>DRAFT - CUPS Software Administrators Manual</H1></A><BR>
+CUPS-SAM-1.0.0<BR>
+Easy Software Products<BR>
+Copyright 1997-1999, All Rights Reserved<BR>
+</CENTER>
+<HR>
+<H1 ALIGN=CENTER><A NAME=CONTENTS>Table of Contents</A></H1>
+<BR>
+<BR><B><A HREF=#1>Preface</A></B>
+<UL>
+<LI><A HREF=#1_1>System Overview</A></LI>
+<LI><A HREF=#1_2>Document Overview</A></LI>
+</UL>
+<B><A HREF=#2>1 - Printing System Overview</A></B>
+<UL>
+<LI><A HREF=#2_1>The Printing Problem</A></LI>
+<LI><A HREF=#2_2>The Technology</A></LI>
+<LI><A HREF=#2_3>Jobs</A></LI>
+<LI><A HREF=#2_4>Classes</A></LI>
+<LI><A HREF=#2_5>Filters</A></LI>
+<LI><A HREF=#2_6>Printer Drivers</A></LI>
+<LI><A HREF=#2_7>Networking</A></LI>
+</UL>
+<B><A HREF=#3>2 - Building and Installing CUPS</A></B>
+<UL>
+<LI><A HREF=#3_1>Installing a Source Distribution</A></LI>
+<UL>
+<LI><A HREF=#3_1_1>Requirements</A></LI>
+<LI><A HREF=#3_1_2>Compiling CUPS</A></LI>
+<LI><A HREF=#3_1_3>Installing the Software</A></LI>
+<LI><A HREF=#3_1_4>Running the Software</A></LI>
+</UL>
+<LI><A HREF=#binary>Installing a Binary Distribution</A></LI>
+</UL>
+<B><A HREF=#4>3 - Printer Queue Management</A></B>
+<UL>
+<LI><A HREF=#4_1>The lpadmin Command</A></LI>
+<LI><A HREF=#4_2>Adding and Modifying Printers</A></LI>
+<UL>
+<LI><A HREF=#4_2_1>Using Standard Printer Drivers</A></LI>
+</UL>
+<LI><A HREF=#4_3>Removing Printers</A></LI>
+<LI><A HREF=#4_4>Printer Classes</A></LI>
+<LI><A HREF=#4_5>Setting the Default Printer</A></LI>
+<LI><A HREF=#4_6>Starting and Stopping Printers</A></LI>
+<LI><A HREF=#4_7>Accepting and Rejecting Print Jobs</A></LI>
+</UL>
+<B><A HREF=#5>4 - Printing System Management</A></B>
+<UL>
+<LI><A HREF=#5_1>Changing the Configuration Files</A></LI>
+<LI><A HREF=#5_2>Temporary Files</A></LI>
+<LI><A HREF=#5_3>Network Configuration</A></LI>
+<UL>
+<LI><A HREF=#5_3_1>Port</A></LI>
+<LI><A HREF=#5_3_2>Listen</A></LI>
+<LI><A HREF=#5_3_3>BrowsePort</A></LI>
+<LI><A HREF=#5_3_4>BrowseAddress</A></LI>
+</UL>
+<LI><A HREF=#5_4>Printer Security</A></LI>
+<UL>
+<LI><A HREF=#5_4_1>Location</A></LI>
+<LI><A HREF=#5_4_2>Order</A></LI>
+<LI><A HREF=#5_4_3>Allow</A></LI>
+<LI><A HREF=#5_4_4>Deny</A></LI>
+<LI><A HREF=#5_4_5>AuthType</A></LI>
+<LI><A HREF=#5_4_6>AuthClass</A></LI>
+<LI><A HREF=#5_4_7>AuthGroupName</A></LI>
+<LI><A HREF=#5_4_8>SystemGroup</A></LI>
+</UL>
+<LI><A HREF=#5_5>File Formats</A></LI>
+<UL>
+<LI><A HREF=#5_5_1>mime.types</A></LI>
+<LI><A HREF=#5_5_2>mime.convs</A></LI>
+</UL>
+</UL>
+<B><A HREF=#6>5 - Printer Accounting</A></B>
+<UL>
+<LI><A HREF=#6_1>Where to Find the Log Files</A></LI>
+<LI><A HREF=#6_2>The access_log File</A></LI>
+<LI><A HREF=#6_3>The error_log File</A></LI>
+<LI><A HREF=#6_4>The page_log File</A></LI>
+</UL>
+<HR>
+<H1 ALIGN=RIGHT><A NAME=1>Preface</A></H1>
+ This software administrators manual provides printer administration
+information for the Common UNIX Printing System (&quot;CUPS&quot;) Version 1.0.0.
+<H2><A NAME=1_1>System Overview</A></H2>
+ The Common UNIX Printing System provides a portable printing layer for
+ UNIX&reg; operating systems. It has been developed by Easy Software
+ Products to promote a standard printing solution for all UNIX vendors
+ and users. CUPS provides the System V and Berkeley command-line
+interfaces.
+<P>CUPS uses the Internet Printing Protocol (IETF-IPP) as the basis for
+managing print jobs and queues. The Line Printer Daemon (LPD, RFC1179),
+Server Message Block (SMB), and AppSocket protocols are also supported
+with reduced functionality. </P>
+<P>CUPS adds network printer browsing and PostScript Printer
+Description (&quot;PPD&quot;)-based printing options to support real world
+applications under UNIX. </P>
+<P>CUPS also includes a customized version of GNU GhostScript
+(currently based off GNU GhostScript 4.03) and an image file RIP that
+can be used to support non-PostScript printers. </P>
+<H2><A NAME=1_2>Document Overview</A></H2>
+<P>This software administrators manual is organized into the following
+sections:</P>
+<UL>
+<LI>1 - Printing System Overview</LI>
+<LI>2 - Building and Installing CUPS</LI>
+<LI>3 - Printer Queue Management</LI>
+<LI>4 - Printing System Management</LI>
+<LI>5 - Printer Accounting</LI>
+</UL>
+<H1 ALIGN=RIGHT><A NAME=2>1 - Printing System Overview</A></H1>
+<P>This chapter provides an overview of how the Common UNIX Printing
+System works. </P>
+<H2><A NAME=2_1>The Printing Problem</A></H2>
+<P>For years <I>the printing problem</I> has plagued UNIX&reg;. Unlike
+Microsoft&reg; Windows&reg; or MacOS, UNIX has no standard interface or system
+in place for supporting printers. Among the solutions previously
+available, the Berkeley and System V printing systems are the most
+prevalent. </P>
+<P>These printing systems support line printers (text only) or
+PostScript printers (text and graphics), and with some coaxing they can
+be made to support a full range of printers and file formats. However,
+because each varient of the UNIX operating system uses a different
+printing system than the next, developing printer drivers for a wide
+range of printers is extremely difficult. That combined with the
+limited volume of customers for each UNIX varient has forced most
+printer vendors to give up supporting UNIX entirely. </P>
+<P>The Common UNIX Printing System, or CUPS, is designed to eliminate <I>
+the printing problem</I>. One common printing system can be used by all
+UNIX varients to support the printing needs of users. Printer vendors
+can use its modular filter interface to develop a single driver program
+that supports a wide range of file formats with little or no effort.
+ Since CUPS provides both the System V and Berkeley printing commands,
+users (and applications) can reap the benefits of this new technology
+with no changes. </P>
+<H2><A NAME=2_2>The Technology</A></H2>
+<P>CUPS is based upon an emerging Internet standard called the Internet
+Printing Protocol, or IPP. IPP has been embraced by dozens of printer
+and printer server manufacturers, and will be supported by the next
+Microsoft Windows operating system. </P>
+<P>IPP defines a standard protocol for printing as well as managing
+print jobs and printer options like media size, resolution, and so
+forth. Like all IP-based protocols, IPP can be used locally or over the
+Internet to printers hundreds or thousands of miles away. Unlike other
+protocols, however, IPP also supports access control, authentication,
+and encryption, making it a much more secure printing solution than
+older ones. </P>
+<P>IPP is layered on top of the Hyper-Text Transport Protocol, or HTTP,
+which is the basis of web servers on the Internet. This allows the user
+to view documentation and status information on a printer or server
+using their web browser. </P>
+<P>CUPS provides a complete IPP/1.0-based printing system that provides
+Basic authentication and domain or IP-based access control. Digest
+authentication and TLS encryption will be available in future versions
+of CUPS. </P>
+<H2><A NAME=2_3>Jobs</A></H2>
+<P>Each file that is submitted for printing is called a <I>job</I>.
+ Jobs are identified by a unique number starting at 1 and are assigned
+to a particular destination (usually a printer). Jobs can also have
+options associated with them such as media size, number of copies, and
+priority. </P>
+<H2><A NAME=2_4>Classes</A></H2>
+<P>CUPS supports collections of printers known as <I>classes</I>. Jobs
+sent to a class are forwarded to the first available printer in the
+class. </P>
+<H2><A NAME=2_5>Filters</A></H2>
+<P>Filters allow a user or application to print many types of files
+without extra effort. Print jobs sent to a CUPS server are filtered
+before sending them to a printer. Some filters convert job files to
+different formats that the printer can understand. Others perform page
+selection and ordering tasks. <I>Backend</I> filters perform the most
+important task of all - they send the filtered print data to the
+printer. </P>
+<P>CUPS provides filters for printing many types of image files,
+HP-GL/2 files, PDF files, and text files. CUPS also supplies PostScript
+and image file Raster Image Processors, or RIPs, that convert
+PostScript or image files into bitmaps that can be sent to a raster
+printer. </P>
+<P>CUPS provides backends for printing over parallel and serial ports,
+and over the network via the JetDirect (AppSocket), Server Message
+Block, and Line Printer Daemon protocols. </P>
+<H2><A NAME=2_6>Printer Drivers</A></H2>
+<P>Printer drivers in CUPS consist of one of more filters specific to a
+printer. CUPS includes a sample printer driver for Hewlett-Packard
+LaserJet and DeskJet printers. While this driver does not generate
+optimal output for different printer models, it does demonstrate how
+you can write your own printer drivers and incorporate them into CUPS. </P>
+<H2><A NAME=2_7>Networking</A></H2>
+<P>Printers and classes on the local system are automatically shared
+with other systems on the network. This allows you to setup one system
+to print to a printer and use this system as a printer server or spool
+host for all of the others. If there is only one occurrence of a
+printer on a network, then that printer can be accessed using its name
+alone. If more than one printer exists with the same name, users must
+select the printer by specifying which server to use (e.g.
+&quot;printer@host1&quot; or &quot;printer@host2&quot;.) </P>
+<P>CUPS also provides <I>implicit classes</I>, which are collections of
+printers and/or classes with the same name. This allows you to setup
+multiple servers pointing to the same physical network printer, for
+example, so that you aren't relying on a single system for printing.
+Because this also works with printer classes, you can setup multiple
+servers and printers and never worry about a &quot;single point of failure&quot;
+unless all of the printers and servers goes down! </P>
+<H1 ALIGN=RIGHT><A NAME=3>2 - Building and Installing CUPS</A></H1>
+<P>This chapter shows how to build and install the Common UNIX Printing
+System. If you are installing a binary distribution from the CUPS web
+site, proceed to the section titled, <A HREF=#binary>Installing a
+Binary Distribution</A>. </P>
+<H2><A NAME=3_1>Installing a Source Distribution</A></H2>
+<H3><A NAME=3_1_1>Requirements</A></H3>
+<P>You'll need an ANSI C compiler to build CUPS on your system. As its
+name implies, CUPS is designed to run on the UNIX operating system,
+however the CUPS interface library and most of the filters and backends
+supplied with CUPS should also run under Microsoft&reg; Windows&reg;. </P>
+<P>For the image file filters and PostScript RIP, you'll need the JPEG,
+PNG, TIFF, and ZLIB libraries. CUPS will build without these, but with
+reduced functionality. Easy Software Products maintains a mirror of the
+current versions of these libraries at: </P>
+<UL>
+<PRE>
+<A HREF=ftp://ftp.easysw.com/pub/libraries>ftp://ftp.easysw.com/pub/libraries</A>
+</PRE>
+</UL>
+<P>If you make changes to the man pages you'll need GNU groff or
+another nroff-like package. GNU groff is available from: </P>
+<UL>
+<PRE>
+<A HREF=ftp://ftp.gnu.org/pub/groff>ftp://ftp.gnu.org/pub/groff</A>
+</PRE>
+</UL>
+<P>The documentation is formatted using the HTMLDOC software. If you
+need to make changes you can get the HTMLDOC software from: </P>
+<UL>
+<PRE>
+<A HREF=http://www.easysw.com/htmldoc>http://www.easysw.com/htmldoc</A>
+</PRE>
+</UL>
+<H3><A NAME=3_1_2>Compiling CUPS</A></H3>
+<P>CUPS uses GNU autoconf to configure the makefiles and source code
+for your system. To configure CUPS for your system type: </P>
+<UL>
+<PRE>
+% ./configure ENTER
+</PRE>
+</UL>
+<P>The default installation will put the CUPS software in the <CODE>/usr</CODE>
+ and <CODE>/var</CODE> directories on your system, which will overwrite
+any existing printing commands on your system. To install the CUPS
+software in another location use the <CODE>--prefix</CODE> option: </P>
+<UL>
+<PRE>
+% ./configure --prefix=/usr/local ENTER
+</PRE>
+</UL>
+<P>If the PNG, JPEG, TIFF, and ZLIB libraries are not installed in a
+system default location (typically <CODE>/usr/include</CODE> and <CODE>
+/usr/lib</CODE>) you'll need to set the <CODE>CFLAGS</CODE> and <CODE>
+LDFLAGS</CODE> environment variables prior to running configure: </P>
+<UL>
+<PRE>
+% setenv CFLAGS &quot;-I/some/directory&quot;
+% setenv LDFLAGS &quot;-L/some/directory&quot;
+% ./configure ... ENTER
+</PRE>
+</UL>
+<P>Once you have configured things, just type: </P>
+<UL>
+<PRE>
+% make ENTER
+</PRE>
+</UL>
+<P>to build the software. </P>
+<H3><A NAME=3_1_3>Installing the Software</A></H3>
+<P>To install the software type: </P>
+<UL>
+<PRE>
+% make install ENTER
+</PRE>
+</UL>
+<H3><A NAME=3_1_4>Running the Software</A></H3>
+ Once you have installed the software you can start the CUPS daemon by
+typing:
+<UL>
+<PRE>
+% /usr/sbin/cupsd &amp; ENTER
+</PRE>
+</UL>
+<H2><A NAME=binary>Installing a Binary Distribution</A></H2>
+<P>We are currently distributing CUPS binary distributions in TAR
+format with installation and removal scripts. </P>
+<UL><B>WARNING:</B>
+<P>Installing CUPS will overwrite your existing printing system. If
+you experience difficulties with the CUPS software and need to go back
+to your old printing system, you will need to remove the CUPS software
+with the provided script and reinstall the printing system from your
+operating system CDs. </P>
+</UL>
+<P>To install the CUPS software you will need to be logged in as root
+(doing an &quot;su&quot; is good enough). Once you are the root user, run the
+installation script with: </P>
+<UL>
+<PRE>
+./cups.install ENTER
+</PRE>
+</UL>
+<P>After asking you a few yes/no questions the CUPS software will be
+installed and the scheduler will be started automatically. </P>
+<H1 ALIGN=RIGHT><A NAME=4>3 - Printer Queue Management</A></H1>
+<P>This chapter discusses how to add, modify, and delete print queues
+on your system. </P>
+<H2><A NAME=4_1>The lpadmin Command</A></H2>
+<P>The <CODE>lpadmin</CODE> command allows you to perform most printer
+administration tasks from the command-line. Since <CODE>lpadmin</CODE>
+ is also a System V printing system command, it is located in the <CODE>
+/usr/lib</CODE> directory instead of a more common one like <CODE>
+/usr/bin</CODE> or <CODE>/usr/sbin</CODE>. </P>
+<H2><A NAME=4_2>Adding and Modifying Printers</A></H2>
+<P>To add a printer to CUPS you simply run the <CODE>lpadmin</CODE>
+ command with the &quot;-p&quot; option: </P>
+<UL>
+<PRE>
+% /usr/lib/lpadmin -p<I>printer</I> -E -v<I>device</I> -P<I>ppd</I> ENTER
+</PRE>
+</UL>
+<P>Spaces between the option letter and value are optional. </P>
+<P>The <I>printer</I> name can be up to 127 letters, digits, hyphens,
+and underscores. Unlike other printing systems, the printer name in
+CUPS is <I>not</I> case-sensitive, so you can't add two printers named <CODE>
+LaserJet</CODE> and <CODE>laserjet</CODE>. </P>
+<P>The <I>device</I> argument specifies the device URI or filename for
+the printer. The following devices are supported in a basic
+installation of CUPS: </P>
+<DL>
+<DT>file:/dev/filename </DT>
+<DT>/dev/filename </DT>
+<DD>Sends all output to the specified file. </DD>
+<DT>http://[username:password@]hostname[:port]/resource </DT>
+<DT>ipp://[username:password@]hostname[:port]/resource </DT>
+<DD>Sends all output to the specified IPP printer or server. The <I>
+port</I> parameters defaults to 631. </DD>
+<DT>lpd://hostname/queue </DT>
+<DD>Sends all output to the specified LPD printer queue. </DD>
+<DT>parallel:/dev/filename </DT>
+<DD>Sends all output to the specified parallel port device. </DD>
+<DT>serial:/dev/filename[?options] </DT>
+<DD>Sends all output to the specified serial port device. The <I>
+options</I> can be any of the following separated by the plus (+)
+character:
+<UL>
+<LI><CODE>baud=<I>rate</I></CODE> - Sets the baud rate for the device. </LI>
+<LI><CODE>bits=<I>7 or 8</I></CODE> - Sets the number of data bits. </LI>
+<LI><CODE>parity=<I>even</I></CODE> - Sets even parity checking. </LI>
+<LI><CODE>parity=<I>odd</I></CODE> - Sets odd parity checking. </LI>
+<LI><CODE>parity=<I>none</I></CODE> - Turns parity checking off. </LI>
+</UL>
+</DD>
+<DT>smb://[username:password@]hostname/queue </DT>
+<DD>Sends all output to the specified SMB (Windows) printer queue
+ using the SAMBA software. </DD>
+<DT>socket://hostname[:port] </DT>
+<DD>Sends all output to the specified printer using the AppSocket
+protocol. The <I>port</I> parameter defaults to 9100. </DD>
+</DL>
+<P>The <I>ppd</I> argument specifies the PostScript Printer Description
+file to use for this printer. Many options (such as media size, etc.)
+will not be available if you omit this part of the <CODE>lpadmin</CODE>
+ command. </P>
+<H3><A NAME=4_2_1>Using Standard Printer Drivers</A></H3>
+<P>The <CODE>lpadmin</CODE> command allows you to use &quot;standard&quot; PPD
+files and interface scripts located in the <CODE>/usr/share/cups/model</CODE>
+ directory with the &quot;-m&quot; option: </P>
+<UL>
+<PRE>
+% /usr/lib/lpadmin -p<I>printer</I> -E -v<I>device</I> -m<I>model</I> ENTER
+</PRE>
+</UL>
+<P>The <I>model</I> argument specifies the name of the PPD file or
+interface script. For example, to add a printer using the sample HP
+DeskJet series driver connected to parallel port 1 under Linux you
+would use: </P>
+<UL>
+<PRE>
+% /usr/lib/lpadmin -pDeskJet -E -vparallel:/dev/par1 -mdeskjet.ppd ENTER
+</PRE>
+</UL>
+<H2><A NAME=4_3>Removing Printers</A></H2>
+<P>To remove a printer to CUPS you simply run the <CODE>lpadmin</CODE>
+ command with the &quot;-x&quot; option: </P>
+<UL>
+<PRE>
+% /usr/lib/lpadmin -x<I>printer</I> ENTER
+</PRE>
+</UL>
+<H2><A NAME=4_4>Printer Classes</A></H2>
+<P>CUPS allows you to group similar printers in a <I>printer class</I>.
+When a user sends a print job to a class, the job will be processed by
+the first available printer in that class. </P>
+<P>To add a printer to a class you simply run the <CODE>lpadmin</CODE>
+ command with the &quot;-p&quot; and &quot;-c&quot; options: </P>
+<UL>
+<PRE>
+% /usr/lib/lpadmin -p<I>printer</I> -c<I>class</I> ENTER
+</PRE>
+</UL>
+<P>The <I>class</I> is created automatically if it doesn't exist. To
+remove a class just use the &quot;-x&quot; option: </P>
+<UL>
+<PRE>
+% /usr/lib/lpadmin -x<I>class</I> ENTER
+</PRE>
+</UL>
+<H2><A NAME=4_5>Setting the Default Printer</A></H2>
+<P>To set the default printer or class simply run the <CODE>lpadmin</CODE>
+ command with the &quot;-d&quot; option: </P>
+<UL>
+<PRE>
+% /usr/lib/lpadmin -d<I>destination</I> ENTER
+</PRE>
+</UL>
+<P>The <I>destination</I> argument is the name of the printer or class. </P>
+<H2><A NAME=4_6>Starting and Stopping Printers</A></H2>
+<P>The <CODE>enable</CODE> and <CODE>disable</CODE> commands start and
+stop printer queues, respectively: </P>
+<UL>
+<PRE>
+% /usr/bin/enable <I>printer</I> ENTER
+% /usr/bin/disable <I>printer</I> ENTER
+</PRE>
+</UL>
+<P>Printers that are disabled may still accept jobs for printing, but
+won't actually print any files until they are restarted. This is useful
+if the printer malfunctions and you need time to correct the problem.
+Any queues jobs are printed after the printer is enabled (started). </P>
+<H2><A NAME=4_7>Accepting and Rejecting Print Jobs</A></H2>
+<P>The <CODE>accept</CODE> and <CODE>reject</CODE> commands accept and
+reject print jobs for the named printer, respectively: </P>
+<UL>
+<PRE>
+% /usr/lib/accept <I>printer</I> ENTER
+% /usr/lib/reject <I>printer</I> ENTER
+</PRE>
+</UL>
+<P>As noted above, a printer can be stopped but accepting new print
+jobs. A printer can also be rejecting new print jobs while it finishes
+those that have been queued. This is useful for when you must perform
+maintenance on the printer and will not have it available to users for
+a long period of time. </P>
+<H1 ALIGN=RIGHT><A NAME=5>4 - Printing System Management</A></H1>
+<P>This chapter shows how you can configure the CUPS server. </P>
+<H2><A NAME=5_1>Changing the Configuration Files</A></H2>
+<P>All of the server configuration files are located in the <CODE>
+/var/cups/conf</CODE> directory. Once you have made a change to a file
+you need to restart the CUPS server by sending it a HUP signal or using
+the supplied script &quot;<CODE>cups.sh</CODE>&quot;: </P>
+<UL>
+<PRE>
+% ./cups.sh restart ENTER
+</PRE>
+</UL>
+<P>The binary distribution installs the script in the <CODE>init.d</CODE>
+ directory with the name <CODE>lp</CODE> or <CODE>lpd</CODE> depending
+on the vendor-supplied printing system. </P>
+<H2><A NAME=5_2>Temporary Files</A></H2>
+<P>Normally CUPS puts all of its temporary files in <CODE>/var/tmp</CODE>
+. If you'd like to change this directory you'll need to edit the <CODE>
+/var/cups/conf/cupsd.conf</CODE> file. </P>
+<P>Start by creating the new temporary directory and setting the
+appropriate permissions: </P>
+<UL>
+<PRE>
+% mkdir <I>/foo/bar/tmp</I> ENTER
+% chmod a+rwxt <I>/foo/bar/tmp</I> ENTER
+</PRE>
+</UL>
+<P>Then change the line containing the <CODE>TempDir</CODE> directive
+in the <CODE>cupsd.conf</CODE> to the directory that you've created: </P>
+<UL>
+<PRE>
+TempDir <I>/foo/bar/tmp</I>
+</PRE>
+</UL>
+<P>Finally, restart the server as outlined in the first section of this
+chapter. </P>
+<H2><A NAME=5_3>Network Configuration</A></H2>
+<P>The default configuration of the CUPS server listens for connections
+from all network interfaces on port 631 (the standard IPP port).
+Administration functions are limited to local connections with the
+appropriate username and password. </P>
+<P>If you'd like to limit access to your system you'll need to edit the <CODE>
+/var/cups/conf/cupsd.conf</CODE> file. </P>
+<H3><A NAME=5_3_1>Port</A></H3>
+<P>The <CODE>Port</CODE> directive specifies a port to listen on for
+all interfaces. Besides the standard IPP port (631) you can also setup
+your server to listen on the HTTP port (80) to use your CUPS server as
+a standard web server as well. </P>
+<H3><A NAME=5_3_2>Listen</A></H3>
+<P>The <CODE>Listen</CODE> directive specifies a listening address and
+port, extending the functionality of the <CODE>Port</CODE> directive.
+If you want to allow connections only from the local machine you can
+use: </P>
+<UL>
+<PRE>
+Listen 127.0.0.1:631
+</PRE>
+</UL>
+<P>instead of the <CODE>Port</CODE> directive. </P>
+<P>If you want to limit access to a specific network/subnet, make sure
+you specify only the network address and not your system's network
+address! </P>
+<H3><A NAME=5_3_3>BrowsePort</A></H3>
+<P>The <CODE>BrowsePort</CODE> directive controls which port is
+monitored for remote printers. By default it is set to the IPP port
+(631), however you can change it as needed. </P>
+<UL><B>NOTE:</B>
+<P>You must set the <CODE>BrowsePort</CODE> to the same value on all
+of the systems that you want to see. </P>
+</UL>
+<H3><A NAME=5_3_4>BrowseAddress</A></H3>
+<P>The <CODE>BrowseAddress</CODE> directive specifies a broadcast
+address to use when sending printer status updates over the network.
+The default browse address is <CODE>255.255.255.255</CODE> which will
+send printer information to all subnets. </P>
+<UL><B>NOTE:</B>
+<P>If you are using HP-UX 10.20 and a subnet that is not 24, 16, or 8
+bits, printer browsing (and in fact all broadcast reception) will not
+work. This problem appears to be fixed in HP-UX 11.0. </P>
+</UL>
+<H2><A NAME=5_4>Printer Security</A></H2>
+<P>CUPS provides IP and domain-name based access control and Basic
+authentication for authentication. </P>
+<H3><A NAME=5_4_1>Location</A></H3>
+<P>The <CODE>Location</CODE> directive defines access control for a
+specific HTTP directory. The following pseudo directories are provided
+by the CUPS server: </P>
+<UL>
+<LI><CODE>/admin</CODE> - This is the URI that must be referenced to
+ do printer administation commands. </LI>
+<LI><CODE>/classes</CODE> - This is the URI that must be referenced to
+ access printer classes. </LI>
+<LI><CODE>/jobs</CODE> - This is the URI that must be referenced to
+ access jobs. </LI>
+<LI><CODE>/printers</CODE> - This is the URI that must be referenced to
+ access printers. </LI>
+</UL>
+<P>All other directories are taken from the <CODE>/usr/share/cups/doc</CODE>
+ directory. </P>
+<P>The <CODE>Location</CODE> directive surrounds the other access
+control directives described below. The default server configuration
+uses: </P>
+<UL>
+<PRE>
+&lt;Location /admin&gt;
+AuthType Basic
+AuthClass System
+
+Order Deny,Allow
+Deny From All
+Allow From 127.0.0.1
+&lt;/Location&gt;
+</PRE>
+</UL>
+<H3><A NAME=5_4_2>Order</A></H3>
+<P>The <CODE>Order</CODE> directive defines the default access control.
+The following values are supported: </P>
+<UL>
+<LI><CODE>Order Allow,Deny</CODE> - Allow requests from all systems <I>
+except</I> for those listed in a <CODE>Deny</CODE> directive. </LI>
+<LI><CODE>Order Deny,Allow</CODE> - Allow requests only from those
+listed in an <CODE>Allow</CODE> directive. </LI>
+</UL>
+<P>The <CODE>Order</CODE> directive must appear inside a <CODE>Location</CODE>
+ directive. </P>
+<H3><A NAME=5_4_3>Allow</A></H3>
+<P>The <CODE>Allow</CODE> directive specifies a hostname, IP address,
+or network that is allowed access to the server: </P>
+<UL>
+<PRE>
+Allow from All
+Allow from None
+Allow from *.domain.com
+Allow from .domain.com
+Allow from host.domain.com
+Allow from nnn.*
+Allow from nnn.nnn.*
+Allow from nnn.nnn.nnn.*
+Allow from nnn.nnn.nnn.nnn
+Allow from nnn.nnn.nnn.nnn/mm
+Allow from nnn.nnn.nnn.nnn/mmm.mmm.mmm.mmm
+</PRE>
+</UL>
+<P><CODE>Allow</CODE> directives are cummulative, so multiple <CODE>
+Allow</CODE> directives can be used to allow access for multiple hosts
+or networks. The <CODE>/mm</CODE> notation specifies a CIDR netmask:
+<CENTER>
+<TABLE BORDER=1 WIDTH=80%>
+<TR><TH>mm</TH><TH>netmask</TH></TR>
+<TR><TD>0</TD><TD>0.0.0.0</TD></TR>
+<TR><TD>1</TD><TD>128.0.0.0</TD></TR>
+<TR><TD>2</TD><TD>192.0.0.0</TD></TR>
+<TR><TD>...</TD><TD>...</TD></TR>
+<TR><TD>8</TD><TD>255.0.0.0</TD></TR>
+<TR><TD>16</TD><TD>255.255.0.0</TD></TR>
+<TR><TD>24</TD><TD>255.255.255.0</TD></TR>
+<TR><TD>32</TD><TD>255.255.255.255</TD></TR>
+</TABLE>
+</CENTER>
+</P>
+<P>The <CODE>Allow</CODE> directive must appear inside a <CODE>Location</CODE>
+ directive. </P>
+<H3><A NAME=5_4_4>Deny</A></H3>
+<P>The <CODE>Deny</CODE> directive specifies a hostname, IP address, or
+network that is allowed access to the server: </P>
+<UL>
+<PRE>
+Deny from All
+Deny from None
+Deny from *.domain.com
+Deny from .domain.com
+Deny from host.domain.com
+Deny from nnn.*
+Deny from nnn.nnn.*
+Deny from nnn.nnn.nnn.*
+Deny from nnn.nnn.nnn.nnn
+Deny from nnn.nnn.nnn.nnn/mm
+Deny from nnn.nnn.nnn.nnn/mmm.mmm.mmm.mmm
+</PRE>
+</UL>
+<P><CODE>Deny</CODE> directives are cummulative, so multiple <CODE>Deny</CODE>
+ directives can be used to allow access for multiple hosts or networks.
+ The <CODE>/mm</CODE> notation specifies a CIDR netmask:
+<CENTER>
+<TABLE BORDER=1 WIDTH=80%>
+<TR><TH>mm</TH><TH>netmask</TH></TR>
+<TR><TD>0</TD><TD>0.0.0.0</TD></TR>
+<TR><TD>1</TD><TD>128.0.0.0</TD></TR>
+<TR><TD>2</TD><TD>192.0.0.0</TD></TR>
+<TR><TD>...</TD><TD>...</TD></TR>
+<TR><TD>8</TD><TD>255.0.0.0</TD></TR>
+<TR><TD>16</TD><TD>255.255.0.0</TD></TR>
+<TR><TD>24</TD><TD>255.255.255.0</TD></TR>
+<TR><TD>32</TD><TD>255.255.255.255</TD></TR>
+</TABLE>
+</CENTER>
+</P>
+<P>The <CODE>Deny</CODE> directive must appear inside a <CODE>Location</CODE>
+ directive. </P>
+<H3><A NAME=5_4_5>AuthType</A></H3>
+<P>The <CODE>AuthType</CODE> directive defines the type of
+authentication to perform: </P>
+<UL>
+<LI><CODE>None</CODE> - No authentication should be performed
+ (default.) </LI>
+<LI><CODE>Basic</CODE> - Basic authentication should be performed
+using the UNIX password and group files. </LI>
+</UL>
+<P>The <CODE>AuthType</CODE> directive must appear inside a <CODE>
+Location</CODE> directive. </P>
+<H3><A NAME=5_4_6>AuthClass</A></H3>
+<P>The <CODE>AuthClass</CODE> directive defines what level of <CODE>
+Basic</CODE> access is required: </P>
+<UL>
+<LI><CODE>Anonymous</CODE> - No authentication should be performed
+ (default.) </LI>
+<LI><CODE>User</CODE> - A valid username and password is required. </LI>
+<LI><CODE>System</CODE> - A valid username and password is required,
+and the username must belong to the &quot;sys&quot; group (this can be changed
+using the <CODE>SystemGroup</CODE> directive, below. </LI>
+<LI><CODE>Group</CODE> - A valid username and password is required,
+and the username must belong to the group named by the <CODE>
+AuthGroupName</CODE> directive. </LI>
+</UL>
+<P>The <CODE>AuthClass</CODE> directive must appear inside a <CODE>
+Location</CODE> directive. </P>
+<H3><A NAME=5_4_7>AuthGroupName</A></H3>
+<P>The <CODE>AuthGroupName</CODE> directive sets the group to use for <CODE>
+Group</CODE> authentication. </P>
+<P>The <CODE>AuthGroupName</CODE> directive must appear inside a <CODE>
+Location</CODE> directive. </P>
+<H3><A NAME=5_4_8>SystemGroup</A></H3>
+<P>The <CODE>SystemGroup</CODE> directive sets the administration group
+used when authenticating the <CODE>System</CODE> type. It defaults to
+the &quot;sys&quot; group. </P>
+<H2><A NAME=5_5>File Formats</A></H2>
+<P>CUPS provides a MIME-based file typing and filtering mechanism to
+convert files to a printable format for each printer. The <CODE>
+mime.types</CODE> and <CODE>mime.convs</CODE> files define the file
+type and filters that are available on the system. </P>
+<H3><A NAME=5_5_1>mime.types</A></H3>
+<P>The <CODE>mime.types</CODE> defines the known file types. Each line
+of the file starts with the MIME type and may be followed by one or
+more file type recognition rules. For example, the <CODE>text/html</CODE>
+ file type is defined as: </P>
+<UL>
+<PRE>
+text/html html htm \
+ printable(0,1024) + (string(0,&quot;&lt;HTML&gt;&quot;) string(0,&quot;&lt;!DOCTYPE&quot;))
+</PRE>
+</UL>
+<P>The first two rules say that any file with an extension of &quot;.html&quot;
+or &quot;.htm&quot; is a HTML file. The third rules says that any file whose
+first 1024 characters are printable text and starts with the strings
+&quot;&lt;HTML&gt;&quot; or &quot;&lt;!DOCTYPE&quot; is a HTML file as well. </P>
+<P>The first two rules deal solely with the name of the file being
+typed. This is useful when the original filename is known, however for
+print files the server doesn't always have a filename to work with. The
+third rule takes care of this possibility and automatically figures out
+the file type based upon the contents of the file instead. </P>
+<P>The available tests are: </P>
+<UL>
+<LI><CODE>( expr )</CODE> - Parenthesis for expression grouping </LI>
+<LI><CODE>+</CODE> - Logical AND </LI>
+<LI><CODE>,</CODE> or whitespace - Logical OR </LI>
+<LI><CODE>!</CODE> - Logical NOT </LI>
+<LI><CODE>match(&quot;pattern&quot;)</CODE> - Pattern match on filename </LI>
+<LI><CODE>extension</CODE> - Pattern match on &quot;*.extension&quot; </LI>
+<LI><CODE>ascii(offset,length)</CODE> - True if bytes are valid
+ printable ASCII (CR, NL, TAB, BS, 32-126) </LI>
+<LI><CODE>printable(offset,length)</CODE> - True if bytes are
+ printable 8-bit chars (CR, NL, TAB, BS, 32-126, 160-254) </LI>
+<LI><CODE>string(offset,&quot;string&quot;)</CODE> - True if bytes are identical
+to string </LI>
+<LI><CODE>char(offset,value)</CODE> - True if byte is identical </LI>
+<LI><CODE>short(offset,value)</CODE> - True if 16-bit integer is
+identical (network or &quot;big-endian&quot; byte order) </LI>
+<LI><CODE>int(offset,value)</CODE> - True if 32-bit integer is
+ identical (network or &quot;big-endian&quot; byte order) </LI>
+<LI><CODE>locale(&quot;string&quot;)</CODE> - True if current locale matches
+string </LI>
+</UL>
+<H3><A NAME=5_5_2>mime.convs</A></H3>
+<P>The <CODE>mime.convs</CODE> file defines all of the filter programs
+that are known to the system. Each line consists of: </P>
+<UL>
+<PRE>
+source destination cost program
+
+text/plain application/postscript 50 texttops
+application/vnd.cups-postscript application/vnd.cups-raster 50 pstoraster
+image/* application/vnd.cups-postscript 50 imagetops
+image/* application/vnd.cups-raster 50 imagetoraster
+</PRE>
+</UL>
+<P>The <I>source</I> field is a MIME type, optionally using a wildcard
+for the super-type or sub-type (e.g. &quot;text/plain&quot;, &quot;image/*&quot;,
+&quot;*/postscript&quot;). </P>
+<P>The <I>destination</I> field is a MIME type defined in the <CODE>
+mime.types</CODE> file. </P>
+<P>The <I>cost</I> field defines a relative cost for the filtering
+operation from 1 to 100. The cost is used to choose between two
+different sets of filters when converting a file. For example, to
+convert from <CODE>image/jpeg</CODE> to <CODE>
+application/vnd.cups-raster</CODE>, you could use the <CODE>imagetops</CODE>
+ and <CODE>pstoraster</CODE> filters for a total cost of 100, or the <CODE>
+imagetoraster</CODE> filter for a total cost of 50. </P>
+<P>The <I>program</I> field defines the filter program to run; the
+special program &quot;-&quot; can be used to make two file types equivalent. The
+program must accept the standard filter arguments and environment
+variables described in the CUPS Interface Design Document: </P>
+<UL>
+<PRE>
+program job user title options [filename]
+</PRE>
+</UL>
+<P>If specified, the <I>filename</I> argument defines a file to read
+when filtering, otherwise the filter must read from the standard input.
+All filtered output must go to the standard output. </P>
+<H1 ALIGN=RIGHT><A NAME=6>5 - Printer Accounting</A></H1>
+ This chapter describes the CUPS log files.
+<H2><A NAME=6_1>Where to Find the Log Files</A></H2>
+<P>The log files are normally stored in the <CODE>/var/cups/logs</CODE>
+ directory. You can change this by editing the <CODE>
+/var/cups/conf/cupsd.conf</CODE> configuration file. </P>
+<H2><A NAME=6_2>The access_log File</A></H2>
+<P>The <CODE>access_log</CODE> file lists each HTTP resource that is
+accessed by a web browser or CUPS/IPP client. Each line is in the
+so-called &quot;Common Log Format&quot; used by many web servers and web
+reporting tools: </P>
+<UL>
+<PRE>
+host group user date-time \&quot;method resource version\&quot; status bytes
+
+127.0.0.1 - - [20/May/1999:19:20:29 +0000] &quot;POST /admin/ HTTP/1.1&quot; 401 0
+127.0.0.1 - mike [20/May/1999:19:20:31 +0000] &quot;POST /admin/ HTTP/1.1&quot; 200 0
+</PRE>
+</UL>
+<P>The <I>host</I> field will normally only be an IP address unless you
+have changed the <CODE>HostnameLookups</CODE> directive on in the <CODE>
+cupsd.conf</CODE> file. </P>
+<P>The <I>group</I> field always contains &quot;-&quot;. </P>
+<P>The <I>user</I> field is the authenticated username of the
+requesting user. If no username and password is supplied for the
+request then this field contains &quot;-&quot;. </P>
+<P>The <I>date-time</I> field is the date and time of the request in
+Greenwich Mean Time (a.k.a. ZULU) and is in the format: </P>
+<UL>
+<PRE>
+[DD/MON/YYYY:HH:MM:SS +0000]
+</PRE>
+</UL>
+<P>The <I>method</I> field is the HTTP method used (&quot;GET&quot;, &quot;PUT&quot;,
+&quot;POST&quot;, etc.) </P>
+<P>The <I>resource</I> field is the filename of the requested resource. </P>
+<P>The <I>version</I> field is the HTTP specification version used by
+the client. For CUPS clients this will always be &quot;HTTP/1.1&quot;. </P>
+<P>The <I>status</I> field contains the HTTP result status of the
+request. Usually it is &quot;200&quot;, but other HTTP status codes are possible.
+For example, 401 is the &quot;unauthorized access&quot; status in the example
+above. </P>
+<P>The <I>bytes</I> field contains the number of bytes in the request.
+For POST requests the <I>bytes</I> field contains the number of bytes
+of non-IPP data that is received from the client. </P>
+<H2><A NAME=6_3>The error_log File</A></H2>
+<P>The <CODE>error_log</CODE> file lists messages from the scheduler
+(errors, warnings, etc.): </P>
+<UL>
+<PRE>
+level date-time message
+
+I [20/May/1999:19:18:28 +0000] Job 1 queued on 'DeskJet' by 'mike'.
+I [20/May/1999:19:21:02 +0000] Job 2 queued on 'DeskJet' by 'mike'.
+I [20/May/1999:19:22:24 +0000] Job 2 was cancelled by 'mike'.
+</PRE>
+</UL>
+<P>The <I>level</I> field contains the type of message: </P>
+<UL>
+<LI><CODE>E</CODE> - An error occurred. </LI>
+<LI><CODE>W</CODE> - The server was unable to perform some action. </LI>
+<LI><CODE>I</CODE> - Informational message. </LI>
+<LI><CODE>D</CODE> - Debugging message. </LI>
+</UL>
+<P>The <I>date-time</I> field contains the date and time of when the
+page started printing. The format of this field is identical to the <I>
+data-time</I> field in the <CODE>access_log</CODE> file. </P>
+<P>The <I>message</I> fields contains a free-form textual message. </P>
+<H2><A NAME=6_4>The page_log File</A></H2>
+<P>The <CODE>page_log</CODE> file lists each page that is sent to a
+printer. Each line contains the following information: </P>
+<UL>
+<PRE>
+printer user job-id date-time page-number num-copies
+
+DeskJet root 2 [20/May/1999:19:21:05 +0000] 1 0
+</PRE>
+</UL>
+<P>The <I>printer</I> field contains the name of the printer that
+printed the page. If you send a job to a printer class, this field will
+contain the name of the printer that was assigned the job. </P>
+<P>The <I>user</I> field contains the name of the user (the IPP <CODE>
+requesting-user-name</CODE> attribute) that submitted this file for
+printing. </P>
+<P>The <I>job-id</I> field contains the job number of the page being
+printed. Job numbers are reset to 1 whenever the CUPS server is
+started, so don't depend on this number being unique! </P>
+<P>The <I>date-time</I> field contains the date and time of when the
+page started printing. The format of this field is identical to the <I>
+data-time</I> field in the <CODE>access_log</CODE> file. </P>
+<P>The <I>page-number</I> and <I>num-pages</I> fields contain the page
+number and number of copies being printed of that page. For printer
+that can not produce copies on their own, the <I>num-pages</I> field
+will always be 1. </P>
+</BODY>
+</HTML>
diff --git a/doc/sam.pdf b/doc/sam.pdf
new file mode 100644
index 000000000..244562c0d
--- /dev/null
+++ b/doc/sam.pdf
@@ -0,0 +1,1114 @@
+%PDF-1.2
+%âãÏÓ
+1 0 obj<</Producer(htmldoc 2.0b1 Copyright 1997-1999 Michael Sweet, All Rights Reserved.)/CreationDate(D:19990730151724Z)/Title(DRAFT - CUPS Software Administrators Manual)/Author(Easy Software Products)>>endobj
+2 0 obj<</Type/Encoding/BaseEncoding/WinAnsiEncoding>>endobj
+3 0 obj<</Type/Font/Subtype/Type1/BaseFont/Courier/Encoding 2 0 R>>endobj
+4 0 obj<</Type/Font/Subtype/Type1/BaseFont/Courier-Oblique/Encoding 2 0 R>>endobj
+5 0 obj<</Type/Font/Subtype/Type1/BaseFont/Times-Roman/Encoding 2 0 R>>endobj
+6 0 obj<</Type/Font/Subtype/Type1/BaseFont/Times-Bold/Encoding 2 0 R>>endobj
+7 0 obj<</Type/Font/Subtype/Type1/BaseFont/Times-Italic/Encoding 2 0 R>>endobj
+8 0 obj<</Type/Font/Subtype/Type1/BaseFont/Helvetica/Encoding 2 0 R>>endobj
+9 0 obj<</Type/Font/Subtype/Type1/BaseFont/Helvetica-Bold/Encoding 2 0 R>>endobj
+10 0 obj<</Type/Font/Subtype/Type1/BaseFont/Symbol>>endobj
+11 0 obj<</Subtype/Link/Rect[395.9 354.4 440.2 367.4]/Border[0 0 0]/Dest[238 0 R/XYZ null 713 0]>>endobj
+12 0 obj<</Subtype/Link/Rect[440.2 354.4 447.8 367.4]/Border[0 0 0]/Dest[238 0 R/XYZ null 713 0]>>endobj
+13 0 obj<</Subtype/Link/Rect[447.8 354.4 480.5 367.4]/Border[0 0 0]/Dest[238 0 R/XYZ null 713 0]>>endobj
+14 0 obj<</Subtype/Link/Rect[480.5 354.4 533.7 367.4]/Border[0 0 0]/Dest[238 0 R/XYZ null 713 0]>>endobj
+15 0 obj<</S/URI/URI(ftp://ftp.easysw.com/pub/libraries)>>endobj
+16 0 obj<</Subtype/Link/Rect[108.0 123.5 291.6 134.5]/Border[0 0 0]/A 15 0 R>>endobj
+17 0 obj[11 0 R
+12 0 R
+13 0 R
+14 0 R
+16 0 R
+]endobj
+18 0 obj<</S/URI/URI(ftp://ftp.gnu.org/pub/groff)>>endobj
+19 0 obj<</Subtype/Link/Rect[72.0 723.0 217.8 732.0]/Border[0 0 0]/A 18 0 R>>endobj
+20 0 obj<</S/URI/URI(http://www.easysw.com/htmldoc)>>endobj
+21 0 obj<</Subtype/Link/Rect[72.0 646.0 228.6 655.0]/Border[0 0 0]/A 20 0 R>>endobj
+22 0 obj[19 0 R
+21 0 R
+]endobj
+23 0 obj<</Subtype/Link/Rect[72.0 673.2 107.4 686.2]/Border[0 0 0]/Dest[214 0 R/XYZ null 818 0]>>endobj
+24 0 obj<</Subtype/Link/Rect[108.0 660.0 143.1 673.0]/Border[0 0 0]/Dest[214 0 R/XYZ null 416 0]>>endobj
+25 0 obj<</Subtype/Link/Rect[143.1 660.0 186.5 673.0]/Border[0 0 0]/Dest[214 0 R/XYZ null 416 0]>>endobj
+26 0 obj<</Subtype/Link/Rect[108.0 646.8 156.6 659.8]/Border[0 0 0]/Dest[217 0 R/XYZ null 800 0]>>endobj
+27 0 obj<</Subtype/Link/Rect[156.6 646.8 199.9 659.8]/Border[0 0 0]/Dest[217 0 R/XYZ null 800 0]>>endobj
+28 0 obj<</Subtype/Link/Rect[72.0 620.4 80.2 633.4]/Border[0 0 0]/Dest[220 0 R/XYZ null 818 0]>>endobj
+29 0 obj<</Subtype/Link/Rect[80.2 620.4 89.3 633.4]/Border[0 0 0]/Dest[220 0 R/XYZ null 818 0]>>endobj
+30 0 obj<</Subtype/Link/Rect[89.3 620.4 131.1 633.4]/Border[0 0 0]/Dest[220 0 R/XYZ null 818 0]>>endobj
+31 0 obj<</Subtype/Link/Rect[131.1 620.4 167.5 633.4]/Border[0 0 0]/Dest[220 0 R/XYZ null 818 0]>>endobj
+32 0 obj<</Subtype/Link/Rect[167.5 620.4 212.7 633.4]/Border[0 0 0]/Dest[220 0 R/XYZ null 818 0]>>endobj
+33 0 obj<</Subtype/Link/Rect[108.0 607.2 127.9 620.2]/Border[0 0 0]/Dest[220 0 R/XYZ null 416 0]>>endobj
+34 0 obj<</Subtype/Link/Rect[127.9 607.2 166.1 620.2]/Border[0 0 0]/Dest[220 0 R/XYZ null 416 0]>>endobj
+35 0 obj<</Subtype/Link/Rect[166.1 607.2 203.3 620.2]/Border[0 0 0]/Dest[220 0 R/XYZ null 416 0]>>endobj
+36 0 obj<</Subtype/Link/Rect[108.0 594.0 127.9 607.0]/Border[0 0 0]/Dest[223 0 R/XYZ null 800 0]>>endobj
+37 0 obj<</Subtype/Link/Rect[127.9 594.0 180.4 607.0]/Border[0 0 0]/Dest[223 0 R/XYZ null 800 0]>>endobj
+38 0 obj<</Subtype/Link/Rect[108.0 580.8 127.6 593.8]/Border[0 0 0]/Dest[223 0 R/XYZ null 522 0]>>endobj
+39 0 obj<</Subtype/Link/Rect[108.0 567.6 141.0 580.6]/Border[0 0 0]/Dest[223 0 R/XYZ null 417 0]>>endobj
+40 0 obj<</Subtype/Link/Rect[108.0 554.4 136.1 567.4]/Border[0 0 0]/Dest[223 0 R/XYZ null 325 0]>>endobj
+41 0 obj<</Subtype/Link/Rect[108.0 541.2 140.7 554.2]/Border[0 0 0]/Dest[226 0 R/XYZ null 800 0]>>endobj
+42 0 obj<</Subtype/Link/Rect[140.7 541.2 173.7 554.2]/Border[0 0 0]/Dest[226 0 R/XYZ null 800 0]>>endobj
+43 0 obj<</Subtype/Link/Rect[108.0 528.0 160.5 541.0]/Border[0 0 0]/Dest[226 0 R/XYZ null 681 0]>>endobj
+44 0 obj<</Subtype/Link/Rect[72.0 501.6 80.2 514.6]/Border[0 0 0]/Dest[232 0 R/XYZ null 818 0]>>endobj
+45 0 obj<</Subtype/Link/Rect[80.2 501.6 89.3 514.6]/Border[0 0 0]/Dest[232 0 R/XYZ null 818 0]>>endobj
+46 0 obj<</Subtype/Link/Rect[89.3 501.6 132.4 514.6]/Border[0 0 0]/Dest[232 0 R/XYZ null 818 0]>>endobj
+47 0 obj<</Subtype/Link/Rect[132.4 501.6 152.9 514.6]/Border[0 0 0]/Dest[232 0 R/XYZ null 818 0]>>endobj
+48 0 obj<</Subtype/Link/Rect[152.9 501.6 200.2 514.6]/Border[0 0 0]/Dest[232 0 R/XYZ null 818 0]>>endobj
+49 0 obj<</Subtype/Link/Rect[200.2 501.6 229.0 514.6]/Border[0 0 0]/Dest[232 0 R/XYZ null 818 0]>>endobj
+50 0 obj<</Subtype/Link/Rect[108.0 488.4 152.3 501.4]/Border[0 0 0]/Dest[232 0 R/XYZ null 403 0]>>endobj
+51 0 obj<</Subtype/Link/Rect[152.3 488.4 159.9 501.4]/Border[0 0 0]/Dest[232 0 R/XYZ null 403 0]>>endobj
+52 0 obj<</Subtype/Link/Rect[159.9 488.4 193.2 501.4]/Border[0 0 0]/Dest[232 0 R/XYZ null 403 0]>>endobj
+53 0 obj<</Subtype/Link/Rect[193.2 488.4 246.4 501.4]/Border[0 0 0]/Dest[232 0 R/XYZ null 403 0]>>endobj
+54 0 obj<</Subtype/Link/Rect[144.0 475.2 205.1 488.2]/Border[0 0 0]/Dest[232 0 R/XYZ null 333 0]>>endobj
+55 0 obj<</Subtype/Link/Rect[144.0 462.0 193.8 475.0]/Border[0 0 0]/Dest[235 0 R/XYZ null 678 0]>>endobj
+56 0 obj<</Subtype/Link/Rect[193.8 462.0 221.3 475.0]/Border[0 0 0]/Dest[235 0 R/XYZ null 678 0]>>endobj
+57 0 obj<</Subtype/Link/Rect[144.0 448.8 188.3 461.8]/Border[0 0 0]/Dest[235 0 R/XYZ null 262 0]>>endobj
+58 0 obj<</Subtype/Link/Rect[188.3 448.8 204.5 461.8]/Border[0 0 0]/Dest[235 0 R/XYZ null 262 0]>>endobj
+59 0 obj<</Subtype/Link/Rect[204.5 448.8 244.2 461.8]/Border[0 0 0]/Dest[235 0 R/XYZ null 262 0]>>endobj
+60 0 obj<</Subtype/Link/Rect[144.0 435.6 184.6 448.6]/Border[0 0 0]/Dest[238 0 R/XYZ null 782 0]>>endobj
+61 0 obj<</Subtype/Link/Rect[184.6 435.6 200.8 448.6]/Border[0 0 0]/Dest[238 0 R/XYZ null 782 0]>>endobj
+62 0 obj<</Subtype/Link/Rect[200.8 435.6 240.5 448.6]/Border[0 0 0]/Dest[238 0 R/XYZ null 782 0]>>endobj
+63 0 obj<</Subtype/Link/Rect[108.0 422.4 152.3 435.4]/Border[0 0 0]/Dest[238 0 R/XYZ null 713 0]>>endobj
+64 0 obj<</Subtype/Link/Rect[152.3 422.4 159.9 435.4]/Border[0 0 0]/Dest[238 0 R/XYZ null 713 0]>>endobj
+65 0 obj<</Subtype/Link/Rect[159.9 422.4 192.6 435.4]/Border[0 0 0]/Dest[238 0 R/XYZ null 713 0]>>endobj
+66 0 obj<</Subtype/Link/Rect[192.6 422.4 245.8 435.4]/Border[0 0 0]/Dest[238 0 R/XYZ null 713 0]>>endobj
+67 0 obj<</Subtype/Link/Rect[72.0 396.0 80.2 409.0]/Border[0 0 0]/Dest[244 0 R/XYZ null 818 0]>>endobj
+68 0 obj<</Subtype/Link/Rect[80.2 396.0 89.3 409.0]/Border[0 0 0]/Dest[244 0 R/XYZ null 818 0]>>endobj
+69 0 obj<</Subtype/Link/Rect[89.3 396.0 126.2 409.0]/Border[0 0 0]/Dest[244 0 R/XYZ null 818 0]>>endobj
+70 0 obj<</Subtype/Link/Rect[126.2 396.0 159.5 409.0]/Border[0 0 0]/Dest[244 0 R/XYZ null 818 0]>>endobj
+71 0 obj<</Subtype/Link/Rect[159.5 396.0 221.2 409.0]/Border[0 0 0]/Dest[244 0 R/XYZ null 818 0]>>endobj
+72 0 obj<</Subtype/Link/Rect[108.0 382.8 127.9 395.8]/Border[0 0 0]/Dest[244 0 R/XYZ null 416 0]>>endobj
+73 0 obj<</Subtype/Link/Rect[127.9 382.8 166.7 395.8]/Border[0 0 0]/Dest[244 0 R/XYZ null 416 0]>>endobj
+74 0 obj<</Subtype/Link/Rect[166.7 382.8 212.5 395.8]/Border[0 0 0]/Dest[244 0 R/XYZ null 416 0]>>endobj
+75 0 obj<</Subtype/Link/Rect[108.0 369.6 143.8 382.6]/Border[0 0 0]/Dest[244 0 R/XYZ null 311 0]>>endobj
+76 0 obj<</Subtype/Link/Rect[143.8 369.6 162.4 382.6]/Border[0 0 0]/Dest[244 0 R/XYZ null 311 0]>>endobj
+77 0 obj<</Subtype/Link/Rect[162.4 369.6 212.2 382.6]/Border[0 0 0]/Dest[244 0 R/XYZ null 311 0]>>endobj
+78 0 obj<</Subtype/Link/Rect[212.2 369.6 246.4 382.6]/Border[0 0 0]/Dest[244 0 R/XYZ null 311 0]>>endobj
+79 0 obj<</Subtype/Link/Rect[144.0 356.4 173.0 369.4]/Border[0 0 0]/Dest[247 0 R/XYZ null 172 0]>>endobj
+80 0 obj<</Subtype/Link/Rect[173.0 356.4 214.9 369.4]/Border[0 0 0]/Dest[247 0 R/XYZ null 172 0]>>endobj
+81 0 obj<</Subtype/Link/Rect[214.9 356.4 247.6 369.4]/Border[0 0 0]/Dest[247 0 R/XYZ null 172 0]>>endobj
+82 0 obj<</Subtype/Link/Rect[247.6 356.4 280.6 369.4]/Border[0 0 0]/Dest[247 0 R/XYZ null 172 0]>>endobj
+83 0 obj<</Subtype/Link/Rect[108.0 343.2 156.6 356.2]/Border[0 0 0]/Dest[250 0 R/XYZ null 666 0]>>endobj
+84 0 obj<</Subtype/Link/Rect[156.6 343.2 190.8 356.2]/Border[0 0 0]/Dest[250 0 R/XYZ null 666 0]>>endobj
+85 0 obj<</Subtype/Link/Rect[108.0 330.0 140.7 343.0]/Border[0 0 0]/Dest[250 0 R/XYZ null 563 0]>>endobj
+86 0 obj<</Subtype/Link/Rect[140.7 330.0 173.7 343.0]/Border[0 0 0]/Dest[250 0 R/XYZ null 563 0]>>endobj
+87 0 obj<</Subtype/Link/Rect[108.0 316.8 141.9 329.8]/Border[0 0 0]/Dest[250 0 R/XYZ null 356 0]>>endobj
+88 0 obj<</Subtype/Link/Rect[141.9 316.8 158.1 329.8]/Border[0 0 0]/Dest[250 0 R/XYZ null 356 0]>>endobj
+89 0 obj<</Subtype/Link/Rect[158.1 316.8 193.9 329.8]/Border[0 0 0]/Dest[250 0 R/XYZ null 356 0]>>endobj
+90 0 obj<</Subtype/Link/Rect[193.9 316.8 223.8 329.8]/Border[0 0 0]/Dest[250 0 R/XYZ null 356 0]>>endobj
+91 0 obj<</Subtype/Link/Rect[108.0 303.6 145.6 316.6]/Border[0 0 0]/Dest[250 0 R/XYZ null 213 0]>>endobj
+92 0 obj<</Subtype/Link/Rect[145.6 303.6 164.2 316.6]/Border[0 0 0]/Dest[250 0 R/XYZ null 213 0]>>endobj
+93 0 obj<</Subtype/Link/Rect[164.2 303.6 206.7 316.6]/Border[0 0 0]/Dest[250 0 R/XYZ null 213 0]>>endobj
+94 0 obj<</Subtype/Link/Rect[206.7 303.6 240.9 316.6]/Border[0 0 0]/Dest[250 0 R/XYZ null 213 0]>>endobj
+95 0 obj<</Subtype/Link/Rect[108.0 290.4 156.0 303.4]/Border[0 0 0]/Dest[253 0 R/XYZ null 703 0]>>endobj
+96 0 obj<</Subtype/Link/Rect[156.0 290.4 174.6 303.4]/Border[0 0 0]/Dest[253 0 R/XYZ null 703 0]>>endobj
+97 0 obj<</Subtype/Link/Rect[174.6 290.4 219.5 303.4]/Border[0 0 0]/Dest[253 0 R/XYZ null 703 0]>>endobj
+98 0 obj<</Subtype/Link/Rect[219.5 290.4 243.7 303.4]/Border[0 0 0]/Dest[253 0 R/XYZ null 703 0]>>endobj
+99 0 obj<</Subtype/Link/Rect[243.7 290.4 263.2 303.4]/Border[0 0 0]/Dest[253 0 R/XYZ null 703 0]>>endobj
+100 0 obj<</Subtype/Link/Rect[72.0 264.0 80.2 277.0]/Border[0 0 0]/Dest[256 0 R/XYZ null 818 0]>>endobj
+101 0 obj<</Subtype/Link/Rect[80.2 264.0 89.3 277.0]/Border[0 0 0]/Dest[256 0 R/XYZ null 818 0]>>endobj
+102 0 obj<</Subtype/Link/Rect[89.3 264.0 131.1 277.0]/Border[0 0 0]/Dest[256 0 R/XYZ null 818 0]>>endobj
+103 0 obj<</Subtype/Link/Rect[131.1 264.0 167.5 277.0]/Border[0 0 0]/Dest[256 0 R/XYZ null 818 0]>>endobj
+104 0 obj<</Subtype/Link/Rect[167.5 264.0 229.2 277.0]/Border[0 0 0]/Dest[256 0 R/XYZ null 818 0]>>endobj
+105 0 obj<</Subtype/Link/Rect[108.0 250.8 153.5 263.8]/Border[0 0 0]/Dest[256 0 R/XYZ null 416 0]>>endobj
+106 0 obj<</Subtype/Link/Rect[153.5 250.8 169.7 263.8]/Border[0 0 0]/Dest[256 0 R/XYZ null 416 0]>>endobj
+107 0 obj<</Subtype/Link/Rect[169.7 250.8 234.2 263.8]/Border[0 0 0]/Dest[256 0 R/XYZ null 416 0]>>endobj
+108 0 obj<</Subtype/Link/Rect[234.2 250.8 255.6 263.8]/Border[0 0 0]/Dest[256 0 R/XYZ null 416 0]>>endobj
+109 0 obj<</Subtype/Link/Rect[108.0 237.6 159.6 250.6]/Border[0 0 0]/Dest[256 0 R/XYZ null 234 0]>>endobj
+110 0 obj<</Subtype/Link/Rect[159.6 237.6 181.0 250.6]/Border[0 0 0]/Dest[256 0 R/XYZ null 234 0]>>endobj
+111 0 obj<</Subtype/Link/Rect[108.0 224.4 149.2 237.4]/Border[0 0 0]/Dest[259 0 R/XYZ null 631 0]>>endobj
+112 0 obj<</Subtype/Link/Rect[149.2 224.4 211.0 237.4]/Border[0 0 0]/Dest[259 0 R/XYZ null 631 0]>>endobj
+113 0 obj<</Subtype/Link/Rect[144.0 211.2 162.3 224.2]/Border[0 0 0]/Dest[259 0 R/XYZ null 484 0]>>endobj
+114 0 obj<</Subtype/Link/Rect[144.0 198.0 171.5 211.0]/Border[0 0 0]/Dest[259 0 R/XYZ null 386 0]>>endobj
+115 0 obj<</Subtype/Link/Rect[144.0 184.8 195.9 197.8]/Border[0 0 0]/Dest[259 0 R/XYZ null 197 0]>>endobj
+116 0 obj<</Subtype/Link/Rect[144.0 171.6 213.7 184.6]/Border[0 0 0]/Dest[262 0 R/XYZ null 726 0]>>endobj
+117 0 obj<</Subtype/Link/Rect[108.0 158.4 140.7 171.4]/Border[0 0 0]/Dest[262 0 R/XYZ null 576 0]>>endobj
+118 0 obj<</Subtype/Link/Rect[140.7 158.4 177.4 171.4]/Border[0 0 0]/Dest[262 0 R/XYZ null 576 0]>>endobj
+119 0 obj<</Subtype/Link/Rect[144.0 145.2 183.1 158.2]/Border[0 0 0]/Dest[262 0 R/XYZ null 482 0]>>endobj
+120 0 obj<</Subtype/Link/Rect[144.0 132.0 169.7 145.0]/Border[0 0 0]/Dest[265 0 R/XYZ null 782 0]>>endobj
+121 0 obj<</Subtype/Link/Rect[144.0 118.8 171.5 131.8]/Border[0 0 0]/Dest[265 0 R/XYZ null 617 0]>>endobj
+122 0 obj<</Subtype/Link/Rect[144.0 105.6 167.8 118.6]/Border[0 0 0]/Dest[268 0 R/XYZ null 782 0]>>endobj
+123 0 obj<</Subtype/Link/Rect[144.0 92.4 188.6 105.4]/Border[0 0 0]/Dest[268 0 R/XYZ null 274 0]>>endobj
+124 0 obj<</Subtype/Link/Rect[144.0 79.2 189.8 92.2]/Border[0 0 0]/Dest[271 0 R/XYZ null 782 0]>>endobj
+125 0 obj<</Subtype/Link/Rect[144.0 66.0 220.4 79.0]/Border[0 0 0]/Dest[271 0 R/XYZ null 577 0]>>endobj
+126 0 obj[23 0 R
+24 0 R
+25 0 R
+26 0 R
+27 0 R
+28 0 R
+29 0 R
+30 0 R
+31 0 R
+32 0 R
+33 0 R
+34 0 R
+35 0 R
+36 0 R
+37 0 R
+38 0 R
+39 0 R
+40 0 R
+41 0 R
+42 0 R
+43 0 R
+44 0 R
+45 0 R
+46 0 R
+47 0 R
+48 0 R
+49 0 R
+50 0 R
+51 0 R
+52 0 R
+53 0 R
+54 0 R
+55 0 R
+56 0 R
+57 0 R
+58 0 R
+59 0 R
+60 0 R
+61 0 R
+62 0 R
+63 0 R
+64 0 R
+65 0 R
+66 0 R
+67 0 R
+68 0 R
+69 0 R
+70 0 R
+71 0 R
+72 0 R
+73 0 R
+74 0 R
+75 0 R
+76 0 R
+77 0 R
+78 0 R
+79 0 R
+80 0 R
+81 0 R
+82 0 R
+83 0 R
+84 0 R
+85 0 R
+86 0 R
+87 0 R
+88 0 R
+89 0 R
+90 0 R
+91 0 R
+92 0 R
+93 0 R
+94 0 R
+95 0 R
+96 0 R
+97 0 R
+98 0 R
+99 0 R
+100 0 R
+101 0 R
+102 0 R
+103 0 R
+104 0 R
+105 0 R
+106 0 R
+107 0 R
+108 0 R
+109 0 R
+110 0 R
+111 0 R
+112 0 R
+113 0 R
+114 0 R
+115 0 R
+116 0 R
+117 0 R
+118 0 R
+119 0 R
+120 0 R
+121 0 R
+122 0 R
+123 0 R
+124 0 R
+125 0 R
+]endobj
+127 0 obj<</Subtype/Link/Rect[108.0 673.2 168.5 686.2]/Border[0 0 0]/Dest[271 0 R/XYZ null 479 0]>>endobj
+128 0 obj<</Subtype/Link/Rect[72.0 660.0 91.9 673.0]/Border[0 0 0]/Dest[271 0 R/XYZ null 408 0]>>endobj
+129 0 obj<</Subtype/Link/Rect[91.9 660.0 127.9 673.0]/Border[0 0 0]/Dest[271 0 R/XYZ null 408 0]>>endobj
+130 0 obj<</Subtype/Link/Rect[108.0 646.8 159.0 659.8]/Border[0 0 0]/Dest[271 0 R/XYZ null 288 0]>>endobj
+131 0 obj<</Subtype/Link/Rect[108.0 633.6 161.5 646.6]/Border[0 0 0]/Dest[274 0 R/XYZ null 488 0]>>endobj
+132 0 obj<</Subtype/Link/Rect[36.0 607.2 44.2 620.2]/Border[0 0 0]/Dest[280 0 R/XYZ null 818 0]>>endobj
+133 0 obj<</Subtype/Link/Rect[44.2 607.2 53.3 620.2]/Border[0 0 0]/Dest[280 0 R/XYZ null 818 0]>>endobj
+134 0 obj<</Subtype/Link/Rect[53.3 607.2 90.2 620.2]/Border[0 0 0]/Dest[280 0 R/XYZ null 818 0]>>endobj
+135 0 obj<</Subtype/Link/Rect[90.2 607.2 144.0 620.2]/Border[0 0 0]/Dest[280 0 R/XYZ null 818 0]>>endobj
+136 0 obj<</Subtype/Link/Rect[72.0 594.0 104.1 607.0]/Border[0 0 0]/Dest[280 0 R/XYZ null 429 0]>>endobj
+137 0 obj<</Subtype/Link/Rect[104.1 594.0 115.4 607.0]/Border[0 0 0]/Dest[280 0 R/XYZ null 429 0]>>endobj
+138 0 obj<</Subtype/Link/Rect[115.4 594.0 138.3 607.0]/Border[0 0 0]/Dest[280 0 R/XYZ null 429 0]>>endobj
+139 0 obj<</Subtype/Link/Rect[138.3 594.0 154.5 607.0]/Border[0 0 0]/Dest[280 0 R/XYZ null 429 0]>>endobj
+140 0 obj<</Subtype/Link/Rect[154.5 594.0 175.0 607.0]/Border[0 0 0]/Dest[280 0 R/XYZ null 429 0]>>endobj
+141 0 obj<</Subtype/Link/Rect[175.0 594.0 196.4 607.0]/Border[0 0 0]/Dest[280 0 R/XYZ null 429 0]>>endobj
+142 0 obj<</Subtype/Link/Rect[72.0 580.8 91.9 593.8]/Border[0 0 0]/Dest[280 0 R/XYZ null 337 0]>>endobj
+143 0 obj<</Subtype/Link/Rect[91.9 580.8 142.3 593.8]/Border[0 0 0]/Dest[280 0 R/XYZ null 337 0]>>endobj
+144 0 obj<</Subtype/Link/Rect[142.3 580.8 159.4 593.8]/Border[0 0 0]/Dest[280 0 R/XYZ null 337 0]>>endobj
+145 0 obj<</Subtype/Link/Rect[72.0 567.6 91.9 580.6]/Border[0 0 0]/Dest[283 0 R/XYZ null 505 0]>>endobj
+146 0 obj<</Subtype/Link/Rect[91.9 567.6 135.5 580.6]/Border[0 0 0]/Dest[283 0 R/XYZ null 505 0]>>endobj
+147 0 obj<</Subtype/Link/Rect[135.5 567.6 152.7 580.6]/Border[0 0 0]/Dest[283 0 R/XYZ null 505 0]>>endobj
+148 0 obj<</Subtype/Link/Rect[72.0 554.4 91.9 567.4]/Border[0 0 0]/Dest[286 0 R/XYZ null 800 0]>>endobj
+149 0 obj<</Subtype/Link/Rect[91.9 554.4 134.9 567.4]/Border[0 0 0]/Dest[286 0 R/XYZ null 800 0]>>endobj
+150 0 obj<</Subtype/Link/Rect[134.9 554.4 152.0 567.4]/Border[0 0 0]/Dest[286 0 R/XYZ null 800 0]>>endobj
+151 0 obj[127 0 R
+128 0 R
+129 0 R
+130 0 R
+131 0 R
+132 0 R
+133 0 R
+134 0 R
+135 0 R
+136 0 R
+137 0 R
+138 0 R
+139 0 R
+140 0 R
+141 0 R
+142 0 R
+143 0 R
+144 0 R
+145 0 R
+146 0 R
+147 0 R
+148 0 R
+149 0 R
+150 0 R
+]endobj
+152 0 obj<</Dests 153 0 R>>endobj
+153 0 obj<</Kids[154 0 R]>>endobj
+154 0 obj<</Limits[(1)(binary)]/Names[(1)155 0 R(1_1)156 0 R(1_2)157 0 R(2)158 0 R(2_1)159 0 R(2_2)160 0 R(2_3)161 0 R(2_4)162 0 R(2_5)163 0 R(2_6)164 0 R(2_7)165 0 R(3)166 0 R(3_1)167 0 R(3_1_1)168 0 R(3_1_2)169 0 R(3_1_3)170 0 R(3_1_4)171 0 R(4)172 0 R(4_1)173 0 R(4_2)174 0 R(4_2_1)175 0 R(4_3)176 0 R(4_4)177 0 R(4_5)178 0 R(4_6)179 0 R(4_7)180 0 R(5)181 0 R(5_1)182 0 R(5_2)183 0 R(5_3)184 0 R(5_3_1)185 0 R(5_3_2)186 0 R(5_3_3)187 0 R(5_3_4)188 0 R(5_4)189 0 R(5_4_1)190 0 R(5_4_2)191 0 R(5_4_3)192 0 R(5_4_4)193 0 R(5_4_5)194 0 R(5_4_6)195 0 R(5_4_7)196 0 R(5_4_8)197 0 R(5_5)198 0 R(5_5_1)199 0 R(5_5_2)200 0 R(6)201 0 R(6_1)202 0 R(6_2)203 0 R(6_3)204 0 R(6_4)205 0 R(binary)206 0 R]>>endobj
+155 0 obj<</D[214 0 R/XYZ null 818 null]>>endobj
+156 0 obj<</D[214 0 R/XYZ null 416 null]>>endobj
+157 0 obj<</D[217 0 R/XYZ null 800 null]>>endobj
+158 0 obj<</D[220 0 R/XYZ null 818 null]>>endobj
+159 0 obj<</D[220 0 R/XYZ null 416 null]>>endobj
+160 0 obj<</D[223 0 R/XYZ null 800 null]>>endobj
+161 0 obj<</D[223 0 R/XYZ null 522 null]>>endobj
+162 0 obj<</D[223 0 R/XYZ null 417 null]>>endobj
+163 0 obj<</D[223 0 R/XYZ null 325 null]>>endobj
+164 0 obj<</D[226 0 R/XYZ null 800 null]>>endobj
+165 0 obj<</D[226 0 R/XYZ null 681 null]>>endobj
+166 0 obj<</D[232 0 R/XYZ null 818 null]>>endobj
+167 0 obj<</D[232 0 R/XYZ null 403 null]>>endobj
+168 0 obj<</D[232 0 R/XYZ null 333 null]>>endobj
+169 0 obj<</D[235 0 R/XYZ null 678 null]>>endobj
+170 0 obj<</D[235 0 R/XYZ null 262 null]>>endobj
+171 0 obj<</D[238 0 R/XYZ null 782 null]>>endobj
+172 0 obj<</D[244 0 R/XYZ null 818 null]>>endobj
+173 0 obj<</D[244 0 R/XYZ null 416 null]>>endobj
+174 0 obj<</D[244 0 R/XYZ null 311 null]>>endobj
+175 0 obj<</D[247 0 R/XYZ null 172 null]>>endobj
+176 0 obj<</D[250 0 R/XYZ null 666 null]>>endobj
+177 0 obj<</D[250 0 R/XYZ null 563 null]>>endobj
+178 0 obj<</D[250 0 R/XYZ null 356 null]>>endobj
+179 0 obj<</D[250 0 R/XYZ null 213 null]>>endobj
+180 0 obj<</D[253 0 R/XYZ null 703 null]>>endobj
+181 0 obj<</D[256 0 R/XYZ null 818 null]>>endobj
+182 0 obj<</D[256 0 R/XYZ null 416 null]>>endobj
+183 0 obj<</D[256 0 R/XYZ null 234 null]>>endobj
+184 0 obj<</D[259 0 R/XYZ null 631 null]>>endobj
+185 0 obj<</D[259 0 R/XYZ null 484 null]>>endobj
+186 0 obj<</D[259 0 R/XYZ null 386 null]>>endobj
+187 0 obj<</D[259 0 R/XYZ null 197 null]>>endobj
+188 0 obj<</D[262 0 R/XYZ null 726 null]>>endobj
+189 0 obj<</D[262 0 R/XYZ null 576 null]>>endobj
+190 0 obj<</D[262 0 R/XYZ null 482 null]>>endobj
+191 0 obj<</D[265 0 R/XYZ null 782 null]>>endobj
+192 0 obj<</D[265 0 R/XYZ null 617 null]>>endobj
+193 0 obj<</D[268 0 R/XYZ null 782 null]>>endobj
+194 0 obj<</D[268 0 R/XYZ null 274 null]>>endobj
+195 0 obj<</D[271 0 R/XYZ null 782 null]>>endobj
+196 0 obj<</D[271 0 R/XYZ null 577 null]>>endobj
+197 0 obj<</D[271 0 R/XYZ null 479 null]>>endobj
+198 0 obj<</D[271 0 R/XYZ null 408 null]>>endobj
+199 0 obj<</D[271 0 R/XYZ null 288 null]>>endobj
+200 0 obj<</D[274 0 R/XYZ null 488 null]>>endobj
+201 0 obj<</D[280 0 R/XYZ null 818 null]>>endobj
+202 0 obj<</D[280 0 R/XYZ null 429 null]>>endobj
+203 0 obj<</D[280 0 R/XYZ null 337 null]>>endobj
+204 0 obj<</D[283 0 R/XYZ null 505 null]>>endobj
+205 0 obj<</D[286 0 R/XYZ null 800 null]>>endobj
+206 0 obj<</D[238 0 R/XYZ null 713 null]>>endobj
+207 0 obj<</Type/Pages/MediaBox[0 0 595 792]/Count 30/Kids[208 0 R
+211 0 R
+292 0 R
+295 0 R
+214 0 R
+217 0 R
+220 0 R
+223 0 R
+226 0 R
+229 0 R
+232 0 R
+235 0 R
+238 0 R
+241 0 R
+244 0 R
+247 0 R
+250 0 R
+253 0 R
+256 0 R
+259 0 R
+262 0 R
+265 0 R
+268 0 R
+271 0 R
+274 0 R
+277 0 R
+280 0 R
+283 0 R
+286 0 R
+289 0 R
+]>>endobj
+208 0 obj<</Type/Page/Parent 207 0 R/Contents 209 0 R/Resources<</ProcSet[/PDF/Text/ImageB/ImageC/ImageI]/Font<</F4 5 0 R/F8 8 0 R/F9 9 0 R>>>>>>endobj
+209 0 obj<</Length 210 0 R/Filter/FlateDecode>>stream
+xÚíßÜ8rÇ¥nõ‹Ÿ4³îwNH€ØîOÛg 4?´‰(IòäÁ¸õå6Ø»ÍÙÞ÷ߧM·~RU‘’¨¹á Ï ÕâG,~«HÕ}q%V›ÿ®Ä‡kñî½øß_Üɱ|ñWñnõ»7v|÷þý›•øÝÇ7×âúúêÍ֧ͮ·÷Ù¼ýô6ý×;±þçÕ®¼Û•÷»òqWîwåÇ]ù—ÿ|ûI|ÿúÛ—·ÿ.nÞ]½ý7±¾ºz{÷âF|zx‘?ÍÏ\fE>\/‹‹³mÙücqó0v.y¿8 =U9;¿|'—¼¾P#à7#ã’×mLåü2 ê€v3®û \üEì8×]èñÊùÚa®k.•MsgúÊnŸY溞yù!vŒ+»ð¬ÿÒ)®kÏZ™¬á’³Y.á2Ö »]f‰K^xÖ‹ÿjp®,ôº(? ÌuçuT&ñ\W^gÅ_ÆeY-ÛX›òj.zž‹`खËËÞ¹zÁbÁ°8`0,8-|ñ€q`yÞ²'®Îý–™ƒfsõŒµ‹ûàZy½—I\·Þ
+QNŒÀy•µ5®Ì%¬Vé€ñ‰*ꀊ&ê€QEx­Çr%®aµtŒOãQ#ÔxŒsÆqI±ãz£Æ#FŒÏ%£Fô×]gç‹Å«‡m¹Û|è¶Ã —îòÏ_ÕùöDgÝw—ßô/ï/ºé0躻Î[§JwhK®U7]Uìµ…õ°:ì.Bú±äæ¯Ù\ìPƒ–.Î$›r¹¸¡==7cMb&³»XéÔœ. ˜\¬@ž› Éè2ŸÇ•t£í»lÉâ
+û²Á£7 ­H=Ø_Õ0I{ç$¬\¢·¡Åϼ
+è\Ù
+å[¸<=Wzø¬Œb†ˆ¨¸B¢¶r-µ\û{Íö¿Â/Ùµ
+ÛTÁ%©fØÊè¹V{ QßC5Znó\)Õ [¹|=×þfÄéAŠD {åË5=ÖVrí㌺rŒŒê•'9–k>Ê“’« R”YªÀÙPecŽçŠƒš+aÍR[CÄY+#›a3WZ©qIÖbI‚Ó5 ÊÆ$ÇsÉG³PsMŠ´X’áZĹr@àÚ5<Ðs%¬5;œÐQ6–.8ÜFÃ%Ykv­­¬q1Ò›¹²ƒ(h¸M$®ÙE¨&M6&9…+?(½Ž 8N ʤ€&3×Aéu\§¿R”'šl,i\¥×q…œñ%Qϸ‚(®ƒÒk¸RÞFˆ‘l E"×Aé5\o±¿íùOË\’1¼Z¹öJ¯á
+y›3æáI6–T®½Ò«¹2æ.F‚qÌ@ZÛÈ©\{¥WsÞ Ì"©2WÄ^í\;¥Ws fæŽÄ8f DQkgÝ‘~^¹`b›À­K\¬Mårl¥àÚµ^¨¸öv/!‡@4(QT¬~tqÑ@ü2שU.±×dA7Ä"à
+ñ\Ç+Ÿn៕áqE™«ð‰‚ûŠö
+\aý1ZæÚ¬.w®ÒÄ ‡kRçz ÿ,<Ú= Ür€æZª¹|Wõ8Š«vð(ç[rÌÇ5käÚýë^x¥ÌÊŽ%VqÍ•®¨".*:™ÇÍ6@…óv®Ã‹ð§µ]‡[¯t< ØÄ)‡«vQZr
+C*M¬çCpÕ/
+•ŽÅ5ÇpI¯¦ÈóÅq> Wí"-W£
+m8|Eã=¯TçÃpÕ.*V?ÝŵΠç_sekÇù0\µ‹´\À£ruÇhVUkM<ˆ6«z‘WŽæ
+T¿õcM«ÇùP\Õ‹´\‘E®µºcu«ÇùP\Õ‹´\+~x˜« N³¼_kbõ8Š«zQXIS=r k\~¬á*„ª¥&VŽóá¸*ñ¹&H.YøíÁ/–=æê&VŽóá¸*‰‚b—¸B;\…üÃb¼±_öÔM¬çÃqU.ªÅQ –+Èqó”\ÅUx?”¢‰;Ѧr•/ZU#¢èÀåuË•6q•ó!¹g
+Ê–Ò Wö¸ñtV¬[|uíi·^Y—Á5)ݺ®ä¸Q(¯
+7-Hà)»BY—Ï•vÉv¬W'ïRpY§äsM]Õ2¿ú®åí¤K.QyU' ,¼YS—Æå·O7ƒxÒWX\†¥?OÜP—Æ59>Ž¨Ë$ž/ýNjq`i9I[U—Ä5=>ŽÍ“ úáJN?S` œ¤­ªKâ
+ŠnfÆÕ’~xüsv¶)ó‚XÅ…3Rúº$®íù¯ãcšwÊ5ÑýaËSH*nZêÇs‰Ã Ýáeg\¡.ç°§œ`†Ô<×êðyÛO^wƵך¸qRFÚº$®è C[K;ã:ìuž¯ÕÖ.“âQεuI\pÊ~õóθŽ!z­µû/~ ¾X_WÉUOìÜQ$‡ñºu_ÝqòA*­½ÍÑëëR¸Òƒ¬¶lÆÕ´¯W¬Wz/qRÏ¢ÕÖ¥pe‡·nÙ«i6Ržã(MŽãÖº.é_ˆ>kæ2Ú_–ÚÌRQËjÑ×%påǼûmw\•,Z?®õd€¨KáÚ;æ[î’«ð¢­2…êKætu)\{Çœî||—\ak¹TªdÓƺ8®½cNwõ͸Zó¾îBÕ¨ʇ¢®‹÷Ë;7oÿï·pÙÈg»¿¨OjÍ$GU—À•ì,pç–»ç:µvZ ¥Ö¨º®½cÞ¹å>¸Ea¢4Mu \{Ǽsˆ\Øw‹¦ÍbÐT—À%½Ø¬/®Sâu;W¥.kï˜÷"ÙÈe’½}•Ì²ìŠµ\Mu)\œd§õÓN¸ÊîZ;uu)\ÉðÓ½c4äŠqë‡-\Mu)\ÛžN¼ã‹ê:ãš”lËoäÒÕ¥pÁþ0¬ßÆerîF”þ,u¾©.…+ÙŸ¬™˜s-›§_óâ<dªåjªKáJON½3®¤ØŸ«Êm*mªKáÊN³F.“sˆûký›Ó¦ÖRËÕT·%ž/½mK–w ¸æ­û°§MHýd±©.…«ð^Ø†¹Z•«¡.‰KฌÎeg U«\ u\±9× —nS ýk±Š¾.‰+ªý¦®L¯05®Œ»U¦€ãSiä2zÿFy-æeÞÈ¥¯KâÒ½ŸÕ.W¡±—y —¶.‰+=úôf.£…ì­7ºµ%&uŠá{{…òõzý|Ÿá{–œ-†ïÅzæ꟫ýuËãäŠþn¹âQrµ/p¬GÉe2a~æêŸËhb9j®Ù(¹äß-W0J®ö Øô‰rMÆÉ>ÁÀ×ðû8ž¹àZ=ÁÀ÷=>ËQrÁåJž`€hø½fC•¶—½Î ¿‡n¨ÒÖä%î{ ¤;\ÎRmR~/çP%Bq…£ 8ÚB ì÷Þºpˆ6ûB~O±k+ma› ¿WÚµ€ÃCqÁØÙêo‘ßoîXÀ‘â¸F÷}ôIk7
+„õBzôYkÈIÂ1ô
+Ç€Œ²I´~tFÙC
+®}/vG¸qNåJHfS;(š¹¤7˜ Í
+Va®ŠWvLlÌW-‚Ñ°ôñ
+W.íÚâ¥y‹ìpmlÑ^—MÖ¹;\¹¼p§³lrYê²óuîW.¯eð•­¶Øäâ§V¨.íµÄ.—Az5"u{H..™]ª.¸8dç¯l·¡ .b^¼¿èà=ÝpmÊ.;Þ?¿éäöqíz­Å Ï]Ý»K®]·]/ÎÔýtùÐå}»æÚwÜö„Ãââl[΋ˇ‡ÎoÙ ×
+endobj
+210 0 obj
+6321
+endobj
+211 0 obj<</Type/Page/Parent 207 0 R/Contents 212 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F8 8 0 R>>>>>>endobj
+212 0 obj<</Length 213 0 R/Filter/FlateDecode>>stream
+xÚ+ä2T0
+ár á
+ä
+endobj
+213 0 obj
+31
+endobj
+214 0 obj<</Type/Page/Parent 207 0 R/Contents 215 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 5 0 R/F8 8 0 R/F9 9 0 R>>>>>>endobj
+215 0 obj<</Length 216 0 R/Filter/FlateDecode>>stream
+xÚmTMoâ0½ó+F=T²(”ãÒ/!µÝl“V{àb’IqëØ©í€Ø_¿3I
+”­rqìç™÷ÞÌø£€¾¦CM -:ó¤óãvá ’Æá8˜Âd:†$ëFs‘b/y#ÄÂýÒ §ÁQÉZ:p&÷[aDVH-·Âë º
+Jk62CG ©=Úc˜4¤Î-š5­À¯®LQÐïóãâ3@?5#"õ+Ä;ç±€e÷ìê9ŠÏ–=xAë8H èk˜“¶‹à’¹SŒÑ,¨µµWmÐn$n¿j$ÜpÒà’¯Là4÷^™€ÒX/V
+‘ŒQbGZYß]/&`JdÍtèê
+¥Ô—‡Ð'y+GÀ~´q9Z½/|æhßQáR²†6úJj„º´Ü1.h£~ºX‡£ÐM¨ã4úƒ™¤Æ›Ô(*åâ&¹í/¢ˆŠ)øJ8ê0æO©Ä+ãkyðfVµø¨°¢¤@Å:‘sϼ¢¶é®r—Ýûèúžn¯Âp:[öÎ!¦& ótN¼"Ì•Iß ?Ìù”3ü,˘6‰sÙR¥ÌÜîÊ™“”®*¹¨h[é×`‘êD?y¥S.„PÒï¾õGd™²ekìû~PVÖlKf‘q>N­,ýAºzCÖÂ΢èšÆ O–áQ˜úœ{å{®D’†”òª DY*™ŠæB¥3lš÷{Ƥžªžªª™€´rÞò/åÞ´ƒhr¸{|†»õú²›VÖ¢öj Q“ÿƒÑ²w—Mô\\¦\Ò¬=-"já!¥ýr“e<ŸÊ´Ñý#ÛZ[]û4\>´/ÃÑ«7¾3:¤Ýÿo’ÎïÎ?ÅÁ´yendstream
+endobj
+216 0 obj
+698
+endobj
+217 0 obj<</Type/Page/Parent 207 0 R/Contents 218 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 5 0 R/F8 8 0 R/F9 9 0 R/Fc 10 0 R>>>>>>endobj
+218 0 obj<</Length 219 0 R/Filter/FlateDecode>>stream
+xÚ’AOà †ïýßqZ íhçms.ñ°¸Y¼y!”u˜"Ð-úë…u‹Î¸Ã \€—ç}?>Þ#©2&ï¢n@㤺ñg¤(´Í5ï;¡<í„ÙI±¿¡o^›Bƒ2Î&ƒ’n¥«7nÏŒ
+endobj
+219 0 obj
+307
+endobj
+220 0 obj<</Type/Page/Parent 207 0 R/Contents 221 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 5 0 R/F6 7 0 R/F8 8 0 R/F9 9 0 R>>>>>>endobj
+221 0 obj<</Length 222 0 R/Filter/FlateDecode>>stream
+xÚUMsÛ6½ëWìÑž‘}8Šul2É´‡ÔêXnsð"—"b`PŠþ}ß‚ÔG¨L2ÓÑE$w÷½}ûøw4¥ ~Sz7£ù‚òzô~=zóiIÓ%­KšMÙœïîi]ÜLéŽV^ۨ햞!rM;ö;ÍûÛõW¤ÝÓt*iw]ÞÝü~’¥Ôu¥å•j"{j¼Ûé‚)K®Ï'WRåö+¦®®¥ç?ÿør…·wþ5dH¾Ío¨å‰ÏI+ï6†ëïÉuÁ þä<Xù@]È¢¹ͱJó£*7T©@QÛ–‹Äõeöv‘ѳ5ú•é³Î½ ®Œò–þѶpûþò³ÊŸÆ]ƒRÅ:© ^Óy6^!*[(_°/UÎ’: ´X¼*å]Û4Î÷4StÈè7È·MRgÚ¨SÏ;íÚ`¤vJ…–Æ3€~Ïþ• #ÊGÑÿ>kÑqÀè<'€Ú…˜j+Ã6f}±Ùâ4ŒÀ×É=i2Úò‰6½ÜDþÉYsx¹•~W¨ý”{ÝÄ« !·õª©t^nÇéy¯c5ÔÑÕL¹Sßt§ÇrxnÚª
+ Å~kW´Fyqª$œO @õ¶#@àäÎ&Ò'6(¹,ù„+ß\z
+Xx< UÓ«t°á¨Á=«&•ß°åR4I»X\.‘óÊ:㶇ÎÙàŠË-¯’‡óÝ°XÊòëÛîþa’-‘€Ð¹<\þýçm»endstream
+endobj
+222 0 obj
+843
+endobj
+223 0 obj<</Type/Page/Parent 207 0 R/Contents 224 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 5 0 R/F6 7 0 R/F8 8 0 R/F9 9 0 R>>>>>>endobj
+224 0 obj<</Length 225 0 R/Filter/FlateDecode>>stream
+xÚWÁRÜ8½ó}$UŒa äBX HílÆ©½pÑÈ2£Œ-9’ŒwòõûZ²=ŒC¨­Pa<’ZÝï½~m~Ìéÿæôþœd}p•ß| ùÙ%å%ÖÎ/.²sÊ‹Ã|­(WrmleŸ¶ïòïØxFóyÚ6{ÿ!mûôm±$íi%¼*¨m¬!aHÕÊ=ióDw&(gT „)„+HŠªÂ΀ðãâÂixûÂÙ`¥­ŽÈ:º[,2þÖñ•2œ®ž¿ÏNùjU¯œˆµÚRa*ãÉ–Ôp,åE1~öÊ=ãW-L[
+Z§œ?Š;:]UˆM¾mëB
+ƹõo /Z:ëm&7ÿ£Ma;\×('bâ~냪³~ßéyvÆû8ùB•Ú(ObAÓWI%ªl†ÚQd§ ~#Qá‹‹ôÝ®ü^=¶ Ú¢ÜJoÕªÐb’ ×?Õ9åmÕòÖT¬·|cXgôÀAà%憜€ g-A"`iy©²ÌÙ–)± ãuÁN®îsô´nMáTáù\XÛÖ#…ÈP­+Æ£ÛŒ¾™XƒEL÷2‡µíîJɈ
+™÷ á ”ÊûÉ­ÒšàX7¢E(
+¥° ×ÈÝn!YÎBÊ0žÓßWømž/Ž¨[kä0|lèˆR§V½nñ8•þK
+2Ê×8ŠX’¼Ö
+endobj
+225 0 obj
+1372
+endobj
+226 0 obj<</Type/Page/Parent 207 0 R/Contents 227 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 5 0 R/F6 7 0 R/F8 8 0 R/F9 9 0 R>>>>>>endobj
+227 0 obj<</Length 228 0 R/Filter/FlateDecode>>stream
+xÚ…UMsÓ0½çW,¹PfÓ$%mo|`
+ Ã¥U–kQY2’ŒÉ¿ç­d‡4…azh-íîÛ}ï­úc² cü,ètI«5Éfòr3yúæœÏŠ3ÚT¸[ŸžkÚ”G—^Û¨<]xýSùðdó‘'´Xä¸ùêü~\™ãH[zõõòŠ¤³A‡H®"gÿjœWTi9,´JêJKŠŽµ¹H‘Sµ•¦+UÀEMkÔxÏM
+wü÷P%ô­Ö¨k†¾©tÀ³.Ò­²Ê‹ˆŽÛ¨aÈu±íâ4c–ºª”WvW–Ê„é˜ë•ª 1•«]O[ב–z¯q‚/O®·»ô‘Bî8ß:N=€Žµjp Þ˜¬" ³/!Ks‘ŸTì¿ÓööêeLiDhÛYP£È8‰ñÃ6D
+h'ºè5ŽÍ–B³’zkrˆ÷Cè.ßfø‚6àù`Tp}H„` b×&› h8K¤ÜsGê± ƒnc_a/
+endobj
+228 0 obj
+827
+endobj
+229 0 obj<</Type/Page/Parent 207 0 R/Contents 230 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F8 8 0 R>>>>>>endobj
+230 0 obj<</Length 231 0 R/Filter/FlateDecode>>stream
+xÚ-‹A‚0D÷=Å,uQäWnQdg‚RÐHIj¤Äߪ×ת™ÙÌ›¼» äŸÖeêe[-VíDÐ#H™B¥rèaÑœêVCbwîzôó_†-êarÞ…È&Îp0þanK}ò'Ë¿]&VP™UøѰ{ZéÝkqoµ-']endstream
+endobj
+231 0 obj
+131
+endobj
+232 0 obj<</Type/Page/Parent 207 0 R/Contents 233 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F4 5 0 R/F8 8 0 R/F9 9 0 R>>>>/Annots 17 0 R>>endobj
+233 0 obj<</Length 234 0 R/Filter/FlateDecode>>stream
+xÚUÛrÚ0}ç+ö­é ›;y iÈÐi34éeú"lÔX’#ÉaøûîJ†8iÒv4±Ù=:g÷ìæ¡•@ŒŸÆ=è •­ÙºÕO!™Â:‡d<Ž†0`õ ³J™P[`*ƒ…²Ž=^Þ-Wï׿0u
+ýdŠ\$*D½ú©€iŽ1¤&ºŠ™dÂ:#6•È"7ZV(ö|V8Þ†Òè”óŒTЯ–§>Ü Wð¬ oói0@>ãa4iðy%¾!aO°´‹oF`ü(‰Æ/ô>O1Ûc‰ŽUh4—Á,”äC£$$í $®A΢à4&ÜNú
+îJW&åàúü>ócè H ¦Þò‡J.¹rö¹Ùb?¢ ïºz‡RÔ¦àâfµ€KHµ,EÆ;ÙÍ7[…6B;Ö¶º° œÅ$úJ–…à¶"Ñ»·b«|³½x´¸ç˜™J9ÀÛU—Ü0ï×
+ɶžÂsKä·J(Ü.–m*Þ©Ø”öqyu݆å ëÅ|ÞöI?>-fµJ¬gtÚ !{(¡/$EWŽà,š"¨3<«R¼&¯”Ÿ1VwˆàŠÙÚ(w{ÚK£1
+;(¿H¤0e…²¾¸8­ŒA;öÆ"¨­£," Ì·Å0 žÄMyBøˈG1LȺz ¹Ó?¼=0 FÏ]yÞíâq”g÷Ú·[V›î‰ÔëÓ—LúxM|¤ÕXÆÀiHêõ)Ù=§•¬¶(³^a‡¦dô¢ÙÜë›;Ø瀕dJc¤E/:…@ ŠIQ#P¼t+{d¢`²nÓÕ´;ŸÉÓO¸D†ÿûOh0‰qãø2Œëö|iýíû=endstream
+endobj
+234 0 obj
+831
+endobj
+235 0 obj<</Type/Page/Parent 207 0 R/Contents 236 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F4 5 0 R/F8 8 0 R/F9 9 0 R>>>>/Annots 22 0 R>>endobj
+236 0 obj<</Length 237 0 R/Filter/FlateDecode>>stream
+xÚU]sÓ:}ϯØa†¹a¦–í´ 3÷¡´M JiÍË^[N¶d©!ÿž]9n\ÓÞ
+m*n­ÈÁÕR-Á¢Ñ»ôÃüâã9Ôº°nƒY[í@ ´³*þM@¶âj)j¿‘qKaÉÛ‰¹ X>ìÇ‚ÂèêMShýƒmý¿Ñ¤áw2BPÍÓ³ü©±'fe=«›Í† ^oë Ët®lU"!ÏÐz:Æ Z'ДHÄúìÁñ¨!ö\WkY{çŸoî7!ÚóO›È2Rvuý¸³:Óª >éW.²Cô»…,ÑŽ«‰s&C¶u.¨YD·„`EÅ íú¶É;ì÷Àn×âùäÓÃK`á>úåuzyÛ¹>R—(¸+-HU[^–È6²,aí¼8|{-Hå_¶5Q‡¡«ÍãDCÏDßìžÿb–K#2«Dò0uúlV2[õøñµé{a6FZY¶ ~ÈÚR/×F*ÿ¥Raþ~DÏþéÓظÒøÞôr–:kˆA)<?Öûÿ£M¯Éë/»×ý—È ©„ò`GñÐSy7×WGðþæ×t6ù^ü7Ÿ½…R. ÷\`ÛÒS‚à·ªkeñ€üË…(±†rû|‡&_¨TYérñ;jð¸ä¢gúåµììP;»êfLõýϧ󳫻ßH4¿xÒR¨{i´¢Ù
+(LÉx€{GEáyÄŒSªQÖ®?ÐU¬sAS/¼fa­+¶Êß¾xÚ~W6:Ì8teÃ;(“
+Ç]+~/öìb¬à««íŸNÃÊŒL.œ,sßχ˪?­£ý ž5òlﹻ˳»wÀÎö߀iu@%»ŒI‚nãQÌ&dq{6M!hÆI["œå•T8™ ÇžÕð+ÇK
+Ä£t^ü]•Ð»“8a§ÍÝu+¾;ì4é²Þ]­Ÿ?´Nendstream
+endobj
+237 0 obj
+858
+endobj
+238 0 obj<</Type/Page/Parent 207 0 R/Contents 239 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F4 5 0 R/F5 6 0 R/F8 8 0 R/F9 9 0 R>>>>>>endobj
+239 0 obj<</Length 240 0 R/Filter/FlateDecode>>stream
+xÚTIo1¾ó+>EjE¤0¬aÉlUM[2U.¹xÆp3ØÄ dþ}¼Ì jUqñð–oy~~kõÑs¿>& ÇÈ×­ë´Õ½Ÿ¡?LH OûþHÛ +K˜Ó,ÌŽ(vžþqé#ôû1¹3'#ŸüCä •´X‘-Ú²d4ëº8Äs"à‚Ê„ÐÍïŸO „­¥@VÁT‡xQz˜ENŽh'ò£mr°Ÿ¶¿ kµêꌋnn7šâ+îÓ»ElàT]&Sß¡ã[ g‘êC$ç¥\sAT…[®â™5\ŠO]áØ>3x¹UŠ SV û*×+HÉb;zÐN;7Î(¤Zƒ7«Æ â@…rlI +¾1:‰$.kž~ãôó|ñøðøíꯣ8Ð8íxYBn™Ú)nÂØ»cè36Š‹pЕ6là¡cbï¦8óS¥¼(xnKÙŽìÝäš9Ôc @û9{A‚ùéK,%2’¿úc@–%=½ˆfSì`û+r‚¶jظŽrË©«þÕ–Ö.×G˜(”\׌œRrº¹þ7·& ?•8lw,øÿŒ¡”Ë¥ûpÃ'JJƒ—6•áÖ œi{®;’‚ i—«—óÍèõ4t±š© (+Â_G©ï]ùïJÂæ$¶ƒõi®Ö‘ óÂ0å½z!^6AÁv¨˜î
+‰7Ët¼óŸ=
+þd‡oƒRxò£¶t}›œð@0zjƒ5ÒmÏ]yUoÈ´&ÙŒ\Òp2‰;p»˜ß§èD
+Íã…9]sáW“©4¾aI&Ë;“Aï_/ßhÚKf)3ÿ}—¶~µ>
+endobj
+240 0 obj
+640
+endobj
+241 0 obj<</Type/Page/Parent 207 0 R/Contents 242 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F8 8 0 R>>>>>>endobj
+242 0 obj<</Length 243 0 R/Filter/FlateDecode>>stream
+xÚ+ä2T0
+áÒw³P04TIS042Ñ3R072PIÑp rt QÐUp VÎO+)O,JUpLÉÍÌË,.)J,É/*VðMÌ+MÌÑ ÉâÒ…hÖ…ê64
+%©pÓ@J\C¸¹
+endobj
+243 0 obj
+132
+endobj
+244 0 obj<</Type/Page/Parent 207 0 R/Contents 245 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F2 4 0 R/F4 5 0 R/F6 7 0 R/F8 8 0 R/F9 9 0 R>>>>>>endobj
+245 0 obj<</Length 246 0 R/Filter/FlateDecode>>stream
+xÚUaoÚ0ýί8UšÖIHÈ ìÛV±Ó:Ñ‘î»›â6±SŸâßïlÇ°†n*QûÝ{÷îû<HaB?)Ì3˜Î ¬_ŠÁøëÒú=…Ù<‡‚_N!•‘Ê
+·­hÜ0ÅD-”ýP<(‡4u Ä¡’i>y`±•å–5É%–-¢@Øê=X Œó!ÔšËÍaLqà¢V@ã¨àÙ!hÝÀZQiü8ºr„b[D2UÃx-\뺦Š/Å…»³x7NºÃËúr e¨¬ªô'½f£MMêѽԠÇK´†YI²-Ã'„Ñ5X¢ë
+%•T·ArÒé(srÖR•ÿ+ˆe’{°ö–À¯ @ª‡Î¤H5iÝõJ—Ì
+ä‹=ï{Ü¢Wò¾ÏÃ¥¥ÕæÐÓ*q0zCjmBgÔ¯Vä¿|zàþ¼m^½‰çWÿ5õÏœ»Î݈n|”Ü·.¬ø÷ùûôQqv4Óë»ÕÚÏeÝT0í«~½‘“½´[»Hš Ћ§Xbá,ܸm;ÚÝMÝ—w§1ŽINš
+ûƒ´ô¹=4[¡0<G­âtXRÄqwÊg[“TÓ_8övÄõ“å™Ý“äâEÛØÓ«´=‹C‘ ÉVîÄhÙ],Iý{ëck÷:–G_Ÿ÷CúJ˜oâ¬ôñMœœv"Š®æñ Ó-ßÕK”…!¾õo!Ÿç1‰iêþ°,·ƒß`*ݤendstream
+endobj
+246 0 obj
+682
+endobj
+247 0 obj<</Type/Page/Parent 207 0 R/Contents 248 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F2 4 0 R/F4 5 0 R/F6 7 0 R/F8 8 0 R/F9 9 0 R>>>>>>endobj
+248 0 obj<</Length 249 0 R/Filter/FlateDecode>>stream
+xÚ­VßoÚ0~ç¯8í‰jãG
+ }«—áêËG0¤R Í2)þ!s^HíDÑÂIRf-õ]ð²´ÄéC'vÄÇú<7…¤Kfe‚?­cJ1'“»«y<ÝeYvãîˆb‹i1z5Ÿ
+zð_2Ï_WÖA OñšŒ: WmrzçaÑÔ îG‘Î6QN©É'p´Ï–4
+)pæpaŒ(ƒt›&ˆ¸z™ òB¥DòÅýëø†ó=áÑrttmô…XøBÛfl”)=ØéÙò«ìøá/ϱó¾JÍÍÚbÿí,ð–h’G<»<Ÿ5©[ãtµI¾ ·µ¡Â¾=jGV|fy—@h3Î$FýýÖmŒåö
+>‹úý2ÉÎé
+„KºXεDUµqåfºgR±%ÂÈ6ƃɤ 8 W^ØZ»ýŽ§ ã™üm “eLóÐgPÑ(çc8¨Ôº*‹;|ü1éBâÙXŸý§D~&êlÞ2¥ à P_ÀO7ÒLÓ )¤ /`•ò”IX¸=aöÄ$@Dƒf5žL*‰/¾Ì>,p˜é6q˜5inWƒ9VÊ3U¶NåŽÔ'ïh@‡£~wåƒç$Ѻ4\¦úT*åy¿h}ný•÷4endstream
+endobj
+249 0 obj
+883
+endobj
+250 0 obj<</Type/Page/Parent 207 0 R/Contents 251 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F2 4 0 R/F4 5 0 R/F6 7 0 R/F8 8 0 R/F9 9 0 R>>>>>>endobj
+251 0 obj<</Length 252 0 R/Filter/FlateDecode>>stream
+xÚÅVQoÛ6~÷¯8š•dÉŽl÷­­ Ã6x‰Š>Óâ)a*‘*IÙñ¿ß‘’<Uq‚ ZøÁ"yÇ;~ßwG~Ä0¥_ ‹f)äÕäC6‰®h&†¬ •t9—ñó¨1:2wLc”7µ‰*űü=»'ëyg}\hÌ­ÒØ {öá,¨Î@ÕV(ù®5ŸÂ*Lœ= â™ûæçniJÃØ ~­Û¨¬¯„„ n“Þ9&Ï9%HæµÒ¢ï¾ Äý:—ìFþ³¤wç¸9Ž½ËTãÐ)ýûµGÏdÑù]þ“]^‹P¬.È*˜­ÂÔÙdPk‘öžÄ•éÛ¦BiÁÔ˜‹B ñØJV!¨Âo6k(DIc Ž‚å&×¢¶!\Ñ,>°ª.ñ-XŒs`Ðò¶g #Äíi¼ü±5š/"ÅGí‚s-vä—+)‰oänËšiV–XB­´%E5’“É_B6pP ìUSr
+…?&ƒ>Ïió]D$F4Š‰-N÷húæCV_8) ."Q0wA®±R;:>lZ0Ì·ØOd)ÐÎÈѹ?~ÚÜøAh@7ÒÒŸ²ã°;Á˜Ù\U“|X/¯R/?P/§Q‹—‹N»¸/øX2cðiØ<>Ä’ÚAv«US;¼DÉt¥!Ù²£rèÎ]”x!|¾CçC¢Ò$MÉMO Ü«­×yëøÖCKs#‘ïEYÂÉGåH§à°=xÓBhcíe¸-ñÈ·pÜ2Ûîv»%i‹H6®«ÿxUÔgàf΂¼WŠùu­5²9({asœM)îsÍñ”
+@È52׆XcUŬÈIo \¡‘o,µ?a¨«¸¥æ¾!ªIB?«ü^ŠË°ôi‡ŒÚ ZëZ–KxkJÛ·¯çº—¡¶é\xçÒËTuåõjå¯!Bjñ
+endobj
+252 0 obj
+915
+endobj
+253 0 obj<</Type/Page/Parent 207 0 R/Contents 254 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F2 4 0 R/F4 5 0 R/F8 8 0 R/F9 9 0 R>>>>>>endobj
+254 0 obj<</Length 255 0 R/Filter/FlateDecode>>stream
+xÚ­TMoÚ@½ó+æR•HÁØ@ ôF[r¨Ô* î-—µ=®­wÉîÄ¿ïŒ×|¹½Tª@Bx>Þ›7óü>H ¦OÓ9ózð9ŒŸbXFHK~6\@Z ?À¸qvœI=.¤™B¸K·”;9å&ñ$šS3JÞY©=Úpi¶ˆCÖ?ÒõkÏ I8:Jóh£é’ºPÎsèáÀWƒ°nµ8‚óR)yŽ;[“9(…Xê_÷5F¤ÒÜ7B©c‚ÐG(¥B ¥*jGnÏlbb1e²ÅТóÂz,"H+逾òQ K®nF"£ÊFç^í¨yGÓ€F¢éeà äÆZÌ}Weh„:‚Ñxo°!̾‡Îã€DÉ0×ÄuâmØÑ|»‹‚žKHHgR4nÅœq¿U« Ó2|Å-ñá­Èð­#pYF|ÙCZ6wÁaPý¶bØvî%Ú¨Ÿ˜›º¦dwÚ†ÌnEçuòÐZÔ4g7ú=%º“ߣ:~êØü¢àIÐäêx•ÌÆfïv— úýÇÓ%¨‡¿
+ ) ‘{!Uûn"Û– g$@ˆ0¤)À”­3;»<v'“aNË€üõuõ”¾ü|ÞÀÆ”þÀ–\5ë­ð†zšÞ,áÚòÑbÒî0™ñÃé,Ž¦a©6éÉ~^ÏÙ}D’“×éàeð„u²ˆendstream
+endobj
+255 0 obj
+652
+endobj
+256 0 obj<</Type/Page/Parent 207 0 R/Contents 257 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F4 5 0 R/F8 8 0 R/F9 9 0 R>>>>>>endobj
+257 0 obj<</Length 258 0 R/Filter/FlateDecode>>stream
+xÚ…T]Ú0|çW¬NªJ88úÖž8µ½Ò’û&qˆÛÄvm”ß];ÉÝ…û2agvg<›£fø‰a=‡Å
+Òjô5Mï6o É!^/`µ^B’—0Ò y„}c¯à“ìÈ+.ݧä–Ç›n²XÎ"M
+a!-˜vÜ€-ÔÙ~A£jH™„TÉ\kÃÁnv{°Üœ¸‰+sÝï I7ó¶`òH£xLËÀœPîDÉíópEÀ/e *÷°Ð¥ïÐ9¡á4¥J™ãé«ã¬eOOÌLÓZÛ)៷C& O2M?eʽւ8T,ãÀ¨gŠÑœfJI ÊWÀ|_.9öÆG†[ÇŒš‡O2#„Cà·‡Xq” õ¨mgŽ­µ.rÙÔí­¯²HQd‹ž«Ï]Ù†`9e¥§ècúñ¢iËÑO¾½O¶¿ !‚îJœò $3 úf‡Ú_…ˆ/Kdøé_¹ !…‹²W/ÎÂ'Yu.õˆþ]Ô\²sÝÚ¯äÀTêtÂ?•™ôîënw¬ß·²ðJ+Cv¼å{e*ô§ ±ÐµÃ܆t <ºž%D{)®Êà{N ü˜A)þúXv¥E~t•jÊr ½‹-Ï„{oü)‹^Z!š:jÉç«àÌÞç “ŸÎ\—pÉÏOÔ>ÎÇ$†ž»¾ŽimÞn5hn*a-Æ̶پybð<üþ;o¹^âõͨ8¾¦Ûdôkô9åšõendstream
+endobj
+258 0 obj
+587
+endobj
+259 0 obj<</Type/Page/Parent 207 0 R/Contents 260 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F2 4 0 R/F4 5 0 R/F8 8 0 R/F9 9 0 R>>>>>>endobj
+260 0 obj<</Length 261 0 R/Filter/FlateDecode>>stream
+xÚ¥VÛrÛ6}×Wl2‘§/’BÉy‹kš™4ã&ì›_`´P“
+#2õ ÀmTR (´rL*©üÚ x˜æ¢n>Isêw
+ˆ(œ| Õ¹cEÛX¡ër|Òioh³Ã'æ`§Û·ObÀ Åt #˜üý¨fäiorÂTŸëÿdj áry
+×ió@ùÐFNKNh.JÖVŽØ<œ!|üóöûf%­ÊB© «>]ª-&Y²B`]
+Õf‹tDÑÝÔ#à˜âÌpø|{ë-ï."øÀkT•u}*e«úHÌòj‰Ô’*]°ê$•Nº­Ï›5Ñ‘¨‚QÜ«Q¬FÅ¡aÖbÊ<êæY@ñséuÅ1Ú£ð¡(*°k²ôŒ»ì©½¥¯>d%¸tç„?¡|HÝ1!?¯óRVâ@ñ"¨ŒH›Òn¦þÙþ¸ m#
+YJ䊪|µÄ4‘G\µJ#¸Vr´?ËÜM‘ë» B
+¦Fè³ÊjÔ“k› ­“˜ä÷·<ßû['è-¹pèX”ŒòÞgщûÃÆ(t'ªê%X¿ø^ì¹ÏAÊ£QÉ87¤"¯?¬ðÄÜâÃäÎ*év}ž>*êEn#:†Ž)Ï+2©»“†ÑªÚ…öSÝ7TÍŠ-÷žAÂý_Ò
+§âVÛÐMx‰ÔZI¼>qXQsQk'
+endobj
+261 0 obj
+955
+endobj
+262 0 obj<</Type/Page/Parent 207 0 R/Contents 263 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F4 5 0 R/F5 6 0 R/F8 8 0 R/F9 9 0 R/Fc 10 0 R>>>>>>endobj
+263 0 obj<</Length 264 0 R/Filter/FlateDecode>>stream
+xÚ­VQoÛ8 ~ϯàc4Nì¤I:¤ÛŠ¸[s« 쀾Ȳ¼h³%O’›åß)Ùiífz7“"?’ßGùû(†~bX%0_¯F—éhzuq iA–ëE´†4¼Iß¿~•~Eó¢5Ï`’,£YÿÑ Tu`…·<g­çøÒè½[m\?Äœöþ–UXÙÐ
+XY‚.‚á`¨,þf˜fÏ”£SVˆ(»€x%n‚ˆ'ó$Z¦s“çFXû ùü"x¥?{òàriwò‘Õ‚ËB
+ 2£YÎ6€…c„°±ö;¡ªÊ¥úµ‘Ê Ö1×Xhêœ9á3  PC>ÖèA•+áöÚ|‹€ æ¢`Mé(b;f‘v>9?ž|‡ð÷;Éw°—Ø`Bu„$U¡MÅœÄî;=€Dã°M†€lÛò' 9rà× ¹.üô˜ØjƇíäî3ij(™C$¬M&…)í YœA¼<m` ™töìˆØw‚Ýé¸TP0îN œ NNÔTâý«ÐJÑõ3ÖFg¥¨€Õµ`ÆÏ0PÈÂÇoÇÑì‘x礎Žx¡Ôm‹ðVðÆHwø)÷ÞÞmo)éƒÌ‘E×[߇\WLª‰"AdÌbjÆ9ÍškåŒ.½Ó%³’k'ÊIæ†<z&Ù£<þÔÁç%Ê8}æ©(§R‘$ú˜=´N1>¤é¶=¤Í!0¼Ðe©÷^&V4ùƒ·×’¨m[ÙÁ«Å÷Ò
+ƒêiIÈ[xÉ#yxŸ,Wò¦,¯ä³¢&ÒúøwŸ®3ýšCZQ#GÈ“\‰éƒIÒ8 …ëªÂ‰uÂá}î*;‰ˆ—ÌZaÿ¦v®6ä |ÕÙoAAq^˜ºþ;› L%:¢oè¶ÁP†$sì®ïÂèêÔ¥6m¬™Ú:NySÛi®ùiaÇ[:w4üOâ²1ºA2y4ò@hGg‹R´ÜÈŒ$"PYý{$(e 1 RÈ/ ÌÅë˾î0^„ýAk®óîÇôç=1½éY7¸•ÒC-ÂÞzfzKÌ„[Ó÷Œ½?7&Çzß u8ÛЪèé1\Ñ ÐÖO@¾Á'+Üݳ(îCŸvØß„j×më×Ä’å|… Ÿ6W)rÏ/š[]¸=1dÓŠ{¦ñÆø‹©†ùü“8YÉVx½_Fº ÅŠÞª¼!^у÷éèïÑ¿ÀÛ×endstream
+endobj
+264 0 obj
+922
+endobj
+265 0 obj<</Type/Page/Parent 207 0 R/Contents 266 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F4 5 0 R/F5 6 0 R/F6 7 0 R/F8 8 0 R/F9 9 0 R/Fc 10 0 R>>>>>>endobj
+266 0 obj<</Length 267 0 R/Filter/FlateDecode>>stream
+xÚ­–Ks›0Çïþ{L3† ñ0ä–&M§3}¤‰{ëE¹¡ÉA"n¾}W\ccw:6Ì>ôÛÕ_BÏ~ø‘ù¦åäí|rq›
+óÚ¢˜˜ÛììK•ñêÍüÚ ÄZ?q#c?r°F¯5œA–W<Õù ‡Œ/rÁèÇæžÕ…–¦\)H¥Ð•,\0Y²(ä*?á…5°Šƒª—KYiž]ÚÒv
+’Ø !=
+‚{éŽf:Gs´ÞC$I̸´O‘Ð1$ñ›ßq÷ Å,F]àÈ®ëöHÚÿf#G3´Þƒ alWqÜ# a8ª!=A©÷ ‡[}’h¤…9‚‚¯½ñ"í¼QHlEJƒA”ç ¡'(µó‚Iøt/ ^‡YŒªG£´Î;$Ô6åôsÃÿ<èÄÝ>Kĉf3»€nî¯nçxä»þv÷
+endobj
+267 0 obj
+841
+endobj
+268 0 obj<</Type/Page/Parent 207 0 R/Contents 269 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F4 5 0 R/F5 6 0 R/F8 8 0 R/F9 9 0 R/Fc 10 0 R>>>>>>endobj
+269 0 obj<</Length 270 0 R/Filter/FlateDecode>>stream
+xÚ­–]o›0†ïó+Îe[
+†ð±»´]§JÛÔµ©´‹Þx`V°†Vý÷;Ʀ Iˆ6EDÀyíóØ~±ÏŸ™ þ\ x$åì|9;»ŠÁõlË cAäªÛôè’ñ×ãåo ûàº:hy±¨àrÅ@ÑAš×,iòg²bIžåL…• §%;…ë iZ3)OAÔÀYó"ê'hV´¥E!^X
+4IPÀvÅêgVêÓÇš‡céQ¤G*„°ŠB²Z”°(Š-‘-¡;%͹ˆr‹d¿Bx¿ŠsnŸìˆM‰OÕà5MuV–“…¥½v ía©õéݳÓ*¸ì5ƒ¤-˶ êÅ)HøÐäUq˜ë$$”Ã/­D¡}:CõvÊÐqoݪ’ý@Þ)mqüY?È÷Ô\4È,øÀï×—·ª§’Ê'cÛ¹iázáÄ8¶¯2êÝÈÇ—ŽzaZ©·Ÿ—3Çvðó}û»ý~d;à»»q€÷ÄÁ[œ¿»1ñý5õ| îö~s|§r:ž8Ð4Ø‘úí¦ñbõ?•¦WoÐ ƒy8˜×P¸$šÄ†Øz2‡Qržö-rÄdÇ<ÄÖ“9Œz”Ã÷´MlÛ˜ç ^ˆ-'3õ(1G4 óù¤¹pÜګG9¯[ƒ`àì!qx€I{õ‰ˆ6)ñGQ:œ=0ap€S{õ(L@´S=²¯=8óà
+²¼`²[+KÏÁ?-ÁÿtNÔÒÄG® ðMõr»¸Zâ\ÜßÜÁÈšU°,Ò2ç¹ljÚˆZÂ7Ê[Ú•›–nn…ÄY¯}üðíwcóAþ˜ý­¦õÞendstream
+endobj
+270 0 obj
+816
+endobj
+271 0 obj<</Type/Page/Parent 207 0 R/Contents 272 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F4 5 0 R/F8 8 0 R/F9 9 0 R/Fc 10 0 R>>>>>>endobj
+272 0 obj<</Length 273 0 R/Filter/FlateDecode>>stream
+xÚµVÛrÛ6}×WlõdOMF¤YÎd:ã8v“™8Mkå¡3zIHD 
+
+l¡+™Ã#‡%7smJžÃì ñ³Jºxv¾… *ŽüÑrÓ z +&Eî*Vr`*‡%ê·Ö&ßeÿ¯`ëxùá.üªÑ˜–•u¨…ÔjNû½¾ÝØ>E¹Œ'  …ÑÕ…r:̘"³‚©§À‚Žw ' þŽ- ~Ý’"Ú ~Hƒ^¸‹:ʲÆÚâLÍâ±½G££¬=Ý»õ;ûÎCfË%g„²"GÂm7ït(ýÓ`öF4L¬t¾<4¾¬Xîl£4ÉŽé
+endobj
+273 0 obj
+974
+endobj
+274 0 obj<</Type/Page/Parent 207 0 R/Contents 275 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F4 5 0 R/F6 7 0 R/F8 8 0 R/F9 9 0 R/Fc 10 0 R>>>>>>endobj
+275 0 obj<</Length 276 0 R/Filter/FlateDecode>>stream
+xÚWKSã8¾ó+z}Ù ›„$„
+,{æ¦a<dzíÂ(”1b.2aWÀd¬´*gV„,Ã0Ä")5¢Q¥}‰
+ŒCÁB^Süía/½¼ ñþÛt/UH€0u‚‚Y˵ Ú#쾓Œkê;[â?‘6”– ‡ýJ<ØË
+3¡³ŽŠcÃma&6móiªKdm ófƒ8 –‰È¦cëÅãÕí-Ì:W]¸ÿÚ…éÅe.»p<ê G“ÒqUz Û ª³Þ\XS¦M 6ÒuŠm¯‚â±va8ôF'ã&èÞ–pncÑtRüs°lae:Zbçò'÷
+¹Y™Æü”ü#»Ô¡+«{Ù2©ÒvwcÉK¦…'ظëf1/’[ר±Þƒ¹Hz\F‚ÉÀA|•#¥#®÷Ï šÞ.òà7áB+¼‚™)TŒTß….a©©ƒ?ã{
+ËP%šå4²™u w£Øí†ø
+‡`Þ‡/ »X†:hVâXr³òóĹ÷—–*„›©=¤£J
+cq¯°4ÇB…ÛËÚø–ìփŖyTdLH`E‘anéðŽ}kB-
+ ' !«
+³u².¾Q?, Ó«ký®™¡˜ ÎÂX埶´Šœ%üè>ÒŽœè+Xï*x1¿>ü‚à%£=Šòk&Lª¢w±~ÍžET# înᄌ-¨ ª
+4¼ùô™Äë^ŧïRŽ7Ä…f ¨0€ÿ(N]d”gäFY^¢7, yá¯QÆb\‹ð€˜NÊÜ]Ÿ(3\.„V’^àþ©­xt;¥ÚŸó¨y•ôÕzõï÷G¸ÅI®cºE\ãÍ'‘p­B§xç9³Aü¤æä7æRXœ~¾Íøo³äÿïž­ƒ}Fxr:ò—ë뇋›)s‡éQÅvISñ"ʅҬNwL–~ÃetLóût4¨ÆwÕhƧtcwFCzñezðÏÁ/ΩÂÏendstream
+endobj
+276 0 obj
+1382
+endobj
+277 0 obj<</Type/Page/Parent 207 0 R/Contents 278 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 5 0 R/F6 7 0 R/F8 8 0 R>>>>>>endobj
+278 0 obj<</Length 279 0 R/Filter/FlateDecode>>stream
+xÚUNKnƒ0ÝsŠ·¤Rp€ Ê–~"uQ©mè,<&®°Ú¦\¿8NÕÌêý¿³
+åvmüQgC¶?6¨* rcÚ®a‘¿Hø J*;„3ánøÚ¤íMšK5“ášü—ƒ»iÑdIeȃ#J,qõL&"œ2ÓvËv«òt-IôâC’Kgu¬(QTVÇiQç7‚;e.K`èçùæ%»„ L!“Íÿ,‰fixw^ÕÍ–Þt¬‰Oýq@ÇÏ·NV†•;B/´2Êǃu¯Ü,|Ž1Er÷uÝuÁ¦îØW@+Ml´æÇGâyÈÞ³_\ãw›endstream
+endobj
+279 0 obj
+257
+endobj
+280 0 obj<</Type/Page/Parent 207 0 R/Contents 281 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F4 5 0 R/F6 7 0 R/F8 8 0 R/F9 9 0 R>>>>>>endobj
+281 0 obj<</Length 282 0 R/Filter/FlateDecode>>stream
+xÚ…TaoÚ0ýί8åS§‰$ ¾mU«Vê4¦fš¦ušŒcˆWÇf¶Ó*ÿ~w mZJY,…8ðÞó½»Ç߃ƒy“ˆzô©%9°Š5dóIÌ`6ŸBQž|€1,2A:ø(„mLPfó®øƒ€)0F€qO²yœ¨¨”Qñ-¡Jé…S+é!Tξ-o@Û ¬•–>î‰PùC|JT)²äq§ü½’NB°p¡LÙa¯vA°—ò=fÖ Ë8âu5׺¬“%(ÓQõ éŽá$¹ç.ÍÖ'ˆÞ£?R9)ÞÆðÃ6 ¸¡âÌOG…®Z¥"[ˆú%3žMzS aͺ{*czÜ—£wjÓ8”5])Çl¢’¹ÒûßzçÏQ{ö* ûÇ aÐÊ’‹
+.‹b NzÛ8A•ó
+kµ_<V›“Øš¦ýIøñŒ6•õ6Î6[ÒÀ¡åAŽƒª%ÜÞFµ •-‡ÚI[ƒßàTñÐЄ~*Ÿ_l#Å…Áõ3K“ϼMXžç –/²t‘åð>ÅëDË/7$¼¬•I:ϳ¦)†öMÒZÝÉC¼ö?Þ,M{Þgq&—^Ììq2È«×Ã"5v@i=$μ­p: \-—%è¡1š>Z RÅïå.IåÑè\¢¢áµ¼¶öcs8Ÿ
+Ép\üí´=å õ²Ù©½¢»Ù8\5×¼õÛÀ•ñ£]`O‡R²ÙñÓé|ŠÑNéGÙ„^œ£¯£d ¢endstream
+endobj
+282 0 obj
+652
+endobj
+283 0 obj<</Type/Page/Parent 207 0 R/Contents 284 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F4 5 0 R/F6 7 0 R/F8 8 0 R/F9 9 0 R/Fc 10 0 R>>>>>>endobj
+284 0 obj<</Length 285 0 R/Filter/FlateDecode>>stream
+xÚ¥VmSã6þίØÉ`JÛ¤¹$ßh7M+f:íÑé(öTl)'ÉäÒ_»VlòäÚIˆ¼oϳ«GúrAHœöø?%‹.D$SzÒëwƒ>$ÙQò€pœüC{ˇG¥Eã—jû#˜JÌ3Ù‹’>•“©p˜›+Q èiõÔà—­“ê¾zÀÕ”~6*ƒ™°v®MÑ–³Y.1ã”!´£Ó æ¦ڬ†ãÿ}ƒ¯%ÕÊ ©,´Ú­`é÷‚î  2ªµíd¯"c«ª@¶Ü
+ [݉à1üyûËíÝqåNá$W‹Û 
+ᆾ†¼:å5µCÄ?>FñÇ_;Ðkxy9‡77ðCH¯¿Öq´9Æé è½
+endobj
+285 0 obj
+1010
+endobj
+286 0 obj<</Type/Page/Parent 207 0 R/Contents 287 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F4 5 0 R/F6 7 0 R/F8 8 0 R/F9 9 0 R>>>>>>endobj
+287 0 obj<</Length 288 0 R/Filter/FlateDecode>>stream
+xÚ•UQo›@ ~ϯðž–j…I“’·nm¤VªÔ­ìi¦ Éu—»”;†òïks švY"ˆ}öçÏŸÍó „
+ W/>¢,§´#
+£Ž4lY¥1Yp|ÓNnw^HrÁ¡à†×ãBµâŠÿ©»À{è¿|¿ÀÎd¥‘³¬Àh§`4dZ}´ñ éBSkÑ¡Áár—J ‘þ]ênšŽ¨–|aÊzú°`B¿¯ºA¸'’zÐÐ;zv¦W¬Èp£ˆ”I¢Å„ÉÞÙÈ»'&–¦Ü˜·–Û:økÁ¼!ÒÐó¦D'ÌAŒ¦3ˆ5aMÇ(Ô^Dn‰u%äØc¶GšÛóVÛí,§LÒ´NtV¦¼Xë„ LR©ÓC¿ß-&+¶%túÎí¢q £1¢ᆬټúv9OÀƒZÅ:·)ÿ2[ …YÃpÇTÉ$ÅñÜqoo¾ÑÆÓ1¾új{tN\'ƒ¯ƒÑ*Jendstream
+endobj
+288 0 obj
+709
+endobj
+289 0 obj<</Type/Page/Parent 207 0 R/Contents 290 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F8 8 0 R>>>>>>endobj
+290 0 obj<</Length 291 0 R/Filter/FlateDecode>>stream
+xÚ-‹=‚0„÷þŠu¨ÒbPFüèf¢ò:›
+Öªãßת¹[î¹<&|"f±UÇÖÄæj!@ „\Ì$–2Õ“í©PŽÍùP¢ôÍøÒÁ ¨;ÛÛa zôaÀ^÷Oí¦tcü'ó¿-³Ó<ÇwÒÕà®[sq¾…²ÎÄwGìÈÞÛN'pendstream
+endobj
+291 0 obj
+133
+endobj
+292 0 obj<</Type/Page/Parent 207 0 R/Contents 293 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 5 0 R/F5 6 0 R/F8 8 0 R/F9 9 0 R>>>>/Annots 126 0 R>>endobj
+293 0 obj<</Length 294 0 R/Filter/FlateDecode>>stream
+xÚÕ[ÛrÛ6¾÷S𲽈BxÀ¥ãÔþÓ´©­>
+n,–é‹Ä2ƒ–‚<Íç: ™ VkF‚Ü5ˆ-¬‰)ÚAк Ž‰‹Gü3^¯`;ã”@¥k¸Ë1éÓ›ždˆeÌòt¡R—¥¥„Œ|ªwŸ»þÐ?¾|[ŒÓæsM¾]FÄ)3ã ôs"‚â”9:†ÿõŸŽß–
+„§è n›ÃX3ѱG¶êƒJš»›—à JZ8f½æí‘öˆ½†'>Á½š/³2éÊÌ‘þËÂb•JC\–úx «Tæ¤ýVÏýð·ö–3vy5¡êRa@à*óÜ"ˆ#ñû¢ÀUæ$\-¼‰/=|äJ,pµ09Üz†Ø„óîÔöÁxÆAЛNµÛõàˆMpU·loš iÎW$>AýÒÇêp>› ¢!³â¬»YîIíæÏ÷3†ï¢W²ˆ\¿L`®”‚ê ±,
+’Po)b`À™¦‚š(ÀB>Ý÷§aW‡T<À­€iœ@xï›ã84ŸNcÓw³fç2n^Ë:?ÐZÖyæ B{t¤fÕ\è-K†Þæ®þçÔ ukKh_o9O×ÿÅ$À Œ÷9?
+J€›¸÷¦oŸš˜¡˜!c†xØ÷ʘ™¸tŪŒˆ ¬"±¶h¨ ™Ääa‘¹u@áœrˆÜ%ì
+ÇU½½èÕÄ>BÕ’êe–
+Å>‚sôAÃÂsð@à<t=`»µj«aRÆ„
+ðv‰ vsEéêX aõ:ˆ:V€b„Olµ$V˜&C‘"5Šøç'럞ÂÞÎÀÔ\ù€v€Zq¨°@µ5É3TmFÝ-×»]ý4Ɯ˄ÏR¨Šx ”r‚ ªÖ ¤G’@`wõ_õnŒ‚ÄÆV{4Àí4
+E€†S”ùp4€à=åw>(LÓ{ÎÕ9½Çï‹Ò{.Ìd8½·à&÷Ð 1bé½1½\²ea«˜3­\²lQ£ØysYñZvÈG¬ þåzXä 5gØ3SÂC"±,»çŒºgn>WÝcØŒLØZ/\#w!bç "
+‚û"ÖQRPËÆûº›1¦÷ieqSadÔÏÀÀ¯dEäÍRFÍ ×§ñóöåi¦mE
+}¥V"‡Øþ¥$Ko… ì*¢ŸÜÂþMÁV
+endobj
+294 0 obj
+2304
+endobj
+295 0 obj<</Type/Page/Parent 207 0 R/Contents 296 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 5 0 R/F5 6 0 R/F8 8 0 R/F9 9 0 R>>>>/Annots 151 0 R>>endobj
+296 0 obj<</Length 297 0 R/Filter/FlateDecode>>stream
+xÚÍ–MÚ0†ïü
+Û^ÄNr¤Û²—VÚ.YõX¥!@*’Ð$´Úß™ØCÃ
+vX%‰ù9ØvPÝ”©që_¥¢üö
+^/¨9ZœÓ–‡Ö¨¢x ¸€°Ç²(sÚ½íò5‚˜ÊûPã’*Ž¨î%Pp»LË'Šcªduõ{D%îB
+ ×=p—5˲z_uEµ±XÞË)7VKúT*Ž U™Æu©Òý&¿mòÆ“IlI˜§s˜nCêêqŒ‰ ô×å˜~ÙÕÒstœ`@ñ/¸‹²ìš6ž½¹ÁX‰ãƒœÊ 0¨ÏõÚƒr‚•÷L(pÜßv,Ã
+™øFgïâA T_Vúâi¾:­mªJ²¹T¨2gsѶߢÒ,ËÛöûÖë„1508E,Ýø¿\ÝI±»d ¼Bêƒ1}ÃkŒ”¦Ö]aŒ%CuT§dÛmIyÓÔß·VÓðhû-ÐcŠ¦â^²³.Ã1« Æô ¯1³ÆuÆX2¼Øœ‘M÷@Ú¥ëü‚/‘ƒC_Ø)Ïô<¯/ÑÝø¢póÈ»χ7¼/³yB¦äñõyAõªû“Bý›-Ë¢*Ú®I»ºiÉ—´Ú§[¤L¹À·‡i(úÍ…Uòëä/
+KáŠendstream
+endobj
+297 0 obj
+714
+endobj
+298 0 obj<</Count 7/First 299 0 R/Last 347 0 R>>endobj
+299 0 obj<</Parent 298 0 R/Title(Table of Contents)/Dest[292 0 R/XYZ null 756 null]/Next 300 0 R>>endobj
+300 0 obj<</Parent 298 0 R/Count -2/First 301 0 R/Last 302 0 R/Title(Preface)/Dest[214 0 R/XYZ null 743 null]/Prev 299 0 R/Next 303 0 R>>endobj
+301 0 obj<</Parent 300 0 R/Title(System Overview)/Dest[214 0 R/XYZ null 371 null]/Next 302 0 R>>endobj
+302 0 obj<</Parent 300 0 R/Title(Document Overview)/Dest[217 0 R/XYZ null 736 null]/Prev 301 0 R>>endobj
+303 0 obj<</Parent 298 0 R/Count -7/First 304 0 R/Last 310 0 R/Title(1 - Printing System Overview)/Dest[220 0 R/XYZ null 743 null]/Prev 300 0 R/Next 311 0 R>>endobj
+304 0 obj<</Parent 303 0 R/Title(The Printing Problem)/Dest[220 0 R/XYZ null 371 null]/Next 305 0 R>>endobj
+305 0 obj<</Parent 303 0 R/Title(The Technology)/Dest[223 0 R/XYZ null 736 null]/Prev 304 0 R/Next 306 0 R>>endobj
+306 0 obj<</Parent 303 0 R/Title(Jobs)/Dest[223 0 R/XYZ null 478 null]/Prev 305 0 R/Next 307 0 R>>endobj
+307 0 obj<</Parent 303 0 R/Title(Classes)/Dest[223 0 R/XYZ null 372 null]/Prev 306 0 R/Next 308 0 R>>endobj
+308 0 obj<</Parent 303 0 R/Title(Filters)/Dest[223 0 R/XYZ null 280 null]/Prev 307 0 R/Next 309 0 R>>endobj
+309 0 obj<</Parent 303 0 R/Title(Printer Drivers)/Dest[226 0 R/XYZ null 736 null]/Prev 308 0 R/Next 310 0 R>>endobj
+310 0 obj<</Parent 303 0 R/Title(Networking)/Dest[226 0 R/XYZ null 636 null]/Prev 309 0 R>>endobj
+311 0 obj<</Parent 298 0 R/Count -2/First 312 0 R/Last 317 0 R/Title(2 - Building and Installing CUPS)/Dest[232 0 R/XYZ null 743 null]/Prev 303 0 R/Next 318 0 R>>endobj
+312 0 obj<</Parent 311 0 R/Count -4/First 313 0 R/Last 316 0 R/Title(Installing a Source Distribution)/Dest[232 0 R/XYZ null 358 null]/Next 317 0 R>>endobj
+313 0 obj<</Parent 312 0 R/Title(Requirements)/Dest[232 0 R/XYZ null 296 null]/Next 314 0 R>>endobj
+314 0 obj<</Parent 312 0 R/Title(Compiling CUPS)/Dest[235 0 R/XYZ null 641 null]/Prev 313 0 R/Next 315 0 R>>endobj
+315 0 obj<</Parent 312 0 R/Title(Installing the Software)/Dest[235 0 R/XYZ null 226 null]/Prev 314 0 R/Next 316 0 R>>endobj
+316 0 obj<</Parent 312 0 R/Title(Running the Software)/Dest[238 0 R/XYZ null 729 null]/Prev 315 0 R>>endobj
+317 0 obj<</Parent 311 0 R/Title(Installing a Binary Distribution)/Dest[238 0 R/XYZ null 668 null]/Prev 312 0 R>>endobj
+318 0 obj<</Parent 298 0 R/Count -7/First 319 0 R/Last 326 0 R/Title(3 - Printer Queue Management)/Dest[244 0 R/XYZ null 743 null]/Prev 311 0 R/Next 327 0 R>>endobj
+319 0 obj<</Parent 318 0 R/Title(The lpadmin Command)/Dest[244 0 R/XYZ null 371 null]/Next 320 0 R>>endobj
+320 0 obj<</Parent 318 0 R/Count -1/First 321 0 R/Last 321 0 R/Title(Adding and Modifying Printers)/Dest[244 0 R/XYZ null 266 null]/Prev 319 0 R/Next 322 0 R>>endobj
+321 0 obj<</Parent 320 0 R/Title(Using Standard Printer Drivers)/Dest[247 0 R/XYZ null 135 null]>>endobj
+322 0 obj<</Parent 318 0 R/Title(Removing Printers)/Dest[250 0 R/XYZ null 621 null]/Prev 320 0 R/Next 323 0 R>>endobj
+323 0 obj<</Parent 318 0 R/Title(Printer Classes)/Dest[250 0 R/XYZ null 518 null]/Prev 322 0 R/Next 324 0 R>>endobj
+324 0 obj<</Parent 318 0 R/Title(Setting the Default Printer)/Dest[250 0 R/XYZ null 311 null]/Prev 323 0 R/Next 325 0 R>>endobj
+325 0 obj<</Parent 318 0 R/Title(Starting and Stopping Printers)/Dest[250 0 R/XYZ null 168 null]/Prev 324 0 R/Next 326 0 R>>endobj
+326 0 obj<</Parent 318 0 R/Title(Accepting and Rejecting Print Jobs)/Dest[253 0 R/XYZ null 659 null]/Prev 325 0 R>>endobj
+327 0 obj<</Parent 298 0 R/Count -5/First 328 0 R/Last 344 0 R/Title(4 - Printing System Management)/Dest[256 0 R/XYZ null 743 null]/Prev 318 0 R/Next 347 0 R>>endobj
+328 0 obj<</Parent 327 0 R/Title(Changing the Configuration Files)/Dest[256 0 R/XYZ null 371 null]/Next 329 0 R>>endobj
+329 0 obj<</Parent 327 0 R/Title(Temporary Files)/Dest[256 0 R/XYZ null 189 null]/Prev 328 0 R/Next 330 0 R>>endobj
+330 0 obj<</Parent 327 0 R/Count -4/First 331 0 R/Last 334 0 R/Title(Network Configuration)/Dest[259 0 R/XYZ null 586 null]/Prev 329 0 R/Next 335 0 R>>endobj
+331 0 obj<</Parent 330 0 R/Title(Port)/Dest[259 0 R/XYZ null 448 null]/Next 332 0 R>>endobj
+332 0 obj<</Parent 330 0 R/Title(Listen)/Dest[259 0 R/XYZ null 349 null]/Prev 331 0 R/Next 333 0 R>>endobj
+333 0 obj<</Parent 330 0 R/Title(BrowsePort)/Dest[259 0 R/XYZ null 160 null]/Prev 332 0 R/Next 334 0 R>>endobj
+334 0 obj<</Parent 330 0 R/Title(BrowseAddress)/Dest[262 0 R/XYZ null 690 null]/Prev 333 0 R>>endobj
+335 0 obj<</Parent 327 0 R/Count -8/First 336 0 R/Last 343 0 R/Title(Printer Security)/Dest[262 0 R/XYZ null 531 null]/Prev 330 0 R/Next 344 0 R>>endobj
+336 0 obj<</Parent 335 0 R/Title(Location)/Dest[262 0 R/XYZ null 446 null]/Next 337 0 R>>endobj
+337 0 obj<</Parent 335 0 R/Title(Order)/Dest[265 0 R/XYZ null 729 null]/Prev 336 0 R/Next 338 0 R>>endobj
+338 0 obj<</Parent 335 0 R/Title(Allow)/Dest[265 0 R/XYZ null 580 null]/Prev 337 0 R/Next 339 0 R>>endobj
+339 0 obj<</Parent 335 0 R/Title(Deny)/Dest[268 0 R/XYZ null 729 null]/Prev 338 0 R/Next 340 0 R>>endobj
+340 0 obj<</Parent 335 0 R/Title(AuthType)/Dest[268 0 R/XYZ null 238 null]/Prev 339 0 R/Next 341 0 R>>endobj
+341 0 obj<</Parent 335 0 R/Title(AuthClass)/Dest[271 0 R/XYZ null 729 null]/Prev 340 0 R/Next 342 0 R>>endobj
+342 0 obj<</Parent 335 0 R/Title(AuthGroupName)/Dest[271 0 R/XYZ null 541 null]/Prev 341 0 R/Next 343 0 R>>endobj
+343 0 obj<</Parent 335 0 R/Title(SystemGroup)/Dest[271 0 R/XYZ null 442 null]/Prev 342 0 R>>endobj
+344 0 obj<</Parent 327 0 R/Count -2/First 345 0 R/Last 346 0 R/Title(File Formats)/Dest[271 0 R/XYZ null 363 null]/Prev 335 0 R>>endobj
+345 0 obj<</Parent 344 0 R/Title(mime.types)/Dest[271 0 R/XYZ null 251 null]/Next 346 0 R>>endobj
+346 0 obj<</Parent 344 0 R/Title(mime.convs)/Dest[274 0 R/XYZ null 452 null]/Prev 345 0 R>>endobj
+347 0 obj<</Parent 298 0 R/Count -4/First 348 0 R/Last 351 0 R/Title(5 - Printer Accounting)/Dest[280 0 R/XYZ null 743 null]/Prev 327 0 R>>endobj
+348 0 obj<</Parent 347 0 R/Title(Where to Find the Log Files)/Dest[280 0 R/XYZ null 384 null]/Next 349 0 R>>endobj
+349 0 obj<</Parent 347 0 R/Title(The access_log File)/Dest[280 0 R/XYZ null 292 null]/Prev 348 0 R/Next 350 0 R>>endobj
+350 0 obj<</Parent 347 0 R/Title(The error_log File)/Dest[283 0 R/XYZ null 461 null]/Prev 349 0 R/Next 351 0 R>>endobj
+351 0 obj<</Parent 347 0 R/Title(The page_log File)/Dest[286 0 R/XYZ null 736 null]/Prev 350 0 R>>endobj
+352 0 obj<</Type/Catalog/Pages 207 0 R/Names 152 0 R/ViewerPreferences<</PageLayout/TwoColumnRight>>/Outlines 298 0 R/PageMode/UseOutlines/OpenAction[214 0 R/XYZ null null null]>>endobj
+xref
+0 353
+0000000000 65535 f
+0000000015 00000 n
+0000000227 00000 n
+0000000288 00000 n
+0000000362 00000 n
+0000000444 00000 n
+0000000522 00000 n
+0000000599 00000 n
+0000000678 00000 n
+0000000754 00000 n
+0000000835 00000 n
+0000000894 00000 n
+0000000999 00000 n
+0000001104 00000 n
+0000001209 00000 n
+0000001314 00000 n
+0000001379 00000 n
+0000001464 00000 n
+0000001516 00000 n
+0000001574 00000 n
+0000001658 00000 n
+0000001718 00000 n
+0000001802 00000 n
+0000001833 00000 n
+0000001937 00000 n
+0000002042 00000 n
+0000002147 00000 n
+0000002252 00000 n
+0000002357 00000 n
+0000002460 00000 n
+0000002563 00000 n
+0000002667 00000 n
+0000002772 00000 n
+0000002877 00000 n
+0000002982 00000 n
+0000003087 00000 n
+0000003192 00000 n
+0000003297 00000 n
+0000003402 00000 n
+0000003507 00000 n
+0000003612 00000 n
+0000003717 00000 n
+0000003822 00000 n
+0000003927 00000 n
+0000004032 00000 n
+0000004135 00000 n
+0000004238 00000 n
+0000004342 00000 n
+0000004447 00000 n
+0000004552 00000 n
+0000004657 00000 n
+0000004762 00000 n
+0000004867 00000 n
+0000004972 00000 n
+0000005077 00000 n
+0000005182 00000 n
+0000005287 00000 n
+0000005392 00000 n
+0000005497 00000 n
+0000005602 00000 n
+0000005707 00000 n
+0000005812 00000 n
+0000005917 00000 n
+0000006022 00000 n
+0000006127 00000 n
+0000006232 00000 n
+0000006337 00000 n
+0000006442 00000 n
+0000006545 00000 n
+0000006648 00000 n
+0000006752 00000 n
+0000006857 00000 n
+0000006962 00000 n
+0000007067 00000 n
+0000007172 00000 n
+0000007277 00000 n
+0000007382 00000 n
+0000007487 00000 n
+0000007592 00000 n
+0000007697 00000 n
+0000007802 00000 n
+0000007907 00000 n
+0000008012 00000 n
+0000008117 00000 n
+0000008222 00000 n
+0000008327 00000 n
+0000008432 00000 n
+0000008537 00000 n
+0000008642 00000 n
+0000008747 00000 n
+0000008852 00000 n
+0000008957 00000 n
+0000009062 00000 n
+0000009167 00000 n
+0000009272 00000 n
+0000009377 00000 n
+0000009482 00000 n
+0000009587 00000 n
+0000009692 00000 n
+0000009797 00000 n
+0000009902 00000 n
+0000010006 00000 n
+0000010110 00000 n
+0000010215 00000 n
+0000010321 00000 n
+0000010427 00000 n
+0000010533 00000 n
+0000010639 00000 n
+0000010745 00000 n
+0000010851 00000 n
+0000010957 00000 n
+0000011063 00000 n
+0000011169 00000 n
+0000011275 00000 n
+0000011381 00000 n
+0000011487 00000 n
+0000011593 00000 n
+0000011699 00000 n
+0000011805 00000 n
+0000011911 00000 n
+0000012017 00000 n
+0000012123 00000 n
+0000012229 00000 n
+0000012335 00000 n
+0000012440 00000 n
+0000012544 00000 n
+0000012648 00000 n
+0000013413 00000 n
+0000013519 00000 n
+0000013623 00000 n
+0000013728 00000 n
+0000013834 00000 n
+0000013940 00000 n
+0000014044 00000 n
+0000014148 00000 n
+0000014252 00000 n
+0000014357 00000 n
+0000014462 00000 n
+0000014568 00000 n
+0000014674 00000 n
+0000014780 00000 n
+0000014886 00000 n
+0000014992 00000 n
+0000015096 00000 n
+0000015201 00000 n
+0000015307 00000 n
+0000015411 00000 n
+0000015516 00000 n
+0000015622 00000 n
+0000015726 00000 n
+0000015831 00000 n
+0000015937 00000 n
+0000016147 00000 n
+0000016181 00000 n
+0000016215 00000 n
+0000016916 00000 n
+0000016965 00000 n
+0000017014 00000 n
+0000017063 00000 n
+0000017112 00000 n
+0000017161 00000 n
+0000017210 00000 n
+0000017259 00000 n
+0000017308 00000 n
+0000017357 00000 n
+0000017406 00000 n
+0000017455 00000 n
+0000017504 00000 n
+0000017553 00000 n
+0000017602 00000 n
+0000017651 00000 n
+0000017700 00000 n
+0000017749 00000 n
+0000017798 00000 n
+0000017847 00000 n
+0000017896 00000 n
+0000017945 00000 n
+0000017994 00000 n
+0000018043 00000 n
+0000018092 00000 n
+0000018141 00000 n
+0000018190 00000 n
+0000018239 00000 n
+0000018288 00000 n
+0000018337 00000 n
+0000018386 00000 n
+0000018435 00000 n
+0000018484 00000 n
+0000018533 00000 n
+0000018582 00000 n
+0000018631 00000 n
+0000018680 00000 n
+0000018729 00000 n
+0000018778 00000 n
+0000018827 00000 n
+0000018876 00000 n
+0000018925 00000 n
+0000018974 00000 n
+0000019023 00000 n
+0000019072 00000 n
+0000019121 00000 n
+0000019170 00000 n
+0000019219 00000 n
+0000019268 00000 n
+0000019317 00000 n
+0000019366 00000 n
+0000019415 00000 n
+0000019464 00000 n
+0000019773 00000 n
+0000019925 00000 n
+0000026317 00000 n
+0000026339 00000 n
+0000026452 00000 n
+0000026554 00000 n
+0000026574 00000 n
+0000026705 00000 n
+0000027474 00000 n
+0000027495 00000 n
+0000027636 00000 n
+0000028014 00000 n
+0000028035 00000 n
+0000028175 00000 n
+0000029089 00000 n
+0000029110 00000 n
+0000029250 00000 n
+0000030693 00000 n
+0000030715 00000 n
+0000030855 00000 n
+0000031753 00000 n
+0000031774 00000 n
+0000031887 00000 n
+0000032089 00000 n
+0000032110 00000 n
+0000032264 00000 n
+0000033166 00000 n
+0000033187 00000 n
+0000033341 00000 n
+0000034270 00000 n
+0000034291 00000 n
+0000034440 00000 n
+0000035151 00000 n
+0000035172 00000 n
+0000035285 00000 n
+0000035488 00000 n
+0000035509 00000 n
+0000035667 00000 n
+0000036420 00000 n
+0000036441 00000 n
+0000036599 00000 n
+0000037553 00000 n
+0000037574 00000 n
+0000037732 00000 n
+0000038718 00000 n
+0000038739 00000 n
+0000038888 00000 n
+0000039611 00000 n
+0000039632 00000 n
+0000039772 00000 n
+0000040430 00000 n
+0000040451 00000 n
+0000040600 00000 n
+0000041626 00000 n
+0000041647 00000 n
+0000041806 00000 n
+0000042799 00000 n
+0000042820 00000 n
+0000042988 00000 n
+0000043900 00000 n
+0000043921 00000 n
+0000044080 00000 n
+0000044967 00000 n
+0000044988 00000 n
+0000045138 00000 n
+0000046183 00000 n
+0000046204 00000 n
+0000046363 00000 n
+0000047816 00000 n
+0000047838 00000 n
+0000047969 00000 n
+0000048297 00000 n
+0000048318 00000 n
+0000048467 00000 n
+0000049190 00000 n
+0000049211 00000 n
+0000049370 00000 n
+0000050451 00000 n
+0000050473 00000 n
+0000050622 00000 n
+0000051402 00000 n
+0000051423 00000 n
+0000051536 00000 n
+0000051740 00000 n
+0000051761 00000 n
+0000051916 00000 n
+0000054291 00000 n
+0000054313 00000 n
+0000054468 00000 n
+0000055253 00000 n
+0000055274 00000 n
+0000055329 00000 n
+0000055434 00000 n
+0000055578 00000 n
+0000055681 00000 n
+0000055786 00000 n
+0000055951 00000 n
+0000056059 00000 n
+0000056174 00000 n
+0000056279 00000 n
+0000056387 00000 n
+0000056495 00000 n
+0000056611 00000 n
+0000056709 00000 n
+0000056878 00000 n
+0000057034 00000 n
+0000057134 00000 n
+0000057249 00000 n
+0000057373 00000 n
+0000057481 00000 n
+0000057601 00000 n
+0000057766 00000 n
+0000057873 00000 n
+0000058039 00000 n
+0000058144 00000 n
+0000058262 00000 n
+0000058378 00000 n
+0000058506 00000 n
+0000058637 00000 n
+0000058759 00000 n
+0000058926 00000 n
+0000059046 00000 n
+0000059162 00000 n
+0000059320 00000 n
+0000059412 00000 n
+0000059519 00000 n
+0000059630 00000 n
+0000059731 00000 n
+0000059884 00000 n
+0000059980 00000 n
+0000060086 00000 n
+0000060192 00000 n
+0000060297 00000 n
+0000060406 00000 n
+0000060516 00000 n
+0000060630 00000 n
+0000060729 00000 n
+0000060865 00000 n
+0000060963 00000 n
+0000061061 00000 n
+0000061207 00000 n
+0000061322 00000 n
+0000061442 00000 n
+0000061561 00000 n
+0000061666 00000 n
+trailer
+<</Size 353/Root 352 0 R/Info 1 0 R>>
+startxref
+61852
+%%EOF
diff --git a/doc/sam.shtml b/doc/sam.shtml
new file mode 100644
index 000000000..bffb8c2a8
--- /dev/null
+++ b/doc/sam.shtml
@@ -0,0 +1,1038 @@
+<HTML>
+<HEAD>
+ <META NAME="COPYRIGHT" CONTENT="Copyright 1997-1999, All Rights Reserved">
+ <META NAME="DOCNUMBER" CONTENT="CUPS-SAM-1.0.0">
+ <META NAME="Author" CONTENT="Easy Software Products">
+ <TITLE>DRAFT - CUPS Software Administrators Manual</TITLE>
+</HEAD>
+<BODY>
+
+<H1 ALIGN=RIGHT>Preface</H1>
+
+This software administrators manual provides printer administration
+information for the Common UNIX Printing System ("CUPS") Version 1.0.0.
+
+<H2>System Overview</H2>
+
+The Common UNIX Printing System provides a portable printing layer for
+UNIX&reg; operating systems. It has been developed by Easy Software
+Products to promote a standard printing solution for all UNIX vendors
+and users. CUPS provides the System V and Berkeley command-line interfaces.
+
+<P>CUPS uses the Internet Printing Protocol (IETF-IPP) as the basis
+for managing print jobs and queues. The Line Printer Daemon (LPD,
+RFC1179), Server Message Block (SMB), and AppSocket protocols are also
+supported with reduced functionality.
+
+<P>CUPS adds network printer browsing and PostScript Printer
+Description (&quot;PPD&quot;)-based printing options to support real
+world applications under UNIX.
+
+<P>CUPS also includes a customized version of GNU GhostScript
+(currently based off GNU GhostScript 4.03) and an image file RIP that
+can be used to support non-PostScript printers.
+
+<H2>Document Overview</H2>
+
+<P>This software administrators manual is organized into the following sections:</P>
+
+<UL>
+ <LI>1 - Printing System Overview</LI>
+ <LI>2 - Building and Installing CUPS</LI>
+ <LI>3 - Printer Queue Management</LI>
+ <LI>4 - Printing System Management</LI>
+ <LI>5 - Printer Accounting</LI>
+</UL>
+
+<H1 ALIGN=RIGHT>1 - Printing System Overview</H1>
+
+<P>This chapter provides an overview of how the Common UNIX Printing System
+works.
+
+<H2>The Printing Problem</H2>
+
+<P>For years <I>the printing problem</I> has plagued UNIX&reg;. Unlike
+Microsoft&reg; Windows&reg; or MacOS, UNIX has no standard interface or
+system in place for supporting printers. Among the solutions previously
+available, the Berkeley and System V printing systems are the most
+prevalent.
+
+<P>These printing systems support line printers (text only) or
+PostScript printers (text and graphics), and with some coaxing they can
+be made to support a full range of printers and file formats. However,
+because each varient of the UNIX operating system uses a different
+printing system than the next, developing printer drivers for a wide
+range of printers is extremely difficult. That combined with the
+limited volume of customers for each UNIX varient has forced most
+printer vendors to give up supporting UNIX entirely.
+
+<P>The Common UNIX Printing System, or CUPS, is designed to eliminate
+<I>the printing problem</I>. One common printing system can be used by
+all UNIX varients to support the printing needs of users. Printer
+vendors can use its modular filter interface to develop a single driver
+program that supports a wide range of file formats with little or no
+effort. Since CUPS provides both the System V and Berkeley printing
+commands, users (and applications) can reap the benefits of this new
+technology with no changes.
+
+<H2>The Technology</H2>
+
+<P>CUPS is based upon an emerging Internet standard called the Internet
+Printing Protocol, or IPP. IPP has been embraced by dozens of printer
+and printer server manufacturers, and will be supported by the next
+Microsoft Windows operating system.
+
+<P>IPP defines a standard protocol for printing as well as managing print
+jobs and printer options like media size, resolution, and so forth. Like all
+IP-based protocols, IPP can be used locally or over the Internet to printers
+hundreds or thousands of miles away. Unlike other protocols, however, IPP
+also supports access control, authentication, and encryption, making it a
+much more secure printing solution than older ones.
+
+<P>IPP is layered on top of the Hyper-Text Transport Protocol, or HTTP,
+which is the basis of web servers on the Internet. This allows the user to
+view documentation and status information on a printer or server using their
+web browser.
+
+<P>CUPS provides a complete IPP/1.0-based printing system that provides Basic
+authentication and domain or IP-based access control. Digest authentication
+and TLS encryption will be available in future versions of CUPS.
+
+<H2>Jobs</H2>
+
+<P>Each file that is submitted for printing is called a <I>job</I>.
+Jobs are identified by a unique number starting at 1 and are assigned
+to a particular destination (usually a printer). Jobs can also have
+options associated with them such as media size, number of copies, and
+priority.
+
+<H2>Classes</H2>
+
+<P>CUPS supports collections of printers known as <I>classes</I>. Jobs sent
+to a class are forwarded to the first available printer in the class.
+
+<H2>Filters</H2>
+
+<P>Filters allow a user or application to print many types of files
+without extra effort. Print jobs sent to a CUPS server are filtered
+before sending them to a printer. Some filters convert job files to
+different formats that the printer can understand. Others perform page
+selection and ordering tasks. <I>Backend</I> filters perform the most
+important task of all - they send the filtered print data to the
+printer.
+
+<P>CUPS provides filters for printing many types of image files,
+HP-GL/2 files, PDF files, and text files. CUPS also supplies
+PostScript and image file Raster Image Processors, or RIPs, that
+convert PostScript or image files into bitmaps that can be sent to a
+raster printer.
+
+<P>CUPS provides backends for printing over parallel and serial ports,
+and over the network via the JetDirect (AppSocket), Server Message
+Block, and Line Printer Daemon protocols.
+
+<H2>Printer Drivers</H2>
+
+<P>Printer drivers in CUPS consist of one of more filters specific to a
+printer. CUPS includes a sample printer driver for Hewlett-Packard
+LaserJet and DeskJet printers. While this driver does not generate
+optimal output for different printer models, it does demonstrate how
+you can write your own printer drivers and incorporate them into CUPS.
+
+<H2>Networking</H2>
+
+<P>Printers and classes on the local system are automatically shared with
+other systems on the network. This allows you to setup one system to print
+to a printer and use this system as a printer server or spool host for all
+of the others. If there is only one occurrence of a printer on a network,
+then that printer can be accessed using its name alone. If more than one
+printer exists with the same name, users must select the printer by specifying
+which server to use (e.g. "printer@host1" or "printer@host2".)
+
+<P>CUPS also provides <I>implicit classes</I>, which are collections of
+printers and/or classes with the same name. This allows you to setup multiple
+servers pointing to the same physical network printer, for example, so that
+you aren't relying on a single system for printing. Because this also works
+with printer classes, you can setup multiple servers and printers and never
+worry about a "single point of failure" unless all of the printers and servers
+goes down!
+
+<H1 ALIGN=RIGHT>2 - Building and Installing CUPS</H1>
+
+<P>This chapter shows how to build and install the Common UNIX Printing System.
+If you are installing a binary distribution from the CUPS web site, proceed to
+the section titled, <A HREF="#binary">Installing a Binary Distribution</A>.
+
+<H2>Installing a Source Distribution</H2>
+
+<H3>Requirements</H3>
+
+<P>You'll need an ANSI C compiler to build CUPS on your system. As its name
+implies, CUPS is designed to run on the UNIX operating system, however
+the CUPS interface library and most of the filters and backends supplied
+with CUPS should also run under Microsoft&reg; Windows&reg;.
+
+<P>For the image file filters and PostScript RIP, you'll need the JPEG,
+PNG, TIFF, and ZLIB libraries. CUPS will build without these, but with
+reduced functionality. Easy Software Products maintains a mirror of the
+current versions of these libraries at:
+
+<UL><PRE>
+<A HREF="ftp://ftp.easysw.com/pub/libraries">ftp://ftp.easysw.com/pub/libraries</A>
+</PRE></UL>
+
+<P>If you make changes to the man pages you'll need GNU groff or another
+nroff-like package. GNU groff is available from:
+
+<UL><PRE>
+<A HREF="ftp://ftp.gnu.org/pub/groff">ftp://ftp.gnu.org/pub/groff</A>
+</PRE></UL>
+
+<P>The documentation is formatted using the HTMLDOC software. If you need to
+make changes you can get the HTMLDOC software from:
+
+<UL><PRE>
+<A HREF="http://www.easysw.com/htmldoc">http://www.easysw.com/htmldoc</A>
+</PRE></UL>
+
+<H3>Compiling CUPS</H3>
+
+<P>CUPS uses GNU autoconf to configure the makefiles and source code
+for your system. To configure CUPS for your system type:
+
+<UL><PRE>
+% ./configure ENTER
+</PRE></UL>
+
+<P>The default installation will put the CUPS software in the
+<CODE>/usr</CODE> and <CODE>/var</CODE> directories on your system,
+which will overwrite any existing printing commands on your system. To
+install the CUPS software in another location use the
+<CODE>--prefix</CODE> option:
+
+<UL><PRE>
+% ./configure --prefix=/usr/local ENTER
+</PRE></UL>
+
+<P>If the PNG, JPEG, TIFF, and ZLIB libraries are not installed in a
+system default location (typically <CODE>/usr/include</CODE> and
+<CODE>/usr/lib</CODE>) you'll need to set the <CODE>CFLAGS</CODE> and
+<CODE>LDFLAGS</CODE> environment variables prior to running configure:
+
+<UL><PRE>
+% setenv CFLAGS "-I/some/directory"
+% setenv LDFLAGS "-L/some/directory"
+% ./configure ... ENTER
+</PRE></UL>
+
+<P>Once you have configured things, just type:
+
+<UL><PRE>
+% make ENTER
+</PRE></UL>
+
+<P>to build the software.
+
+<H3>Installing the Software</H3>
+
+<P>To install the software type:
+
+<UL><PRE>
+% make install ENTER
+</PRE></UL>
+
+<H3>Running the Software</A></H3>
+
+Once you have installed the software you can start the CUPS daemon by
+typing:
+
+<UL><PRE>
+% /usr/sbin/cupsd &amp; ENTER
+</PRE></UL>
+
+<H2><A NAME="binary">Installing a Binary Distribution</A></H2>
+
+<P>We are currently distributing CUPS binary distributions in TAR format
+with installation and removal scripts.
+
+<UL>
+ <B>WARNING:</B>
+
+ <P>Installing CUPS will overwrite your existing printing
+ system. If you experience difficulties with the CUPS software
+ and need to go back to your old printing system, you will need
+ to remove the CUPS software with the provided script and
+ reinstall the printing system from your operating system CDs.
+</UL>
+
+<P>To install the CUPS software you will need to be logged in as root
+(doing an "su" is good enough). Once you are the root user, run the
+installation script with:
+
+<UL><PRE>
+./cups.install ENTER
+</PRE></UL>
+
+<P>After asking you a few yes/no questions the CUPS software will be
+installed and the scheduler will be started automatically.
+
+<H1 ALIGN=RIGHT>3 - Printer Queue Management</H1>
+
+<P>This chapter discusses how to add, modify, and delete print queues
+on your system.
+
+<H2>The lpadmin Command</H2>
+
+<P>The <CODE>lpadmin</CODE> command allows you to perform most printer
+administration tasks from the command-line. Since <CODE>lpadmin</CODE>
+is also a System V printing system command, it is located in the
+<CODE>/usr/lib</CODE> directory instead of a more common one like
+<CODE>/usr/bin</CODE> or <CODE>/usr/sbin</CODE>.
+
+<H2>Adding and Modifying Printers</H2>
+
+<P>To add a printer to CUPS you simply run the <CODE>lpadmin</CODE> command
+with the "-p" option:
+
+<UL><PRE>
+% /usr/lib/lpadmin -p<I>printer</I> -E -v<I>device</I> -P<I>ppd</I> ENTER
+</PRE></UL>
+
+<P>Spaces between the option letter and value are optional.
+
+<P>The <I>printer</I> name can be up to 127 letters, digits, hyphens,
+and underscores. Unlike other printing systems, the printer name in
+CUPS is <I>not</I> case-sensitive, so you can't add two printers named
+<CODE>LaserJet</CODE> and <CODE>laserjet</CODE>.
+
+<P>The <I>device</I> argument specifies the device URI or filename for the
+printer. The following devices are supported in a basic installation of
+CUPS:
+
+<DL>
+
+ <DT>file:/dev/filename
+ <DT>/dev/filename
+ <DD>Sends all output to the specified file.
+
+ <DT>http://[username:password@]hostname[:port]/resource
+ <DT>ipp://[username:password@]hostname[:port]/resource
+ <DD>Sends all output to the specified IPP printer or server.
+ The <I>port</I> parameters defaults to 631.
+
+ <DT>lpd://hostname/queue
+ <DD>Sends all output to the specified LPD printer queue.
+
+ <DT>parallel:/dev/filename
+ <DD>Sends all output to the specified parallel port device.
+
+ <DT>serial:/dev/filename[?options]
+ <DD>Sends all output to the specified serial port device. The
+ <I>options</I> can be any of the following separated by the
+ plus (+) character:
+ <UL>
+ <LI><CODE>baud=<I>rate</I></CODE> - Sets the baud rate
+ for the device.
+ <LI><CODE>bits=<I>7 or 8</I></CODE> - Sets the number
+ of data bits.
+ <LI><CODE>parity=<I>even</I></CODE> - Sets even parity
+ checking.
+ <LI><CODE>parity=<I>odd</I></CODE> - Sets odd parity
+ checking.
+ <LI><CODE>parity=<I>none</I></CODE> - Turns parity
+ checking off.
+ </UL>
+
+ <DT>smb://[username:password@]hostname/queue
+ <DD>Sends all output to the specified SMB (Windows) printer queue
+ using the SAMBA software.
+
+ <DT>socket://hostname[:port]
+ <DD>Sends all output to the specified printer using the
+ AppSocket protocol. The <I>port</I> parameter defaults to 9100.
+
+</DL>
+
+<P>The <I>ppd</I> argument specifies the PostScript Printer Description file
+to use for this printer. Many options (such as media size, etc.) will not
+be available if you omit this part of the <CODE>lpadmin</CODE> command.
+
+<H3>Using Standard Printer Drivers</H2>
+
+<P>The <CODE>lpadmin</CODE> command allows you to use "standard" PPD files
+and interface scripts located in the <CODE>/usr/share/cups/model</CODE>
+directory with the "-m" option:
+
+<UL><PRE>
+% /usr/lib/lpadmin -p<I>printer</I> -E -v<I>device</I> -m<I>model</I> ENTER
+</PRE></UL>
+
+<P>The <I>model</I> argument specifies the name of the PPD file or interface
+script. For example, to add a printer using the sample HP DeskJet series
+driver connected to parallel port 1 under Linux you would use:
+
+<UL><PRE>
+% /usr/lib/lpadmin -pDeskJet -E -vparallel:/dev/par1 -mdeskjet.ppd ENTER
+</PRE></UL>
+
+<H2>Removing Printers</H2>
+
+<P>To remove a printer to CUPS you simply run the <CODE>lpadmin</CODE> command
+with the "-x" option:
+
+<UL><PRE>
+% /usr/lib/lpadmin -x<I>printer</I> ENTER
+</PRE></UL>
+
+<H2>Printer Classes</H2>
+
+<P>CUPS allows you to group similar printers in a <I>printer class</I>. When
+a user sends a print job to a class, the job will be processed by the first
+available printer in that class.
+
+<P>To add a printer to a class you simply run the <CODE>lpadmin</CODE> command
+with the "-p" and "-c" options:
+
+<UL><PRE>
+% /usr/lib/lpadmin -p<I>printer</I> -c<I>class</I> ENTER
+</PRE></UL>
+
+<P>The <I>class</I> is created automatically if it doesn't exist. To remove a
+class just use the "-x" option:
+
+<UL><PRE>
+% /usr/lib/lpadmin -x<I>class</I> ENTER
+</PRE></UL>
+
+<H2>Setting the Default Printer</H2>
+
+<P>To set the default printer or class simply run the <CODE>lpadmin</CODE>
+command with the "-d" option:
+
+<UL><PRE>
+% /usr/lib/lpadmin -d<I>destination</I> ENTER
+</PRE></UL>
+
+<P>The <I>destination</I> argument is the name of the printer or class.
+
+<H2>Starting and Stopping Printers</H2>
+
+<P>The <CODE>enable</CODE> and <CODE>disable</CODE> commands start and stop
+printer queues, respectively:
+
+<UL><PRE>
+% /usr/bin/enable <I>printer</I> ENTER
+% /usr/bin/disable <I>printer</I> ENTER
+</PRE></UL>
+
+<P>Printers that are disabled may still accept jobs for printing, but won't
+actually print any files until they are restarted. This is useful if the
+printer malfunctions and you need time to correct the problem. Any queues
+jobs are printed after the printer is enabled (started).
+
+<H2>Accepting and Rejecting Print Jobs</H2>
+
+<P>The <CODE>accept</CODE> and <CODE>reject</CODE> commands accept and reject
+print jobs for the named printer, respectively:
+
+<UL><PRE>
+% /usr/lib/accept <I>printer</I> ENTER
+% /usr/lib/reject <I>printer</I> ENTER
+</PRE></UL>
+
+<P>As noted above, a printer can be stopped but accepting new print
+jobs. A printer can also be rejecting new print jobs while it finishes
+those that have been queued. This is useful for when you must perform
+maintenance on the printer and will not have it available to users for
+a long period of time.
+
+<H1 ALIGN=RIGHT>4 - Printing System Management</H1>
+
+<P>This chapter shows how you can configure the CUPS server.
+
+<H2>Changing the Configuration Files</H2>
+
+<P>All of the server configuration files are located in the
+<CODE>/var/cups/conf</CODE> directory. Once you have made a change to a
+file you need to restart the CUPS server by sending it a HUP signal or
+using the supplied script "<CODE>cups.sh</CODE>":
+
+<UL><PRE>
+% ./cups.sh restart ENTER
+</PRE></UL>
+
+<P>The binary distribution installs the script in the
+<CODE>init.d</CODE> directory with the name <CODE>lp</CODE> or
+<CODE>lpd</CODE> depending on the vendor-supplied printing system.
+
+<H2>Temporary Files</H2>
+
+<P>Normally CUPS puts all of its temporary files in <CODE>/var/tmp</CODE>.
+If you'd like to change this directory you'll need to edit the
+<CODE>/var/cups/conf/cupsd.conf</CODE> file.
+
+<P>Start by creating the new temporary directory and setting the appropriate
+permissions:
+
+<UL><PRE>
+% mkdir <I>/foo/bar/tmp</I> ENTER
+% chmod a+rwxt <I>/foo/bar/tmp</I> ENTER
+</PRE></UL>
+
+<P>Then change the line containing the <CODE>TempDir</CODE> directive in
+the <CODE>cupsd.conf</CODE> to the directory that you've created:
+
+<UL><PRE>
+TempDir <I>/foo/bar/tmp</I>
+</PRE></UL>
+
+<P>Finally, restart the server as outlined in the first section of this
+chapter.
+
+<H2>Network Configuration</H2>
+
+<P>The default configuration of the CUPS server listens for connections from
+all network interfaces on port 631 (the standard IPP port). Administration
+functions are limited to local connections with the appropriate username and
+password.
+
+<P>If you'd like to limit access to your system you'll need to edit the
+<CODE>/var/cups/conf/cupsd.conf</CODE> file.
+
+<H3>Port</H3>
+
+<P>The <CODE>Port</CODE> directive specifies a port to listen on for
+all interfaces. Besides the standard IPP port (631) you can also setup
+your server to listen on the HTTP port (80) to use your CUPS server as
+a standard web server as well.
+
+<H3>Listen</H3>
+
+<P>The <CODE>Listen</CODE> directive specifies a listening address and port,
+extending the functionality of the <CODE>Port</CODE> directive. If you want
+to allow connections only from the local machine you can use:
+
+<UL><PRE>
+Listen 127.0.0.1:631
+</PRE></UL>
+
+<P>instead of the <CODE>Port</CODE> directive.
+
+<P>If you want to limit access to a specific network/subnet, make sure you
+specify only the network address and not your system's network address!
+
+<H3>BrowsePort</H3>
+
+<P>The <CODE>BrowsePort</CODE> directive controls which port is monitored for
+remote printers. By default it is set to the IPP port (631), however you can
+change it as needed.
+
+<UL>
+
+ <B>NOTE:</B>
+
+ <P>You must set the <CODE>BrowsePort</CODE> to the same value
+ on all of the systems that you want to see.
+
+</UL>
+
+<H3>BrowseAddress</H3>
+
+<P>The <CODE>BrowseAddress</CODE> directive specifies a broadcast address to
+use when sending printer status updates over the network. The default
+browse address is <CODE>255.255.255.255</CODE> which will send printer
+information to all subnets.
+
+<UL>
+
+ <B>NOTE:</B>
+
+ <P>If you are using HP-UX 10.20 and a subnet that is not 24,
+ 16, or 8 bits, printer browsing (and in fact all broadcast
+ reception) will not work. This problem appears to be fixed in
+ HP-UX 11.0.
+
+</UL>
+
+<H2>Printer Security</H2>
+
+<P>CUPS provides IP and domain-name based access control and Basic
+authentication for authentication.
+
+<H3>Location</H3>
+
+<P>The <CODE>Location</CODE> directive defines access control for a
+specific HTTP directory. The following pseudo directories are provided
+by the CUPS server:
+
+<UL>
+
+ <LI><CODE>/admin</CODE> - This is the URI that must be referenced to
+ do printer administation commands.
+
+ <LI><CODE>/classes</CODE> - This is the URI that must be referenced to
+ access printer classes.
+
+ <LI><CODE>/jobs</CODE> - This is the URI that must be referenced to
+ access jobs.
+
+ <LI><CODE>/printers</CODE> - This is the URI that must be referenced to
+ access printers.
+
+</UL>
+
+<P>All other directories are taken from the
+<CODE>/usr/share/cups/doc</CODE> directory.
+
+<P>The <CODE>Location</CODE> directive surrounds the other access control
+directives described below. The default server configuration uses:
+
+<UL><PRE>
+&lt;Location /admin&gt;
+AuthType Basic
+AuthClass System
+
+Order Deny,Allow
+Deny From All
+Allow From 127.0.0.1
+&lt;/Location&gt;
+</PRE></UL>
+
+<H3>Order</H3>
+
+<P>The <CODE>Order</CODE> directive defines the default access control.
+The following values are supported:
+
+<UL>
+
+ <LI><CODE>Order Allow,Deny</CODE> - Allow requests from all
+ systems <I>except</I> for those listed in a <CODE>Deny</CODE>
+ directive.
+
+ <LI><CODE>Order Deny,Allow</CODE> - Allow requests only from
+ those listed in an <CODE>Allow</CODE> directive.
+
+</UL>
+
+<P>The <CODE>Order</CODE> directive must appear inside a
+<CODE>Location</CODE> directive.
+
+<H3>Allow</H3>
+
+<P>The <CODE>Allow</CODE> directive specifies a hostname, IP address, or
+network that is allowed access to the server:
+
+<UL><PRE>
+Allow from All
+Allow from None
+Allow from *.domain.com
+Allow from .domain.com
+Allow from host.domain.com
+Allow from nnn.*
+Allow from nnn.nnn.*
+Allow from nnn.nnn.nnn.*
+Allow from nnn.nnn.nnn.nnn
+Allow from nnn.nnn.nnn.nnn/mm
+Allow from nnn.nnn.nnn.nnn/mmm.mmm.mmm.mmm
+</PRE></UL>
+
+<P><CODE>Allow</CODE> directives are cummulative, so multiple <CODE>Allow</CODE>
+directives can be used to allow access for multiple hosts or networks. The
+<CODE>/mm</CODE> notation specifies a CIDR netmask:
+
+<CENTER><TABLE WIDTH="80%" BORDER="1">
+<TR>
+ <TH>mm</TH>
+ <TH>netmask</TH>
+</TR>
+<TR>
+ <TD>0</TD>
+ <TD>0.0.0.0</TD>
+</TR>
+<TR>
+ <TD>1</TD>
+ <TD>128.0.0.0</TD>
+</TR>
+<TR>
+ <TD>2</TD>
+ <TD>192.0.0.0</TD>
+</TR>
+<TR>
+ <TD>...</TD>
+ <TD>...</TD>
+</TR>
+<TR>
+ <TD>8</TD>
+ <TD>255.0.0.0</TD>
+</TR>
+<TR>
+ <TD>16</TD>
+ <TD>255.255.0.0</TD>
+</TR>
+<TR>
+ <TD>24</TD>
+ <TD>255.255.255.0</TD>
+</TR>
+<TR>
+ <TD>32</TD>
+ <TD>255.255.255.255</TD>
+</TR>
+</TABLE></CENTER>
+
+<P>The <CODE>Allow</CODE> directive must appear inside a
+<CODE>Location</CODE> directive.
+
+<H3>Deny</H3>
+
+<P>The <CODE>Deny</CODE> directive specifies a hostname, IP address, or
+network that is allowed access to the server:
+
+<UL><PRE>
+Deny from All
+Deny from None
+Deny from *.domain.com
+Deny from .domain.com
+Deny from host.domain.com
+Deny from nnn.*
+Deny from nnn.nnn.*
+Deny from nnn.nnn.nnn.*
+Deny from nnn.nnn.nnn.nnn
+Deny from nnn.nnn.nnn.nnn/mm
+Deny from nnn.nnn.nnn.nnn/mmm.mmm.mmm.mmm
+</PRE></UL>
+
+<P><CODE>Deny</CODE> directives are cummulative, so multiple <CODE>Deny</CODE>
+directives can be used to allow access for multiple hosts or networks. The
+<CODE>/mm</CODE> notation specifies a CIDR netmask:
+
+<CENTER><TABLE WIDTH="80%" BORDER="1">
+<TR>
+ <TH>mm</TH>
+ <TH>netmask</TH>
+</TR>
+<TR>
+ <TD>0</TD>
+ <TD>0.0.0.0</TD>
+</TR>
+<TR>
+ <TD>1</TD>
+ <TD>128.0.0.0</TD>
+</TR>
+<TR>
+ <TD>2</TD>
+ <TD>192.0.0.0</TD>
+</TR>
+<TR>
+ <TD>...</TD>
+ <TD>...</TD>
+</TR>
+<TR>
+ <TD>8</TD>
+ <TD>255.0.0.0</TD>
+</TR>
+<TR>
+ <TD>16</TD>
+ <TD>255.255.0.0</TD>
+</TR>
+<TR>
+ <TD>24</TD>
+ <TD>255.255.255.0</TD>
+</TR>
+<TR>
+ <TD>32</TD>
+ <TD>255.255.255.255</TD>
+</TR>
+</TABLE></CENTER>
+
+<P>The <CODE>Deny</CODE> directive must appear inside a
+<CODE>Location</CODE> directive.
+
+<H3>AuthType</H3>
+
+<P>The <CODE>AuthType</CODE> directive defines the type of authentication to
+perform:
+
+<UL>
+
+ <LI><CODE>None</CODE> - No authentication should be performed
+ (default.)
+
+ <LI><CODE>Basic</CODE> - Basic authentication should be
+ performed using the UNIX password and group files.
+
+</UL>
+
+<P>The <CODE>AuthType</CODE> directive must appear inside a
+<CODE>Location</CODE> directive.
+
+<H3>AuthClass</H3>
+
+<P>The <CODE>AuthClass</CODE> directive defines what level of <CODE>Basic</CODE>
+access is required:
+
+<UL>
+
+ <LI><CODE>Anonymous</CODE> - No authentication should be performed
+ (default.)
+
+ <LI><CODE>User</CODE> - A valid username and password is required.
+
+ <LI><CODE>System</CODE> - A valid username and password is
+ required, and the username must belong to the "sys" group (this
+ can be changed using the <CODE>SystemGroup</CODE> directive,
+ below.
+
+ <LI><CODE>Group</CODE> - A valid username and password is
+ required, and the username must belong to the group named by
+ the <CODE>AuthGroupName</CODE> directive.
+
+</UL>
+
+<P>The <CODE>AuthClass</CODE> directive must appear inside a
+<CODE>Location</CODE> directive.
+
+<H3>AuthGroupName</H3>
+
+<P>The <CODE>AuthGroupName</CODE> directive sets the group to use for
+<CODE>Group</CODE> authentication.
+
+<P>The <CODE>AuthGroupName</CODE> directive must appear inside a
+<CODE>Location</CODE> directive.
+
+<H3>SystemGroup</H3>
+
+<P>The <CODE>SystemGroup</CODE> directive sets the administration group used
+when authenticating the <CODE>System</CODE> type. It defaults to the "sys"
+group.
+
+<H2>File Formats</H2>
+
+<P>CUPS provides a MIME-based file typing and filtering mechanism to
+convert files to a printable format for each printer. The
+<CODE>mime.types</CODE> and <CODE>mime.convs</CODE> files define the
+file type and filters that are available on the system.
+
+<H3>mime.types</H3>
+
+<P>The <CODE>mime.types</CODE> defines the known file types. Each line
+of the file starts with the MIME type and may be followed by one or
+more file type recognition rules. For example, the
+<CODE>text/html</CODE> file type is defined as:
+
+<UL><PRE>
+text/html html htm \
+ printable(0,1024) + (string(0,"&lt;HTML&gt;") string(0,"&lt;!DOCTYPE"))
+</PRE></UL>
+
+<P>The first two rules say that any file with an extension of ".html" or
+".htm" is a HTML file. The third rules says that any file whose first
+1024 characters are printable text and starts with the strings "&lt;HTML&gt;"
+or "&lt;!DOCTYPE" is a HTML file as well.
+
+<P>The first two rules deal solely with the name of the file being
+typed. This is useful when the original filename is known, however for
+print files the server doesn't always have a filename to work with. The
+third rule takes care of this possibility and automatically figures out
+the file type based upon the contents of the file instead.
+
+<P>The available tests are:
+
+<UL>
+
+ <LI><CODE>( expr )</CODE> - Parenthesis for expression grouping
+
+ <LI><CODE>+</CODE> - Logical AND
+
+ <LI><CODE>,</CODE> or whitespace - Logical OR
+
+ <LI><CODE>!</CODE> - Logical NOT
+
+ <LI><CODE>match("pattern")</CODE> - Pattern match on filename
+
+ <LI><CODE>extension</CODE> - Pattern match on "*.extension"
+
+ <LI><CODE>ascii(offset,length)</CODE> - True if bytes are valid
+ printable ASCII (CR, NL, TAB, BS, 32-126)
+
+ <LI><CODE>printable(offset,length)</CODE> - True if bytes are
+ printable 8-bit chars (CR, NL, TAB, BS, 32-126, 160-254)
+
+ <LI><CODE>string(offset,"string")</CODE> - True if bytes are
+ identical to string
+
+ <LI><CODE>char(offset,value)</CODE> - True if byte is identical
+
+ <LI><CODE>short(offset,value)</CODE> - True if 16-bit integer
+ is identical (network or "big-endian" byte order)
+
+ <LI><CODE>int(offset,value)</CODE> - True if 32-bit integer is
+ identical (network or "big-endian" byte order)
+
+ <LI><CODE>locale("string")</CODE> - True if current locale
+ matches string
+
+</UL>
+
+<H3>mime.convs</H3>
+
+<P>The <CODE>mime.convs</CODE> file defines all of the filter programs that
+are known to the system. Each line consists of:
+
+<UL><PRE>
+source destination cost program
+
+text/plain application/postscript 50 texttops
+application/vnd.cups-postscript application/vnd.cups-raster 50 pstoraster
+image/* application/vnd.cups-postscript 50 imagetops
+image/* application/vnd.cups-raster 50 imagetoraster
+</PRE></UL>
+
+<P>The <I>source</I> field is a MIME type, optionally using a wildcard for
+the super-type or sub-type (e.g. "text/plain", "image/*", "*/postscript").
+
+<P>The <I>destination</I> field is a MIME type defined in the
+<CODE>mime.types</CODE> file.
+
+<P>The <I>cost</I> field defines a relative cost for the filtering
+operation from 1 to 100. The cost is used to choose between two
+different sets of filters when converting a file. For example, to convert
+from <CODE>image/jpeg</CODE> to <CODE>application/vnd.cups-raster</CODE>,
+you could use the <CODE>imagetops</CODE> and <CODE>pstoraster</CODE>
+filters for a total cost of 100, or the <CODE>imagetoraster</CODE> filter
+for a total cost of 50.
+
+<P>The <I>program</I> field defines the filter program to run; the
+special program "-" can be used to make two file types equivalent. The
+program must accept the standard filter arguments and environment
+variables described in the CUPS Interface Design Document:
+
+<UL><PRE>
+program job user title options [filename]
+</PRE></UL>
+
+<P>If specified, the <I>filename</I> argument defines a file to read
+when filtering, otherwise the filter must read from the standard input.
+All filtered output must go to the standard output.
+
+<H1 ALIGN=RIGHT>5 - Printer Accounting</H1>
+
+This chapter describes the CUPS log files.
+
+<H2>Where to Find the Log Files</H2>
+
+<P>The log files are normally stored in the <CODE>/var/cups/logs</CODE>
+directory. You can change this by editing the
+<CODE>/var/cups/conf/cupsd.conf</CODE> configuration file.
+
+<H2>The access_log File</H2>
+
+<P>The <CODE>access_log</CODE> file lists each HTTP resource that is accessed
+by a web browser or CUPS/IPP client. Each line is in the so-called "Common
+Log Format" used by many web servers and web reporting tools:
+
+<UL><PRE>
+host group user date-time \"method resource version\" status bytes
+
+127.0.0.1 - - [20/May/1999:19:20:29 +0000] "POST /admin/ HTTP/1.1" 401 0
+127.0.0.1 - mike [20/May/1999:19:20:31 +0000] "POST /admin/ HTTP/1.1" 200 0
+</PRE></UL>
+
+<P>The <I>host</I> field will normally only be an IP address unless you
+have changed the <CODE>HostnameLookups</CODE> directive on in the
+<CODE>cupsd.conf</CODE> file.
+
+<P>The <I>group</I> field always contains "-".
+
+<P>The <I>user</I> field is the authenticated username of the requesting user.
+If no username and password is supplied for the request then this field
+contains "-".
+
+<P>The <I>date-time</I> field is the date and time of the request in Greenwich
+Mean Time (a.k.a. ZULU) and is in the format:
+
+<UL><PRE>
+[DD/MON/YYYY:HH:MM:SS +0000]
+</PRE></UL>
+
+<P>The <I>method</I> field is the HTTP method used ("GET", "PUT", "POST", etc.)
+
+<P>The <I>resource</I> field is the filename of the requested resource.
+
+<P>The <I>version</I> field is the HTTP specification version used by the
+client. For CUPS clients this will always be "HTTP/1.1".
+
+<P>The <I>status</I> field contains the HTTP result status of the
+request. Usually it is "200", but other HTTP status codes are possible.
+For example, 401 is the "unauthorized access" status in the example
+above.
+
+<P>The <I>bytes</I> field contains the number of bytes in the request.
+For POST requests the <I>bytes</I> field contains the number of bytes
+of non-IPP data that is received from the client.
+
+<H2>The error_log File</H2>
+
+<P>The <CODE>error_log</CODE> file lists messages from the scheduler (errors,
+warnings, etc.):
+
+<UL><PRE>
+level date-time message
+
+I [20/May/1999:19:18:28 +0000] Job 1 queued on 'DeskJet' by 'mike'.
+I [20/May/1999:19:21:02 +0000] Job 2 queued on 'DeskJet' by 'mike'.
+I [20/May/1999:19:22:24 +0000] Job 2 was cancelled by 'mike'.
+</PRE></UL>
+
+<P>The <I>level</I> field contains the type of message:
+
+<UL>
+
+ <LI><CODE>E</CODE> - An error occurred.
+
+ <LI><CODE>W</CODE> - The server was unable to perform some action.
+
+ <LI><CODE>I</CODE> - Informational message.
+
+ <LI><CODE>D</CODE> - Debugging message.
+
+</UL>
+
+<P>The <I>date-time</I> field contains the date and time of when the page
+started printing. The format of this field is identical to the <I>data-time</I>
+field in the <CODE>access_log</CODE> file.
+
+<P>The <I>message</I> fields contains a free-form textual message.
+
+<H2>The page_log File</H2>
+
+<P>The <CODE>page_log</CODE> file lists each page that is sent to a printer.
+Each line contains the following information:
+
+<UL><PRE>
+printer user job-id date-time page-number num-copies
+
+DeskJet root 2 [20/May/1999:19:21:05 +0000] 1 0
+</PRE></UL>
+
+<P>The <I>printer</I> field contains the name of the printer that
+printed the page. If you send a job to a printer class, this field will
+contain the name of the printer that was assigned the job.
+
+<P>The <I>user</I> field contains the name of the user (the IPP
+<CODE>requesting-user-name</CODE> attribute) that submitted this file for
+printing.
+
+<P>The <I>job-id</I> field contains the job number of the page being printed.
+Job numbers are reset to 1 whenever the CUPS server is started, so don't depend
+on this number being unique!
+
+<P>The <I>date-time</I> field contains the date and time of when the page
+started printing. The format of this field is identical to the <I>data-time</I>
+field in the <CODE>access_log</CODE> file.
+
+<P>The <I>page-number</I> and <I>num-pages</I> fields contain the page number
+and number of copies being printed of that page. For printer that can not
+produce copies on their own, the <I>num-pages</I> field will always be 1.
+
+</BODY>
+</HTML>
diff --git a/doc/sdd.html b/doc/sdd.html
new file mode 100644
index 000000000..5c99b58e1
--- /dev/null
+++ b/doc/sdd.html
@@ -0,0 +1,473 @@
+<HTML>
+<HEAD>
+<TITLE>DRAFT - CUPS Software Design Description</TITLE>
+<META NAME="AUTHOR" CONTENT="Easy Software Products">
+<META NAME="COPYRIGHT" CONTENT="Copyright 1997-1999, All Rights Reserved">
+<META NAME="DOCNUMBER" CONTENT="CUPS-SDD-1.0">
+</HEAD>
+<BODY>
+<CENTER><A HREF=#contents><IMG SRC="images/cups-large.gif" BORDER=0><BR>
+<H1>DRAFT - CUPS Software Design Description</H1></A><BR>
+CUPS-SDD-1.0<BR>
+Easy Software Products<BR>
+Copyright 1997-1999, All Rights Reserved<BR>
+</CENTER>
+<HR>
+<H1 ALIGN=CENTER><A NAME=CONTENTS>Table of Contents</A></H1>
+<BR>
+<BR><B><A HREF=#1>1 Scope</A></B>
+<UL>
+<LI><A HREF=#1_1>1.1 Identification</A></LI>
+<LI><A HREF=#1_2>1.2 System Overview</A></LI>
+<LI><A HREF=#1_3>1.3 Document Overview</A></LI>
+</UL>
+<B><A HREF=#2>2 References</A></B>
+<UL>
+<LI><A HREF=#2_1>2.1 CUPS Documentation</A></LI>
+<LI><A HREF=#2_2>2.2 Other Documents</A></LI>
+</UL>
+<B><A HREF=#3>3 Design Overview</A></B>
+<UL>
+<LI><A HREF=#3_1>3.1 Backends</A></LI>
+<UL>
+<LI><A HREF=#3_1_1>3.1.1 ipp</A></LI>
+<LI><A HREF=#3_1_2>3.1.2 lpd</A></LI>
+<LI><A HREF=#3_1_3>3.1.3 parallel</A></LI>
+<LI><A HREF=#3_1_4>3.1.4 serial</A></LI>
+<LI><A HREF=#3_1_5>3.1.5 smb</A></LI>
+<LI><A HREF=#3_1_6>3.1.6 socket</A></LI>
+</UL>
+<LI><A HREF=#3_2>3.2 Berkeley Commands</A></LI>
+<UL>
+<LI><A HREF=#3_2_1>3.2.1 lpc</A></LI>
+<LI><A HREF=#3_2_2>3.2.2 lpr</A></LI>
+<LI><A HREF=#3_2_3>3.2.3 lprm</A></LI>
+</UL>
+<LI><A HREF=#3_3>3.3 CGI</A></LI>
+<UL>
+<LI><A HREF=#3_3_1>3.3.1 classes</A></LI>
+<LI><A HREF=#3_3_2>3.3.2 jobs</A></LI>
+<LI><A HREF=#3_3_3>3.3.3 printers</A></LI>
+</UL>
+<LI><A HREF=#3_4>3.4 CUPS Interface Library</A></LI>
+<UL>
+<LI><A HREF=#3_4_1>3.4.1 Convenience Functions</A></LI>
+<LI><A HREF=#3_4_2>3.4.2 HTTP Functions</A></LI>
+<LI><A HREF=#3_4_3>3.4.3 IPP Functions</A></LI>
+<LI><A HREF=#3_4_4>3.4.4 Language Functions</A></LI>
+<LI><A HREF=#3_4_5>3.4.5 MIME Functions</A></LI>
+<LI><A HREF=#3_4_6>3.4.6 PPD Functions</A></LI>
+<LI><A HREF=#3_4_7>3.4.7 Raster Functions</A></LI>
+</UL>
+<LI><A HREF=#3_5>3.5 Filters</A></LI>
+<UL>
+<LI><A HREF=#3_5_1>3.5.1 hpgltops</A></LI>
+<LI><A HREF=#3_5_2>3.5.2 imagetops</A></LI>
+<LI><A HREF=#3_5_3>3.5.3 pstops</A></LI>
+<LI><A HREF=#3_5_4>3.5.4 texttops</A></LI>
+</UL>
+<LI><A HREF=#3_6>3.6 Scheduler</A></LI>
+<UL>
+<LI><A HREF=#3_6_1>3.6.1 Authorization</A></LI>
+<LI><A HREF=#3_6_2>3.6.2 Classes</A></LI>
+<LI><A HREF=#3_6_3>3.6.3 Client</A></LI>
+<LI><A HREF=#3_6_4>3.6.4 Configuration</A></LI>
+<LI><A HREF=#3_6_5>3.6.5 Directory Services</A></LI>
+<LI><A HREF=#3_6_6>3.6.6 IPP</A></LI>
+<LI><A HREF=#3_6_7>3.6.7 Jobs</A></LI>
+<LI><A HREF=#3_6_8>3.6.8 Main</A></LI>
+<LI><A HREF=#3_6_9>3.6.9 Printers</A></LI>
+</UL>
+<LI><A HREF=#3_7>3.7 System V Commands</A></LI>
+<UL>
+<LI><A HREF=#3_7_1>3.7.1 accept</A></LI>
+<LI><A HREF=#3_7_2>3.7.2 cancel</A></LI>
+<LI><A HREF=#3_7_3>3.7.3 disable</A></LI>
+<LI><A HREF=#3_7_4>3.7.4 enable</A></LI>
+<LI><A HREF=#3_7_5>3.7.5 lp</A></LI>
+<LI><A HREF=#3_7_6>3.7.6 lpadmin</A></LI>
+<LI><A HREF=#3_7_7>3.7.7 lpstat</A></LI>
+<LI><A HREF=#3_7_8>3.7.8 reject</A></LI>
+</UL>
+</UL>
+<B><A HREF=#4>4 Detailed Design</A></B>
+<BR>
+<BR><B><A HREF=#5>A Glossary</A></B>
+<UL>
+<LI><A HREF=#5_1>A.1 Terms</A></LI>
+<LI><A HREF=#5_2>A.2 Acronyms</A></LI>
+</UL>
+<HR>
+<H1><A NAME=1>1 Scope</A></H1>
+<H2><A NAME=1_1>1.1 Identification</A></H2>
+ This software design description document provides detailed
+information on the architecture and coding of the Common UNIX Printing
+System (&quot;CUPS&quot;) Version 1.0.
+<H2><A NAME=1_2>1.2 System Overview</A></H2>
+ The Common UNIX Printing System provides a portable printing layer for
+ UNIX&reg; operating systems. It has been developed by Easy Software
+ Products to promote a standard printing solution for all UNIX vendors
+ and users. CUPS provides the System V and Berkeley command-line
+interfaces.
+<P>CUPS uses the Internet Printing Protocol (IETF-IPP) as the basis for
+managing print jobs and queues. The Line Printer Daemon (LPD, RFC1179),
+Server Message Block (SMB), and AppSocket protocols are also supported
+with reduced functionality. </P>
+<P>CUPS adds network printer browsing and PostScript Printer
+Description (&quot;PPD&quot;)-based printing options to support real world
+applications under UNIX. </P>
+<P>CUPS also includes a customized version of GNU GhostScript
+(currently based off GNU GhostScript 4.03) and an image file RIP that
+can be used to support non-PostScript printers. </P>
+<H2><A NAME=1_3>1.3 Document Overview</A></H2>
+ This software design description document is organized into the
+ following sections:
+<UL>
+<LI>1 - Scope </LI>
+<LI>2 - References </LI>
+<LI>3 - Design Overview </LI>
+<LI>4 - Detailed Design </LI>
+<LI>A - Glossary </LI>
+</UL>
+<H1><A NAME=2>2 References</A></H1>
+<H2><A NAME=2_1>2.1 CUPS Documentation</A></H2>
+ The following CUPS documentation is referenced by this document:
+<UL>
+<LI>CUPS-CMP-1.0: CUPS Configuration Management Plan </LI>
+<LI>CUPS-IDD-1.0: CUPS System Interface Design Description </LI>
+<LI>CUPS-SAM-1.0.x: CUPS Software Administrators Manual </LI>
+<LI>CUPS-SDD-1.0: CUPS Software Design Description </LI>
+<LI>CUPS-SPM-1.0: CUPS Software Programming Manual </LI>
+<LI>CUPS-SSR-1.0: CUPS Software Security Report </LI>
+<LI>CUPS-STP-1.0: CUPS Software Test Plan </LI>
+<LI>CUPS-SUM-1.0.x: CUPS Software Users Manual </LI>
+<LI>CUPS-SVD-1.0.x: CUPS Software Version Description </LI>
+</UL>
+<H2><A NAME=2_2>2.2 Other Documents</A></H2>
+ The following non-CUPS documents are referenced by this document:
+<UL>
+<LI>IEEE 1387.4, System Administration: Printing (draft) </LI>
+<LI>IPP/1.0: Additional Optional Operations - Set 1 </LI>
+<LI>IPP/1.0: Encoding and Transport </LI>
+<LI>IPP/1.0: Implementers Guide </LI>
+<LI>IPP/1.0: Model and Semantics </LI>
+<LI>RFC 1179, Line Printer Daemon Protocol </LI>
+</UL>
+<H1><A NAME=3>3 Design Overview</A></H1>
+ CUPS is composed of 7 software sub-systems that operate together to
+ perform common printing tasks:
+<UL>
+<LI>Backends </LI>
+<LI>Berkeley Commands </LI>
+<LI>CGI </LI>
+<LI>CUPS Interface Library </LI>
+<LI>Filters </LI>
+<LI>Scheduler </LI>
+<LI>System V Commands </LI>
+</UL>
+<H2><A NAME=3_1>3.1 Backends</A></H2>
+ The backends implement communications over a number of different
+interfaces. All backends are called with a common set of arguments:
+<UL>
+<LI>Device URI - the Uniform Resource Identifier for the output device
+ (e.g. <CODE>parallel:/dev/plp</CODE>, <CODE>ipp://hostname/resource</CODE>
+). </LI>
+<LI>Job Identifier - the job identifier for this job (integer). </LI>
+<LI>User Name - the user associated with this job (name string). </LI>
+<LI>Title - the title/job-name associated with this job (name string). </LI>
+<LI>Copies - the number of copies required (integer). </LI>
+<LI>Options - the options associated with this job (space separated
+ option strings). </LI>
+<LI>Filename (optional) - the file to print; if this option is not
+ specified, the backend must read the print file from the standard
+ input. </LI>
+</UL>
+ Backends are named using the method of the URI, so a URI of
+&quot;ipp://hostname/resource&quot; would be processed by the &quot;ipp&quot; backend.
+<H3><A NAME=3_1_1>3.1.1 ipp</A></H3>
+ The ipp backend sends the specified job to a network printer or host
+using the Internet Printing Protocol. The URI is as specified by the <CODE>
+printer-uri-supported</CODE> attribute from the printer or host.
+<H3><A NAME=3_1_2>3.1.2 lpd</A></H3>
+ The lpd backend sends the specified job to a network printer or host
+using the Line Printer Daemon protocol. The URI is of the form:
+<UL>
+<PRE>lpd://hostname/queue
+</PRE>
+</UL>
+<H3><A NAME=3_1_3>3.1.3 parallel</A></H3>
+ The parallel backend sends the specified job to a local printer
+connected via the specified parallel port device. The URI is of the
+form:
+<UL>
+<PRE>parallel:/dev/file
+</PRE>
+</UL>
+<H3><A NAME=3_1_4>3.1.4 serial</A></H3>
+ The serial backend sends the specified job to a local printer
+connected via the specified serial port device. The URI is of the
+form:
+<UL>
+<PRE>serial:/dev/file?option[+option+...]
+</PRE>
+</UL>
+ The options can be any combination of the following:
+<UL>
+<LI><CODE>baud=<I>rate</I></CODE> - Sets the baud rate for the device. </LI>
+<LI><CODE>bits=<I>7 or 8</I></CODE> - Sets the number of data bits. </LI>
+<LI><CODE>parity=<I>even</I></CODE> - Sets even parity checking. </LI>
+<LI><CODE>parity=<I>odd</I></CODE> - Sets odd parity checking. </LI>
+<LI><CODE>parity=<I>none</I></CODE> - Turns parity checking off. </LI>
+</UL>
+<H3><A NAME=3_1_5>3.1.5 smb</A></H3>
+ The smb backend sends the specified job to a network host using the
+Server Message Block protocol, which is used by most machines running
+Microsoft&reg; Windows&reg;. The URI is of the form:
+<UL>
+<PRE>smb://hostname/queue
+</PRE>
+</UL>
+ Usernames and passwords required to access the printer are stored in
+an external file.
+<H3><A NAME=3_1_6>3.1.6 socket</A></H3>
+ The socket backend sends the specified job to a network host using the
+AppSocket protocol commonly used by Hewlett-Packard and Tektronix
+printers. The URI is of the form:
+<UL>
+<PRE>socket://hostname[:port]
+</PRE>
+</UL>
+ The default port number is 9100.
+<H2><A NAME=3_2>3.2 Berkeley Commands</A></H2>
+ The Berkeley commands provide a simple command-line interface to CUPS
+to submit and control print jobs. It is provided for compatibility with
+existing software that is hard coded to use the Berkeley commands,
+however since printer options cannot be specified using the Berkeley
+commands their use it not encouraged.
+<H3><A NAME=3_2_1>3.2.1 lpc</A></H3>
+ The lpc command allows users and administrators to check the status
+and control print queues. The version provided with CUPS supports the
+following commands:
+<UL>
+<LI>abort - Stops a printer or all printers and any active print jobs. </LI>
+<LI>disable - Prevents new jobs from being submitted to the specified
+ printer or all printers. </LI>
+<LI>down - Stops a printer or all printers after completing the current
+ print jobs. </LI>
+<LI>enable - Allows new jobs to be submitted. </LI>
+<LI>start - Starts a printer or all printers. </LI>
+<LI>status - Shows the status of printers and jobs in the queue. </LI>
+<LI>up - Starts a printer or all printers. </LI>
+</UL>
+<H3><A NAME=3_2_2>3.2.2 lpr</A></H3>
+ The lpr command submits a job for printing. The CUPS version of lpr
+silently ignores the &quot;i&quot;, &quot;p&quot;, &quot;t&quot;, &quot;m&quot;, &quot;h&quot;, and &quot;s&quot; options.
+<H3><A NAME=3_2_3>3.2.3 lprm</A></H3>
+ The lprm removes one or more print jobs.
+<H2><A NAME=3_3>3.3 CGI</A></H2>
+ The Common Gateway Interface (CGI) programs provide a web-based status
+interface to monitor the status of printers, classes, and jobs.
+<H3><A NAME=3_3_1>3.3.1 classes</A></H3>
+ The classes CGI lists the available printer classes and any pending
+jobs for the class. The user can click on individual classes to limit
+the display and click on jobs to see the job status.
+<H3><A NAME=3_3_2>3.3.2 jobs</A></H3>
+ The jobs CGI lists the queued print jobs in order of priority. The
+list can be limited by printer or job. When the user displays the
+status of an individual print job all job options are displayed.
+<H3><A NAME=3_3_3>3.3.3 printers</A></H3>
+ The printers CGI lists the available printer queues and any pending
+jobs for the printer. The user can click on individual printers to
+limit the display and click on jobs to see the job status.
+<H2><A NAME=3_4>3.4 CUPS Interface Library</A></H2>
+ The CUPS interface library provides common convenience, HTTP, IPP,
+language, MIME, PPD, and raster functions used by the CUPS software.
+<H3><A NAME=3_4_1>3.4.1 Convenience Functions</A></H3>
+ Convenience functions are provided to submit an IPP request, send a
+print file, cancel a job, get a list of available printers, and get a
+list of available classes.
+<H3><A NAME=3_4_2>3.4.2 HTTP Functions</A></H3>
+ The HTTP functions provide functions to connect to HTTP servers, issue
+requests, read data from a server, and write data to a server.
+<H3><A NAME=3_4_3>3.4.3 IPP Functions</A></H3>
+ The IPP function provide functions to manage IPP request data and
+attributes, read IPP responses from a server, and write IPP requests to
+a server.
+<H3><A NAME=3_4_4>3.4.4 Language Functions</A></H3>
+ The language functions provide a standard interface for retrieving
+common textual messages for a particular locale and determining the
+correct encoding (e.g. US ASCII, ISO-8859-1, etc.)
+<H3><A NAME=3_4_5>3.4.5 MIME Functions</A></H3>
+ The Multimedia Internet Mail Exchange functions manage a MIME type and
+conversion database that supports file typing by extension and content,
+and least-cost file filtering from a source to a destination file type.
+<H3><A NAME=3_4_6>3.4.6 PPD Functions</A></H3>
+ The PostScript Printer Description functions manage PPD files, select
+options, check for option conflicts, and emit selected options in the
+correct order.
+<H3><A NAME=3_4_7>3.4.7 Raster Functions</A></H3>
+ The raster functions manage streams of CUPS raster data (described in
+the Interface Design Document) used by non-PostScript printer drivers.
+<H2><A NAME=3_5>3.5 Filters</A></H2>
+ The filters implement file conversion services for CUPS. All filters
+are called with a common set of arguments:
+<UL>
+<LI>Printer name - the name of the destination printer (name string). </LI>
+<LI>Job Identifier - the job identifier for this job (integer). </LI>
+<LI>User Name - the user associated with this job (name string). </LI>
+<LI>Title - the title/job-name associated with this job (name string). </LI>
+<LI>Copies - the number of copies required (integer). </LI>
+<LI>Options - the options associated with this job (space separated
+ option strings). </LI>
+<LI>Filename (optional) - the file to print; if this option is not
+ specified, the filter must read the input file from the standard
+ input. </LI>
+</UL>
+ Filters are added to the MIME conversion data file and implement all
+necessary conversions from one file type to another.
+<H3><A NAME=3_5_1>3.5.1 hpgltops</A></H3>
+ The hpgltops filter converts HP-GL/2 files into PostScript.
+<H3><A NAME=3_5_2>3.5.2 imagetops</A></H3>
+ The imagetops filter converts image files into PostScript.
+<H3><A NAME=3_5_3>3.5.3 pstops</A></H3>
+ The pstops filter inserts printer-specific commands from PPD files and
+performs page filtering as requested by the user.
+<H3><A NAME=3_5_4>3.5.4 texttops</A></H3>
+ The texttops filter converts text files into PostScript.
+<H2><A NAME=3_6>3.6 Scheduler</A></H2>
+ The scheduler is a fully-functional HTTP/1.1 server that manages the
+printers, classes, and jobs in the system. It also handles a simple
+broadcast-based directory service so that remote print queues and
+classes can be accessed transparently from the local system.
+<H3><A NAME=3_6_1>3.6.1 Authorization</A></H3>
+ The authorization module is responsible for performing access control
+and authentication for all HTTP and IPP requests entering the system.
+<H3><A NAME=3_6_2>3.6.2 Classes</A></H3>
+ The classes module is responsible for managing printer classes in the
+system. Each class is a collection of local and/or remote printers.
+ The classes module also reads and writes the classes configuration
+file.
+<H3><A NAME=3_6_3>3.6.3 Client</A></H3>
+ The client module is responsible for all HTTP client communications.
+ It handles listening on selected interfaces, accepting connections
+from prospective clients, processing incoming HTTP requests, and
+sending HTTP responses to those requests. The client module also is
+responsible for executing the external CGI programs as needed to
+support web-based printer, class, and job status monitoring.
+<P>Once authorized, all IPP requests are sent to the IPP module. </P>
+<H3><A NAME=3_6_4>3.6.4 Configuration</A></H3>
+ The configuration module is responsible for reading the CUPS
+configuration file and initializing the appropriate data structures and
+values. The configuration module also stops CUPS services before
+reading the configuration file and restarts them after the
+configuration file has been read.
+<H3><A NAME=3_6_5>3.6.5 Directory Services</A></H3>
+ The directory services module sends and recieves printer state
+information over a broadcast socket. Remote printers and classes are
+automatically added to or removed from the local printer and class
+lists as needed.
+<P>The directory services module can only recieve printer state
+information over a single UDP port, however it can broadcast to
+multiple addresses and ports as needed. </P>
+<H3><A NAME=3_6_6>3.6.6 IPP</A></H3>
+ The IPP module handles IPP requests and acts accordingly. URI
+validation is also performed here, as a client can post IPP data to any
+URI on the server (which might sidestep the access control or
+authentication of the HTTP server.)
+<H3><A NAME=3_6_7>3.6.7 Jobs</A></H3>
+ The jobs module manages print jobs, starts filter and backend
+processes for jobs to be printed, and monitors status messages from
+those filters and backends.
+<H3><A NAME=3_6_8>3.6.8 Main</A></H3>
+ The main module is responsible for timing out and dispatching input
+and output for client connections. It also watches for incoming <CODE>
+SIGHUP</CODE> signals and reloads the server configuration files as
+needed.
+<H3><A NAME=3_6_9>3.6.9 Printers</A></H3>
+ The printers module is responsible for managing printers and PPD files
+in the system. The printers module also reads and writes the printers
+configuration file.
+<H2><A NAME=3_7>3.7 System V Commands</A></H2>
+ The System V commands provide a robust command-line interface to CUPS
+to submit and control print jobs.
+<H3><A NAME=3_7_1>3.7.1 accept</A></H3>
+ The accept command tells the scheduler to accept new jobs for specific
+printers.
+<H3><A NAME=3_7_2>3.7.2 cancel</A></H3>
+ The cancel command tells the scheduler to cancel one or more jobs that
+are queued for printing.
+<H3><A NAME=3_7_3>3.7.3 disable</A></H3>
+ The disable command tells the scheduler to stop printing jobs on the
+specified printers.
+<H3><A NAME=3_7_4>3.7.4 enable</A></H3>
+ The enable command tells the scheduler to start printing jobs on the
+specified printers.
+<H3><A NAME=3_7_5>3.7.5 lp</A></H3>
+ The lp command submits submits files for printing. Unlike the
+standard System V lp command, a single CUPS lp command will generate a
+separate job ID for each file that is printed. Also, the Solaris &quot;f&quot;,
+&quot;H&quot;, &quot;P&quot;, &quot;S&quot;, and &quot;y&quot; options are silently ignored.
+<H3><A NAME=3_7_6>3.7.6 lpadmin</A></H3>
+ The lpadmin command manages printer queues and classes. The Solaris
+&quot;A&quot;, &quot;F&quot;, &quot;I&quot;, &quot;M&quot;, &quot;P&quot;, &quot;Q&quot;, &quot;S&quot;, &quot;T&quot;, &quot;U&quot;, &quot;W&quot;, &quot;f&quot;, &quot;l&quot;, &quot;m&quot;, &quot;o&quot;,
+&quot;s&quot;, &quot;t&quot;, and &quot;u&quot; options are not supported, and new options &quot;P&quot; (PPD
+file) and &quot;F&quot; (filter) are provided to configure CUPS-specific features
+such as PPD file and conversion filters.
+<H3><A NAME=3_7_7>3.7.7 lpstat</A></H3>
+ The lpstat command lists printers, classes, and jobs as requested by
+the user.
+<H3><A NAME=3_7_8>3.7.8 reject</A></H3>
+ The reject command tells the scheduler not to accept new jobs for
+specific printers.
+<H1><A NAME=4>4 Detailed Design</A></H1>
+<H1 TYPE=A VALUE=1><A NAME=5>A Glossary</A></H1>
+<H2><A NAME=5_1>A.1 Terms</A></H2>
+<DL>
+<DT>C </DT>
+<DD>A computer language. </DD>
+<DT>parallel </DT>
+<DD>Sending or receiving data more than 1 bit at a time. </DD>
+<DT>pipe </DT>
+<DD>A one-way communications channel between two programs. </DD>
+<DT>serial </DT>
+<DD>Sending or receiving data 1 bit at a time. </DD>
+<DT>socket </DT>
+<DD>A two-way network communications channel. </DD>
+</DL>
+<H2><A NAME=5_2>A.2 Acronyms</A></H2>
+<DL>
+<DT>ASCII </DT>
+<DD>American Standard Code for Information Interchange </DD>
+<DT>CUPS </DT>
+<DD>Common UNIX Printing System </DD>
+<DT>ESC/P </DT>
+<DD>EPSON Standard Code for Printers </DD>
+<DT>FTP </DT>
+<DD>File Transfer Protocol </DD>
+<DT>HP-GL </DT>
+<DD>Hewlett-Packard Graphics Language </DD>
+<DT>HP-PCL </DT>
+<DD>Hewlett-Packard Printer Control Language </DD>
+<DT>HP-PJL </DT>
+<DD>Hewlett-Packard Printer Job Language </DD>
+<DT>IETF </DT>
+<DD>Internet Engineering Task Force </DD>
+<DT>IPP </DT>
+<DD>Internet Printing Protocol </DD>
+<DT>ISO </DT>
+<DD>International Standards Organization </DD>
+<DT>LPD </DT>
+<DD>Line Printer Daemon </DD>
+<DT>MIME </DT>
+<DD>Multimedia Internet Mail Exchange </DD>
+<DT>PCL </DT>
+<DD>Page Control Language </DD>
+<DT>PPD </DT>
+<DD>PostScript Printer Description </DD>
+<DT>SMB </DT>
+<DD>Server Message Block </DD>
+<DT>TFTP </DT>
+<DD>Trivial File Transfer Protocol </DD>
+</DL>
+</BODY>
+</HTML>
diff --git a/doc/sdd.pdf b/doc/sdd.pdf
new file mode 100644
index 000000000..d3f575b0c
--- /dev/null
+++ b/doc/sdd.pdf
@@ -0,0 +1,989 @@
+%PDF-1.2
+%âãÏÓ
+1 0 obj<</Producer(htmldoc 2.0b1 Copyright 1997-1999 Michael Sweet, All Rights Reserved.)/CreationDate(D:19990621185446Z)/Title(DRAFT - CUPS Software Design Description)/Author(Easy Software Products)>>endobj
+2 0 obj<</Type/Encoding/BaseEncoding/WinAnsiEncoding>>endobj
+3 0 obj<</Type/Font/Subtype/Type1/BaseFont/Courier/Encoding 2 0 R>>endobj
+4 0 obj<</Type/Font/Subtype/Type1/BaseFont/Courier-Oblique/Encoding 2 0 R>>endobj
+5 0 obj<</Type/Font/Subtype/Type1/BaseFont/Times-Roman/Encoding 2 0 R>>endobj
+6 0 obj<</Type/Font/Subtype/Type1/BaseFont/Times-Bold/Encoding 2 0 R>>endobj
+7 0 obj<</Type/Font/Subtype/Type1/BaseFont/Times-Italic/Encoding 2 0 R>>endobj
+8 0 obj<</Type/Font/Subtype/Type1/BaseFont/Helvetica/Encoding 2 0 R>>endobj
+9 0 obj<</Type/Font/Subtype/Type1/BaseFont/Helvetica-Bold/Encoding 2 0 R>>endobj
+10 0 obj<</Type/Font/Subtype/Type1/BaseFont/Symbol>>endobj
+11 0 obj<</Subtype/Link/Rect[72.0 673.2 80.2 686.2]/Border[0 0 0]/Dest[219 0 R/XYZ null 818 0]>>endobj
+12 0 obj<</Subtype/Link/Rect[80.2 673.2 107.8 686.2]/Border[0 0 0]/Dest[219 0 R/XYZ null 818 0]>>endobj
+13 0 obj<</Subtype/Link/Rect[108.0 660.0 124.5 673.0]/Border[0 0 0]/Dest[219 0 R/XYZ null 737 0]>>endobj
+14 0 obj<</Subtype/Link/Rect[124.5 660.0 183.8 673.0]/Border[0 0 0]/Dest[219 0 R/XYZ null 737 0]>>endobj
+15 0 obj<</Subtype/Link/Rect[108.0 646.8 124.5 659.8]/Border[0 0 0]/Dest[219 0 R/XYZ null 658 0]>>endobj
+16 0 obj<</Subtype/Link/Rect[124.5 646.8 159.6 659.8]/Border[0 0 0]/Dest[219 0 R/XYZ null 658 0]>>endobj
+17 0 obj<</Subtype/Link/Rect[159.6 646.8 203.0 659.8]/Border[0 0 0]/Dest[219 0 R/XYZ null 658 0]>>endobj
+18 0 obj<</Subtype/Link/Rect[108.0 633.6 124.5 646.6]/Border[0 0 0]/Dest[219 0 R/XYZ null 434 0]>>endobj
+19 0 obj<</Subtype/Link/Rect[124.5 633.6 173.1 646.6]/Border[0 0 0]/Dest[219 0 R/XYZ null 434 0]>>endobj
+20 0 obj<</Subtype/Link/Rect[173.1 633.6 216.4 646.6]/Border[0 0 0]/Dest[219 0 R/XYZ null 434 0]>>endobj
+21 0 obj<</Subtype/Link/Rect[72.0 607.2 80.2 620.2]/Border[0 0 0]/Dest[225 0 R/XYZ null 818 0]>>endobj
+22 0 obj<</Subtype/Link/Rect[80.2 607.2 131.6 620.2]/Border[0 0 0]/Dest[225 0 R/XYZ null 818 0]>>endobj
+23 0 obj<</Subtype/Link/Rect[108.0 594.0 124.5 607.0]/Border[0 0 0]/Dest[225 0 R/XYZ null 737 0]>>endobj
+24 0 obj<</Subtype/Link/Rect[124.5 594.0 154.8 607.0]/Border[0 0 0]/Dest[225 0 R/XYZ null 737 0]>>endobj
+25 0 obj<</Subtype/Link/Rect[154.8 594.0 222.6 607.0]/Border[0 0 0]/Dest[225 0 R/XYZ null 737 0]>>endobj
+26 0 obj<</Subtype/Link/Rect[108.0 580.8 124.5 593.8]/Border[0 0 0]/Dest[225 0 R/XYZ null 540 0]>>endobj
+27 0 obj<</Subtype/Link/Rect[124.5 580.8 152.3 593.8]/Border[0 0 0]/Dest[225 0 R/XYZ null 540 0]>>endobj
+28 0 obj<</Subtype/Link/Rect[152.3 580.8 202.4 593.8]/Border[0 0 0]/Dest[225 0 R/XYZ null 540 0]>>endobj
+29 0 obj<</Subtype/Link/Rect[72.0 554.4 80.2 567.4]/Border[0 0 0]/Dest[231 0 R/XYZ null 818 0]>>endobj
+30 0 obj<</Subtype/Link/Rect[80.2 554.4 114.8 567.4]/Border[0 0 0]/Dest[231 0 R/XYZ null 818 0]>>endobj
+31 0 obj<</Subtype/Link/Rect[114.8 554.4 160.0 567.4]/Border[0 0 0]/Dest[231 0 R/XYZ null 818 0]>>endobj
+32 0 obj<</Subtype/Link/Rect[108.0 541.2 124.5 554.2]/Border[0 0 0]/Dest[231 0 R/XYZ null 624 0]>>endobj
+33 0 obj<</Subtype/Link/Rect[124.5 541.2 167.3 554.2]/Border[0 0 0]/Dest[231 0 R/XYZ null 624 0]>>endobj
+34 0 obj<</Subtype/Link/Rect[144.0 528.0 168.8 541.0]/Border[0 0 0]/Dest[231 0 R/XYZ null 359 0]>>endobj
+35 0 obj<</Subtype/Link/Rect[168.8 528.0 182.8 541.0]/Border[0 0 0]/Dest[231 0 R/XYZ null 359 0]>>endobj
+36 0 obj<</Subtype/Link/Rect[144.0 514.8 168.8 527.8]/Border[0 0 0]/Dest[231 0 R/XYZ null 287 0]>>endobj
+37 0 obj<</Subtype/Link/Rect[168.8 514.8 182.8 527.8]/Border[0 0 0]/Dest[231 0 R/XYZ null 287 0]>>endobj
+38 0 obj<</Subtype/Link/Rect[144.0 501.6 168.8 514.6]/Border[0 0 0]/Dest[231 0 R/XYZ null 201 0]>>endobj
+39 0 obj<</Subtype/Link/Rect[168.8 501.6 201.7 514.6]/Border[0 0 0]/Dest[231 0 R/XYZ null 201 0]>>endobj
+40 0 obj<</Subtype/Link/Rect[144.0 488.4 168.8 501.4]/Border[0 0 0]/Dest[234 0 R/XYZ null 782 0]>>endobj
+41 0 obj<</Subtype/Link/Rect[168.8 488.4 192.6 501.4]/Border[0 0 0]/Dest[234 0 R/XYZ null 782 0]>>endobj
+42 0 obj<</Subtype/Link/Rect[144.0 475.2 168.8 488.2]/Border[0 0 0]/Dest[234 0 R/XYZ null 590 0]>>endobj
+43 0 obj<</Subtype/Link/Rect[168.8 475.2 187.1 488.2]/Border[0 0 0]/Dest[234 0 R/XYZ null 590 0]>>endobj
+44 0 obj<</Subtype/Link/Rect[144.0 462.0 168.8 475.0]/Border[0 0 0]/Dest[234 0 R/XYZ null 479 0]>>endobj
+45 0 obj<</Subtype/Link/Rect[168.8 462.0 196.9 475.0]/Border[0 0 0]/Dest[234 0 R/XYZ null 479 0]>>endobj
+46 0 obj<</Subtype/Link/Rect[108.0 448.8 124.5 461.8]/Border[0 0 0]/Dest[234 0 R/XYZ null 382 0]>>endobj
+47 0 obj<</Subtype/Link/Rect[124.5 448.8 167.0 461.8]/Border[0 0 0]/Dest[234 0 R/XYZ null 382 0]>>endobj
+48 0 obj<</Subtype/Link/Rect[167.0 448.8 217.1 461.8]/Border[0 0 0]/Dest[234 0 R/XYZ null 382 0]>>endobj
+49 0 obj<</Subtype/Link/Rect[144.0 435.6 168.8 448.6]/Border[0 0 0]/Dest[234 0 R/XYZ null 275 0]>>endobj
+50 0 obj<</Subtype/Link/Rect[168.8 435.6 182.2 448.6]/Border[0 0 0]/Dest[234 0 R/XYZ null 275 0]>>endobj
+51 0 obj<</Subtype/Link/Rect[144.0 422.4 168.8 435.4]/Border[0 0 0]/Dest[237 0 R/XYZ null 782 0]>>endobj
+52 0 obj<</Subtype/Link/Rect[168.8 422.4 181.0 435.4]/Border[0 0 0]/Dest[237 0 R/XYZ null 782 0]>>endobj
+53 0 obj<</Subtype/Link/Rect[144.0 409.2 168.8 422.2]/Border[0 0 0]/Dest[237 0 R/XYZ null 709 0]>>endobj
+54 0 obj<</Subtype/Link/Rect[168.8 409.2 189.5 422.2]/Border[0 0 0]/Dest[237 0 R/XYZ null 709 0]>>endobj
+55 0 obj<</Subtype/Link/Rect[108.0 396.0 124.5 409.0]/Border[0 0 0]/Dest[237 0 R/XYZ null 665 0]>>endobj
+56 0 obj<</Subtype/Link/Rect[124.5 396.0 143.4 409.0]/Border[0 0 0]/Dest[237 0 R/XYZ null 665 0]>>endobj
+57 0 obj<</Subtype/Link/Rect[144.0 382.8 168.8 395.8]/Border[0 0 0]/Dest[237 0 R/XYZ null 571 0]>>endobj
+58 0 obj<</Subtype/Link/Rect[168.8 382.8 199.3 395.8]/Border[0 0 0]/Dest[237 0 R/XYZ null 571 0]>>endobj
+59 0 obj<</Subtype/Link/Rect[144.0 369.6 168.8 382.6]/Border[0 0 0]/Dest[237 0 R/XYZ null 499 0]>>endobj
+60 0 obj<</Subtype/Link/Rect[168.8 369.6 187.1 382.6]/Border[0 0 0]/Dest[237 0 R/XYZ null 499 0]>>endobj
+61 0 obj<</Subtype/Link/Rect[144.0 356.4 168.8 369.4]/Border[0 0 0]/Dest[237 0 R/XYZ null 427 0]>>endobj
+62 0 obj<</Subtype/Link/Rect[168.8 356.4 202.4 369.4]/Border[0 0 0]/Dest[237 0 R/XYZ null 427 0]>>endobj
+63 0 obj<</Subtype/Link/Rect[108.0 343.2 124.5 356.2]/Border[0 0 0]/Dest[237 0 R/XYZ null 369 0]>>endobj
+64 0 obj<</Subtype/Link/Rect[124.5 343.2 154.8 356.2]/Border[0 0 0]/Dest[237 0 R/XYZ null 369 0]>>endobj
+65 0 obj<</Subtype/Link/Rect[154.8 343.2 196.6 356.2]/Border[0 0 0]/Dest[237 0 R/XYZ null 369 0]>>endobj
+66 0 obj<</Subtype/Link/Rect[196.6 343.2 229.6 356.2]/Border[0 0 0]/Dest[237 0 R/XYZ null 369 0]>>endobj
+67 0 obj<</Subtype/Link/Rect[144.0 330.0 168.8 343.0]/Border[0 0 0]/Dest[237 0 R/XYZ null 275 0]>>endobj
+68 0 obj<</Subtype/Link/Rect[168.8 330.0 228.9 343.0]/Border[0 0 0]/Dest[237 0 R/XYZ null 275 0]>>endobj
+69 0 obj<</Subtype/Link/Rect[228.9 330.0 272.3 343.0]/Border[0 0 0]/Dest[237 0 R/XYZ null 275 0]>>endobj
+70 0 obj<</Subtype/Link/Rect[144.0 316.8 168.8 329.8]/Border[0 0 0]/Dest[237 0 R/XYZ null 203 0]>>endobj
+71 0 obj<</Subtype/Link/Rect[168.8 316.8 199.0 329.8]/Border[0 0 0]/Dest[237 0 R/XYZ null 203 0]>>endobj
+72 0 obj<</Subtype/Link/Rect[199.0 316.8 242.4 329.8]/Border[0 0 0]/Dest[237 0 R/XYZ null 203 0]>>endobj
+73 0 obj<</Subtype/Link/Rect[144.0 303.6 168.8 316.6]/Border[0 0 0]/Dest[240 0 R/XYZ null 782 0]>>endobj
+74 0 obj<</Subtype/Link/Rect[168.8 303.6 187.4 316.6]/Border[0 0 0]/Dest[240 0 R/XYZ null 782 0]>>endobj
+75 0 obj<</Subtype/Link/Rect[187.4 303.6 230.8 316.6]/Border[0 0 0]/Dest[240 0 R/XYZ null 782 0]>>endobj
+76 0 obj<</Subtype/Link/Rect[144.0 290.4 168.8 303.4]/Border[0 0 0]/Dest[240 0 R/XYZ null 709 0]>>endobj
+77 0 obj<</Subtype/Link/Rect[168.8 290.4 214.9 303.4]/Border[0 0 0]/Dest[240 0 R/XYZ null 709 0]>>endobj
+78 0 obj<</Subtype/Link/Rect[214.9 290.4 258.3 303.4]/Border[0 0 0]/Dest[240 0 R/XYZ null 709 0]>>endobj
+79 0 obj<</Subtype/Link/Rect[144.0 277.2 168.8 290.2]/Border[0 0 0]/Dest[240 0 R/XYZ null 637 0]>>endobj
+80 0 obj<</Subtype/Link/Rect[168.8 277.2 201.4 290.2]/Border[0 0 0]/Dest[240 0 R/XYZ null 637 0]>>endobj
+81 0 obj<</Subtype/Link/Rect[201.4 277.2 244.8 290.2]/Border[0 0 0]/Dest[240 0 R/XYZ null 637 0]>>endobj
+82 0 obj<</Subtype/Link/Rect[144.0 264.0 168.8 277.0]/Border[0 0 0]/Dest[240 0 R/XYZ null 552 0]>>endobj
+83 0 obj<</Subtype/Link/Rect[168.8 264.0 191.7 277.0]/Border[0 0 0]/Dest[240 0 R/XYZ null 552 0]>>endobj
+84 0 obj<</Subtype/Link/Rect[191.7 264.0 235.1 277.0]/Border[0 0 0]/Dest[240 0 R/XYZ null 552 0]>>endobj
+85 0 obj<</Subtype/Link/Rect[144.0 250.8 168.8 263.8]/Border[0 0 0]/Dest[240 0 R/XYZ null 479 0]>>endobj
+86 0 obj<</Subtype/Link/Rect[168.8 250.8 199.6 263.8]/Border[0 0 0]/Dest[240 0 R/XYZ null 479 0]>>endobj
+87 0 obj<</Subtype/Link/Rect[199.6 250.8 243.0 263.8]/Border[0 0 0]/Dest[240 0 R/XYZ null 479 0]>>endobj
+88 0 obj<</Subtype/Link/Rect[108.0 237.6 124.5 250.6]/Border[0 0 0]/Dest[240 0 R/XYZ null 422 0]>>endobj
+89 0 obj<</Subtype/Link/Rect[124.5 237.6 152.6 250.6]/Border[0 0 0]/Dest[240 0 R/XYZ null 422 0]>>endobj
+90 0 obj<</Subtype/Link/Rect[144.0 224.4 168.8 237.4]/Border[0 0 0]/Dest[240 0 R/XYZ null 170 0]>>endobj
+91 0 obj<</Subtype/Link/Rect[168.8 224.4 206.6 237.4]/Border[0 0 0]/Dest[240 0 R/XYZ null 170 0]>>endobj
+92 0 obj<</Subtype/Link/Rect[144.0 211.2 168.8 224.2]/Border[0 0 0]/Dest[243 0 R/XYZ null 782 0]>>endobj
+93 0 obj<</Subtype/Link/Rect[168.8 211.2 214.0 224.2]/Border[0 0 0]/Dest[243 0 R/XYZ null 782 0]>>endobj
+94 0 obj<</Subtype/Link/Rect[144.0 198.0 168.8 211.0]/Border[0 0 0]/Dest[243 0 R/XYZ null 722 0]>>endobj
+95 0 obj<</Subtype/Link/Rect[168.8 198.0 196.9 211.0]/Border[0 0 0]/Dest[243 0 R/XYZ null 722 0]>>endobj
+96 0 obj<</Subtype/Link/Rect[144.0 184.8 168.8 197.8]/Border[0 0 0]/Dest[243 0 R/XYZ null 650 0]>>endobj
+97 0 obj<</Subtype/Link/Rect[168.8 184.8 203.6 197.8]/Border[0 0 0]/Dest[243 0 R/XYZ null 650 0]>>endobj
+98 0 obj<</Subtype/Link/Rect[108.0 171.6 124.5 184.6]/Border[0 0 0]/Dest[243 0 R/XYZ null 606 0]>>endobj
+99 0 obj<</Subtype/Link/Rect[124.5 171.6 168.5 184.6]/Border[0 0 0]/Dest[243 0 R/XYZ null 606 0]>>endobj
+100 0 obj<</Subtype/Link/Rect[144.0 158.4 168.8 171.4]/Border[0 0 0]/Dest[243 0 R/XYZ null 499 0]>>endobj
+101 0 obj<</Subtype/Link/Rect[168.8 158.4 229.9 171.4]/Border[0 0 0]/Dest[243 0 R/XYZ null 499 0]>>endobj
+102 0 obj<</Subtype/Link/Rect[144.0 145.2 168.8 158.2]/Border[0 0 0]/Dest[243 0 R/XYZ null 427 0]>>endobj
+103 0 obj<</Subtype/Link/Rect[168.8 145.2 201.8 158.2]/Border[0 0 0]/Dest[243 0 R/XYZ null 427 0]>>endobj
+104 0 obj<</Subtype/Link/Rect[144.0 132.0 168.8 145.0]/Border[0 0 0]/Dest[243 0 R/XYZ null 354 0]>>endobj
+105 0 obj<</Subtype/Link/Rect[168.8 132.0 195.6 145.0]/Border[0 0 0]/Dest[243 0 R/XYZ null 354 0]>>endobj
+106 0 obj<</Subtype/Link/Rect[144.0 118.8 168.8 131.8]/Border[0 0 0]/Dest[243 0 R/XYZ null 229 0]>>endobj
+107 0 obj<</Subtype/Link/Rect[168.8 118.8 230.5 131.8]/Border[0 0 0]/Dest[243 0 R/XYZ null 229 0]>>endobj
+108 0 obj<</Subtype/Link/Rect[144.0 105.6 168.8 118.6]/Border[0 0 0]/Dest[246 0 R/XYZ null 782 0]>>endobj
+109 0 obj<</Subtype/Link/Rect[168.8 105.6 213.7 118.6]/Border[0 0 0]/Dest[246 0 R/XYZ null 782 0]>>endobj
+110 0 obj<</Subtype/Link/Rect[213.7 105.6 250.9 118.6]/Border[0 0 0]/Dest[246 0 R/XYZ null 782 0]>>endobj
+111 0 obj<</Subtype/Link/Rect[144.0 92.4 168.8 105.4]/Border[0 0 0]/Dest[246 0 R/XYZ null 670 0]>>endobj
+112 0 obj<</Subtype/Link/Rect[168.8 92.4 184.6 105.4]/Border[0 0 0]/Dest[246 0 R/XYZ null 670 0]>>endobj
+113 0 obj<</Subtype/Link/Rect[144.0 79.2 168.8 92.2]/Border[0 0 0]/Dest[246 0 R/XYZ null 584 0]>>endobj
+114 0 obj<</Subtype/Link/Rect[168.8 79.2 188.3 92.2]/Border[0 0 0]/Dest[246 0 R/XYZ null 584 0]>>endobj
+115 0 obj<</Subtype/Link/Rect[144.0 66.0 168.8 79.0]/Border[0 0 0]/Dest[246 0 R/XYZ null 512 0]>>endobj
+116 0 obj<</Subtype/Link/Rect[168.8 66.0 192.0 79.0]/Border[0 0 0]/Dest[246 0 R/XYZ null 512 0]>>endobj
+117 0 obj[11 0 R
+12 0 R
+13 0 R
+14 0 R
+15 0 R
+16 0 R
+17 0 R
+18 0 R
+19 0 R
+20 0 R
+21 0 R
+22 0 R
+23 0 R
+24 0 R
+25 0 R
+26 0 R
+27 0 R
+28 0 R
+29 0 R
+30 0 R
+31 0 R
+32 0 R
+33 0 R
+34 0 R
+35 0 R
+36 0 R
+37 0 R
+38 0 R
+39 0 R
+40 0 R
+41 0 R
+42 0 R
+43 0 R
+44 0 R
+45 0 R
+46 0 R
+47 0 R
+48 0 R
+49 0 R
+50 0 R
+51 0 R
+52 0 R
+53 0 R
+54 0 R
+55 0 R
+56 0 R
+57 0 R
+58 0 R
+59 0 R
+60 0 R
+61 0 R
+62 0 R
+63 0 R
+64 0 R
+65 0 R
+66 0 R
+67 0 R
+68 0 R
+69 0 R
+70 0 R
+71 0 R
+72 0 R
+73 0 R
+74 0 R
+75 0 R
+76 0 R
+77 0 R
+78 0 R
+79 0 R
+80 0 R
+81 0 R
+82 0 R
+83 0 R
+84 0 R
+85 0 R
+86 0 R
+87 0 R
+88 0 R
+89 0 R
+90 0 R
+91 0 R
+92 0 R
+93 0 R
+94 0 R
+95 0 R
+96 0 R
+97 0 R
+98 0 R
+99 0 R
+100 0 R
+101 0 R
+102 0 R
+103 0 R
+104 0 R
+105 0 R
+106 0 R
+107 0 R
+108 0 R
+109 0 R
+110 0 R
+111 0 R
+112 0 R
+113 0 R
+114 0 R
+115 0 R
+116 0 R
+]endobj
+118 0 obj<</Subtype/Link/Rect[108.0 673.2 132.8 686.2]/Border[0 0 0]/Dest[246 0 R/XYZ null 440 0]>>endobj
+119 0 obj<</Subtype/Link/Rect[132.8 673.2 167.0 686.2]/Border[0 0 0]/Dest[246 0 R/XYZ null 440 0]>>endobj
+120 0 obj<</Subtype/Link/Rect[72.0 660.0 88.5 673.0]/Border[0 0 0]/Dest[246 0 R/XYZ null 382 0]>>endobj
+121 0 obj<</Subtype/Link/Rect[88.5 660.0 123.6 673.0]/Border[0 0 0]/Dest[246 0 R/XYZ null 382 0]>>endobj
+122 0 obj<</Subtype/Link/Rect[123.6 660.0 134.3 673.0]/Border[0 0 0]/Dest[246 0 R/XYZ null 382 0]>>endobj
+123 0 obj<</Subtype/Link/Rect[134.3 660.0 184.5 673.0]/Border[0 0 0]/Dest[246 0 R/XYZ null 382 0]>>endobj
+124 0 obj<</Subtype/Link/Rect[108.0 646.8 132.8 659.8]/Border[0 0 0]/Dest[246 0 R/XYZ null 302 0]>>endobj
+125 0 obj<</Subtype/Link/Rect[132.8 646.8 160.8 659.8]/Border[0 0 0]/Dest[246 0 R/XYZ null 302 0]>>endobj
+126 0 obj<</Subtype/Link/Rect[108.0 633.6 132.8 646.6]/Border[0 0 0]/Dest[246 0 R/XYZ null 243 0]>>endobj
+127 0 obj<</Subtype/Link/Rect[132.8 633.6 160.8 646.6]/Border[0 0 0]/Dest[246 0 R/XYZ null 243 0]>>endobj
+128 0 obj<</Subtype/Link/Rect[108.0 620.4 132.8 633.4]/Border[0 0 0]/Dest[246 0 R/XYZ null 184 0]>>endobj
+129 0 obj<</Subtype/Link/Rect[132.8 620.4 163.9 633.4]/Border[0 0 0]/Dest[246 0 R/XYZ null 184 0]>>endobj
+130 0 obj<</Subtype/Link/Rect[108.0 607.2 132.8 620.2]/Border[0 0 0]/Dest[249 0 R/XYZ null 782 0]>>endobj
+131 0 obj<</Subtype/Link/Rect[132.8 607.2 161.5 620.2]/Border[0 0 0]/Dest[249 0 R/XYZ null 782 0]>>endobj
+132 0 obj<</Subtype/Link/Rect[108.0 594.0 132.8 607.0]/Border[0 0 0]/Dest[249 0 R/XYZ null 722 0]>>endobj
+133 0 obj<</Subtype/Link/Rect[132.8 594.0 141.3 607.0]/Border[0 0 0]/Dest[249 0 R/XYZ null 722 0]>>endobj
+134 0 obj<</Subtype/Link/Rect[108.0 580.8 132.8 593.8]/Border[0 0 0]/Dest[249 0 R/XYZ null 637 0]>>endobj
+135 0 obj<</Subtype/Link/Rect[132.8 580.8 168.8 593.8]/Border[0 0 0]/Dest[249 0 R/XYZ null 637 0]>>endobj
+136 0 obj<</Subtype/Link/Rect[108.0 567.6 132.8 580.6]/Border[0 0 0]/Dest[249 0 R/XYZ null 552 0]>>endobj
+137 0 obj<</Subtype/Link/Rect[132.8 567.6 156.6 580.6]/Border[0 0 0]/Dest[249 0 R/XYZ null 552 0]>>endobj
+138 0 obj<</Subtype/Link/Rect[108.0 554.4 132.8 567.4]/Border[0 0 0]/Dest[249 0 R/XYZ null 493 0]>>endobj
+139 0 obj<</Subtype/Link/Rect[132.8 554.4 157.2 567.4]/Border[0 0 0]/Dest[249 0 R/XYZ null 493 0]>>endobj
+140 0 obj<</Subtype/Link/Rect[36.0 528.0 44.2 541.0]/Border[0 0 0]/Dest[255 0 R/XYZ null 818 0]>>endobj
+141 0 obj<</Subtype/Link/Rect[44.2 528.0 86.1 541.0]/Border[0 0 0]/Dest[255 0 R/XYZ null 818 0]>>endobj
+142 0 obj<</Subtype/Link/Rect[86.1 528.0 117.9 541.0]/Border[0 0 0]/Dest[255 0 R/XYZ null 818 0]>>endobj
+143 0 obj<</Subtype/Link/Rect[36.0 501.6 46.7 514.6]/Border[0 0 0]/Dest[261 0 R/XYZ null 818 0]>>endobj
+144 0 obj<</Subtype/Link/Rect[46.7 501.6 88.2 514.6]/Border[0 0 0]/Dest[261 0 R/XYZ null 818 0]>>endobj
+145 0 obj<</Subtype/Link/Rect[72.0 488.4 90.9 501.4]/Border[0 0 0]/Dest[261 0 R/XYZ null 737 0]>>endobj
+146 0 obj<</Subtype/Link/Rect[90.9 488.4 119.0 501.4]/Border[0 0 0]/Dest[261 0 R/XYZ null 737 0]>>endobj
+147 0 obj<</Subtype/Link/Rect[72.0 475.2 90.9 488.2]/Border[0 0 0]/Dest[261 0 R/XYZ null 434 0]>>endobj
+148 0 obj<</Subtype/Link/Rect[90.9 475.2 136.8 488.2]/Border[0 0 0]/Dest[261 0 R/XYZ null 434 0]>>endobj
+149 0 obj[118 0 R
+119 0 R
+120 0 R
+121 0 R
+122 0 R
+123 0 R
+124 0 R
+125 0 R
+126 0 R
+127 0 R
+128 0 R
+129 0 R
+130 0 R
+131 0 R
+132 0 R
+133 0 R
+134 0 R
+135 0 R
+136 0 R
+137 0 R
+138 0 R
+139 0 R
+140 0 R
+141 0 R
+142 0 R
+143 0 R
+144 0 R
+145 0 R
+146 0 R
+147 0 R
+148 0 R
+]endobj
+150 0 obj<</Dests 151 0 R>>endobj
+151 0 obj<</Kids[152 0 R]>>endobj
+152 0 obj<</Limits[(1)(5_2)]/Names[(1)153 0 R(1_1)154 0 R(1_2)155 0 R(1_3)156 0 R(2)157 0 R(2_1)158 0 R(2_2)159 0 R(3)160 0 R(3_1)161 0 R(3_1_1)162 0 R(3_1_2)163 0 R(3_1_3)164 0 R(3_1_4)165 0 R(3_1_5)166 0 R(3_1_6)167 0 R(3_2)168 0 R(3_2_1)169 0 R(3_2_2)170 0 R(3_2_3)171 0 R(3_3)172 0 R(3_3_1)173 0 R(3_3_2)174 0 R(3_3_3)175 0 R(3_4)176 0 R(3_4_1)177 0 R(3_4_2)178 0 R(3_4_3)179 0 R(3_4_4)180 0 R(3_4_5)181 0 R(3_4_6)182 0 R(3_4_7)183 0 R(3_5)184 0 R(3_5_1)185 0 R(3_5_2)186 0 R(3_5_3)187 0 R(3_5_4)188 0 R(3_6)189 0 R(3_6_1)190 0 R(3_6_2)191 0 R(3_6_3)192 0 R(3_6_4)193 0 R(3_6_5)194 0 R(3_6_6)195 0 R(3_6_7)196 0 R(3_6_8)197 0 R(3_6_9)198 0 R(3_7)199 0 R(3_7_1)200 0 R(3_7_2)201 0 R(3_7_3)202 0 R(3_7_4)203 0 R(3_7_5)204 0 R(3_7_6)205 0 R(3_7_7)206 0 R(3_7_8)207 0 R(4)208 0 R(5)209 0 R(5_1)210 0 R(5_2)211 0 R]>>endobj
+153 0 obj<</D[219 0 R/XYZ null 818 null]>>endobj
+154 0 obj<</D[219 0 R/XYZ null 737 null]>>endobj
+155 0 obj<</D[219 0 R/XYZ null 658 null]>>endobj
+156 0 obj<</D[219 0 R/XYZ null 434 null]>>endobj
+157 0 obj<</D[225 0 R/XYZ null 818 null]>>endobj
+158 0 obj<</D[225 0 R/XYZ null 737 null]>>endobj
+159 0 obj<</D[225 0 R/XYZ null 540 null]>>endobj
+160 0 obj<</D[231 0 R/XYZ null 818 null]>>endobj
+161 0 obj<</D[231 0 R/XYZ null 624 null]>>endobj
+162 0 obj<</D[231 0 R/XYZ null 359 null]>>endobj
+163 0 obj<</D[231 0 R/XYZ null 287 null]>>endobj
+164 0 obj<</D[231 0 R/XYZ null 201 null]>>endobj
+165 0 obj<</D[234 0 R/XYZ null 782 null]>>endobj
+166 0 obj<</D[234 0 R/XYZ null 590 null]>>endobj
+167 0 obj<</D[234 0 R/XYZ null 479 null]>>endobj
+168 0 obj<</D[234 0 R/XYZ null 382 null]>>endobj
+169 0 obj<</D[234 0 R/XYZ null 275 null]>>endobj
+170 0 obj<</D[237 0 R/XYZ null 782 null]>>endobj
+171 0 obj<</D[237 0 R/XYZ null 709 null]>>endobj
+172 0 obj<</D[237 0 R/XYZ null 665 null]>>endobj
+173 0 obj<</D[237 0 R/XYZ null 571 null]>>endobj
+174 0 obj<</D[237 0 R/XYZ null 499 null]>>endobj
+175 0 obj<</D[237 0 R/XYZ null 427 null]>>endobj
+176 0 obj<</D[237 0 R/XYZ null 369 null]>>endobj
+177 0 obj<</D[237 0 R/XYZ null 275 null]>>endobj
+178 0 obj<</D[237 0 R/XYZ null 203 null]>>endobj
+179 0 obj<</D[240 0 R/XYZ null 782 null]>>endobj
+180 0 obj<</D[240 0 R/XYZ null 709 null]>>endobj
+181 0 obj<</D[240 0 R/XYZ null 637 null]>>endobj
+182 0 obj<</D[240 0 R/XYZ null 552 null]>>endobj
+183 0 obj<</D[240 0 R/XYZ null 479 null]>>endobj
+184 0 obj<</D[240 0 R/XYZ null 422 null]>>endobj
+185 0 obj<</D[240 0 R/XYZ null 170 null]>>endobj
+186 0 obj<</D[243 0 R/XYZ null 782 null]>>endobj
+187 0 obj<</D[243 0 R/XYZ null 722 null]>>endobj
+188 0 obj<</D[243 0 R/XYZ null 650 null]>>endobj
+189 0 obj<</D[243 0 R/XYZ null 606 null]>>endobj
+190 0 obj<</D[243 0 R/XYZ null 499 null]>>endobj
+191 0 obj<</D[243 0 R/XYZ null 427 null]>>endobj
+192 0 obj<</D[243 0 R/XYZ null 354 null]>>endobj
+193 0 obj<</D[243 0 R/XYZ null 229 null]>>endobj
+194 0 obj<</D[246 0 R/XYZ null 782 null]>>endobj
+195 0 obj<</D[246 0 R/XYZ null 670 null]>>endobj
+196 0 obj<</D[246 0 R/XYZ null 584 null]>>endobj
+197 0 obj<</D[246 0 R/XYZ null 512 null]>>endobj
+198 0 obj<</D[246 0 R/XYZ null 440 null]>>endobj
+199 0 obj<</D[246 0 R/XYZ null 382 null]>>endobj
+200 0 obj<</D[246 0 R/XYZ null 302 null]>>endobj
+201 0 obj<</D[246 0 R/XYZ null 243 null]>>endobj
+202 0 obj<</D[246 0 R/XYZ null 184 null]>>endobj
+203 0 obj<</D[249 0 R/XYZ null 782 null]>>endobj
+204 0 obj<</D[249 0 R/XYZ null 722 null]>>endobj
+205 0 obj<</D[249 0 R/XYZ null 637 null]>>endobj
+206 0 obj<</D[249 0 R/XYZ null 552 null]>>endobj
+207 0 obj<</D[249 0 R/XYZ null 493 null]>>endobj
+208 0 obj<</D[255 0 R/XYZ null 818 null]>>endobj
+209 0 obj<</D[261 0 R/XYZ null 818 null]>>endobj
+210 0 obj<</D[261 0 R/XYZ null 737 null]>>endobj
+211 0 obj<</D[261 0 R/XYZ null 434 null]>>endobj
+212 0 obj<</Type/Pages/MediaBox[0 0 595 792]/Count 20/Kids[213 0 R
+216 0 R
+267 0 R
+270 0 R
+219 0 R
+222 0 R
+225 0 R
+228 0 R
+231 0 R
+234 0 R
+237 0 R
+240 0 R
+243 0 R
+246 0 R
+249 0 R
+252 0 R
+255 0 R
+258 0 R
+261 0 R
+264 0 R
+]>>endobj
+213 0 obj<</Type/Page/Parent 212 0 R/Contents 214 0 R/Resources<</ProcSet[/PDF/Text/ImageB/ImageC/ImageI]/Font<</F4 5 0 R/F8 8 0 R/F9 9 0 R>>>>>>endobj
+214 0 obj<</Length 215 0 R/Filter/FlateDecode>>stream
+xÚíßoä8rÇ%µúeždïö;Ýž 3Óö¸go€ h“y+Iòäá°;wÙàr{73w‡ûïãþáný ¥ª"%Q>sݵMµø‹ß*’EõŸ_]‰õã?Wâ‡kñþƒøéÿ^ÝÉW‰|õgñ~ý›·?ìþøþÇ·kñ›o¯ÅõõÕÛ›]­Oïîóÿz÷é]öïwbó¯ë]y¿+våã®ÜïÊ»òoÿýî“øöå/Ÿßý§¸yõî?ÄæêêÝÜ‹ñéáUñ< ¼p™ùp½\./ζåñ–7Sç’÷˳ÈS•³óˇirÉë 5R ny31.yÝÅôTÎ/“©pá¡h7S຿ðÈÅ_&ŽsÝE¯œoæºæRÙ4Gp¦¯ìö™e®{á™—ïǸò ÏJñ/âºö¬•`ã —žÍré—±^Øí2K\ò³^ü×£så‘×Gùnd®;¯§$cr]y½3—e´nc=–×£pÉÈó\'…°Z¾œk,L
+ÏÒ˜‚2´&¢Ô!FçʽQ˦/®h\.äƒ 8dŽƒix.²Ø¹äøXžß—p€Ë ­sežec™KFnp–¹Öž#ea•+óœ)‰E.W¬åÄ\±çPÙXãÊ]Âꔘžh ¢˜ h`¢˜T¤×z,WêVG‡Áô4Õa0AÇ8g—t«5®‡)j<b„Áô\2j„ÁpÝuv¾\¾~Ø–»íÁ‡~; é.ÿüusoÏAôÖaÐwù-Iÿòþ¢Ÿƒ¾»ë¼sªtg€¶2àZ÷ÓUå^[Zë¡Çî"¤Knnð†ÍÅ5héâL²—‹jÐÓssÖ”!ar1»‹•NÍé²ÉÅ
+ä¹ÙŒ.óy\i?Ún±ËV,®h(<z³ÈŠÔƒýU “´wN²Á†Á%Zü̫Ε€Eóé\d‘÷í‘ÆÊÑÎ5lFåJG0BŽ)&D.1lAãÊ­xH.XdäÂÀÞÄëûÂjÉŒ ¬Eò–±H§¯®ÔB8cTÖ7Kªá'ö¹-Hð\Ò<H3-9ßõ\0êàÚ—[¶!‚•JPôT×Á†Ìö2¸hƒaåŠÇrÈ<Q°\3œ=Á3D07Ãþ¬¢‰+×Ú +$´$Äq9 …4éðQ\ÙÈ™¡` ^ â÷‚“°9†+rC4(Z ¸2~`6^‡%Ý\±+¢Aé°U7WäˆÆ“Úvrenu²Ã‚N®Ø±îBú°¤‹+r¬»OzÑÁ•;ã’iM
+;¸Rçº %\±х–²¤•K:Ø]¸1¿jåÊì.Ü2RØÊ…TyP,”­\‘C‘!Q9Ú¸°*?0jxlZ¸*ÍUl¸*¿œ aˆ³.ÖÖ؆è¹2E-h‰– \y´Zi¹„£ª3¥¹ŽKºª¸¡?Óqe´Ð°êÉN?ÕŒýé÷px¦iE•3¬Ÿ_㬠jâZh¹dåáÆm ­Dךh¸-Öhåši¹·)ëÆqHü
+q\žž Jê•ãÃM¶$à ¯’k¥åÊKÏ°fˆ%¡š ˆÎ«+Ôrím/8uB`Ié5— :¯v®@ÏuÒ
+I™õ Ÿ90‚Öëɸ\'mÏðfˆQúDÅ•Sc¨®…–ë$‚1e­$Å>t Ï½B<×LÏ?=¢ˆ2ùα‚ä¹v%”ïàòô\Ùá³rŠ"J¨âŠˆfØɵÒríï5ßÿ
+¿d×)l3—¤ša'W¨çZïDsÕh¹ÍWpeT3ìäòõ\û›=§Rì•,×ìX[ɵ7Ž3êÊ12BªW
+,×<z’'%WI¤(³T³' ÊÆÏ?=5WÊš¥v†ˆóWN6Ãv®¬‚Óà’¬Å’§k@” ÀsÉ'³PsMŠ´X’ãZĹrHàÚ5<Ôs¥¬5;œÐQ6V.8ÜFÃ%Ykv­lp1ÒÛ¹òƒ(h¸M$®ÙŨ&M6‚‚ÂU”^Çœ §eR@“9ë ô:®˜Ó_ÊM6V4®ƒÒë¸"Îø’¨gÜ A×Aé5\o#ÂH6¢€ÈuPz WÌ[ìïzþ³*—d ¯N®½Òk¸"ÞæLŒyø@’•k¯ôj®œ¹‹‘b3Ö6
+*×^éÕ\ 7³HªÊ3†W7×NéÕ\‚™¹#1Ž(QTHçÚYw¬ŸW.†Ø%p›
+kS¹[)¸v­*®½ÝKFÈ!ÍJ•¨]R6¿ÊujDKì5YÐ q8€•EPåZ•M 4\OEØMÁ.ÃÌË\ˆ5›™Æ$eE j\¹†+=ðätCL2
+®²ÞdäIà¹Ö,ÙhØĬhrlµÂ•,y&ÚK‰K°d£>0§÷óâ2W5#jÝCF#ÐSÅjåVåáëe®¸bØi©€–Cíäïô^“×…’K6¸¢Šåå=¤j:êÕÛI¾{ñ©>ÀR*Wʔ粬îquGó~1©X™&É%xQ”ó\/ŠrKšÈ¡Ã\¹¡ºÊ•q£CǹÒç&‡®˜ºÍµæG‡Ns‰ç&ó®è¹Éዳ4?.ùìd~Ï•=;™ÇrMBæË“@À¹e÷£y¹ÿ¶’óM‰+fË|¹ÎùFõ[ïì²²ìwZímçSFÛ]iY
+9\õ‹j#eg– Â¨ÃuúA‘ý¯æò9\µ‹êO%žËïƒKqœ¯›KqQVw°ËÇr ˜ð00çjê†â8_7—î `¨š/{v¸¤†k­ÖyÅq>Wó¢Xé`F……‰FÇœÛj›Çù\Í‹„Ò¡¸æ(®Lí¿2M¥8·àj^¤åÊ,qÅÊxãþ†ÔšØ<·àj^)Šká’^C‘[ç)Šã|®ÆEZ®”F•ÚpøŠÆz^©8·áj\T®~º#ŠkSοÊ&6Žóa¸i¹€FêŽÑ¬ª6šxmWý"®Íª~ë'š&Öó¡¸êi¹b‹\u/&ê&Öó¡¸êi¹Öüð°Pœfy¿ÑÄúq>Wý¢¨–¦zäÖ¸üDÃU
+U+M¬çÃqÕ.âsH.UúíÁ/–=ê&ÖŽóá¸j‰’bW¸";\¥üÃr¼±_öÕM¬çÃqÕ.jÄQ)–+,pó”BÅUz?”¢‰;ѦrU/Z×#¢øÀåõË•µqUó!¹g
+‹ºº®GÉð³½c4äJpë‡\mu)\ÛžN½ã‹êzã
+*¶å·réêR¸`Öïâ29w#*­:ßV—•îOÖæ\«öé×¢<™i¹ÚêR¸²“Sï+-÷çºv›ZcÛêR¸òÓl •ËäâþZÿæ´©µÒrµÕíˆç+oÛ’Õ]A®Eç>ìiR?Yl«Ká*½¶?®¸e®Vçj©Kâ8.£sÙyKÕ:WK]WbÎ5Ç¥ÛÔCÿF¬¢¯K⊿é+×+Lƒ+ç®GU)àøTZ¹ŒÞ¿Q]‹ù¾håÒ×%qéÞÏj—«ÔØË¢ƒK[—Ä•}z;—ÑBöÖ]DÚ“ºFÅð½=‡Bùz½a¾ŠÏð=KÎÃ÷b½p ÏÕýºåirÅÿ°\É$¹º86“ä2™0¿p Ïe4±œ4×|’\ò–+œ$W÷löL¹‚irEÏ0ð5ü>Ž®¸ÖÏ0ðÅ}Ïj’\ðL¹Òg ~¯ÙX¥ëe¯ Ãï¡«t5y…ûÞ@ç©Ô—sT—Ô%†ßË9V‰Q\Ñ䎮Pû½·®¢Ë¾ßSìÚJ[Ô¥Èï•v-àðP\0µ€Cvú[ä÷›;pd8®É}}ÚÙ €‰¶œsÌÐ9l
+Ìê\AtÀ3c—+  âÜýáµjpÅ`è³ @Xq ¤GŸµ†‚$c0ô;  ÇȬûÑÏ\Âù†O¼’ñŽ=ÀÖèM8Æ`øÈHÎaä–á§R@Š•G`„ÝG ©èÈ!"a
+¢pŒ8À(›t@ëçQç`”= ^8æ"GLhutÌEÊR;æˆJŸS {zD¥'­HõÒñ”^PšT%mµM’4 ¨Cs4CDØÒL˵v×iË/PÐØÂQ3,ÇB@vé#bJ3$ ÏpF2Ä5íyÝŠG1DI ]<ÅÇSâㆂ1ÀFˆqx@Á`¡‹fX™i
+œéöÐ1‡  à, sd Þ Tê׌ÌÇ3 ÔKÎcîç $ÆÇÜa:\‡±†;peu°õfŽàB*ý@±ž1ð?j˜c† À©ÃtXÄ ë€í‡é°”éL/AƒÈ!Qà¸ÖX°•Ý5CreŽt˜Œ¸A8F†Xûª¥qB Õ,
+3Cìuâ,ùV…™!öªõا»@sá ±GéÈ l
+SCô“‘5^)^`&°}Zâ­Ç7C-—ôƶÄÜHº 06Äž4QùP0´=yg´ªWÆÀxÔöãsCkc_ßË“‘¡l'fƲ6}¤`aàÚ÷bw„T®”Ôa6µƒ¢Y!™Kz#ÉÈ|
+S®Ü³SÎΗËåë‡Çr¿\^D†Ÿ¶0æ²ÑaÖKWþ#†K:ȵ°Àe&‰£tŽË½[Yár®Ãº—¾p\rjÝ…ä"ìÚ Q zH®"r‰kc+›Vw¡¹X+=i|b“+w†kQØärFëqÛx.MEãi\Ü%œDƒÆå„t`÷{)\r2VHãrÀÑ[‡$®Ñ-Ÿu@ãÊ'b…T®‘ã_Â6‘kTK¤ä¾P¹ÆôΔí'*׈=)iŽÌ5Ú£¥½Ð¹FbÄÄ2×8CŒ¸·ËàeˆQ³ 8\”ôÀ14ƒÍ5¸vÐSåx\k#É%…»RhÂ5¤(²R‡¹\Ãñ2ÉØ\Œ´ŸA—1×@“1扮!ü³Ï=HaÂÕ¿)ò³4¸ú3H>5ãêWM’… ¹útÐF9Ц\ý™%ãs‘s¨{š˜ØçêcÚb˜®n‡Ë¾,šŸ›°ÂUH»¶xiÞ";\¶h¯Ë‚MáW!/Üé,›\–ºì|S¸ÆUÈkc|m«-6¹ø©ÕªK{-±Ëe^HÝ“‹Kf—ª.ÙùkÛm胋˜ï/{xÏE?\å—ïŸßôrûÞ¸v½Öagˇ¾îÝ'×®Û®—gê~º|èó¾}sí;n{Âayq¶-çËååÃCï·„k„ɧWé«;ùêÝÅÕG!'n®ÅÍúí?ÿÓCvû£oÄ}¹ÈýÝ·¿ýöËgñðùë/¿ÿãö??}ùåOß~ùõÿ,ÿ÷ñnÄÕÕö®>¼ûA¼¹zÿözû!Û‹ßäo®Þ®·ß\ݼýA¼¹þ°¿GòÛ¯?}6|ùõç¿üôíë®âMõs~ýÓß¿üòûÿùöØÎ?¼yü×Ç·øƒÈ¶¿ü*²Ï_?ùë矷—&ò‘êÿTMòšendstream
+endobj
+215 0 obj
+6311
+endobj
+216 0 obj<</Type/Page/Parent 212 0 R/Contents 217 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F8 8 0 R>>>>>>endobj
+217 0 obj<</Length 218 0 R/Filter/FlateDecode>>stream
+xÚ+ä2T0
+ár á
+ä
+endobj
+218 0 obj
+31
+endobj
+219 0 obj<</Type/Page/Parent 212 0 R/Contents 220 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 5 0 R/F8 8 0 R/F9 9 0 R/Fc 10 0 R>>>>>>endobj
+220 0 obj<</Length 221 0 R/Filter/FlateDecode>>stream
+xÚ•U]OA}çWÜø¤IÙ²€}«¢†DíV°é/Ãì]˜:»w;3 ¡¿¾wfQJŒ$†¹_çœûÁïV þ‹á¢ ½ȼu9k}¾A<‚YÆ–ÁEféi SI%žÍ~ÕÖóhXÛÛçèöˆb˜¤X8•))œ¢¢vîC7®ÝAí:[) –2·!E«–…ÿ'*} ¤$«œSAih­ØÂV'”ÆT‘‘ÉC~à[!#WÊ¡tgE
+’RU,2€ëƽ¨ëëzç+ÊsŽ{z˜ü„Ä(†ËžÓ­u˜Ãüôäê)™žÌÏàëKÄQ':Bº7Ú‘îî‚¿­Ñ¬nÞaý~õ®J2N,4ò[ã£Å 0õ;ÛaD0ÚÀF0q°ö€õÑ«»FÍ),¶p-ì¦;ýCi%GCNŽUëXIaÒ=Kº
+²{BëšÅ‹”ŒõºÔ­,kWtÏÍ· áû#ôêÍ3jÜrÓòœÚZÈ]vh2!ÑFMÖŠ!§®SM¼_n/&³q$Is3'׳›ö$I¸¢v_Ë“çñs)±ôþü¢E 
+ rŸøKVÒ7Bhå¶GõijeÙy®2¦…¡õ”=Š„¬›†}Ûszµ€¼ I2æEh³døj(Øý¬ÇÊ …®«Se©›S`¡*R¬‡÷8bfÏ]—ºª7@VÖQ®þpíu³Š”ÁíÃÜ®öÐ秲2†…ÞB ”²½úQ§7?;ÀëE¨Ü·)ãÓ“„‡D8ü¾@?d©ß‰³‚Šö+ÙYíûç¡ãÝ-ûÀøèYd?2KQ}…éÎHkÚ„UÅ0 öK]K6µºCæþRnÞ\À[0|æÛ¯½ÜƒÜév,Š“Â#fÈàý¯Ð‡Žk¦oõù`|?Ä7¿ u¢ÿŠÿÊñ·šx[Ͷ6 S»Q«3ðm}ó Øv¢gö¯þûõ¬õ½õ'(B‘endstream
+endobj
+221 0 obj
+825
+endobj
+222 0 obj<</Type/Page/Parent 212 0 R/Contents 223 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F8 8 0 R>>>>>>endobj
+223 0 obj<</Length 224 0 R/Filter/FlateDecode>>stream
+xÚ+ä2T0
+áÒw³P04TIS046Ò3W072PIÑp rt QÐUp VÎO+)O,JUpI-ÎLÏQÉE™%™ùyš!Y\ººP­F 13=30ßP!89¿ $êÂÈ
+endobj
+224 0 obj
+121
+endobj
+225 0 obj<</Type/Page/Parent 212 0 R/Contents 226 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 5 0 R/F8 8 0 R/F9 9 0 R/Fc 10 0 R>>>>>>endobj
+226 0 obj<</Length 227 0 R/Filter/FlateDecode>>stream
+xÚ­•OÚ0Åï|Š9²Ò&K 7
+l…T´) {ââÆp•ØÔ6Úòíkc•V%]å@Æ¿7ö¼?ZtÌÀ „nòªõ)k=½ÄÄæMЃŒ¶CX`yŽê!ûîJžýÈyÏ‘ïÊü
+f\£,HŽ0AÅÖÜÞrɶ糹G"Í­„ÿ³…~'aD+Æ™Òf/B*»—)ð¯·PÓ?¤õd~ H±–¤ª¬ w.n¡SÌw’é½±ïVHÝ€›%·¸ª¦I—˜ßRáÿŒímrûf¨ÖÛ¿Mîœ`ÏE§×)áUoPžb¬þ1Â\pï*Æ
+l ™áÙt:…  üÞc° ç›Ý ¡׶ŸU›JRèÕÃ]ç9K’§ÃÌG”2K$%¼nOèdxÆ^‚fì)ϵ=N!“„«» zbͪmyøÕ²ú¼c›qæ‚byh(ÅŠ˜#ÌÕ] ÅËØ<âGøÂ8º1X¬ŒM̵ÈÅÑÛÑqÑÑ{aßúŸéE?6²æU×~žf­¯­_Ð&Ùûendstream
+endobj
+227 0 obj
+522
+endobj
+228 0 obj<</Type/Page/Parent 212 0 R/Contents 229 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F8 8 0 R>>>>>>endobj
+229 0 obj<</Length 230 0 R/Filter/FlateDecode>>stream
+xÚ-‹Á‚0DïûsÔÈ^U䬰~€©[S‹_«f&™ÌLÞ“ÅGŒ²N¶wÚ
+­º5˜!\š¼Ac
+ÈeÑö›Naw: F7¿ÎQÑêä¯!…þ1û1,åFÙÌþh•¶Êp^ãÛ zu5XÒµ:ÒŸ"${endstream
+endobj
+230 0 obj
+125
+endobj
+231 0 obj<</Type/Page/Parent 212 0 R/Contents 232 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F4 5 0 R/F8 8 0 R/F9 9 0 R/Fc 10 0 R>>>>>>endobj
+232 0 obj<</Length 233 0 R/Filter/FlateDecode>>stream
+xÚµVMsÛ6½ëWìø$Ϙ”%9úpNµÝtÔÉ$®#÷¤ D.%Ä$ 5þ÷ÝH™bc§j’Ñöë=<ìê±7„sú a:‚ñ’¢wµì >Ìa8‡eF'“é,ÓþnÐÊM ŸŸÐ<IÜ.¿’Ý ‡Á.Mboy}û¤…DZYLAe0«2·ÁVëÈ>[‡…·”F#‚St[4´
+õ‰ª¯“Tü-¬U‰¤¾.µ†‚uÔ36ǦZJ—7i¯2òMÂk¥%Ú:㋪“°mð±’†2þoî>ëðnBU½Åjî?Ys|\jtöØôԓг³ê‡@"_ÖÅdtæ{=7÷÷ ³PFV¥r`5&¬¥ôÌûÔoŠÊ:bG¤~×GàÌ¢õñ3£
+oc5*aRjôÞ<ŠˆæÝþA_µ»—œ’ÎüÈ!ç‚“òƒÌ?ë»ÅÍ3jCüÔióä•Gv;Uå)¬¹DE‰¦a§û¬Ÿ}HŽpÒ ‹÷íÔy’Ç£xR·Rj¦dýf¥ó=UÖ£ò4\ú«v  D·Sæ!0ÈÒ3À(ZÐýL"3¸mfð­QN%*Ruà0!’õÕJVCìö´1ªŒŒl¥µ2.ÓRG²[W®uJ¿ÇÕr¾Éÿ,®>ÊOt|#0üu©Ùê0µ Bb¶jYñ¨¸lXš4¬Ðƃ
+m‹ì±Â
+»è½¤ÛøÇÐ 7IhŒþ¹¢qºç!Qe‰ ÷Š'):.û¸|¿õûq&ç!¿ó`<k†_Ìük\/fçñœH ówüýû²÷WïÌHCendstream
+endobj
+233 0 obj
+942
+endobj
+234 0 obj<</Type/Page/Parent 212 0 R/Contents 235 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F2 4 0 R/F4 5 0 R/F8 8 0 R/F9 9 0 R/Fc 10 0 R>>>>>>endobj
+235 0 obj<</Length 236 0 R/Filter/FlateDecode>>stream
+xÚµWÛnÛ8}÷WÌcŠÖò-qâ
+¹@ÑŸ:sàùARå
+3øfRð”F’im•öhA­Qz2X*±w¥õZë!Ã¥’˜
+äðì@hÝT)ñHUg `/‹Z «üý~\\¢~"*?‚x drA ÿŽ&ËžˆHOþ€ÚèGH7–ĵŽðÍÛ›NíG-MÆÉ´ëÖpUz¸U«ôy}ªÑ¯Œ]@aœ‡ÆqlyvI|DçÄ-Â%µó‚ºÙx#MùV…’Å^‡Rw6Ž\§÷P±³JÈBit`­ÙïG%­q&÷_Æ'SøWé̬ý/ìð*= 8k-*Ü5ØàŽ¾¡‰À†Ž™YvŽª&T,Þ5ÊRÖ †”TnˆÞÍ-a 4oØ@iº
+øݳ£xª< j¸KÓÁ†þ0SÁä?‘uQ××ÑKǪÊèò>P²Gô7®Jô¾EQ…Í(s\xk´úÞUï~‰—È5ŸÏy²ÿlÚf˜‹¦ôq ´Ã†‚ÎFÃáå“ä¬Cy¯rÐK´ ,ñÞRÍTˆ;÷ÚZ¶ÖŒÙRe4áÁ©ª.±{Ò/I½È…DfàíÍÕ5º&­”¨Ñf#ÔÚMÇLŹ¸÷Ï•´Q²0º)BMk$U%wÿJù‚„¥œgB¹AV¬;_ˆp³`‚¤É¢H‰ÏÀÃ:Þì…-Ì
+¹…I%r#è­­¦çŶ‘ÙFPA¢SeCpª›o¢–¦±4²Ã]0NFPÖò 'ô¼‹‚÷i˜$6öªÈ*¥ Ú{†Ž‚0-c‹xá÷a´ú%ռϚ€~ Ö55ËÏí.ö5ÏÝðë)/R3-ojJrÃå*×]«¤× !½Zâ¾’~¶}Öá2åDJòíÕååI…h\G[SÙA_A»>ªiwÎ<‘à Ó0+ýœ¢óø^Èç;ÝÉÆZJü—!@Ý"pU´®ŸJe©w¥¿Ì+©¬åQ°:ÄïÁ©Õ.¹-8Õ-9Ó¤ÝÑF¨€Ö›a¿,PS??÷³öÒhxL/ÓÙ0™±ÛwŸ.ÞÏÉKh“ën@½C§n5H«Â\a'”Ë89…þéxÈW§|v<áü†ý³ñ×¼÷Oï)~×Çendstream
+endobj
+236 0 obj
+1084
+endobj
+237 0 obj<</Type/Page/Parent 212 0 R/Contents 238 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 5 0 R/F8 8 0 R/F9 9 0 R>>>>>>endobj
+238 0 obj<</Length 239 0 R/Filter/FlateDecode>>stream
+xÚ­V]oÚ0}çW\ñÔJ!k€}ÜèÇV‰­™öÒ'q¨»Ä¡¶CÅ¿ß±J;ؤ )Îý:ç_ó܋迈¦CM(-{ŸãÞ‡›KŠFáâß&³È>fgXÁC±VçñlÆEÞb0œ„ck?rûÒª,™ÌH×I)Œ&FOUBy¥h­„4B®B²¶óË{Úp¥E%©Ê¯—¦Ø’XÉJqM†}Ѩ¿¶c/e?°E µ¯3;ë?bÝæìë>Ukƒˆ:ô…†á¤3² ËShJR¼¬6¨¤’œ€¡DYˆ…µ—åc8k³\ú#ä˜ß.Ž¦˜ƒ,À¿e†¿°--¤á*g)§‡3¸>œ#UµR¬Ôöa#2:_x2H˜æ Ø0Sk—©P …Zæü÷²@µ«Ì”Lk®={¯½GÛ(ŒZ—£° Ÿ
+¡o$Û0Q°¤àmÍÎä–Ö\fˆ«ÄIÆ´Á¼fjm˜ÄšH¡'ØÜÁRÍŠ.4()„èBeB¯ ðlóµ!|2˜iΑ•«gîCç{” ü5 Ï5¯Ñ¼ˆÐ@H+4ß›J ³õ€­—œpŽÉ¶ã !@H?ù!¦¥«Á¬÷aó æ[]1ÄŠÂÝ›mDLu´ñì!£NZGIiNêÃQuBí? ¤+âÿ(ä½ý?öCn·«¿ŠD1µ=>¬ËnKÞ¥ÝÿÚÍWÔ”VrÃ¥à2å}‰ãe@‹%.“«š­°x·¸»h¹¼ò»[1˜|äµL}Ÿkí¥eÚt•›tÿxÏǘó])tÓü#Æ}ë]zæƪƒ˜9ªÝéaU
+TÂP‚6:`¥ÐÈ5ÇiØn§¼ðÇL@+'¿mªü
+›rŠ€¡£ÿ/Ûî:ÓìvªïV€­•<5öÑ™CÓW¬Ðºæ-xWœe”1Ã(WU Þòðx´_0S¸·EØÖ²6kj
+endobj
+239 0 obj
+808
+endobj
+240 0 obj<</Type/Page/Parent 212 0 R/Contents 241 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 5 0 R/F8 8 0 R/F9 9 0 R/Fc 10 0 R>>>>>>endobj
+241 0 obj<</Length 242 0 R/Filter/FlateDecode>>stream
+xÚµVßsÚ8~ç¯ØG2ƒ ÞS. =nš+WÈ›_„$ƒZ[r%9iþûîJ68ôš¤íÜ„ÉØòj÷û>í}é á þ a<¡/{®{§ó ŽÓ¬sü6™ éQôÇéY:†År óZs¯Œv'ëOh}Ãa´MF“ôŒl×;,óÆ*kî•ûÞ@É4ÛFC+¿ÔÒyÌ3`Z
+œ©-žRÈÉ£4 iLÖGá îKé4åòú•Â-ÕŠ[UyXÚ0p-]XŽU#Ï ëÄÉ‚ŽÜK|ç;É?‡ŠK$B^(î]ለ,•o\HÑ:Á”}’NÆŠ—«g
+Q_þ:Æ6ÇÌy,üÒÉáên¹jíB‹Èú"ˆ²‘¢…¸Øꥶ® ¯K<óìj'éb†h£“ŽÜU#·°ŠòîÀò<µ,/"ì1Ã<$ÍóÔbb¡ŠeUHÂó­“ÙÔoJh¦pYûÌ¢5+
+¤ù üÓ±iëÉäGœ˜ÝÆîmDÅT£~ßËF“)<…ÝoSM³RBô ¨==wk •)ë <%,œì$}²ƒê¿âým6°Uå
+}ňŸpQI¿S.¬g}Šº•ög#Ý¡ÂðÏVMïÌ9Ãó­¬0¿Aj­|цñô|Š.“àðÿ xe*…©ÓœW]nžË4ø”ň¿¬Ý‡¦ Ä
+ZTºªý¾±O±Ô÷˜Â0 ›Î
+-9}ûØÙÐ\tŒ–?=aF!×ÝKmù<®ÚÞTÏ÷¬Ö¨•+¢Áùú×2y÷þt§ ÝeLgX5ÁgÏ!žÂd:%¯×/çk<·ÐÅW&÷$TÛ¥Ã-èw&ÓÑÚ:£µñ]S|½Y÷þí}rÌ[endstream
+endobj
+242 0 obj
+1039
+endobj
+243 0 obj<</Type/Page/Parent 212 0 R/Contents 244 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 5 0 R/F8 8 0 R/F9 9 0 R>>>>>>endobj
+244 0 obj<</Length 245 0 R/Filter/FlateDecode>>stream
+xÚVËvÚ0Ýó³lÏ)N ”$Ë6é#«Ò†~€Ç [r¥qRúõ‘dB)käû˜«1?G%\ò§„« Lç ÛÑûåèâã ”ÓbËš×æ×¥\V¯¦Å[¾0­Z#¹.¼^þàÊ”eªOæÅLê–|®‚Ú4„´³è)¤¹‹|mÉÁÂzÐÞtT¤G“b>€O¡ g‘SÉ
+*ãQ“óÛÈÁh&í­£L¸U}nifZÙÃÎ!(­1ÈsÉ+:åÑR³MÉAÓ¬;³<ÙÝ9Ûò®§óæ·¿N:­ö+¡u⺘î1tγâŸÄ!1‚‘ª„¼k¢4y
+6:=F6¨&µIÖäÞ/Cˆ ÍÉ•ÿ§o·ÉÊ“Ê»_Ö#Ð9-»-)!ø ô&­¥8j×4£®>ЖúÄš/`? ÆŽðŠi󨪒'o(gx—gk³î}ö–OÝ9ƒ¦lacÏø#%'ìÙu0WʸêmîqˆGe8%a§¬XÉ Š;ÿŒ§èA­t<‹¡Ž¤žÅÙäeƒw2 É<¹žoJædƒ±ÌC."µ!Féx´/
+¢—ytûéþ
+endobj
+245 0 obj
+911
+endobj
+246 0 obj<</Type/Page/Parent 212 0 R/Contents 247 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F4 5 0 R/F8 8 0 R/F9 9 0 R>>>>>>endobj
+247 0 obj<</Length 248 0 R/Filter/FlateDecode>>stream
+xÚ•VÁ’Ú8½ó}$Uƒƒ!™c6l6³U[ÅÌžr²Œ•µ%G’¡øû}mÉLÂ&ÔLFju¿÷úµÌ—QNSüå4_ð¿lF¿mG¯?<P>Ïf´-±·¸Ïù±ϳEö–VÚ)¬;ÑF¹ƒ–Ê¿Ú~Æ‘7”çñÀd¶ÈÞðm¥¨8‡ûN-ºZá»)< S"´:`§uÚåÈiSZ׈ ­!{À² ³¢ÂòVþ«BFOª±A1Ž ‹qʳËZxäÂ)]°œQŠº>‘(
+UP°d04¨QPélCÀk‹ 3¢s"ªµHæÉ(…ãY*ýsœ¥
+3† P(¨ºNãXY×ßL1ʨcœRnšoñ(µ<Kz ÏŒß RÕWñÄ[xR”…nlëÒ-*úw%nïŽ_ˆØíáÁ\·àÍybÅ.zéÊO‚>æ@l{®± ·z”M—ºÝ§‚9@-i±˜£(ºzz÷aK“h‰-Ñù­O6H§[¶0'™Ä³“ålÊGó)/ΗSðÞúôûvô÷è?¢÷†endstream
+endobj
+248 0 obj
+920
+endobj
+249 0 obj<</Type/Page/Parent 212 0 R/Contents 250 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 5 0 R/F8 8 0 R/F9 9 0 R>>>>>>endobj
+250 0 obj<</Length 251 0 R/Filter/FlateDecode>>stream
+xÚ…TQoÚ0~çWœòT$È À»1´>L¢#l/}1É…ºsìÔvZñïwgCDÙŠtI|ç»ï¾ïì×^·ôd0Áx
+EÝû’÷>-?C6NGWä›Î3þ,oÆé,
+ùi/÷ÜÆ’Í›‡® yd³þ·¿œ×7l~³ \(65ÃƱñGÚ<hãIþ¦1–è!ß»*O7«Õ"ÈñÔ?+2.9„ÜÔàS?$m¬y“% - wat%w-­ò$ ]@…ÂÓ*‰-‹DvŒ~£a' 3_û‘Oê¯èÃ!<J:߉ãGM" á.‹¤Í{ Û}¼Ö¡½†fNÛ^°¸Œ&†\¼X"Q6>(`ñ!é˜<»æ‡bá™Áøn¥Zü¼_æ0 *мUþ•Z £À¯ÂÊ 9'ƽÃÙèö×Ýd6¡ƒ+Ëxá[Þ{ìýt_‘"endstream
+endobj
+251 0 obj
+601
+endobj
+252 0 obj<</Type/Page/Parent 212 0 R/Contents 253 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F8 8 0 R>>>>>>endobj
+253 0 obj<</Length 254 0 R/Filter/FlateDecode>>stream
+xÚ-‹A‚0D÷ÿ³ÔE+¿EêVEÖ*ß S£E‘ÄëkÕÌ$“¼É{#û„a‹ÔöF+¡Yµ
+endobj
+254 0 obj
+126
+endobj
+255 0 obj<</Type/Page/Parent 212 0 R/Contents 256 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F8 8 0 R/F9 9 0 R>>>>>>endobj
+256 0 obj<</Length 257 0 R/Filter/FlateDecode>>stream
+xÚ+ä2T0
+Qœ™ž§’Tg¡`hQ§‹O¡‰¹‰žPPÞÐ$àÂÈ
+endobj
+257 0 obj
+94
+endobj
+258 0 obj<</Type/Page/Parent 212 0 R/Contents 259 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F8 8 0 R>>>>>>endobj
+259 0 obj<</Length 260 0 R/Filter/FlateDecode>>stream
+xÚ+ä2T0
+áÒw³P04TIS046Ò3W072PIÑp rt QÐUp VÎO+)O,JUpI-ÎLÏQÉE™%™ùyš!Y\ººP­†& A=#°€ PCIbfNj
+endobj
+260 0 obj
+126
+endobj
+261 0 obj<</Type/Page/Parent 212 0 R/Contents 262 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 5 0 R/F6 7 0 R/F8 8 0 R/F9 9 0 R>>>>>>endobj
+262 0 obj<</Length 263 0 R/Filter/FlateDecode>>stream
+xÚTÛn£0}ç+æqû
+„€9œ9ÇsÌk’à 9Ì'p;®’¯Mr½YB¾„fO•Ù¼€¦ý²‚­4Î1{¾j^À4[ tºÈP–CƒV¹3ƒ<âM'³R¥â“Ò
+¸QýÑ£Étwdf¿¥¿Â{f™”(Ç kÔ­Ð 9Š·ðÐ2Ï@‹àŸ™&ãO£¼Pñ†¢Ç˜z£1=±sp¡ŽZpæ…Ñ8µÑ(á ý Qƒ?è­é,S.ÖΡì¿Üýƒ'gø}Ìɽ¸Ò$ßØÈ»ìÏT¤”§ôvù‘‹ ¬¸5ú‹Æª.w»ˆE Âidµgºe¶…Ò´{ò¿ÓtUEtO
+ª:Œø.«z¼UI‰êña÷*+´K[ŸGá\×åu5Nº®êoŸˆ¿4@ë"Ì›&»¡±L»=6ã 72ÂvW¥Ûûq¾;<Iô>­?¡[ËúgÁÜ¿oÊáÓÅut±  ÿõ—(æ…á&òix±n’ïÉOÇ^8Tendstream
+endobj
+263 0 obj
+425
+endobj
+264 0 obj<</Type/Page/Parent 212 0 R/Contents 265 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 5 0 R/F6 7 0 R/F8 8 0 R>>>>>>endobj
+265 0 obj<</Length 266 0 R/Filter/FlateDecode>>stream
+xÚ’ÝRÂ0…ïû{©­´@©—ü´# ¢C¨‘’h@}z“ª08ÓˆN{µgöÛœ³ûâ…Ð1_ÝØþtã°w•Å†€W¶'½ ¼¼¸A>Ï/ñ“Ñ{GÝâ ×èl_1­}D蚨% Å…f
+ÆRh%+˜QnIÉ> ßü„1ûLj™|8?MqÖŸZ”`RQrÁ˜a—€I½†L*êä"t¶y¬e"%µ¤²r‹ÅoD¢¹¤‚B±4aÔ°P%ü½ì9š´³çÆù!Ö a'*Ÿæi;+ßVšoØ’8„^AúJͶ\‘:/ ™5ÿå¬Ë1’µ.¨âÏúè›ÕMÁc‘Ú©S;CÊY]ÛÇŽ*I×ΰ㄰â;nVñŠVDÔ+Ã>=£ä«/îè'×Al;'wà ƒã{T@!WzO³þx)~ÚôÃndzýAÔ±­al‹½0 úІACª¤xÛÔVJ±wë}
+endobj
+266 0 obj
+380
+endobj
+267 0 obj<</Type/Page/Parent 212 0 R/Contents 268 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 5 0 R/F5 6 0 R/F8 8 0 R/F9 9 0 R>>>>/Annots 117 0 R>>endobj
+268 0 obj<</Length 269 0 R/Filter/FlateDecode>>stream
+xÚÝ›ÛrÛ6†ïý¼l/Âgâ2±ãÔdâØêÈ2m3Ñ©”œÔ}úâ° ‰õUD7QYò£ø/°‹ÃêïRTæ?R(Z0Y,V'ïg'oÏuAt1»+ˆd%-¤æsvûÛl~³lŠÍ]qºYï›õ~÷ûìÛɇÙIUV†b?ˆý¸úh…dµ¹mUØOÿ÷²¸¶üƒË»ûâí¹(±O´·iÿ4RŒâäª`¢¬S¾·åz±Ù64P*x*æqæ’ròÿȨLZ¥©0Ÿ«BÐR@##5©¿%•Né2£u]™;‰U;~ØèâÖôŠö®]Ì÷ífý¼êB•jJºcÚº7ںƨ¶VQBÍNSŠi
+äZ•2%ƒH×O»}³Ê †á~ã Fú/†ƒ·í˦ûÑ6?Ÿw'¥ž„s¯]›—¯øFÎ+Bëz:C¼Ȥ²£"AƒPg›ÅãÊt÷q^Œ0@ÎKžÃ@D\CM:²kH6à Eû€ïÿ~QÀŠùPMó?à…•+á{+P®š»¦kÖ‹f7¢£žVüyæC:¿Q ÷¾ñ²p/¤{oŠ†ûð„Úä Þì9§]^g\6Ük:¿¨¬Ób’·{RK™´!åq†ù„‹!MøF6 qéÃ1EÓD ו‰] Ì@ú²hºŒ;†Ûm–¨l,Ša`XpÈø¢“C,ˆ6¯ Éýý²€D­:æå
+YW>œZ))&%°AÊîí¶ÜÞþ?Ä”ÒMŒýÛ¹FVL©ü4ÙŠÉ01M¨í%p°l;ïæËe³|^Q6•xŠI)˜ËþÝ\#+¥àe?Ä9&%° µS¸v€íš®Iízt*]S"bòÊ'
+÷v®‘Ó¥S`b›abk;Àv«›‘A.'•€0-©Š2kdµ¤}¸”˜’@&”—:EƒÝ¡v“Í÷#R™82 )e~~ÄMhïW¾‘[]pÂCÞÁV¬Eÿ˜xVÔ“Þ7Ý÷fÙ<e¼"¢/J¸ëß1OG´ÓÍj5ŸcÕÇ÷ Ö¯«*Ê]®‘í× ¹‹âs*`Û‰
+;À>_|þ PÃí6@ÑÃo.
+;À®æ»}®>$Ø1Aú/>$mw|Yڶޜؘp«ZÑ¡rÐ7r«Z±·²: äÚnØ&d0é¼]Ž/ò¨šìÞÒËI­‡ ì¹^Nt(²)º8lâ6"¸·ØÃö~¹ßlÇVÐbâbJ*9dPßÈ*©úNñÛÎ3ªv€µ+3‘—’³ lFhDG•½øFVGÙ÷Gl‚Èþð1AƒÝïçìƤj2Ûc˜Œ<*zñ¬Œ¢/zèÔ:°mÙºLáÞ`ûæŸ}f`OekLç“arH>¾‘K>„õ%/I>¬å!Ì@º^<4·Ë¦{MåmX¥Qõ‹od;(í«_$žy€mwõt
+;ÀÞ=î6]ûo®„¹:ê¹6¦"‰Ê^|#«"éË^$žu€m×Up°ì4{.3ýQî”ÔQÍ‹ÆJ^tDÄ’Ž/6èÃzÏ9]¶Íz¬Ü…Ogj‰(X³!ãÔ I8u_ì"Ñ|`»&²·öå)ë»öþ±Ëe:ݱ¬¢ê…§¨¾6E¢0
+Rå] ¢’U´ˆzÈHØÖ € ¡îgPù(../_C‘0¦"çq)&V‰)†Œ¤ÐBL¨c±q—–±ÒŸã§Û“©bÁ$ddÈFŒ ÙˆõÖØ¡6lT…g¤GÚnvÞ®Gë§RSåõ{{^ïJˆí ²ö?ƒºzw>+Þî@ùzs·ÿ9ïš~Ðcþ·èÚmÈLØßξQÔ½V ê}=ù41ž×endstream
+endobj
+269 0 obj
+2170
+endobj
+270 0 obj<</Type/Page/Parent 212 0 R/Contents 271 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 5 0 R/F5 6 0 R/F8 8 0 R/F9 9 0 R>>>>/Annots 149 0 R>>endobj
+271 0 obj<</Length 272 0 R/Filter/FlateDecode>>stream
+xÚÕ˜Ms›0†ïþ:¶‡(è8ºIkšÐÞ)&2`»ÀL'ÿ¾+i1à‰Dn¦q{gÑ+ô¬V+ñgÃHF„6ÿE³ù–mnw)a)É^Ó‚r¢c×lÿ%Ë×%9¾»ã¡/}÷5{Û|Ï6@Å\˜¹<=˜Óˆh‘@»†¤š&hÔäÙôpÑ }%·;I3}ÆСH]‡‚jšo/á†0ÁÎ}b7èG±Ç¶‚§níCZ>€à4p½æ‹¼£ÚŒ+¸6DqªÐð2…€j®ÇØOq"Õ$¦z®‹n«óüÞõeã—š´†°'TÌ¥Ðm¥~Â:6„°Êär¤è·2wǦÉ{OPe|å ÂéR„1˜¢CŠXÃN“ŒÃ. ”-%j3./ÅÑbyQ”§þc‚<†ä¹~Z,“T0+ô@Ò!’*Mé0Â0ÉAÛ‘œ‰£ÅŠüP”õÿN2QTžIZ#H2Ñp’K$Q›t.Ž~ÛW)6žµ:ZÈEŽp=—?g9Æb(z10XàˆÚ ÖE5G?Š•?FnⵊÉHªÔU;4kIêhਖ8¢2‹°˜I;¿“ªOLVï³¥k3‚¥k^âˆÚLðKqô£X}Ê÷Muðäµ™ÌW'ºÈÆ8Vk9
+1Všx‰#j3Á÷Lý(VŸº>÷Õl¶Š­ì§X²tRk¬d Xε&Yb‰Ú xð¹¸óbmùV~–ÉúY‚Ad*Üë~[
+[¦™e(8£ ;"ØvÎÔ×jÜ—}^Õå>pԛÃ2ØÌ´Ðb]õêY'x
+-W´òŽ!áií²;‚u†7FæîØåúÖt¢dÏ„|.‹nÔy¨]—·ïžÅ7¥l•P§|Uøt,•°)¯¤™ZÖøÄ8'Kha§Ý6t¬šÈÂôç‡éëܨ”•mÓy7^ÉQ/â•‘ÝuºZ#ôòAJæ6¡ÛÐ k"f2#43iôRE{<¼ûÀJ¹’íÒÛ]2Ì5['n7tÿ´Ýeä†Üý||&ÏÇ—þoÞ–Ä-}æ«h«S_í2xÃì+ª›˜ÛVý±ùõÖñfendstream
+endobj
+272 0 obj
+837
+endobj
+273 0 obj<</Count 6/First 274 0 R/Last 331 0 R>>endobj
+274 0 obj<</Parent 273 0 R/Title(Table of Contents)/Dest[267 0 R/XYZ null 756 null]/Next 275 0 R>>endobj
+275 0 obj<</Parent 273 0 R/Count -3/First 276 0 R/Last 278 0 R/Title(1 Scope)/Dest[219 0 R/XYZ null 743 null]/Prev 274 0 R/Next 279 0 R>>endobj
+276 0 obj<</Parent 275 0 R/Title(1.1 Identification)/Dest[219 0 R/XYZ null 693 null]/Next 277 0 R>>endobj
+277 0 obj<</Parent 275 0 R/Title(1.2 System Overview)/Dest[219 0 R/XYZ null 613 null]/Prev 276 0 R/Next 278 0 R>>endobj
+278 0 obj<</Parent 275 0 R/Title(1.3 Document Overview)/Dest[219 0 R/XYZ null 389 null]/Prev 277 0 R>>endobj
+279 0 obj<</Parent 273 0 R/Count -2/First 280 0 R/Last 281 0 R/Title(2 References)/Dest[225 0 R/XYZ null 743 null]/Prev 275 0 R/Next 282 0 R>>endobj
+280 0 obj<</Parent 279 0 R/Title(2.1 CUPS Documentation)/Dest[225 0 R/XYZ null 693 null]/Next 281 0 R>>endobj
+281 0 obj<</Parent 279 0 R/Title(2.2 Other Documents)/Dest[225 0 R/XYZ null 495 null]/Prev 280 0 R>>endobj
+282 0 obj<</Parent 273 0 R/Count -7/First 283 0 R/Last 321 0 R/Title(3 Design Overview)/Dest[231 0 R/XYZ null 743 null]/Prev 279 0 R/Next 330 0 R>>endobj
+283 0 obj<</Parent 282 0 R/Count -6/First 284 0 R/Last 289 0 R/Title(3.1 Backends)/Dest[231 0 R/XYZ null 580 null]/Next 290 0 R>>endobj
+284 0 obj<</Parent 283 0 R/Title(3.1.1 ipp)/Dest[231 0 R/XYZ null 323 null]/Next 285 0 R>>endobj
+285 0 obj<</Parent 283 0 R/Title(3.1.2 lpd)/Dest[231 0 R/XYZ null 250 null]/Prev 284 0 R/Next 286 0 R>>endobj
+286 0 obj<</Parent 283 0 R/Title(3.1.3 parallel)/Dest[231 0 R/XYZ null 165 null]/Prev 285 0 R/Next 287 0 R>>endobj
+287 0 obj<</Parent 283 0 R/Title(3.1.4 serial)/Dest[234 0 R/XYZ null 729 null]/Prev 286 0 R/Next 288 0 R>>endobj
+288 0 obj<</Parent 283 0 R/Title(3.1.5 smb)/Dest[234 0 R/XYZ null 554 null]/Prev 287 0 R/Next 289 0 R>>endobj
+289 0 obj<</Parent 283 0 R/Title(3.1.6 socket)/Dest[234 0 R/XYZ null 442 null]/Prev 288 0 R>>endobj
+290 0 obj<</Parent 282 0 R/Count -3/First 291 0 R/Last 293 0 R/Title(3.2 Berkeley Commands)/Dest[234 0 R/XYZ null 337 null]/Prev 283 0 R/Next 294 0 R>>endobj
+291 0 obj<</Parent 290 0 R/Title(3.2.1 lpc)/Dest[234 0 R/XYZ null 238 null]/Next 292 0 R>>endobj
+292 0 obj<</Parent 290 0 R/Title(3.2.2 lpr)/Dest[237 0 R/XYZ null 729 null]/Prev 291 0 R/Next 293 0 R>>endobj
+293 0 obj<</Parent 290 0 R/Title(3.2.3 lprm)/Dest[237 0 R/XYZ null 673 null]/Prev 292 0 R>>endobj
+294 0 obj<</Parent 282 0 R/Count -3/First 295 0 R/Last 297 0 R/Title(3.3 CGI)/Dest[237 0 R/XYZ null 620 null]/Prev 290 0 R/Next 298 0 R>>endobj
+295 0 obj<</Parent 294 0 R/Title(3.3.1 classes)/Dest[237 0 R/XYZ null 535 null]/Next 296 0 R>>endobj
+296 0 obj<</Parent 294 0 R/Title(3.3.2 jobs)/Dest[237 0 R/XYZ null 462 null]/Prev 295 0 R/Next 297 0 R>>endobj
+297 0 obj<</Parent 294 0 R/Title(3.3.3 printers)/Dest[237 0 R/XYZ null 390 null]/Prev 296 0 R>>endobj
+298 0 obj<</Parent 282 0 R/Count -7/First 299 0 R/Last 305 0 R/Title(3.4 CUPS Interface Library)/Dest[237 0 R/XYZ null 324 null]/Prev 294 0 R/Next 306 0 R>>endobj
+299 0 obj<</Parent 298 0 R/Title(3.4.1 Convenience Functions)/Dest[237 0 R/XYZ null 239 null]/Next 300 0 R>>endobj
+300 0 obj<</Parent 298 0 R/Title(3.4.2 HTTP Functions)/Dest[237 0 R/XYZ null 167 null]/Prev 299 0 R/Next 301 0 R>>endobj
+301 0 obj<</Parent 298 0 R/Title(3.4.3 IPP Functions)/Dest[240 0 R/XYZ null 729 null]/Prev 300 0 R/Next 302 0 R>>endobj
+302 0 obj<</Parent 298 0 R/Title(3.4.4 Language Functions)/Dest[240 0 R/XYZ null 673 null]/Prev 301 0 R/Next 303 0 R>>endobj
+303 0 obj<</Parent 298 0 R/Title(3.4.5 MIME Functions)/Dest[240 0 R/XYZ null 601 null]/Prev 302 0 R/Next 304 0 R>>endobj
+304 0 obj<</Parent 298 0 R/Title(3.4.6 PPD Functions)/Dest[240 0 R/XYZ null 515 null]/Prev 303 0 R/Next 305 0 R>>endobj
+305 0 obj<</Parent 298 0 R/Title(3.4.7 Raster Functions)/Dest[240 0 R/XYZ null 443 null]/Prev 304 0 R>>endobj
+306 0 obj<</Parent 282 0 R/Count -4/First 307 0 R/Last 310 0 R/Title(3.5 Filters)/Dest[240 0 R/XYZ null 377 null]/Prev 298 0 R/Next 311 0 R>>endobj
+307 0 obj<</Parent 306 0 R/Title(3.5.1 hpgltops)/Dest[240 0 R/XYZ null 133 null]/Next 308 0 R>>endobj
+308 0 obj<</Parent 306 0 R/Title(3.5.2 imagetops)/Dest[243 0 R/XYZ null 729 null]/Prev 307 0 R/Next 309 0 R>>endobj
+309 0 obj<</Parent 306 0 R/Title(3.5.3 pstops)/Dest[243 0 R/XYZ null 686 null]/Prev 308 0 R/Next 310 0 R>>endobj
+310 0 obj<</Parent 306 0 R/Title(3.5.4 texttops)/Dest[243 0 R/XYZ null 614 null]/Prev 309 0 R>>endobj
+311 0 obj<</Parent 282 0 R/Count -9/First 312 0 R/Last 320 0 R/Title(3.6 Scheduler)/Dest[243 0 R/XYZ null 561 null]/Prev 306 0 R/Next 321 0 R>>endobj
+312 0 obj<</Parent 311 0 R/Title(3.6.1 Authorization)/Dest[243 0 R/XYZ null 462 null]/Next 313 0 R>>endobj
+313 0 obj<</Parent 311 0 R/Title(3.6.2 Classes)/Dest[243 0 R/XYZ null 390 null]/Prev 312 0 R/Next 314 0 R>>endobj
+314 0 obj<</Parent 311 0 R/Title(3.6.3 Client)/Dest[243 0 R/XYZ null 318 null]/Prev 313 0 R/Next 315 0 R>>endobj
+315 0 obj<</Parent 311 0 R/Title(3.6.4 Configuration)/Dest[243 0 R/XYZ null 193 null]/Prev 314 0 R/Next 316 0 R>>endobj
+316 0 obj<</Parent 311 0 R/Title(3.6.5 Directory Services)/Dest[246 0 R/XYZ null 729 null]/Prev 315 0 R/Next 317 0 R>>endobj
+317 0 obj<</Parent 311 0 R/Title(3.6.6 IPP)/Dest[246 0 R/XYZ null 633 null]/Prev 316 0 R/Next 318 0 R>>endobj
+318 0 obj<</Parent 311 0 R/Title(3.6.7 Jobs)/Dest[246 0 R/XYZ null 548 null]/Prev 317 0 R/Next 319 0 R>>endobj
+319 0 obj<</Parent 311 0 R/Title(3.6.8 Main)/Dest[246 0 R/XYZ null 475 null]/Prev 318 0 R/Next 320 0 R>>endobj
+320 0 obj<</Parent 311 0 R/Title(3.6.9 Printers)/Dest[246 0 R/XYZ null 403 null]/Prev 319 0 R>>endobj
+321 0 obj<</Parent 282 0 R/Count -8/First 322 0 R/Last 329 0 R/Title(3.7 System V Commands)/Dest[246 0 R/XYZ null 337 null]/Prev 311 0 R>>endobj
+322 0 obj<</Parent 321 0 R/Title(3.7.1 accept)/Dest[246 0 R/XYZ null 265 null]/Next 323 0 R>>endobj
+323 0 obj<</Parent 321 0 R/Title(3.7.2 cancel)/Dest[246 0 R/XYZ null 206 null]/Prev 322 0 R/Next 324 0 R>>endobj
+324 0 obj<</Parent 321 0 R/Title(3.7.3 disable)/Dest[246 0 R/XYZ null 147 null]/Prev 323 0 R/Next 325 0 R>>endobj
+325 0 obj<</Parent 321 0 R/Title(3.7.4 enable)/Dest[249 0 R/XYZ null 729 null]/Prev 324 0 R/Next 326 0 R>>endobj
+326 0 obj<</Parent 321 0 R/Title(3.7.5 lp)/Dest[249 0 R/XYZ null 686 null]/Prev 325 0 R/Next 327 0 R>>endobj
+327 0 obj<</Parent 321 0 R/Title(3.7.6 lpadmin)/Dest[249 0 R/XYZ null 601 null]/Prev 326 0 R/Next 328 0 R>>endobj
+328 0 obj<</Parent 321 0 R/Title(3.7.7 lpstat)/Dest[249 0 R/XYZ null 515 null]/Prev 327 0 R/Next 329 0 R>>endobj
+329 0 obj<</Parent 321 0 R/Title(3.7.8 reject)/Dest[249 0 R/XYZ null 456 null]/Prev 328 0 R>>endobj
+330 0 obj<</Parent 273 0 R/Title(4 Detailed Design)/Dest[255 0 R/XYZ null 743 null]/Prev 282 0 R/Next 331 0 R>>endobj
+331 0 obj<</Parent 273 0 R/Count -2/First 332 0 R/Last 333 0 R/Title(A Glossary)/Dest[261 0 R/XYZ null 743 null]/Prev 330 0 R>>endobj
+332 0 obj<</Parent 331 0 R/Title(A.1 Terms)/Dest[261 0 R/XYZ null 693 null]/Next 333 0 R>>endobj
+333 0 obj<</Parent 331 0 R/Title(A.2 Acronyms)/Dest[261 0 R/XYZ null 389 null]/Prev 332 0 R>>endobj
+334 0 obj<</Type/Catalog/Pages 212 0 R/Names 150 0 R/ViewerPreferences<</PageLayout/TwoColumnRight>>/Outlines 273 0 R/PageMode/UseOutlines/OpenAction[219 0 R/XYZ null null null]>>endobj
+xref
+0 335
+0000000000 65535 f
+0000000015 00000 n
+0000000224 00000 n
+0000000285 00000 n
+0000000359 00000 n
+0000000441 00000 n
+0000000519 00000 n
+0000000596 00000 n
+0000000675 00000 n
+0000000751 00000 n
+0000000832 00000 n
+0000000891 00000 n
+0000000994 00000 n
+0000001098 00000 n
+0000001203 00000 n
+0000001308 00000 n
+0000001413 00000 n
+0000001518 00000 n
+0000001623 00000 n
+0000001728 00000 n
+0000001833 00000 n
+0000001938 00000 n
+0000002041 00000 n
+0000002145 00000 n
+0000002250 00000 n
+0000002355 00000 n
+0000002460 00000 n
+0000002565 00000 n
+0000002670 00000 n
+0000002775 00000 n
+0000002878 00000 n
+0000002982 00000 n
+0000003087 00000 n
+0000003192 00000 n
+0000003297 00000 n
+0000003402 00000 n
+0000003507 00000 n
+0000003612 00000 n
+0000003717 00000 n
+0000003822 00000 n
+0000003927 00000 n
+0000004032 00000 n
+0000004137 00000 n
+0000004242 00000 n
+0000004347 00000 n
+0000004452 00000 n
+0000004557 00000 n
+0000004662 00000 n
+0000004767 00000 n
+0000004872 00000 n
+0000004977 00000 n
+0000005082 00000 n
+0000005187 00000 n
+0000005292 00000 n
+0000005397 00000 n
+0000005502 00000 n
+0000005607 00000 n
+0000005712 00000 n
+0000005817 00000 n
+0000005922 00000 n
+0000006027 00000 n
+0000006132 00000 n
+0000006237 00000 n
+0000006342 00000 n
+0000006447 00000 n
+0000006552 00000 n
+0000006657 00000 n
+0000006762 00000 n
+0000006867 00000 n
+0000006972 00000 n
+0000007077 00000 n
+0000007182 00000 n
+0000007287 00000 n
+0000007392 00000 n
+0000007497 00000 n
+0000007602 00000 n
+0000007707 00000 n
+0000007812 00000 n
+0000007917 00000 n
+0000008022 00000 n
+0000008127 00000 n
+0000008232 00000 n
+0000008337 00000 n
+0000008442 00000 n
+0000008547 00000 n
+0000008652 00000 n
+0000008757 00000 n
+0000008862 00000 n
+0000008967 00000 n
+0000009072 00000 n
+0000009177 00000 n
+0000009282 00000 n
+0000009387 00000 n
+0000009492 00000 n
+0000009597 00000 n
+0000009702 00000 n
+0000009807 00000 n
+0000009912 00000 n
+0000010017 00000 n
+0000010122 00000 n
+0000010227 00000 n
+0000010333 00000 n
+0000010439 00000 n
+0000010545 00000 n
+0000010651 00000 n
+0000010757 00000 n
+0000010863 00000 n
+0000010969 00000 n
+0000011075 00000 n
+0000011181 00000 n
+0000011287 00000 n
+0000011393 00000 n
+0000011498 00000 n
+0000011603 00000 n
+0000011707 00000 n
+0000011811 00000 n
+0000011915 00000 n
+0000012019 00000 n
+0000012796 00000 n
+0000012902 00000 n
+0000013008 00000 n
+0000013112 00000 n
+0000013217 00000 n
+0000013323 00000 n
+0000013429 00000 n
+0000013535 00000 n
+0000013641 00000 n
+0000013747 00000 n
+0000013853 00000 n
+0000013959 00000 n
+0000014065 00000 n
+0000014171 00000 n
+0000014277 00000 n
+0000014383 00000 n
+0000014489 00000 n
+0000014595 00000 n
+0000014701 00000 n
+0000014807 00000 n
+0000014913 00000 n
+0000015019 00000 n
+0000015125 00000 n
+0000015229 00000 n
+0000015333 00000 n
+0000015438 00000 n
+0000015542 00000 n
+0000015646 00000 n
+0000015750 00000 n
+0000015855 00000 n
+0000015959 00000 n
+0000016064 00000 n
+0000016330 00000 n
+0000016364 00000 n
+0000016398 00000 n
+0000017221 00000 n
+0000017270 00000 n
+0000017319 00000 n
+0000017368 00000 n
+0000017417 00000 n
+0000017466 00000 n
+0000017515 00000 n
+0000017564 00000 n
+0000017613 00000 n
+0000017662 00000 n
+0000017711 00000 n
+0000017760 00000 n
+0000017809 00000 n
+0000017858 00000 n
+0000017907 00000 n
+0000017956 00000 n
+0000018005 00000 n
+0000018054 00000 n
+0000018103 00000 n
+0000018152 00000 n
+0000018201 00000 n
+0000018250 00000 n
+0000018299 00000 n
+0000018348 00000 n
+0000018397 00000 n
+0000018446 00000 n
+0000018495 00000 n
+0000018544 00000 n
+0000018593 00000 n
+0000018642 00000 n
+0000018691 00000 n
+0000018740 00000 n
+0000018789 00000 n
+0000018838 00000 n
+0000018887 00000 n
+0000018936 00000 n
+0000018985 00000 n
+0000019034 00000 n
+0000019083 00000 n
+0000019132 00000 n
+0000019181 00000 n
+0000019230 00000 n
+0000019279 00000 n
+0000019328 00000 n
+0000019377 00000 n
+0000019426 00000 n
+0000019475 00000 n
+0000019524 00000 n
+0000019573 00000 n
+0000019622 00000 n
+0000019671 00000 n
+0000019720 00000 n
+0000019769 00000 n
+0000019818 00000 n
+0000019867 00000 n
+0000019916 00000 n
+0000019965 00000 n
+0000020014 00000 n
+0000020063 00000 n
+0000020112 00000 n
+0000020341 00000 n
+0000020493 00000 n
+0000026875 00000 n
+0000026897 00000 n
+0000027010 00000 n
+0000027112 00000 n
+0000027132 00000 n
+0000027273 00000 n
+0000028169 00000 n
+0000028190 00000 n
+0000028303 00000 n
+0000028495 00000 n
+0000028516 00000 n
+0000028657 00000 n
+0000029250 00000 n
+0000029271 00000 n
+0000029384 00000 n
+0000029580 00000 n
+0000029601 00000 n
+0000029751 00000 n
+0000030764 00000 n
+0000030785 00000 n
+0000030944 00000 n
+0000032099 00000 n
+0000032121 00000 n
+0000032252 00000 n
+0000033131 00000 n
+0000033152 00000 n
+0000033293 00000 n
+0000034403 00000 n
+0000034425 00000 n
+0000034556 00000 n
+0000035538 00000 n
+0000035559 00000 n
+0000035699 00000 n
+0000036690 00000 n
+0000036711 00000 n
+0000036842 00000 n
+0000037514 00000 n
+0000037535 00000 n
+0000037648 00000 n
+0000037845 00000 n
+0000037866 00000 n
+0000037988 00000 n
+0000038153 00000 n
+0000038173 00000 n
+0000038286 00000 n
+0000038483 00000 n
+0000038504 00000 n
+0000038644 00000 n
+0000039140 00000 n
+0000039161 00000 n
+0000039292 00000 n
+0000039743 00000 n
+0000039764 00000 n
+0000039919 00000 n
+0000042160 00000 n
+0000042182 00000 n
+0000042337 00000 n
+0000043245 00000 n
+0000043266 00000 n
+0000043321 00000 n
+0000043426 00000 n
+0000043570 00000 n
+0000043676 00000 n
+0000043796 00000 n
+0000043905 00000 n
+0000044054 00000 n
+0000044164 00000 n
+0000044271 00000 n
+0000044425 00000 n
+0000044561 00000 n
+0000044658 00000 n
+0000044768 00000 n
+0000044883 00000 n
+0000044996 00000 n
+0000045106 00000 n
+0000045206 00000 n
+0000045364 00000 n
+0000045461 00000 n
+0000045571 00000 n
+0000045669 00000 n
+0000045813 00000 n
+0000045914 00000 n
+0000046025 00000 n
+0000046127 00000 n
+0000046290 00000 n
+0000046405 00000 n
+0000046526 00000 n
+0000046646 00000 n
+0000046771 00000 n
+0000046892 00000 n
+0000047012 00000 n
+0000047122 00000 n
+0000047270 00000 n
+0000047372 00000 n
+0000047488 00000 n
+0000047601 00000 n
+0000047703 00000 n
+0000047853 00000 n
+0000047960 00000 n
+0000048074 00000 n
+0000048187 00000 n
+0000048307 00000 n
+0000048432 00000 n
+0000048542 00000 n
+0000048653 00000 n
+0000048764 00000 n
+0000048866 00000 n
+0000049011 00000 n
+0000049111 00000 n
+0000049224 00000 n
+0000049338 00000 n
+0000049451 00000 n
+0000049560 00000 n
+0000049674 00000 n
+0000049787 00000 n
+0000049887 00000 n
+0000050005 00000 n
+0000050139 00000 n
+0000050236 00000 n
+0000050336 00000 n
+trailer
+<</Size 335/Root 334 0 R/Info 1 0 R>>
+startxref
+50522
+%%EOF
diff --git a/doc/sdd.shtml b/doc/sdd.shtml
new file mode 100644
index 000000000..9d8d59d30
--- /dev/null
+++ b/doc/sdd.shtml
@@ -0,0 +1,567 @@
+<HTML>
+<HEAD>
+ <META NAME="COPYRIGHT" CONTENT="Copyright 1997-1999, All Rights Reserved">
+ <META NAME="DOCNUMBER" CONTENT="CUPS-SDD-1.0">
+ <META NAME="Author" CONTENT="Easy Software Products">
+ <TITLE>DRAFT - CUPS Software Design Description</TITLE>
+</HEAD>
+<BODY>
+
+<H1>Scope</H1>
+
+<H2>Identification</H2>
+
+This software design description document provides detailed information
+on the architecture and coding of the Common UNIX Printing System
+(&quot;CUPS&quot;) Version 1.0.
+
+<H2>System Overview</H2>
+
+The Common UNIX Printing System provides a portable printing layer for
+UNIX&reg; operating systems. It has been developed by Easy Software
+Products to promote a standard printing solution for all UNIX vendors
+and users. CUPS provides the System V and Berkeley command-line interfaces.
+
+<P>CUPS uses the Internet Printing Protocol (IETF-IPP) as the basis
+for managing print jobs and queues. The Line Printer Daemon (LPD,
+RFC1179), Server Message Block (SMB), and AppSocket protocols are also
+supported with reduced functionality.
+
+<P>CUPS adds network printer browsing and PostScript Printer
+Description (&quot;PPD&quot;)-based printing options to support real
+world applications under UNIX.
+
+<P>CUPS also includes a customized version of GNU GhostScript
+(currently based off GNU GhostScript 4.03) and an image file RIP that
+can be used to support non-PostScript printers.
+
+<H2>Document Overview</H2>
+
+This software design description document is organized into the
+following sections:
+
+<UL>
+
+ <LI>1 - Scope
+
+ <LI>2 - References
+
+ <LI>3 - Design Overview
+
+ <LI>4 - Detailed Design
+
+ <LI>A - Glossary
+
+</UL>
+
+<H1>References</H1>
+
+<H2>CUPS Documentation</H2>
+
+The following CUPS documentation is referenced by this document:
+
+<UL>
+ <LI>CUPS-CMP-1.0: CUPS Configuration Management Plan
+ <LI>CUPS-IDD-1.0: CUPS System Interface Design Description
+ <LI>CUPS-SAM-1.0.x: CUPS Software Administrators Manual
+ <LI>CUPS-SDD-1.0: CUPS Software Design Description
+ <LI>CUPS-SPM-1.0: CUPS Software Programming Manual
+ <LI>CUPS-SSR-1.0: CUPS Software Security Report
+ <LI>CUPS-STP-1.0: CUPS Software Test Plan
+ <LI>CUPS-SUM-1.0.x: CUPS Software Users Manual
+ <LI>CUPS-SVD-1.0.x: CUPS Software Version Description
+</UL>
+
+<H2>Other Documents</H2>
+
+The following non-CUPS documents are referenced by this document:
+
+<UL>
+ <LI>IEEE 1387.4, System Administration: Printing (draft)
+ <LI>IPP/1.0: Additional Optional Operations - Set 1
+ <LI>IPP/1.0: Encoding and Transport
+ <LI>IPP/1.0: Implementers Guide
+ <LI>IPP/1.0: Model and Semantics
+ <LI>RFC 1179, Line Printer Daemon Protocol
+</UL>
+
+<H1>Design Overview</H1>
+
+CUPS is composed of 7 software sub-systems that operate together to
+perform common printing tasks:
+
+<UL>
+
+ <LI>Backends
+
+ <LI>Berkeley Commands
+
+ <LI>CGI
+
+ <LI>CUPS Interface Library
+
+ <LI>Filters
+
+ <LI>Scheduler
+
+ <LI>System V Commands
+
+</UL>
+
+<H2>Backends</H2>
+
+The backends implement communications over a number of different interfaces.
+All backends are called with a common set of arguments:
+
+<UL>
+
+ <LI>Device URI - the Uniform Resource Identifier for the output device
+ (e.g. <CODE>parallel:/dev/plp</CODE>,
+ <CODE>ipp://hostname/resource</CODE>).
+
+ <LI>Job Identifier - the job identifier for this job (integer).
+
+ <LI>User Name - the user associated with this job (name string).
+
+ <LI>Title - the title/job-name associated with this job (name string).
+
+ <LI>Copies - the number of copies required (integer).
+
+ <LI>Options - the options associated with this job (space separated
+ option strings).
+
+ <LI>Filename (optional) - the file to print; if this option is not
+ specified, the backend must read the print file from the standard
+ input.
+
+</UL>
+
+Backends are named using the method of the URI, so a URI of
+"ipp://hostname/resource" would be processed by the "ipp" backend.
+
+<H3>ipp</H3>
+
+The ipp backend sends the specified job to a network printer or host using
+the Internet Printing Protocol. The URI is as specified by the
+<CODE>printer-uri-supported</CODE> attribute from the printer or host.
+
+<H3>lpd</H3>
+
+The lpd backend sends the specified job to a network printer or host using
+the Line Printer Daemon protocol. The URI is of the form:
+
+<UL><PRE>lpd://hostname/queue
+</PRE></UL>
+
+<H3>parallel</H3>
+
+The parallel backend sends the specified job to a local printer connected
+via the specified parallel port device. The URI is of the form:
+
+<UL><PRE>parallel:/dev/file
+</PRE></UL>
+
+<H3>serial</H3>
+
+The serial backend sends the specified job to a local printer connected
+via the specified serial port device. The URI is of the form:
+
+<UL><PRE>serial:/dev/file?option[+option+...]
+</PRE></UL>
+
+The options can be any combination of the following:
+
+<UL>
+
+ <LI><CODE>baud=<I>rate</I></CODE> - Sets the baud rate for the device.
+
+ <LI><CODE>bits=<I>7 or 8</I></CODE> - Sets the number of data bits.
+
+ <LI><CODE>parity=<I>even</I></CODE> - Sets even parity checking.
+
+ <LI><CODE>parity=<I>odd</I></CODE> - Sets odd parity checking.
+
+ <LI><CODE>parity=<I>none</I></CODE> - Turns parity checking off.
+
+</UL>
+
+<H3>smb</H3>
+
+The smb backend sends the specified job to a network host using the Server
+Message Block protocol, which is used by most machines running Microsoft&reg;
+Windows&reg;. The URI is of the form:
+
+<UL><PRE>smb://hostname/queue
+</PRE></UL>
+
+Usernames and passwords required to access the printer are stored in an
+external file.
+
+<H3>socket</H3>
+
+The socket backend sends the specified job to a network host using the
+AppSocket protocol commonly used by Hewlett-Packard and Tektronix
+printers. The URI is of the form:
+
+<UL><PRE>socket://hostname[:port]
+</PRE></UL>
+
+The default port number is 9100.
+
+<H2>Berkeley Commands</H2>
+
+The Berkeley commands provide a simple command-line interface to CUPS
+to submit and control print jobs. It is provided for compatibility with
+existing software that is hard coded to use the Berkeley commands,
+however since printer options cannot be specified using the Berkeley
+commands their use it not encouraged.
+
+<H3>lpc</H3>
+
+The lpc command allows users and administrators to check the status and
+control print queues. The version provided with CUPS supports the following
+commands:
+
+<UL>
+
+ <LI>abort - Stops a printer or all printers and any active print jobs.
+
+ <LI>disable - Prevents new jobs from being submitted to the specified
+ printer or all printers.
+
+ <LI>down - Stops a printer or all printers after completing the current
+ print jobs.
+
+ <LI>enable - Allows new jobs to be submitted.
+
+ <LI>start - Starts a printer or all printers.
+
+ <LI>status - Shows the status of printers and jobs in the queue.
+
+ <LI>up - Starts a printer or all printers.
+
+</UL>
+
+<H3>lpr</H3>
+
+The lpr command submits a job for printing. The CUPS version of lpr silently
+ignores the "i", "p", "t", "m", "h", and "s" options.
+
+<H3>lprm</H3>
+
+The lprm removes one or more print jobs.
+
+<H2>CGI</H2>
+
+The Common Gateway Interface (CGI) programs provide a web-based status interface
+to monitor the status of printers, classes, and jobs.
+
+<H3>classes</H3>
+
+The classes CGI lists the available printer classes and any pending jobs for
+the class. The user can click on individual classes to limit the display and
+click on jobs to see the job status.
+
+<H3>jobs</H3>
+
+The jobs CGI lists the queued print jobs in order of priority. The list can
+be limited by printer or job. When the user displays the status of an
+individual print job all job options are displayed.
+
+<H3>printers</H3>
+
+The printers CGI lists the available printer queues and any pending jobs for
+the printer. The user can click on individual printers to limit the display and
+click on jobs to see the job status.
+
+<H2>CUPS Interface Library</H2>
+
+The CUPS interface library provides common convenience, HTTP, IPP,
+language, MIME, PPD, and raster functions used by the CUPS software.
+
+<H3>Convenience Functions</H3>
+
+Convenience functions are provided to submit an IPP request, send a print file,
+cancel a job, get a list of available printers, and get a list of available
+classes.
+
+<H3>HTTP Functions</H3>
+
+The HTTP functions provide functions to connect to HTTP servers, issue requests,
+read data from a server, and write data to a server.
+
+<H3>IPP Functions</H3>
+
+The IPP function provide functions to manage IPP request data and attributes,
+read IPP responses from a server, and write IPP requests to a server.
+
+<H3>Language Functions</H3>
+
+The language functions provide a standard interface for retrieving common
+textual messages for a particular locale and determining the correct encoding
+(e.g. US ASCII, ISO-8859-1, etc.)
+
+<H3>MIME Functions</H3>
+
+The Multimedia Internet Mail Exchange functions manage a MIME type and
+conversion database that supports file typing by extension and content, and
+least-cost file filtering from a source to a destination file type.
+
+<H3>PPD Functions</H3>
+
+The PostScript Printer Description functions manage PPD files, select options,
+check for option conflicts, and emit selected options in the correct order.
+
+<H3>Raster Functions</H3>
+
+The raster functions manage streams of CUPS raster data (described in the
+Interface Design Document) used by non-PostScript printer drivers.
+
+<H2>Filters</H2>
+
+The filters implement file conversion services for CUPS. All filters
+are called with a common set of arguments:
+
+<UL>
+
+ <LI>Printer name - the name of the destination printer (name string).
+
+ <LI>Job Identifier - the job identifier for this job (integer).
+
+ <LI>User Name - the user associated with this job (name string).
+
+ <LI>Title - the title/job-name associated with this job (name string).
+
+ <LI>Copies - the number of copies required (integer).
+
+ <LI>Options - the options associated with this job (space separated
+ option strings).
+
+ <LI>Filename (optional) - the file to print; if this option is not
+ specified, the filter must read the input file from the standard
+ input.
+
+</UL>
+
+Filters are added to the MIME conversion data file and implement all necessary
+conversions from one file type to another.
+
+<H3>hpgltops</H3>
+
+The hpgltops filter converts HP-GL/2 files into PostScript.
+
+<H3>imagetops</H3>
+
+The imagetops filter converts image files into PostScript.
+
+<H3>pstops</H3>
+
+The pstops filter inserts printer-specific commands from PPD files and
+performs page filtering as requested by the user.
+
+<H3>texttops</H3>
+
+The texttops filter converts text files into PostScript.
+
+<H2>Scheduler</H2>
+
+The scheduler is a fully-functional HTTP/1.1 server that manages the printers,
+classes, and jobs in the system. It also handles a simple broadcast-based
+directory service so that remote print queues and classes can be accessed
+transparently from the local system.
+
+<H3>Authorization</H3>
+
+The authorization module is responsible for performing access control and
+authentication for all HTTP and IPP requests entering the system.
+
+<H3>Classes</H3>
+
+The classes module is responsible for managing printer classes in the system.
+Each class is a collection of local and/or remote printers. The classes module
+also reads and writes the classes configuration file.
+
+<H3>Client</H3>
+
+The client module is responsible for all HTTP client communications. It handles
+listening on selected interfaces, accepting connections from prospective
+clients, processing incoming HTTP requests, and sending HTTP responses to
+those requests. The client module also is responsible for executing the
+external CGI programs as needed to support web-based printer, class, and job
+status monitoring.
+
+<P>Once authorized, all IPP requests are sent to the IPP module.
+
+<H3>Configuration</H3>
+
+The configuration module is responsible for reading the CUPS configuration file
+and initializing the appropriate data structures and values. The configuration
+module also stops CUPS services before reading the configuration file and
+restarts them after the configuration file has been read.
+
+<H3>Directory Services</H3>
+
+The directory services module sends and recieves printer state information over
+a broadcast socket. Remote printers and classes are automatically added to or
+removed from the local printer and class lists as needed.
+
+<P>The directory services module can only recieve printer state information
+over a single UDP port, however it can broadcast to multiple addresses and
+ports as needed.
+
+<H3>IPP</H3>
+
+The IPP module handles IPP requests and acts accordingly. URI
+validation is also performed here, as a client can post IPP data to any
+URI on the server (which might sidestep the access control or
+authentication of the HTTP server.)
+
+<H3>Jobs</H3>
+
+The jobs module manages print jobs, starts filter and backend processes
+for jobs to be printed, and monitors status messages from those filters
+and backends.
+
+<H3>Main</H3>
+
+The main module is responsible for timing out and dispatching input and output
+for client connections. It also watches for incoming <CODE>SIGHUP</CODE>
+signals and reloads the server configuration files as needed.
+
+<H3>Printers</H3>
+
+The printers module is responsible for managing printers and PPD files
+in the system. The printers module also reads and writes the printers
+configuration file.
+
+<H2>System V Commands</H2>
+
+The System V commands provide a robust command-line interface to CUPS
+to submit and control print jobs.
+
+<H3>accept</H3>
+
+The accept command tells the scheduler to accept new jobs for specific
+printers.
+
+<H3>cancel</H3>
+
+The cancel command tells the scheduler to cancel one or more jobs that are
+queued for printing.
+
+<H3>disable</H3>
+
+The disable command tells the scheduler to stop printing jobs on the
+specified printers.
+
+<H3>enable</H3>
+
+The enable command tells the scheduler to start printing jobs on the
+specified printers.
+
+<H3>lp</H3>
+
+The lp command submits submits files for printing. Unlike the standard
+System V lp command, a single CUPS lp command will generate a separate
+job ID for each file that is printed. Also, the Solaris "f", "H", "P", "S",
+and "y" options are silently ignored.
+
+<H3>lpadmin</H3>
+
+The lpadmin command manages printer queues and classes. The Solaris
+"A", "F", "I", "M", "P", "Q", "S", "T", "U", "W", "f", "l", "m", "o",
+"s", "t", and "u" options are not supported, and new options "P" (PPD
+file) and "F" (filter) are provided to configure CUPS-specific features
+such as PPD file and conversion filters.
+
+<H3>lpstat</H3>
+
+The lpstat command lists printers, classes, and jobs as requested by the
+user.
+
+<H3>reject</H3>
+
+The reject command tells the scheduler not to accept new jobs for specific
+printers.
+
+<H1>Detailed Design</H1>
+
+<H1 TYPE=A VALUE=1>Glossary</H1>
+
+<H2>Terms</H2>
+
+<DL>
+
+ <DT>C
+ <DD>A computer language.
+
+ <DT>parallel
+ <DD>Sending or receiving data more than 1 bit at a time.
+
+ <DT>pipe
+ <DD>A one-way communications channel between two programs.
+
+ <DT>serial
+ <DD>Sending or receiving data 1 bit at a time.
+
+ <DT>socket
+ <DD>A two-way network communications channel.
+
+</DL>
+
+<H2>Acronyms</H2>
+
+<DL>
+
+ <DT>ASCII
+ <DD>American Standard Code for Information Interchange
+
+ <DT>CUPS
+ <DD>Common UNIX Printing System
+
+ <DT>ESC/P
+ <DD>EPSON Standard Code for Printers
+
+ <DT>FTP
+ <DD>File Transfer Protocol
+
+ <DT>HP-GL
+ <DD>Hewlett-Packard Graphics Language
+
+ <DT>HP-PCL
+ <DD>Hewlett-Packard Printer Control Language
+
+ <DT>HP-PJL
+ <DD>Hewlett-Packard Printer Job Language
+
+ <DT>IETF
+ <DD>Internet Engineering Task Force
+
+ <DT>IPP
+ <DD>Internet Printing Protocol
+
+ <DT>ISO
+ <DD>International Standards Organization
+
+ <DT>LPD
+ <DD>Line Printer Daemon
+
+ <DT>MIME
+ <DD>Multimedia Internet Mail Exchange
+
+ <DT>PCL
+ <DD>Page Control Language
+
+ <DT>PPD
+ <DD>PostScript Printer Description
+
+ <DT>SMB
+ <DD>Server Message Block
+
+ <DT>TFTP
+ <DD>Trivial File Transfer Protocol
+
+</DL>
+
+</BODY>
+</HTML>
diff --git a/doc/ssr.html b/doc/ssr.html
new file mode 100644
index 000000000..51be5b88e
--- /dev/null
+++ b/doc/ssr.html
@@ -0,0 +1,221 @@
+<HTML>
+<HEAD>
+<TITLE>DRAFT - CUPS Software Security Report</TITLE>
+<META NAME="AUTHOR" CONTENT="Easy Software Products">
+<META NAME="COPYRIGHT" CONTENT="Copyright 1997-1999, All Rights Reserved">
+<META NAME="DOCNUMBER" CONTENT="CUPS-SSR-1.0">
+</HEAD>
+<BODY>
+<CENTER><A HREF=#contents><IMG SRC="images/cups-large.gif" BORDER=0><BR>
+<H1>DRAFT - CUPS Software Security Report</H1></A><BR>
+CUPS-SSR-1.0<BR>
+Easy Software Products<BR>
+Copyright 1997-1999, All Rights Reserved<BR>
+</CENTER>
+<HR>
+<H1 ALIGN=CENTER><A NAME=CONTENTS>Table of Contents</A></H1>
+<BR>
+<BR><B><A HREF=#1>1 Scope</A></B>
+<UL>
+<LI><A HREF=#1_1>1.1 Identification</A></LI>
+<LI><A HREF=#1_2>1.2 System Overview</A></LI>
+<LI><A HREF=#1_3>1.3 Document Overview</A></LI>
+</UL>
+<B><A HREF=#2>2 References</A></B>
+<UL>
+<LI><A HREF=#2_1>2.1 CUPS Documentation</A></LI>
+<LI><A HREF=#2_2>2.2 Other Documents</A></LI>
+</UL>
+<B><A HREF=#3>3 Local Access Risks</A></B>
+<UL>
+<LI><A HREF=#3_1>3.1 Security Breaches</A></LI>
+</UL>
+<B><A HREF=#4>4 Remote Access Risks</A></B>
+<UL>
+<LI><A HREF=#4_1>4.1 Denial of Service Attacks</A></LI>
+<LI><A HREF=#4_2>4.2 Security Breaches</A></LI>
+</UL>
+<B><A HREF=#5>A Glossary</A></B>
+<UL>
+<LI><A HREF=#5_1>A.1 Terms</A></LI>
+<LI><A HREF=#5_2>A.2 Acronyms</A></LI>
+</UL>
+<HR>
+<H1><A NAME=1>1 Scope</A></H1>
+<H2><A NAME=1_1>1.1 Identification</A></H2>
+ This software security report provides an analysis of possible
+security concerns for the Common UNIX Printing System (&quot;CUPS&quot;) Version
+1.0.
+<H2><A NAME=1_2>1.2 System Overview</A></H2>
+ The Common UNIX Printing System provides a portable printing layer for
+ UNIX&reg; operating systems. It has been developed by Easy Software
+ Products to promote a standard printing solution for all UNIX vendors
+ and users. CUPS provides the System V and Berkeley command-line
+interfaces.
+<P>CUPS uses the Internet Printing Protocol (IETF-IPP) as the basis for
+managing print jobs and queues. The Line Printer Daemon (LPD, RFC1179),
+Server Message Block (SMB), and AppSocket protocols are also supported
+with reduced functionality. </P>
+<P>CUPS adds network printer browsing and PostScript Printer
+Description (&quot;PPD&quot;)-based printing options to support real world
+applications under UNIX. </P>
+<P>CUPS also includes a customized version of GNU GhostScript
+(currently based off GNU GhostScript 4.03) and an image file RIP that
+can be used to support non-PostScript printers. </P>
+<H2><A NAME=1_3>1.3 Document Overview</A></H2>
+<P>This software security report is organized into the following
+sections:</P>
+<UL>
+<LI>1 - Scope</LI>
+<LI>2 - References</LI>
+<LI>3 - Local Access Risks</LI>
+<LI>4 - Remote Access Risks</LI>
+<LI>A - Glossary</LI>
+</UL>
+<H1><A NAME=2>2 References</A></H1>
+<H2><A NAME=2_1>2.1 CUPS Documentation</A></H2>
+ The following CUPS documentation is referenced by this document:
+<UL>
+<LI>CUPS-CMP-1.0: CUPS Configuration Management Plan </LI>
+<LI>CUPS-IDD-1.0: CUPS System Interface Design Description </LI>
+<LI>CUPS-SAM-1.0.x: CUPS Software Administrators Manual </LI>
+<LI>CUPS-SDD-1.0: CUPS Software Design Description </LI>
+<LI>CUPS-SPM-1.0: CUPS Software Programming Manual </LI>
+<LI>CUPS-SSR-1.0: CUPS Software Security Report </LI>
+<LI>CUPS-STP-1.0: CUPS Software Test Plan </LI>
+<LI>CUPS-SUM-1.0.x: CUPS Software Users Manual </LI>
+<LI>CUPS-SVD-1.0.x: CUPS Software Version Description </LI>
+</UL>
+<H2><A NAME=2_2>2.2 Other Documents</A></H2>
+ The following non-CUPS documents are referenced by this document:
+<UL>
+<LI>IEEE 1387.4, System Administration: Printing (draft) </LI>
+<LI>IPP/1.0: Additional Optional Operations - Set 1 </LI>
+<LI>IPP/1.0: Encoding and Transport </LI>
+<LI>IPP/1.0: Implementers Guide </LI>
+<LI>IPP/1.0: Model and Semantics </LI>
+<LI>RFC 1179, Line Printer Daemon Protocol </LI>
+</UL>
+<H1><A NAME=3>3 Local Access Risks</A></H1>
+<P>Local access risks are those that can be exploited only with a local
+user account. This section does not address issues related to
+dissemination of the root password or other security issues associated
+with the UNIX operating system. </P>
+<H2><A NAME=3_1>3.1 Security Breaches</A></H2>
+<P>Since the default installation creates a world-readable request
+directory, it is possible for local users to read the contents of print
+files before they are printed. </P>
+<P>This problem can be alleviated by making the request directory
+readable only by the user specified in the CUPS configuration file. </P>
+<H1><A NAME=4>4 Remote Access Risks</A></H1>
+<P>Remote access risks are those that can be exploited without a local
+user account and/or from a remote system. This section does not address
+issues related to network or firewall security. </P>
+<H2><A NAME=4_1>4.1 Denial of Service Attacks</A></H2>
+<P>Like all internet services, the CUPS server is vulnerable to denial
+of service attacks, including: </P>
+<OL>
+<LI>Establishing multiple connections to the server until the server
+ will accept no more.
+<P>This cannot be protected against by the current software. It is
+possible that future versions of the CUPS software could be configured
+to limit the number of connections allowed from a single host, however
+that still would not prevent a determined attack. </P>
+</LI>
+<LI>Repeatedly opening and closing connections to the server as fast
+ as possible.
+<P>There is no easy way of protecting against this in the CUPS
+ software. If the attack is coming from outside the local network it
+might be possible to filter such an attack, however once the
+connection request has been received by the server it must at least
+accept the connection to find out who is connecting. </P>
+</LI>
+<LI>Flooding the network with broadcast packets on port 631.
+<P>It might be possible to disable browsing if this condition is
+detected by the CUPS software, however if there are large numbers of
+printers available on the network such an algorithm might think that
+an attack was occurring when instead a valid update was being
+received. </P>
+</LI>
+<LI>Sending partial IPP requests; specifically, sending part of an
+ attribute value and then stopping transmission.
+<P>The current code is structured to read and write the IPP request
+data on-the-fly, so there is no easy way to protect against this for
+large attribute values. </P>
+</LI>
+<LI>Sending large/long print jobs to printers, preventing other users
+ from printing.
+<P>There are limited facilities for protecting against large print
+ jobs (the <CODE>MaxRequestSize</CODE> attribute), however this will
+ not protect printers from malicious users and print files that
+ generate hundreds or thousands of pages. In general, we recommend
+ restricting printer access to known hosts or networks, and adding
+ user-level access control as needed for expensive printers. </P>
+</LI>
+</OL>
+<H2><A NAME=4_2>4.2 Security Breaches</A></H2>
+<P>The current CUPS server only supports Basic authentication with
+usernames and passwords. This essentially places the clear text of the
+username and password on the network. Since CUPS uses the UNIX username
+and password account information, the authentication information could
+be used to gain access to accounts (possibly priviledged accounts) on
+the server. </P>
+<P>The default CUPS configuration disables remote administration. We do
+not recommend that remote administration be enabled for all hosts,
+however if you have a trusted network or subnet access can be
+restricted accordingly. </P>
+<P>The next minor release of CUPS will support Digest authentication of
+the entire message body using separate MD5-based username and password
+files. This will protect password information and prevent unauthorized
+access due to compromised account passwords. </P>
+<H1 TYPE=A VALUE=1><A NAME=5>A Glossary</A></H1>
+<H2><A NAME=5_1>A.1 Terms</A></H2>
+<DL>
+<DT>C </DT>
+<DD>A computer language. </DD>
+<DT>parallel </DT>
+<DD>Sending or receiving data more than 1 bit at a time. </DD>
+<DT>pipe </DT>
+<DD>A one-way communications channel between two programs. </DD>
+<DT>serial </DT>
+<DD>Sending or receiving data 1 bit at a time. </DD>
+<DT>socket </DT>
+<DD>A two-way network communications channel. </DD>
+</DL>
+<H2><A NAME=5_2>A.2 Acronyms</A></H2>
+<DL>
+<DT>ASCII </DT>
+<DD>American Standard Code for Information Interchange </DD>
+<DT>CUPS </DT>
+<DD>Common UNIX Printing System </DD>
+<DT>ESC/P </DT>
+<DD>EPSON Standard Code for Printers </DD>
+<DT>FTP </DT>
+<DD>File Transfer Protocol </DD>
+<DT>HP-GL </DT>
+<DD>Hewlett-Packard Graphics Language </DD>
+<DT>HP-PCL </DT>
+<DD>Hewlett-Packard Printer Control Language </DD>
+<DT>HP-PJL </DT>
+<DD>Hewlett-Packard Printer Job Language </DD>
+<DT>IETF </DT>
+<DD>Internet Engineering Task Force </DD>
+<DT>IPP </DT>
+<DD>Internet Printing Protocol </DD>
+<DT>ISO </DT>
+<DD>International Standards Organization </DD>
+<DT>LPD </DT>
+<DD>Line Printer Daemon </DD>
+<DT>MIME </DT>
+<DD>Multimedia Internet Mail Exchange </DD>
+<DT>PCL </DT>
+<DD>Page Control Language </DD>
+<DT>PPD </DT>
+<DD>PostScript Printer Description </DD>
+<DT>SMB </DT>
+<DD>Server Message Block </DD>
+<DT>TFTP </DT>
+<DD>Trivial File Transfer Protocol </DD>
+</DL>
+</BODY>
+</HTML>
diff --git a/doc/ssr.pdf b/doc/ssr.pdf
new file mode 100644
index 000000000..8ae80cf93
--- /dev/null
+++ b/doc/ssr.pdf
@@ -0,0 +1,446 @@
+%PDF-1.2
+%âãÏÓ
+1 0 obj<</Producer(htmldoc 2.0b1 Copyright 1997-1999 Michael Sweet, All Rights Reserved.)/CreationDate(D:19990709142834Z)/Title(DRAFT - CUPS Software Security Report)/Author(Easy Software Products)>>endobj
+2 0 obj<</Type/Encoding/BaseEncoding/WinAnsiEncoding>>endobj
+3 0 obj<</Type/Font/Subtype/Type1/BaseFont/Courier/Encoding 2 0 R>>endobj
+4 0 obj<</Type/Font/Subtype/Type1/BaseFont/Times-Roman/Encoding 2 0 R>>endobj
+5 0 obj<</Type/Font/Subtype/Type1/BaseFont/Times-Bold/Encoding 2 0 R>>endobj
+6 0 obj<</Type/Font/Subtype/Type1/BaseFont/Times-Italic/Encoding 2 0 R>>endobj
+7 0 obj<</Type/Font/Subtype/Type1/BaseFont/Helvetica/Encoding 2 0 R>>endobj
+8 0 obj<</Type/Font/Subtype/Type1/BaseFont/Helvetica-Bold/Encoding 2 0 R>>endobj
+9 0 obj<</Type/Font/Subtype/Type1/BaseFont/Symbol>>endobj
+10 0 obj<</Subtype/Link/Rect[72.0 673.2 80.2 686.2]/Border[0 0 0]/Dest[79 0 R/XYZ null 818 0]>>endobj
+11 0 obj<</Subtype/Link/Rect[80.2 673.2 107.8 686.2]/Border[0 0 0]/Dest[79 0 R/XYZ null 818 0]>>endobj
+12 0 obj<</Subtype/Link/Rect[108.0 660.0 124.5 673.0]/Border[0 0 0]/Dest[79 0 R/XYZ null 737 0]>>endobj
+13 0 obj<</Subtype/Link/Rect[124.5 660.0 183.8 673.0]/Border[0 0 0]/Dest[79 0 R/XYZ null 737 0]>>endobj
+14 0 obj<</Subtype/Link/Rect[108.0 646.8 124.5 659.8]/Border[0 0 0]/Dest[79 0 R/XYZ null 658 0]>>endobj
+15 0 obj<</Subtype/Link/Rect[124.5 646.8 159.6 659.8]/Border[0 0 0]/Dest[79 0 R/XYZ null 658 0]>>endobj
+16 0 obj<</Subtype/Link/Rect[159.6 646.8 203.0 659.8]/Border[0 0 0]/Dest[79 0 R/XYZ null 658 0]>>endobj
+17 0 obj<</Subtype/Link/Rect[108.0 633.6 124.5 646.6]/Border[0 0 0]/Dest[79 0 R/XYZ null 434 0]>>endobj
+18 0 obj<</Subtype/Link/Rect[124.5 633.6 173.1 646.6]/Border[0 0 0]/Dest[79 0 R/XYZ null 434 0]>>endobj
+19 0 obj<</Subtype/Link/Rect[173.1 633.6 216.4 646.6]/Border[0 0 0]/Dest[79 0 R/XYZ null 434 0]>>endobj
+20 0 obj<</Subtype/Link/Rect[72.0 607.2 80.2 620.2]/Border[0 0 0]/Dest[85 0 R/XYZ null 818 0]>>endobj
+21 0 obj<</Subtype/Link/Rect[80.2 607.2 131.6 620.2]/Border[0 0 0]/Dest[85 0 R/XYZ null 818 0]>>endobj
+22 0 obj<</Subtype/Link/Rect[108.0 594.0 124.5 607.0]/Border[0 0 0]/Dest[85 0 R/XYZ null 737 0]>>endobj
+23 0 obj<</Subtype/Link/Rect[124.5 594.0 154.8 607.0]/Border[0 0 0]/Dest[85 0 R/XYZ null 737 0]>>endobj
+24 0 obj<</Subtype/Link/Rect[154.8 594.0 222.6 607.0]/Border[0 0 0]/Dest[85 0 R/XYZ null 737 0]>>endobj
+25 0 obj<</Subtype/Link/Rect[108.0 580.8 124.5 593.8]/Border[0 0 0]/Dest[85 0 R/XYZ null 540 0]>>endobj
+26 0 obj<</Subtype/Link/Rect[124.5 580.8 152.3 593.8]/Border[0 0 0]/Dest[85 0 R/XYZ null 540 0]>>endobj
+27 0 obj<</Subtype/Link/Rect[152.3 580.8 202.4 593.8]/Border[0 0 0]/Dest[85 0 R/XYZ null 540 0]>>endobj
+28 0 obj<</Subtype/Link/Rect[72.0 554.4 80.2 567.4]/Border[0 0 0]/Dest[91 0 R/XYZ null 818 0]>>endobj
+29 0 obj<</Subtype/Link/Rect[80.2 554.4 109.3 567.4]/Border[0 0 0]/Dest[91 0 R/XYZ null 818 0]>>endobj
+30 0 obj<</Subtype/Link/Rect[109.3 554.4 143.2 567.4]/Border[0 0 0]/Dest[91 0 R/XYZ null 818 0]>>endobj
+31 0 obj<</Subtype/Link/Rect[143.2 554.4 168.9 567.4]/Border[0 0 0]/Dest[91 0 R/XYZ null 818 0]>>endobj
+32 0 obj<</Subtype/Link/Rect[108.0 541.2 124.5 554.2]/Border[0 0 0]/Dest[91 0 R/XYZ null 690 0]>>endobj
+33 0 obj<</Subtype/Link/Rect[124.5 541.2 163.9 554.2]/Border[0 0 0]/Dest[91 0 R/XYZ null 690 0]>>endobj
+34 0 obj<</Subtype/Link/Rect[163.9 541.2 204.2 554.2]/Border[0 0 0]/Dest[91 0 R/XYZ null 690 0]>>endobj
+35 0 obj<</Subtype/Link/Rect[72.0 514.8 80.2 527.8]/Border[0 0 0]/Dest[97 0 R/XYZ null 818 0]>>endobj
+36 0 obj<</Subtype/Link/Rect[80.2 514.8 119.0 527.8]/Border[0 0 0]/Dest[97 0 R/XYZ null 818 0]>>endobj
+37 0 obj<</Subtype/Link/Rect[119.0 514.8 152.9 527.8]/Border[0 0 0]/Dest[97 0 R/XYZ null 818 0]>>endobj
+38 0 obj<</Subtype/Link/Rect[152.9 514.8 178.6 527.8]/Border[0 0 0]/Dest[97 0 R/XYZ null 818 0]>>endobj
+39 0 obj<</Subtype/Link/Rect[108.0 501.6 124.5 514.6]/Border[0 0 0]/Dest[97 0 R/XYZ null 704 0]>>endobj
+40 0 obj<</Subtype/Link/Rect[124.5 501.6 156.6 514.6]/Border[0 0 0]/Dest[97 0 R/XYZ null 704 0]>>endobj
+41 0 obj<</Subtype/Link/Rect[156.6 501.6 168.5 514.6]/Border[0 0 0]/Dest[97 0 R/XYZ null 704 0]>>endobj
+42 0 obj<</Subtype/Link/Rect[168.5 501.6 204.2 514.6]/Border[0 0 0]/Dest[97 0 R/XYZ null 704 0]>>endobj
+43 0 obj<</Subtype/Link/Rect[204.2 501.6 237.8 514.6]/Border[0 0 0]/Dest[97 0 R/XYZ null 704 0]>>endobj
+44 0 obj<</Subtype/Link/Rect[108.0 488.4 124.5 501.4]/Border[0 0 0]/Dest[97 0 R/XYZ null 255 0]>>endobj
+45 0 obj<</Subtype/Link/Rect[124.5 488.4 163.9 501.4]/Border[0 0 0]/Dest[97 0 R/XYZ null 255 0]>>endobj
+46 0 obj<</Subtype/Link/Rect[163.9 488.4 204.2 501.4]/Border[0 0 0]/Dest[97 0 R/XYZ null 255 0]>>endobj
+47 0 obj<</Subtype/Link/Rect[72.0 462.0 82.7 475.0]/Border[0 0 0]/Dest[103 0 R/XYZ null 818 0]>>endobj
+48 0 obj<</Subtype/Link/Rect[82.7 462.0 124.2 475.0]/Border[0 0 0]/Dest[103 0 R/XYZ null 818 0]>>endobj
+49 0 obj<</Subtype/Link/Rect[108.0 448.8 126.9 461.8]/Border[0 0 0]/Dest[103 0 R/XYZ null 737 0]>>endobj
+50 0 obj<</Subtype/Link/Rect[126.9 448.8 155.0 461.8]/Border[0 0 0]/Dest[103 0 R/XYZ null 737 0]>>endobj
+51 0 obj<</Subtype/Link/Rect[108.0 435.6 126.9 448.6]/Border[0 0 0]/Dest[103 0 R/XYZ null 434 0]>>endobj
+52 0 obj<</Subtype/Link/Rect[126.9 435.6 172.8 448.6]/Border[0 0 0]/Dest[103 0 R/XYZ null 434 0]>>endobj
+53 0 obj[10 0 R
+11 0 R
+12 0 R
+13 0 R
+14 0 R
+15 0 R
+16 0 R
+17 0 R
+18 0 R
+19 0 R
+20 0 R
+21 0 R
+22 0 R
+23 0 R
+24 0 R
+25 0 R
+26 0 R
+27 0 R
+28 0 R
+29 0 R
+30 0 R
+31 0 R
+32 0 R
+33 0 R
+34 0 R
+35 0 R
+36 0 R
+37 0 R
+38 0 R
+39 0 R
+40 0 R
+41 0 R
+42 0 R
+43 0 R
+44 0 R
+45 0 R
+46 0 R
+47 0 R
+48 0 R
+49 0 R
+50 0 R
+51 0 R
+52 0 R
+]endobj
+54 0 obj<</Dests 55 0 R>>endobj
+55 0 obj<</Kids[56 0 R]>>endobj
+56 0 obj<</Limits[(1)(5_2)]/Names[(1)57 0 R(1_1)58 0 R(1_2)59 0 R(1_3)60 0 R(2)61 0 R(2_1)62 0 R(2_2)63 0 R(3)64 0 R(3_1)65 0 R(4)66 0 R(4_1)67 0 R(4_2)68 0 R(5)69 0 R(5_1)70 0 R(5_2)71 0 R]>>endobj
+57 0 obj<</D[79 0 R/XYZ null 818 null]>>endobj
+58 0 obj<</D[79 0 R/XYZ null 737 null]>>endobj
+59 0 obj<</D[79 0 R/XYZ null 658 null]>>endobj
+60 0 obj<</D[79 0 R/XYZ null 434 null]>>endobj
+61 0 obj<</D[85 0 R/XYZ null 818 null]>>endobj
+62 0 obj<</D[85 0 R/XYZ null 737 null]>>endobj
+63 0 obj<</D[85 0 R/XYZ null 540 null]>>endobj
+64 0 obj<</D[91 0 R/XYZ null 818 null]>>endobj
+65 0 obj<</D[91 0 R/XYZ null 690 null]>>endobj
+66 0 obj<</D[97 0 R/XYZ null 818 null]>>endobj
+67 0 obj<</D[97 0 R/XYZ null 704 null]>>endobj
+68 0 obj<</D[97 0 R/XYZ null 255 null]>>endobj
+69 0 obj<</D[103 0 R/XYZ null 818 null]>>endobj
+70 0 obj<</D[103 0 R/XYZ null 737 null]>>endobj
+71 0 obj<</D[103 0 R/XYZ null 434 null]>>endobj
+72 0 obj<</Type/Pages/MediaBox[0 0 595 792]/Count 14/Kids[73 0 R
+76 0 R
+109 0 R
+112 0 R
+79 0 R
+82 0 R
+85 0 R
+88 0 R
+91 0 R
+94 0 R
+97 0 R
+100 0 R
+103 0 R
+106 0 R
+]>>endobj
+73 0 obj<</Type/Page/Parent 72 0 R/Contents 74 0 R/Resources<</ProcSet[/PDF/Text/ImageB/ImageC/ImageI]/Font<</F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+74 0 obj<</Length 75 0 R/Filter/FlateDecode>>stream
+xÚíßÜ8rÇ%µúÅOšÙíwNH€ØîO{Ï@pgF›ì[I¢‡ ‡]_r‡ öÎë½`ÿûô¯éÖRª*R57<à°¶Én~Äâ·ŠdQý·W7b½ýßøîV¼ÿ ~üßW÷òU"_ýM¼_ÿîíwû|ÿáÃÛµøÝÇ··âööæíݾÖïòÿ|÷ûìßîÅæ_×ûò~_>ìËÇ}yØ—ï÷å÷ÿõîñõ˯Ÿßý‡¸{óîßÅæææÝ=<ˆ;ñÃã«âyxá2+òñv¹\^]ìÊö?–wSç’Ë‹ÈS•‹ËëÇirÉÛ+5R ny71.yÛÅôT.¯“©pá¡ŽhwSàz¸òÈÅ_&ŽsÝG¯\næºåRÙ4Gpf¬ìŽ™e®á™—oǸò+ÏJñ¯âºõ¬•`ã —žÍrí—±^Ø2K\òʳ^ü×£så‘×Gùfd®{¯§$crÝx½3—e´ncmËëQ¸däy.‚“BX-ßÎ5 &€Åƒ)`1ÀÀiÉà‹LËóVqõî·Ì4›k`¬-X2×Ú¼p}òF(³Þ¹2o”òmÏ\y4AÁ}…giLA
+ÚÑ ê£såÞ¨eÓW4.rŠÁ2Ç‹Á4<Yì‰\r|,ÏïK8Àå…Ö¹2ω²±Ì%#7¸Ë\kÏ‘²°Ê•yΔÄ"—+Vˆrb®Øs¨l¬qå.auJLO4PQLP40QL*ÒÀk=–+u «cÀ`z0˜ Æcœ3ŽKºˆÕ×Ã51Ã`z.5Ã`¸áº¸\._?îÊýîâC¿ƒ —ùº9Éw÷ z0è¸ü–¤ùpÕÏ€AßÃuÙ¹Tº7@[p­ûªò¨-­‡õÐãpÒ%77xÃæb‡´tq&ÙŒËÅ 5èé¹9kÉ0¹˜ÃÅJ§æ YÈäbòÜlHÆù<®´m·8d+W4” ž¼YdEêÁþ®†IÚ;'Ù`ÃàƒM-~æUHçÊGÀ"ƒùt.²Èûv®H
+cåhç ‹6£r¥#!Ç"— ‹¶ qåV<$,2ra`oáõmaµdF†Ö"yËX¤ÛW
+Wj!œ1*kƒ/Kªá'ö¹=Hð\Ò<H3-9ßõ\0êä:”OlC++” è©®!‚ ™íerÑ&ÃËåy¢`¹f8+z,‚gˆ`n†ýY!EW8®µVHèIˆãr@ iÒ᣸²‘=2CÁ6.¼†Eï'as Wä†hP´>@peüÀl¼Kº¹bWDƒ2`«n®È'õ'ìäÊÜ.ä€\±cÃ…ôaIWäØp!Ÿô¢ƒ+wÆ%Óºvp¥Î .J :¸„c³ -eI+—tp¸ps~ÕÊ•98\¸m¤°• ©òþ X(+
+Z¹"‡"C¢r´qaU~`,ÔôØ´p!U>š« ÙpU~38Âg-\ì­± 1Ðse.Š<ZÐ-¸(òh´Òr GUgJs—tU5pS¦ãÊh¡aÕ“ÿT3ö§¿‡ã3M+ªœaýüß1`Q ×BË%+7nKh%ºÖDÃ%h±F+×LËuüš²Îa‡ÄOàL¯Çåé¹ ¤^9>ÜhKÎôZ!¹VZ®¼ô|
+ô\g­”Uú™#8\a¹žŒKÁuÖö o†¥OT\95†êàZh¹Î"SöJRìCúÚ+ÄsÍô\ñÓ#Š(‹ï+ˆ@^kWBù.OÏ•?+§˜!b¢„*®ˆh†\+-×á»æ‡¿ÂoÙu
+ÛLÁ%©fØÉê¹Ö Ñ<C5Únó\Õ ;¹|=×áˉ˃ )ˆ@öÊ –kvª­ä:Çuç!Õ+–k=É“’«$R”UªÀÙPec犟ƒš+e­R;CÄyƒ+'›a;WVÁipIÖfIŠÓ5 ÊFPà¹ä“Y¨¹N&EÚ,Éq=âZ9$pí;ê¹RÖžNè(+
+¿FÃ%Y{v½lp1ÒÛ¹ò£(h¸Ž]$îÙŨ.M6‚‚ÂU•^Çœ§eR@“9ë¨ô:®˜3^ÊM6V4®£Òë¸"Îü’¨gÜA×Qé5\ï #ÂH6¢€ÈuTz WÌÛìïzþ³*—dL¯N®ƒÒk¸"ÞáLŒyø@’•ë ôj®œyŠ‘b3ö6
+*×AéÕ\ 7³HªÊ3¦W7×^éÕ\‚™¹#1Ž(QTHçÚ[w¬_W.†Ø%p›
+ëP¹[)¸ö½*®ƒÝKFÈ!ÝJ•¨]R6¿ÊuîDK4YÐ q8€•EPåZ•M 4\OE8MÁnÃÌË\ˆ=›™Æ$eE j\¹†+=òätCL2
+WTž°äE˜|l/%.Á’úÄ<ÝÞS¬‹Ë\ÕŒ¨u@O«•O*§ØÇ(sÅÃN{Hý´jç÷š¼.”\²ÁU,/ï!UÐQ¯ÞNòý‹OýË®R¹R¦>•Ãdu«;š÷‹IÀÊ|0I.Á‹¢œçŠxQ”ë\ÒDæÊ åÐU®Œ:Ε>79<rÅìèÐm®5?:tšK<7™?rEÏM\œ­ù pÉg'ó®ìÙÉ<–k2_^Î-»ÍËï•\nJ\1[æËu.7ª¿õ.®+Û~çÝÞÆu>e´ÝÕH³É8·¼Bp÷mê“3Qr-8\ 5Wy&}{âl÷¥ÆרN­‹3×LÉ•«Ï—#¶ûRh̸ÖEÃå)¹„úÜ¿i£¶W]nj­‹+×JÁ•6UPn9ÀrÍ4áËJÅr¸BWÔ|Œ–¹«Ë«u1àpM®'Ñð/¢“ÝÊ-‡h®•šËWqÕ¯ó¡¸w
+Uë'š.Ö¯ó¡¸ê´\±E®zuë×ùP\õFZ®5?<,Ô§ÙÞot±~ÅUoÕÒTO\—Ÿh¸J¡j¥‹µë|8®Z#>W€äòW¥¿=úåã¶ÇBÝÅÚu>W­‘()v…+²ÃUÊ?,LJmPÝÅÚu>W­Q#ŽJ±\a[§*®Òû¡]Ü‹6•«Úh]ˆâ#—×/WÖÆU½Î‡äRÜ,wqåšqåºxþT
+"WµQZŸ*ÑKöÌ%5·8×ù\ÕFYM²¥‡åZq­\Àáª4’µÐ<u‚+çpUEÕ &Ž\ݹ‡«¹*×ù°\ÕF±"Ë•Ës¸*Òó©òy“wt®ŒÃUi$˽ÌNñ†É²Ò—äpU‰óù×½GàJúä*_çCsU¥óJ¸RWµQ-{.“å2Š+jçÊ9\ÕFÕ•q¸v‚«tÏUkT°$k£ïbÌáª6ª¦Áž«s{Ã7ä^y‹v®ø©\µFŸÊ‹Åt®Ó›
+§É½p½p½p½p½pá¸æ/\/\ýsyæ\Û•ú^)¥î¸\÷ûw˜ùטîRêŽÌuU¿¾V}ÙPé sŠºêÓ
+N#îâ2‰ç+ÿ gµ8M°¬š¤­ªKâšÇöÉ„Ãp¥ç?R` š¤­ªKâ
+ËnnÆÕ‘~xúçüb[%±JJw¤ôuI\»û_§Ç´è•+ÐýÃŽ§”TܶÕçÇIº›Â«Þ¸"]:ÎñL9/ÁD-©;x®õñóvŸ¼éë n4qcP™FÚº$®ø(C;KOzã:žu^nÔÖ>“âIεuI\pÎ~õ‹Þ¸N!z£·‡~ˆJ¾X_WÉÕLìÜS¤Çùºs_ýq•òAj½Ÿ¼ÍÉëëR¸²£¬wlÆÕv®W®Wy/qÚÌ¢ÕÖ¥påÇ?îܲ!WÛ9l¬¼ÇQY'u)\Ò;½}ÞÎet¾,µ™¥¢‘Õ¢¯Kà*ŽŽyÿ·ýqÕ²hý¤1’!¢.…ëà˜÷n¹O®Ò‹¶ªª™ÓÕ¥ps¶÷ñ}r=]„mäR©’M[ë⸎9Û×7ãêÌûºT³F(Šº.Þ/ïÝ`²û¿ƒËF>ÛÃUsQ“j9ªº®to{·Ü?×¹·³Z(µAÕ%póÞ-Áõ$
+vÒ´Õ%póÞ-raß-šµ‹A[]—|òbó¡¸Î‰×Ý\µº®ƒc>ˆd+—I>öîU2«ª+ÖrµÕ¥pmM0ÈÏû§½pUÝ+tnvêêR¸¶’ágÇhÈ•àö;¸ÚêR¸v#z§ÕõÆTlËoåÒÕ¥pÁá2¬ßÅerïFTþY´ê|[]
+Wz¸Y˜s­Ú—_‹ò:d¦åj«KáÊÎN½7®´<žëÚ×Ô:ÛV—•ŸW­\&÷mý»ó¡ÖJËÕV·#ž¯¼mKVO ¸ç°çCHýb±­.…«ô^Øþ¸â–µZ«¥.‰Kฌîeç-Uë\-u\‰9×—nSý±Š¾.‰+nüM\¹^a\9w?ªJ§§ÒÊeôþê^Ì·E+—¾.‰K÷~V»\¥Î^\Úº$®ìäÓÛ¹Œ6²wÞè*Ò”˜Ô5*†ïí9ÊÏë óS|†ïYr¶¾ë…kx®î×-O“+þ‡åJ&ÉÕ½Á±™$—É‚ù…kx.£…夹æ“ä’ÿ°\á$¹º`³gÊL“+z†¯áïq¼pÀµ~†/îw|V“ä‚gÊ•>Ã
+´Ð;!樈
+´Ð‡‘Ãy™ !ôŽ ì
+
+´Ð;"k”
+’pŒ=ÁÐïh€‚$#O°îG?Sp ç'>ñHÆ;ö[£'
+4áw‚á# 9‡‘'X†_J)Vy‚N¤¢#‡ˆ„Sp(ˆÂ1â£ÒmœG]ƒQ΀ØpÌMŽ˜Ð9 ê蘛”­v NÌ•>§L ŽôˆJOÚ‘jÓñ”^PºT%m·M’4 ¨Ss4CDØÒL˵v×iÛ/PÐ'ØÂQ3,ÇB@vé#bJ3$ ¯pF2Ä5íyÝŠG1DI ]¼ÄÇSâㆂ1ÁFˆqz@Á™`¡‹fXYi
+Î|Õ,¨jk‚ œd™‘½¬ 6° ‹ÉsXC>°rHz€5EVFÔ
+œåöÐ1‡ Ï àl sd Þ$Tê׌ ÌÇ3 ÔKÎcîç $ÆÇÜi:Ü€±¦;peu°ÃõfŽàB*ý@±ž1ð?j˜c† ÀŸ©Ã XÄ ë€í‡°”éL/AƒÈ)Qà¸ÖX°•Ã5CreŽ ˜Œ¸A8F†Xû©¥qB Õ*
+3Cìuá,ùV…™!öªõا»@sá ±GéÈ l
+SCô“‘5^)^`&°}Zâ'o†Z.ém‰¹‘tAalˆ=i¢0ò¡`<i{òÎh+TïŒñ¬íÇ;ç†Öƾ¾—)&#CÙOÌþŠemúHÁÂĵïÅî _\P¹RÒ€ÙÔŠf…d.é&#ó
+w¸
+yåÎ`Ùä²4d—›Â5®BÞËàk[}±ÉÅO­>R]Ûë‰].ƒôjDêö˜\\2»T}pqÈ._ÛîC\ļxÙÃ{.úáÚ–G\v¼y×Ë×÷Ƶµƒ¼X>öõÝ}rí‡ívy¡§ëÇ>¿·o®ÃÀín8,¯.vår¹¼~|ìý+á¡@òëôÕ½|õîûâ棛ïÞ~wë·wBþôOÙ§ï¥x#
+ÈEþó¿þß¾|ùçýò§¯¿‰ìó_þòõŸåŸ·íïÄÍÍ®ýÍÝGñææýÛÛ]û]»7yž½¹y»ÞU{s³Ù~ú›Û‡OþðËoç…/?ÿôë_ÙW¼{ÿöCés~þëo_þôßÿóuÛÉß½ÙþßÇŸþò‘íþò—mG~ùüåïŸÚ5MäéÿiÁñ½endstream
+endobj
+75 0 obj
+6308
+endobj
+76 0 obj<</Type/Page/Parent 72 0 R/Contents 77 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F8 7 0 R>>>>>>endobj
+77 0 obj<</Length 78 0 R/Filter/FlateDecode>>stream
+xÚ+ä2T0
+ár á
+ä
+endobj
+78 0 obj
+31
+endobj
+79 0 obj<</Type/Page/Parent 72 0 R/Contents 80 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F8 7 0 R/F9 8 0 R/Fc 9 0 R>>>>>>endobj
+80 0 obj<</Length 81 0 R/Filter/FlateDecode>>stream
+xÚUKo1¾ó+F9%RÙfè-o!¥í6ª.Æ;œx=[Û ¢¿¾c/Bi«TpÁóú¾o|o¥pÊŸÎ;Ðíƒ,[—“ÖûÛ!¤C˜léŸ÷`’§0–TáÉä¹±ž%ƒÆÞ>$G’Â(GãU¡¤ðŠLã܃4]»vúëd®8*üRX‡²¶Ê¯ÀbEÖCei¡rt …^9ö¦*rNÍôN€$#ÑYðs„+*K2ðøiô-ç’i7é„’™UŒÌ<Áxå<–0=>ºzÌÆGÓøŠÖ1ZH“Óä
+ TüîÕKN»Ó“=¼A¾%ª m*ïÚÃ(ã!$¿Ï0 YvbÃÌiïȶ–Õýý<tášd]2Ä?öîÿû†{gŸ„‰špiŠ]Ö´Œë‰q(܇&¿\çï ˜ï/­§þ9¼ÀW¼½{ÇåØF«CQœ°@VŸ·ôM¡]½'Ésr!9ÖÁƒr/oKÑ‹Õã©úïœãNóÿ‡°«Æ4X›Úhé°Û4fç®78M†œ9¼†ß7“Ö—ÖOªÏ8˜endstream
+endobj
+81 0 obj
+824
+endobj
+82 0 obj<</Type/Page/Parent 72 0 R/Contents 83 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F8 7 0 R>>>>>>endobj
+83 0 obj<</Length 84 0 R/Filter/FlateDecode>>stream
+xÚ+ä2T0
+áÒw³P04TIS041T072PIÑp rt QÐUp VÎO+)O,JUNM.-Ê,©TJ-È/*Ñ ÉâÒiÒ…ê2‰˜˜˜é™)€ù†
+ÁÉù© Q×®@.
+endobj
+84 0 obj
+118
+endobj
+85 0 obj<</Type/Page/Parent 72 0 R/Contents 86 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F8 7 0 R/F9 8 0 R/Fc 9 0 R>>>>>>endobj
+86 0 obj<</Length 87 0 R/Filter/FlateDecode>>stream
+xÚ­•OÚ0Åï|Š9²Ò&K 7
+l…T´) {ââÆp•ØÔ6Úòíkc•V%]å@Æ¿7ö¼?ZtÌÀ „nòªõ)k=½ÄÄæMЃŒ¶CX`yŽê!ûîJžýÈyÏ‘ïÊü
+f\£,HŽ0AÅÖÜÞrɶ糹G"Í­„ÿ³…~'aD+Æ™Òf/B*»—)ð¯·PÓ?¤õd~ H±–¤ª¬ w.n¡SÌw’é½±ïVHÝ€›%·¸ª¦I—˜ßRáÿŒímrûf¨ÖÛ¿Mîœ`ÏE§×)áUoPžb¬þ1Â\pï*Æ
+l ™áÙt:…  üÞc° ç›Ý ¡׶ŸU›JRèÕÃ]ç9K’§ÃÌG”2K$%¼nOèdxÆ^‚fì)ϵ=N!“„«» zbͪmyøÕ²ú¼c›qæ‚byh(ÅŠ˜#ÌÕ] ÅËØ<âGøÂ8º1X¬ŒM̵ÈÅÑÛÑqÑÑ{aßúŸéE?6²æU×~žf­¯­_Ð&Ùûendstream
+endobj
+87 0 obj
+522
+endobj
+88 0 obj<</Type/Page/Parent 72 0 R/Contents 89 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F8 7 0 R>>>>>>endobj
+89 0 obj<</Length 90 0 R/Filter/FlateDecode>>stream
+xÚ-‹Í1…÷÷)Î’E™[Í°5˜5Óë¤n3TE¼½©È9›óó=ˆQb,êâp£FhÞ®À ‰`ÇXÚ
+ržl»u+0Ø÷~ˆù}J
+¯á•.ùƒNïCÊS¹’)ùS®4Îò¬Æ/Ûñ5iôY¦Ð¾­u"§endstream
+endobj
+90 0 obj
+121
+endobj
+91 0 obj<</Type/Page/Parent 72 0 R/Contents 92 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+92 0 obj<</Length 93 0 R/Filter/FlateDecode>>stream
+xÚuRÛjÜ0}÷WÌcïnöòØ”
+¥´]úª•ÆY%²ÆÑÈMý÷‘³]Xƒå3ç6z®¸‘§Í–k°}u×V¾ì ÙAÛÉŸõf­{·„odM€Ö"3üòüÄWí£@WÐ43ôz¹«×
+ž¡f†&…‚IùH¬o“Ášü;òP ¼ø|¡ŒŒI9hŒ¹†öèmöÁ2DÊj@d›e½PYã\REÏ<
+ a0J œaï£)ãÔ‰ „D”a0Ì/”Ä@’Ó¤"còy:Ñ€¬/LÅŸŽÞÿúûBœLB€'ÎØ×s;Räm½=÷3—Y7°?ÉÜ%4öˆo·¹÷Ñb‘uØ™1d𑳠aNce>«OÁ]˧3‡ ùðYüg Ÿ¤7JÓ{ð2Ë0³WD'¡Ïe³6¥ÓÉTÙRÌ3kwCò1C烈P8Š¹©¬¸üCW¿R,Ösಽ!‘ˆö§ÝK
+endobj
+93 0 obj
+447
+endobj
+94 0 obj<</Type/Page/Parent 72 0 R/Contents 95 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F8 7 0 R>>>>>>endobj
+95 0 obj<</Length 96 0 R/Filter/FlateDecode>>stream
+xÚ+ä2T0
+áÒw³P04TIS041T072PIÑp rt QÐUp VÎO+)O,JUNM.-Ê,©TJ-È/*Ñ ÉâÒiÒ…ê2‰[ëìò|ò“s““S‹‹‚2‹³‹AJ\C¸¹
+endobj
+96 0 obj
+131
+endobj
+97 0 obj<</Type/Page/Parent 72 0 R/Contents 98 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+98 0 obj<</Length 99 0 R/Filter/FlateDecode>>stream
+xÚ}WKo7¾ûWÌÑ$Ù’ßí©nÀ@[¤¶‚öà Å¥$Æ\rCr½V~}¿!¹z¬í @‘¬È™ù3œ~?šÒþLézFçW$룻ùÑéç[šÞÒ|‰_®®/h^_Ѓª]Tô›”*zÐá9œÌ¿áìM§ùìøüvrŧËY‘Ïz>KÂ+Škø¿"’–ŠÔkcœŽª¢Nã×6’ ã¤0Ôå9‚k->ÚêÔyZzWã€ÏáÃ&DUO¸
+äžžOfœ{¾Ö‚’Q;K•S¬C€ªò\‹¡Å'¯Œà¤Ñ‘U±sþ™8¼öªÆðõÖ븙d„`ãrr³Ã˜™Lée5JuKzTþEKУ?aæOý ^BÛ¨<r#WºFàEÑï_¿<¦O
+ z½‚®Á-cgLè>2ÞÆ… ZöȲ-\ƒä!à¸<Ð;3Vâ
+© o­-CO%ªÆÌUiV„yPb3š ¹
+Cؤq\ÆO–œŠoï¢ÀŠæ& %†:±aðE™”¬HY;m÷l¸'_p4éj¾Êt øA ]©t>7rß` Ö«u6ÆVg‡®3àŠB+×
+<âhá¨$§o€SEØÚ‚éê|úF¿ûH«tHSѺd½Ì¢¸J'ø»0µ_!â@Ö-Åx)»†ûÉ¿ê›&dã¤É…–yÚ¤œ=@¹Ò¬†èº.Pž}Î}#†guPÐI ©[CK¶¦è"zFWÔ6š%\(>Õ‹ýV‹‹¢Å£²IŠFøÈóóþË*ž ¿Rh”ÔK §šÍVÙe¸Ù^/Z$E­J͹´]Óàp¯YŸ7zaC—*ô¯Ó^?n‡ŸtUjÍ}+c?ª|B‹ÈËÎß+˜€^€ñ1¾—©bWäÚ5ù€\ny.-ØïK<{YãÌð–ÏËŸéÞ©qL»‚¾¹EÈ™²GFý@äÓŽ«L¯zÈc6ê›èíÌJîã±ÍcYHmàj• ~gxe {e<3sù>+ïðñ_âõ!óø¨¨ÃGúxÇÀÓÉÇSœ¥‡0ûÌè¶'®>•Úµ¡€e-sa˜t@:`¥ø]ÙëÖb3©àûaò:p9·XAº·å¦Q§Øû®®!þéÌI)§_¿ ȳuMÏçé;5 Qr¥X”8—>6Àlú0,Ñ;ïUªbU
+û›²ý·%áÍÎ4Æ^¹·5Í°+åíŠîàu¹VoKûݲ¿9‹Ç2´ Í@w"hI¢å¦Œhä4ýÒ¬eVÔªˆ B
+<¦=¨ø7þ€ˆÆ™”Bz<Ð]½Æ²~lƒÄ Á =j~·RѸc}ýûþ¿ƒëô}°~íÕ× P^ ÷~ßm>H wÆžJÌácöt\ž• +ø‡V+µ­ <ôÈ2õïͳJ-öÉ ¶ß»rQå™
+ýÊ.*ì VM?Oè_Üv©—vVNý‘ÏgcÜìŒ1»ÎÆøðG.nÎ&·8‰3×üïOó£ŽþúYU»endstream
+endobj
+99 0 obj
+1404
+endobj
+100 0 obj<</Type/Page/Parent 72 0 R/Contents 101 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F8 7 0 R>>>>>>endobj
+101 0 obj<</Length 102 0 R/Filter/FlateDecode>>stream
+xÚ]Q]o›0}çWœÇU4NXš=®Ëò6ikØ0æÜÍ|ífÙ¯ßuˆZ©Âºæ|ÝßBa%Âf›™ŠÇ¦¸?ÔP
+M/_¶»ºÚ¡é>èn²Îr :ZïÐÈév¤½ÐãˆÁsäò:Ó ØŸ0è‚F ‰£€ų¿!N­LÐÆ3ŒÎ¢wÍs±B©6Õ:›Ck2Q`>tÖÆKuC­·UQÍ@¢û7BŠn ‘4|¯¿~q¶ŽÓ<û±·'Ñ„Nq ­Y¶¨ÌÈ70I}"´¾» ±x‚iÖ²ùû|ß÷ŸÊV¼:Qpz’U]‡Y3Ë–R‰+4ƒå%Æ|$ßÖI}Ó’âJ Rž‹H.GôÁþ£îé­°.¢‡ñ“ˆN–o%÷¦Îצî»ÛïTµ‚Z©EfÿôåР\::ú>žµì~$“‚<Qî+óËL+Ö«ÌÚå›ÍÃgiþ:×"öÊy ¤Í@œ1ßšâgñ)Åõendstream
+endobj
+102 0 obj
+381
+endobj
+103 0 obj<</Type/Page/Parent 72 0 R/Contents 104 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F6 6 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+104 0 obj<</Length 105 0 R/Filter/FlateDecode>>stream
+xÚT]o›0}çWÜÇíZÒ4…Ç %i¤ªc‚J}uÍ õâf;‹òïwZ©ÝŠ¥N „uçžã{ð¯$‡Kºr¸™ÁÕ¸J¾µÉź„¼„vG•ÅÍÚîË6Ò8Çìékûs\gÅI¯‹le9´h•1 Èó€ Þt¶!ÕXšPZ7j8x´ ™î¬Çì=Qú>0ˤD9MØ î„îÁX°ÈQü‹ŽyÊXÿÌ4Ýà…Š7ÆÔé‘‚ uЂ3/ŒvÀ©F OèˆüÑÀ`Mo™r±v­`ÿåîžœá{ô1W$÷ìJ“|c÷S‘RžÒ«ò53Xrkô)eSm·)Š6„ÓÈÏtÇl•évä«é©ÎŠèTõñ]=ÔÍt«Š<ÕÃýöj+´[ÛœœGá\5ÕE=Mºª›ï÷ˆ?7@ë"Ìë6»¡µL»6ã 72Âv[§›»i¾[<Jô>­ß¡ˆgÁܽü”ã§Å+uô¼  ÿsJ̋ˬ¤ó
+eX¯ÚäGòE8%endstream
+endobj
+105 0 obj
+426
+endobj
+106 0 obj<</Type/Page/Parent 72 0 R/Contents 107 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F6 6 0 R/F8 7 0 R>>>>>>endobj
+107 0 obj<</Length 108 0 R/Filter/FlateDecode>>stream
+xÚ’MSÂ0†ïý{ÔC+ÅRË‘v„¡C¤ñÄj¤$˜½IU“œö}v÷Ý}õBè˜Âml?ÝxCìÝd1„!à• ÅI$€—W÷ÈG£Ù5~1ztÖýnDÎÓÚG„®‰ZR\h¦`$…V²‚厔ì“ð]Á¿@˜Ó”˜Ê§ßà')ÎÚá‹LC*J.3ì0©×IE\„~mšµL¤¤–TV.b1ÿ‰H4—‚TPh"–ÆŒæª$‚¿7‚ƒ=CãvöÌL~²uLØƉÊ'yÚÎÊw•æ¶äN&ä„W¾Ñg³-—¥ÎKCfÍ9+äšÉZTñ­>ÏÍê&àö±È‡íÔ‚©½!嬮m³ÃJÒµƒ…3ì8!¬øž›Ug¼b€õÊ°/Ï(ùÊ‹ûÐKúAlóÆ‹A†Á‡Ñ#* +} ŠAÁèNq}„ÛJ¥mºF!øwÝŽÍ
+;6…IЃ&0º0 JŠã¦¶RŠ½ïõG%¿endstream
+endobj
+108 0 obj
+383
+endobj
+109 0 obj<</Type/Page/Parent 72 0 R/Contents 110 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>/Annots 53 0 R>>endobj
+110 0 obj<</Length 111 0 R/Filter/FlateDecode>>stream
+xÚÕ˜ÍnÔ0Çïû>Â4þNŽ…R„„ì.°/,4ÈPߌ»jìܶRkâßÄ3žÿ8ýµÁ¨ÔI‚¨@M»y¹ß\ÝÖ×hDXЂ !¹¾î¿<Û>ß)ÔÑ«î<¨ópy¾ÿ¾y½ß”E©)æ‚ÍeûÆ  •žÖ"su÷whgøï¿¢«[Ž06Í´ÚyÃh?![DyQÅ|gÊ®é~*š(%x%\»Ó«ÿáÙHPa"M¸¾¶ˆ“‚à j¡ÖùDØH‰XÐVÏÄ&Ú¡ °[ÐÛ/zWœŽ§æ0œºóãQç²kŠ{.¶vÅ>¶v0[QLô6¦$S W²1Ì@ÚÝ_Õ&Šaš¯DñøÒ¾œhïÿ¨þÏIý}<9 õ*’“É
+¯+½(ÈŠ¤²Â뺀N3Yñd\šªˆÐ`ÔM×ünõvŸç… d¬`1Ð?
+”­:ª^u™‰c½.ýyäG3›_G`”{7X&÷\Øu“¬Ü{•ȃ3;ΫOv‰”Msõæç¥IZHrvGòµ”hB<mÒr9a|jn$&œ“l›ðäªÔÚ‘Á ¤÷Ã7Õ'Ò1M7]¢4ZÂÀ0Ÿù"«¨!š$\ëe‚ Ùûe‚DLtôâhF
+§¶Y¹œÙAêàÄ8sÍ‹eNžl•4"ƒH µÚã ê!ìÀûO´Z¦µšHwp/õ×’,kâ”1õU<mÖIŒ³¥¼¹ë.—C?ɺÀëÿ^Ùþ˜ºïf¶,JÌ  %…rÂj¡¤£C_Î ¤½êÛ¹$×Ö€–Æ·,]#²+µƒ¤¼”Ä4)/ÌTˆéßìÕôÝù~.²Œ­ï¤mczu[ÁncX—$Å•“·›íõí½@öëׇ¿‡^¡QD·êg×@Õx!‰]Ý ¢ùqóã×ï<endstream
+endobj
+111 0 obj
+1027
+endobj
+112 0 obj<</Type/Page/Parent 72 0 R/Contents 113 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F8 7 0 R>>>>>>endobj
+113 0 obj<</Length 114 0 R/Filter/FlateDecode>>stream
+xÚ+ä2T0
+áÒw³P04TIS041T072PIÑp rt QÐUp VÎO+)O,JUNM.-Ê,©TJ-È/*Ñ ÉâÒiÒ…êÊÌ ¹†pr
+endobj
+114 0 obj
+103
+endobj
+115 0 obj<</Count 6/First 116 0 R/Last 129 0 R>>endobj
+116 0 obj<</Parent 115 0 R/Title(Table of Contents)/Dest[109 0 R/XYZ null 756 null]/Next 117 0 R>>endobj
+117 0 obj<</Parent 115 0 R/Count -3/First 118 0 R/Last 120 0 R/Title(1 Scope)/Dest[79 0 R/XYZ null 743 null]/Prev 116 0 R/Next 121 0 R>>endobj
+118 0 obj<</Parent 117 0 R/Title(1.1 Identification)/Dest[79 0 R/XYZ null 693 null]/Next 119 0 R>>endobj
+119 0 obj<</Parent 117 0 R/Title(1.2 System Overview)/Dest[79 0 R/XYZ null 613 null]/Prev 118 0 R/Next 120 0 R>>endobj
+120 0 obj<</Parent 117 0 R/Title(1.3 Document Overview)/Dest[79 0 R/XYZ null 389 null]/Prev 119 0 R>>endobj
+121 0 obj<</Parent 115 0 R/Count -2/First 122 0 R/Last 123 0 R/Title(2 References)/Dest[85 0 R/XYZ null 743 null]/Prev 117 0 R/Next 124 0 R>>endobj
+122 0 obj<</Parent 121 0 R/Title(2.1 CUPS Documentation)/Dest[85 0 R/XYZ null 693 null]/Next 123 0 R>>endobj
+123 0 obj<</Parent 121 0 R/Title(2.2 Other Documents)/Dest[85 0 R/XYZ null 495 null]/Prev 122 0 R>>endobj
+124 0 obj<</Parent 115 0 R/Count -1/First 125 0 R/Last 125 0 R/Title(3 Local Access Risks)/Dest[91 0 R/XYZ null 743 null]/Prev 121 0 R/Next 126 0 R>>endobj
+125 0 obj<</Parent 124 0 R/Title(3.1 Security Breaches)/Dest[91 0 R/XYZ null 646 null]>>endobj
+126 0 obj<</Parent 115 0 R/Count -2/First 127 0 R/Last 128 0 R/Title(4 Remote Access Risks)/Dest[97 0 R/XYZ null 743 null]/Prev 124 0 R/Next 129 0 R>>endobj
+127 0 obj<</Parent 126 0 R/Title(4.1 Denial of Service Attacks)/Dest[97 0 R/XYZ null 659 null]/Next 128 0 R>>endobj
+128 0 obj<</Parent 126 0 R/Title(4.2 Security Breaches)/Dest[97 0 R/XYZ null 210 null]/Prev 127 0 R>>endobj
+129 0 obj<</Parent 115 0 R/Count -2/First 130 0 R/Last 131 0 R/Title(A Glossary)/Dest[103 0 R/XYZ null 743 null]/Prev 126 0 R>>endobj
+130 0 obj<</Parent 129 0 R/Title(A.1 Terms)/Dest[103 0 R/XYZ null 693 null]/Next 131 0 R>>endobj
+131 0 obj<</Parent 129 0 R/Title(A.2 Acronyms)/Dest[103 0 R/XYZ null 389 null]/Prev 130 0 R>>endobj
+132 0 obj<</Type/Catalog/Pages 72 0 R/Names 54 0 R/ViewerPreferences<</PageLayout/TwoColumnRight>>/Outlines 115 0 R/PageMode/UseOutlines/OpenAction[79 0 R/XYZ null null null]>>endobj
+xref
+0 133
+0000000000 65535 f
+0000000015 00000 n
+0000000221 00000 n
+0000000282 00000 n
+0000000356 00000 n
+0000000434 00000 n
+0000000511 00000 n
+0000000590 00000 n
+0000000666 00000 n
+0000000747 00000 n
+0000000805 00000 n
+0000000907 00000 n
+0000001010 00000 n
+0000001114 00000 n
+0000001218 00000 n
+0000001322 00000 n
+0000001426 00000 n
+0000001530 00000 n
+0000001634 00000 n
+0000001738 00000 n
+0000001842 00000 n
+0000001944 00000 n
+0000002047 00000 n
+0000002151 00000 n
+0000002255 00000 n
+0000002359 00000 n
+0000002463 00000 n
+0000002567 00000 n
+0000002671 00000 n
+0000002773 00000 n
+0000002876 00000 n
+0000002980 00000 n
+0000003084 00000 n
+0000003188 00000 n
+0000003292 00000 n
+0000003396 00000 n
+0000003498 00000 n
+0000003601 00000 n
+0000003705 00000 n
+0000003809 00000 n
+0000003913 00000 n
+0000004017 00000 n
+0000004121 00000 n
+0000004225 00000 n
+0000004329 00000 n
+0000004433 00000 n
+0000004537 00000 n
+0000004641 00000 n
+0000004744 00000 n
+0000004848 00000 n
+0000004953 00000 n
+0000005058 00000 n
+0000005163 00000 n
+0000005268 00000 n
+0000005586 00000 n
+0000005618 00000 n
+0000005650 00000 n
+0000005849 00000 n
+0000005896 00000 n
+0000005943 00000 n
+0000005990 00000 n
+0000006037 00000 n
+0000006084 00000 n
+0000006131 00000 n
+0000006178 00000 n
+0000006225 00000 n
+0000006272 00000 n
+0000006319 00000 n
+0000006366 00000 n
+0000006413 00000 n
+0000006461 00000 n
+0000006509 00000 n
+0000006557 00000 n
+0000006728 00000 n
+0000006877 00000 n
+0000013254 00000 n
+0000013275 00000 n
+0000013385 00000 n
+0000013485 00000 n
+0000013504 00000 n
+0000013641 00000 n
+0000014534 00000 n
+0000014554 00000 n
+0000014664 00000 n
+0000014851 00000 n
+0000014871 00000 n
+0000015008 00000 n
+0000015599 00000 n
+0000015619 00000 n
+0000015729 00000 n
+0000015919 00000 n
+0000015939 00000 n
+0000016067 00000 n
+0000016583 00000 n
+0000016603 00000 n
+0000016713 00000 n
+0000016913 00000 n
+0000016933 00000 n
+0000017070 00000 n
+0000018543 00000 n
+0000018564 00000 n
+0000018685 00000 n
+0000019137 00000 n
+0000019158 00000 n
+0000019297 00000 n
+0000019794 00000 n
+0000019815 00000 n
+0000019945 00000 n
+0000020399 00000 n
+0000020420 00000 n
+0000020573 00000 n
+0000021671 00000 n
+0000021693 00000 n
+0000021805 00000 n
+0000021979 00000 n
+0000022000 00000 n
+0000022055 00000 n
+0000022160 00000 n
+0000022303 00000 n
+0000022408 00000 n
+0000022527 00000 n
+0000022635 00000 n
+0000022783 00000 n
+0000022892 00000 n
+0000022998 00000 n
+0000023154 00000 n
+0000023249 00000 n
+0000023406 00000 n
+0000023522 00000 n
+0000023630 00000 n
+0000023764 00000 n
+0000023861 00000 n
+0000023961 00000 n
+trailer
+<</Size 133/Root 132 0 R/Info 1 0 R>>
+startxref
+24144
+%%EOF
diff --git a/doc/ssr.shtml b/doc/ssr.shtml
new file mode 100644
index 000000000..91ece988e
--- /dev/null
+++ b/doc/ssr.shtml
@@ -0,0 +1,252 @@
+<HTML>
+<HEAD>
+ <META NAME="COPYRIGHT" CONTENT="Copyright 1997-1999, All Rights Reserved">
+ <META NAME="DOCNUMBER" CONTENT="CUPS-SSR-1.0">
+ <META NAME="Author" CONTENT="Easy Software Products">
+ <TITLE>DRAFT - CUPS Software Security Report</TITLE>
+</HEAD>
+<BODY>
+
+<H1>Scope</H1>
+
+<H2>Identification</H2>
+
+This software security report provides an analysis of possible security
+concerns for the Common UNIX Printing System ("CUPS") Version 1.0.</P>
+
+<H2>System Overview</H2>
+
+The Common UNIX Printing System provides a portable printing layer for
+UNIX&reg; operating systems. It has been developed by Easy Software
+Products to promote a standard printing solution for all UNIX vendors
+and users. CUPS provides the System V and Berkeley command-line interfaces.
+
+<P>CUPS uses the Internet Printing Protocol (IETF-IPP) as the basis
+for managing print jobs and queues. The Line Printer Daemon (LPD,
+RFC1179), Server Message Block (SMB), and AppSocket protocols are also
+supported with reduced functionality.
+
+<P>CUPS adds network printer browsing and PostScript Printer
+Description (&quot;PPD&quot;)-based printing options to support real
+world applications under UNIX.
+
+<P>CUPS also includes a customized version of GNU GhostScript
+(currently based off GNU GhostScript 4.03) and an image file RIP that
+can be used to support non-PostScript printers.
+
+<H2>Document Overview</H2>
+
+<P>This software security report is organized into the following sections:</P>
+
+<UL>
+ <LI>1 - Scope</LI>
+ <LI>2 - References</LI>
+ <LI>3 - Local Access Risks</LI>
+ <LI>4 - Remote Access Risks</LI>
+ <LI>A - Glossary</LI>
+</UL>
+
+<H1>References</H1>
+
+<H2>CUPS Documentation</H2>
+
+The following CUPS documentation is referenced by this document:
+
+<UL>
+ <LI>CUPS-CMP-1.0: CUPS Configuration Management Plan
+ <LI>CUPS-IDD-1.0: CUPS System Interface Design Description
+ <LI>CUPS-SAM-1.0.x: CUPS Software Administrators Manual
+ <LI>CUPS-SDD-1.0: CUPS Software Design Description
+ <LI>CUPS-SPM-1.0: CUPS Software Programming Manual
+ <LI>CUPS-SSR-1.0: CUPS Software Security Report
+ <LI>CUPS-STP-1.0: CUPS Software Test Plan
+ <LI>CUPS-SUM-1.0.x: CUPS Software Users Manual
+ <LI>CUPS-SVD-1.0.x: CUPS Software Version Description
+</UL>
+
+<H2>Other Documents</H2>
+
+The following non-CUPS documents are referenced by this document:
+
+<UL>
+ <LI>IEEE 1387.4, System Administration: Printing (draft)
+ <LI>IPP/1.0: Additional Optional Operations - Set 1
+ <LI>IPP/1.0: Encoding and Transport
+ <LI>IPP/1.0: Implementers Guide
+ <LI>IPP/1.0: Model and Semantics
+ <LI>RFC 1179, Line Printer Daemon Protocol
+</UL>
+
+<H1>Local Access Risks</H1>
+
+<P>Local access risks are those that can be exploited only with a local user
+account. This section does not address issues related to dissemination of the
+root password or other security issues associated with the UNIX operating
+system.
+
+<H2>Security Breaches</H2>
+
+<P>Since the default installation creates a world-readable request directory,
+it is possible for local users to read the contents of print files before they
+are printed.
+
+<P>This problem can be alleviated by making the request directory readable only
+by the user specified in the CUPS configuration file.
+
+<H1>Remote Access Risks</H1>
+
+<P>Remote access risks are those that can be exploited without a local user
+account and/or from a remote system. This section does not address issues
+related to network or firewall security.
+
+<H2>Denial of Service Attacks</H2>
+
+<P>Like all internet services, the CUPS server is vulnerable to denial of
+service attacks, including:
+
+<OL>
+
+ <LI>Establishing multiple connections to the server until the server
+ will accept no more.
+
+ <P>This cannot be protected against by the current software. It
+ is possible that future versions of the CUPS software could be
+ configured to limit the number of connections allowed from a
+ single host, however that still would not prevent a determined
+ attack.
+
+ <LI>Repeatedly opening and closing connections to the server as fast
+ as possible.
+
+ <P>There is no easy way of protecting against this in the CUPS
+ software. If the attack is coming from outside the local
+ network it might be possible to filter such an attack, however
+ once the connection request has been received by the server it
+ must at least accept the connection to find out who is
+ connecting.
+
+ <LI>Flooding the network with broadcast packets on port 631.
+
+ <P>It might be possible to disable browsing if this condition
+ is detected by the CUPS software, however if there are large
+ numbers of printers available on the network such an algorithm
+ might think that an attack was occurring when instead a valid
+ update was being received.
+
+ <LI>Sending partial IPP requests; specifically, sending part of an
+ attribute value and then stopping transmission.
+
+ <P>The current code is structured to read and write the IPP
+ request data on-the-fly, so there is no easy way to protect
+ against this for large attribute values.
+
+ <LI>Sending large/long print jobs to printers, preventing other users
+ from printing.
+
+ <P>There are limited facilities for protecting against large print
+ jobs (the <CODE>MaxRequestSize</CODE> attribute), however this will
+ not protect printers from malicious users and print files that
+ generate hundreds or thousands of pages. In general, we recommend
+ restricting printer access to known hosts or networks, and adding
+ user-level access control as needed for expensive printers.
+
+</OL>
+
+<H2>Security Breaches</H2>
+
+<P>The current CUPS server only supports Basic authentication with
+usernames and passwords. This essentially places the clear text of the
+username and password on the network. Since CUPS uses the UNIX username
+and password account information, the authentication information could
+be used to gain access to accounts (possibly priviledged accounts) on
+the server.
+
+<P>The default CUPS configuration disables remote administration. We do
+not recommend that remote administration be enabled for all hosts,
+however if you have a trusted network or subnet access can be
+restricted accordingly.
+
+<P>The next minor release of CUPS will support Digest authentication of
+the entire message body using separate MD5-based username and password
+files. This will protect password information and prevent unauthorized
+access due to compromised account passwords.
+
+<H1 TYPE=A VALUE=1>Glossary</H1>
+
+<H2>Terms</H2>
+
+<DL>
+
+ <DT>C
+ <DD>A computer language.
+
+ <DT>parallel
+ <DD>Sending or receiving data more than 1 bit at a time.
+
+ <DT>pipe
+ <DD>A one-way communications channel between two programs.
+
+ <DT>serial
+ <DD>Sending or receiving data 1 bit at a time.
+
+ <DT>socket
+ <DD>A two-way network communications channel.
+
+</DL>
+
+<H2>Acronyms</H2>
+
+<DL>
+
+ <DT>ASCII
+ <DD>American Standard Code for Information Interchange
+
+ <DT>CUPS
+ <DD>Common UNIX Printing System
+
+ <DT>ESC/P
+ <DD>EPSON Standard Code for Printers
+
+ <DT>FTP
+ <DD>File Transfer Protocol
+
+ <DT>HP-GL
+ <DD>Hewlett-Packard Graphics Language
+
+ <DT>HP-PCL
+ <DD>Hewlett-Packard Printer Control Language
+
+ <DT>HP-PJL
+ <DD>Hewlett-Packard Printer Job Language
+
+ <DT>IETF
+ <DD>Internet Engineering Task Force
+
+ <DT>IPP
+ <DD>Internet Printing Protocol
+
+ <DT>ISO
+ <DD>International Standards Organization
+
+ <DT>LPD
+ <DD>Line Printer Daemon
+
+ <DT>MIME
+ <DD>Multimedia Internet Mail Exchange
+
+ <DT>PCL
+ <DD>Page Control Language
+
+ <DT>PPD
+ <DD>PostScript Printer Description
+
+ <DT>SMB
+ <DD>Server Message Block
+
+ <DT>TFTP
+ <DD>Trivial File Transfer Protocol
+
+</DL>
+
+</BODY>
+</HTML>
diff --git a/doc/stp.html b/doc/stp.html
new file mode 100644
index 000000000..ec2a16f92
--- /dev/null
+++ b/doc/stp.html
@@ -0,0 +1,145 @@
+<HTML>
+<HEAD>
+<TITLE>DRAFT - CUPS Software Test Plan</TITLE>
+<META NAME="AUTHOR" CONTENT="Easy Software Products">
+<META NAME="COPYRIGHT" CONTENT="Copyright 1997-1999, All Rights Reserved">
+<META NAME="DOCNUMBER" CONTENT="CUPS-STP-1.0">
+</HEAD>
+<BODY>
+<CENTER><A HREF=#contents><IMG SRC="images/cups-large.gif" BORDER=0><BR>
+<H1>DRAFT - CUPS Software Test Plan</H1></A><BR>
+CUPS-STP-1.0<BR>
+Easy Software Products<BR>
+Copyright 1997-1999, All Rights Reserved<BR>
+</CENTER>
+<HR>
+<H1 ALIGN=CENTER><A NAME=CONTENTS>Table of Contents</A></H1>
+<BR>
+<BR><B><A HREF=#1>1 Scope</A></B>
+<UL>
+<LI><A HREF=#1_1>1.1 Identification</A></LI>
+<LI><A HREF=#1_2>1.2 System Overview</A></LI>
+<LI><A HREF=#1_3>1.3 Document Overview</A></LI>
+</UL>
+<B><A HREF=#2>2 References</A></B>
+<UL>
+<LI><A HREF=#2_1>2.1 CUPS Documentation</A></LI>
+<LI><A HREF=#2_2>2.2 Other Documents</A></LI>
+</UL>
+<B><A HREF=#3>3 Local Tests</A></B>
+<BR>
+<BR><B><A HREF=#4>4 Remote Tests</A></B>
+<BR>
+<BR><B><A HREF=#5>A Glossary</A></B>
+<UL>
+<LI><A HREF=#5_1>A.1 Terms</A></LI>
+<LI><A HREF=#5_2>A.2 Acronyms</A></LI>
+</UL>
+<HR>
+<H1><A NAME=1>1 Scope</A></H1>
+<H2><A NAME=1_1>1.1 Identification</A></H2>
+ This software test plan provides detailed tests that are used to
+evaluate the stability of the Common UNIX Printing System (&quot;CUPS&quot;)
+Version 1.0.
+<H2><A NAME=1_2>1.2 System Overview</A></H2>
+ The Common UNIX Printing System provides a portable printing layer for
+ UNIX&reg; operating systems. It has been developed by Easy Software
+ Products to promote a standard printing solution for all UNIX vendors
+ and users. CUPS provides the System V and Berkeley command-line
+interfaces.
+<P>CUPS uses the Internet Printing Protocol (IETF-IPP) as the basis for
+managing print jobs and queues. The Line Printer Daemon (LPD, RFC1179),
+Server Message Block (SMB), and AppSocket protocols are also supported
+with reduced functionality. </P>
+<P>CUPS adds network printer browsing and PostScript Printer
+Description (&quot;PPD&quot;)-based printing options to support real world
+applications under UNIX. </P>
+<P>CUPS also includes a customized version of GNU GhostScript
+(currently based off GNU GhostScript 4.03) and an image file RIP that
+can be used to support non-PostScript printers. </P>
+<H2><A NAME=1_3>1.3 Document Overview</A></H2>
+ This software test plan is organized into the following sections:
+<UL>
+<LI>1 - Scope</LI>
+<LI>2 - References</LI>
+<LI>3 - Local Tests</LI>
+<LI>4 - Remote Tests</LI>
+<LI>A - Glossary</LI>
+</UL>
+<H1><A NAME=2>2 References</A></H1>
+<H2><A NAME=2_1>2.1 CUPS Documentation</A></H2>
+ The following CUPS documentation is referenced by this document:
+<UL>
+<LI>CUPS-CMP-1.0: CUPS Configuration Management Plan </LI>
+<LI>CUPS-IDD-1.0: CUPS System Interface Design Description </LI>
+<LI>CUPS-SAM-1.0.x: CUPS Software Administrators Manual </LI>
+<LI>CUPS-SDD-1.0: CUPS Software Design Description </LI>
+<LI>CUPS-SPM-1.0: CUPS Software Programming Manual </LI>
+<LI>CUPS-SSR-1.0: CUPS Software Security Report </LI>
+<LI>CUPS-STP-1.0: CUPS Software Test Plan </LI>
+<LI>CUPS-SUM-1.0.x: CUPS Software Users Manual </LI>
+<LI>CUPS-SVD-1.0.x: CUPS Software Version Description </LI>
+</UL>
+<H2><A NAME=2_2>2.2 Other Documents</A></H2>
+ The following non-CUPS documents are referenced by this document:
+<UL>
+<LI>IEEE 1387.4, System Administration: Printing (draft) </LI>
+<LI>IPP/1.0: Additional Optional Operations - Set 1 </LI>
+<LI>IPP/1.0: Encoding and Transport </LI>
+<LI>IPP/1.0: Implementers Guide </LI>
+<LI>IPP/1.0: Model and Semantics </LI>
+<LI>RFC 1179, Line Printer Daemon Protocol </LI>
+</UL>
+<H1><A NAME=3>3 Local Tests</A></H1>
+<H1><A NAME=4>4 Remote Tests</A></H1>
+<H1 TYPE=A VALUE=1><A NAME=5>A Glossary</A></H1>
+<H2><A NAME=5_1>A.1 Terms</A></H2>
+<DL>
+<DT>C </DT>
+<DD>A computer language. </DD>
+<DT>parallel </DT>
+<DD>Sending or receiving data more than 1 bit at a time. </DD>
+<DT>pipe </DT>
+<DD>A one-way communications channel between two programs. </DD>
+<DT>serial </DT>
+<DD>Sending or receiving data 1 bit at a time. </DD>
+<DT>socket </DT>
+<DD>A two-way network communications channel. </DD>
+</DL>
+<H2><A NAME=5_2>A.2 Acronyms</A></H2>
+<DL>
+<DT>ASCII </DT>
+<DD>American Standard Code for Information Interchange </DD>
+<DT>CUPS </DT>
+<DD>Common UNIX Printing System </DD>
+<DT>ESC/P </DT>
+<DD>EPSON Standard Code for Printers </DD>
+<DT>FTP </DT>
+<DD>File Transfer Protocol </DD>
+<DT>HP-GL </DT>
+<DD>Hewlett-Packard Graphics Language </DD>
+<DT>HP-PCL </DT>
+<DD>Hewlett-Packard Printer Control Language </DD>
+<DT>HP-PJL </DT>
+<DD>Hewlett-Packard Printer Job Language </DD>
+<DT>IETF </DT>
+<DD>Internet Engineering Task Force </DD>
+<DT>IPP </DT>
+<DD>Internet Printing Protocol </DD>
+<DT>ISO </DT>
+<DD>International Standards Organization </DD>
+<DT>LPD </DT>
+<DD>Line Printer Daemon </DD>
+<DT>MIME </DT>
+<DD>Multimedia Internet Mail Exchange </DD>
+<DT>PCL </DT>
+<DD>Page Control Language </DD>
+<DT>PPD </DT>
+<DD>PostScript Printer Description </DD>
+<DT>SMB </DT>
+<DD>Server Message Block </DD>
+<DT>TFTP </DT>
+<DD>Trivial File Transfer Protocol </DD>
+</DL>
+</BODY>
+</HTML>
diff --git a/doc/stp.pdf b/doc/stp.pdf
new file mode 100644
index 000000000..7d51055f8
--- /dev/null
+++ b/doc/stp.pdf
Binary files differ
diff --git a/doc/stp.shtml b/doc/stp.shtml
new file mode 100644
index 000000000..511764e3d
--- /dev/null
+++ b/doc/stp.shtml
@@ -0,0 +1,169 @@
+<HTML>
+<HEAD>
+ <META NAME="Description" CONTENT="Common UNIX Printing System Software Test Plan">
+ <META NAME="COPYRIGHT" CONTENT="Copyright 1997-1999, All Rights Reserved">
+ <META NAME="DOCNUMBER" CONTENT="CUPS-STP-1.0">
+ <META NAME="Author" CONTENT="Easy Software Products">
+ <TITLE>DRAFT - CUPS Software Test Plan</TITLE>
+</HEAD>
+<BODY>
+
+<H1>Scope</H1>
+
+<H2>Identification</H2>
+
+This software test plan provides detailed tests that are used to evaluate
+the stability of the Common UNIX Printing System ("CUPS") Version 1.0.
+
+<H2>System Overview</H2>
+
+The Common UNIX Printing System provides a portable printing layer for
+UNIX&reg; operating systems. It has been developed by Easy Software
+Products to promote a standard printing solution for all UNIX vendors
+and users. CUPS provides the System V and Berkeley command-line interfaces.
+
+<P>CUPS uses the Internet Printing Protocol (IETF-IPP) as the basis
+for managing print jobs and queues. The Line Printer Daemon (LPD,
+RFC1179), Server Message Block (SMB), and AppSocket protocols are also
+supported with reduced functionality.
+
+<P>CUPS adds network printer browsing and PostScript Printer
+Description (&quot;PPD&quot;)-based printing options to support real
+world applications under UNIX.
+
+<P>CUPS also includes a customized version of GNU GhostScript
+(currently based off GNU GhostScript 4.03) and an image file RIP that
+can be used to support non-PostScript printers.
+
+<H2>Document Overview</H2>
+
+This software test plan is organized into the following sections:
+
+<UL>
+ <LI>1 - Scope</LI>
+ <LI>2 - References</LI>
+ <LI>3 - Local Tests</LI>
+ <LI>4 - Remote Tests</LI>
+ <LI>A - Glossary</LI>
+</UL>
+
+<H1>References</H1>
+
+<H2>CUPS Documentation</H2>
+
+The following CUPS documentation is referenced by this document:
+
+<UL>
+ <LI>CUPS-CMP-1.0: CUPS Configuration Management Plan
+ <LI>CUPS-IDD-1.0: CUPS System Interface Design Description
+ <LI>CUPS-SAM-1.0.x: CUPS Software Administrators Manual
+ <LI>CUPS-SDD-1.0: CUPS Software Design Description
+ <LI>CUPS-SPM-1.0: CUPS Software Programming Manual
+ <LI>CUPS-SSR-1.0: CUPS Software Security Report
+ <LI>CUPS-STP-1.0: CUPS Software Test Plan
+ <LI>CUPS-SUM-1.0.x: CUPS Software Users Manual
+ <LI>CUPS-SVD-1.0.x: CUPS Software Version Description
+</UL>
+
+<H2>Other Documents</H2>
+
+The following non-CUPS documents are referenced by this document:
+
+<UL>
+ <LI>IEEE 1387.4, System Administration: Printing (draft)
+ <LI>IPP/1.0: Additional Optional Operations - Set 1
+ <LI>IPP/1.0: Encoding and Transport
+ <LI>IPP/1.0: Implementers Guide
+ <LI>IPP/1.0: Model and Semantics
+ <LI>RFC 1179, Line Printer Daemon Protocol
+</UL>
+
+<H1>Local Tests</H1>
+
+
+
+
+<H1>Remote Tests</H1>
+
+
+
+
+<H1 TYPE=A VALUE=1>Glossary</H1>
+
+<H2>Terms</H2>
+
+<DL>
+
+ <DT>C
+ <DD>A computer language.
+
+ <DT>parallel
+ <DD>Sending or receiving data more than 1 bit at a time.
+
+ <DT>pipe
+ <DD>A one-way communications channel between two programs.
+
+ <DT>serial
+ <DD>Sending or receiving data 1 bit at a time.
+
+ <DT>socket
+ <DD>A two-way network communications channel.
+
+</DL>
+
+<H2>Acronyms</H2>
+
+<DL>
+
+ <DT>ASCII
+ <DD>American Standard Code for Information Interchange
+
+ <DT>CUPS
+ <DD>Common UNIX Printing System
+
+ <DT>ESC/P
+ <DD>EPSON Standard Code for Printers
+
+ <DT>FTP
+ <DD>File Transfer Protocol
+
+ <DT>HP-GL
+ <DD>Hewlett-Packard Graphics Language
+
+ <DT>HP-PCL
+ <DD>Hewlett-Packard Printer Control Language
+
+ <DT>HP-PJL
+ <DD>Hewlett-Packard Printer Job Language
+
+ <DT>IETF
+ <DD>Internet Engineering Task Force
+
+ <DT>IPP
+ <DD>Internet Printing Protocol
+
+ <DT>ISO
+ <DD>International Standards Organization
+
+ <DT>LPD
+ <DD>Line Printer Daemon
+
+ <DT>MIME
+ <DD>Multimedia Internet Mail Exchange
+
+ <DT>PCL
+ <DD>Page Control Language
+
+ <DT>PPD
+ <DD>PostScript Printer Description
+
+ <DT>SMB
+ <DD>Server Message Block
+
+ <DT>TFTP
+ <DD>Trivial File Transfer Protocol
+
+</DL>
+
+</BODY>
+</HTML>
diff --git a/doc/sum.html b/doc/sum.html
new file mode 100644
index 000000000..21f2d2481
--- /dev/null
+++ b/doc/sum.html
@@ -0,0 +1,511 @@
+<HTML>
+<HEAD>
+<TITLE>DRAFT - CUPS Software Users Manual</TITLE>
+<META NAME="AUTHOR" CONTENT="Easy Software Products">
+<META NAME="COPYRIGHT" CONTENT="Copyright 1997-1999, All Rights Reserved">
+<META NAME="DOCNUMBER" CONTENT="CUPS-SUM-1.0.0b1">
+</HEAD>
+<BODY>
+<CENTER><A HREF=#contents><IMG SRC="images/cups-large.gif" BORDER=0><BR>
+<H1>DRAFT - CUPS Software Users Manual</H1></A><BR>
+CUPS-SUM-1.0.0b1<BR>
+Easy Software Products<BR>
+Copyright 1997-1999, All Rights Reserved<BR>
+</CENTER>
+<HR>
+<H1 ALIGN=CENTER><A NAME=CONTENTS>Table of Contents</A></H1>
+<BR>
+<BR><B><A HREF=#1>Preface</A></B>
+<UL>
+<LI><A HREF=#1_1>System Overview</A></LI>
+<LI><A HREF=#1_2>Document Overview</A></LI>
+</UL>
+<B><A HREF=#2>1 - Printing System Overview</A></B>
+<UL>
+<LI><A HREF=#2_1>The Printing Problem</A></LI>
+<LI><A HREF=#2_2>The Technology</A></LI>
+<LI><A HREF=#2_3>Jobs</A></LI>
+<LI><A HREF=#2_4>Classes</A></LI>
+<LI><A HREF=#2_5>Filters</A></LI>
+<LI><A HREF=#2_6>Printer Drivers</A></LI>
+<LI><A HREF=#2_7>Networking</A></LI>
+</UL>
+<B><A HREF=#3>2 - Using the Printing System</A></B>
+<UL>
+<LI><A HREF=#3_1>Submitting Files for Printing</A></LI>
+<LI><A HREF=#3_2>Choosing a Printer</A></LI>
+<LI><A HREF=#3_3>Setting Printer Options</A></LI>
+<LI><A HREF=#3_4>Printing Multiple Copies</A></LI>
+<LI><A HREF=#3_5>Checking the Printer Status from the Command-Line</A></LI>
+<LI><A HREF=#3_6>Checking the Printer Status from the Web</A></LI>
+<LI><A HREF=#3_7>Canceling a Print Job</A></LI>
+</UL>
+<B><A HREF=#4>3 - Standard Printer Options</A></B>
+<UL>
+<LI><A HREF=#4_1>General Options</A></LI>
+<UL>
+<LI><A HREF=#4_1_1>Selecting the Media Size, Type, and Source</A></LI>
+<LI><A HREF=#4_1_2>Setting the Orientation</A></LI>
+<LI><A HREF=#4_1_3>Printing On Both Sides of the Paper</A></LI>
+<LI><A HREF=#4_1_4>Selecting a Range of Pages</A></LI>
+<LI><A HREF=#4_1_5>Setting the Brightness</A></LI>
+<LI><A HREF=#4_1_6>Setting the Gamma Correction</A></LI>
+</UL>
+<LI><A HREF=#4_2>Text Options</A></LI>
+<UL>
+<LI><A HREF=#4_2_1>Setting the Number of Characters Per Inch</A></LI>
+<LI><A HREF=#4_2_2>Setting the Number of Lines Per Inch</A></LI>
+<LI><A HREF=#4_2_3>Setting the Number of Columns</A></LI>
+<LI><A HREF=#4_2_4>Setting the Page Margins</A></LI>
+<LI><A HREF=#4_2_5>Pretty Printing</A></LI>
+</UL>
+<LI><A HREF=#4_3>Image Options</A></LI>
+<UL>
+<LI><A HREF=#4_3_1>Scaling the Image</A></LI>
+<LI><A HREF=#4_3_2>Adjusting the Hue (Tint) of an Image</A></LI>
+<LI><A HREF=#4_3_3>Adjusting the Saturation (Color) of an Image</A></LI>
+</UL>
+</UL>
+<HR>
+<H1 ALIGN=RIGHT><A NAME=1>Preface</A></H1>
+ This software users manual describes how to use the Common UNIX
+Printing System (&quot;CUPS&quot;) Version 1.0.0.
+<H2><A NAME=1_1>System Overview</A></H2>
+ The Common UNIX Printing System provides a portable printing layer for
+ UNIX&reg; operating systems. It has been developed by Easy Software
+ Products to promote a standard printing solution for all UNIX vendors
+ and users. CUPS provides the System V and Berkeley command-line
+interfaces.
+<P>CUPS uses the Internet Printing Protocol (IETF-IPP) as the basis for
+managing print jobs and queues. The Line Printer Daemon (LPD, RFC1179),
+Server Message Block (SMB), and AppSocket protocols are also supported
+with reduced functionality. </P>
+<P>CUPS adds network printer browsing and PostScript Printer
+Description (&quot;PPD&quot;)-based printing options to support real world
+applications under UNIX. </P>
+<P>CUPS also includes a customized version of GNU GhostScript
+(currently based off GNU GhostScript 4.03) and an image file RIP that
+can be used to support non-PostScript printers. </P>
+<H2><A NAME=1_2>Document Overview</A></H2>
+<P>This software users manual is organized into the following sections:</P>
+<UL>
+<LI>1 - Printing System Overview</LI>
+<LI>2 - Using the Printing System</LI>
+<LI>3 - Standard Printer Options</LI>
+<LI>4 - Checking the Status Via the Web</LI>
+</UL>
+<H1 ALIGN=RIGHT><A NAME=2>1 - Printing System Overview</A></H1>
+<P>This chapter provides an overview of how the Common UNIX Printing
+System works. </P>
+<H2><A NAME=2_1>The Printing Problem</A></H2>
+<P>For years <I>the printing problem</I> has plagued UNIX&reg;. Unlike
+Microsoft&reg; Windows&reg; or MacOS, UNIX has no standard interface or system
+in place for supporting printers. Among the solutions previously
+available, the Berkeley and System V printing systems are the most
+prevalent. </P>
+<P>These printing systems support line printers (text only) or
+PostScript printers (text and graphics), and with some coaxing they can
+be made to support a full range of printers and file formats. However,
+because each varient of the UNIX operating system uses a different
+printing system than the next, developing printer drivers for a wide
+range of printers is extremely difficult. That combined with the
+limited volume of customers for each UNIX varient has forced most
+printer vendors to give up supporting UNIX entirely. </P>
+<P>The Common UNIX Printing System, or CUPS, is designed to eliminate <I>
+the printing problem</I>. One common printing system can be used by all
+UNIX varients to support the printing needs of users. Printer vendors
+can use its modular filter interface to develop a single driver program
+that supports a wide range of file formats with little or no effort.
+ Since CUPS provides both the System V and Berkeley printing commands,
+users (and applications) can reap the benefits of this new technology
+with no changes. </P>
+<H2><A NAME=2_2>The Technology</A></H2>
+<P>CUPS is based upon an emerging Internet standard called the Internet
+Printing Protocol, or IPP. IPP has been embraced by dozens of printer
+and printer server manufacturers, and will be supported by the next
+Microsoft Windows operating system. </P>
+<P>IPP defines a standard protocol for printing as well as managing
+print jobs and printer options like media size, resolution, and so
+forth. Like all IP-based protocols, IPP can be used locally or over the
+Internet to printers hundreds or thousands of miles away. Unlike other
+protocols, however, IPP also supports access control, authentication,
+and encryption, making it a much more secure printing solution than
+older ones. </P>
+<P>IPP is layered on top of the Hyper-Text Transport Protocol, or HTTP,
+which is the basis of web servers on the Internet. This allows the user
+to view documentation and status information on a printer or server
+using their web browser. </P>
+<P>CUPS provides a complete IPP/1.0-based printing system that provides
+Basic authentication and domain or IP-based access control. Digest
+authentication and TLS encryption will be available in future versions
+of CUPS. </P>
+<H2><A NAME=2_3>Jobs</A></H2>
+<P>Each file that is submitted for printing is called a <I>job</I>.
+ Jobs are identified by a unique number starting at 1 and are assigned
+to a particular destination (usually a printer). Jobs can also have
+options associated with them such as media size, number of copies, and
+priority. </P>
+<H2><A NAME=2_4>Classes</A></H2>
+<P>CUPS supports collections of printers known as <I>classes</I>. Jobs
+sent to a class are forwarded to the first available printer in the
+class. </P>
+<H2><A NAME=2_5>Filters</A></H2>
+<P>Filters allow a user or application to print many types of files
+without extra effort. Print jobs sent to a CUPS server are filtered
+before sending them to a printer. Some filters convert job files to
+different formats that the printer can understand. Others perform page
+selection and ordering tasks. <I>Backend</I> filters perform the most
+important task of all - they send the filtered print data to the
+printer. </P>
+<P>CUPS provides filters for printing many types of image files,
+HP-GL/2 files, PDF files, and text files. CUPS also supplies PostScript
+and image file Raster Image Processors, or RIPs, that convert
+PostScript or image files into bitmaps that can be sent to a raster
+printer. </P>
+<P>CUPS provides backends for printing over parallel and serial ports,
+and over the network via the JetDirect (AppSocket), Server Message
+Block, and Line Printer Daemon protocols. </P>
+<H2><A NAME=2_6>Printer Drivers</A></H2>
+<P>Printer drivers in CUPS consist of one of more filters specific to a
+printer. CUPS includes a sample printer driver for Hewlett-Packard
+LaserJet and DeskJet printers. While this driver does not generate
+optimal output for different printer models, it does demonstrate how
+you can write your own printer drivers and incorporate them into CUPS. </P>
+<H2><A NAME=2_7>Networking</A></H2>
+<P>Printers and classes on the local system are automatically shared
+with other systems on the network. This allows you to setup one system
+to print to a printer and use this system as a printer server or spool
+host for all of the others. If there is only one occurrence of a
+printer on a network, then that printer can be accessed using its name
+alone. If more than one printer exists with the same name, users must
+select the printer by specifying which server to use (e.g.
+&quot;printer@host1&quot; or &quot;printer@host2&quot;.) </P>
+<P>CUPS also provides <I>implicit classes</I>, which are collections of
+printers and/or classes with the same name. This allows you to setup
+multiple servers pointing to the same physical network printer, for
+example, so that you aren't relying on a single system for printing.
+Because this also works with printer classes, you can setup multiple
+servers and printers and never worry about a &quot;single point of failure&quot;
+unless all of the printers and servers goes down! </P>
+<H1 ALIGN=RIGHT><A NAME=3>2 - Using the Printing System</A></H1>
+<P>This chapter shows you how to submit, query, and cancel print jobs
+to different printers. </P>
+<H2><A NAME=3_1>Submitting Files for Printing</A></H2>
+<P>CUPS provides both the System V (<CODE>lp</CODE>) and Berkeley (<CODE>
+lpr</CODE>) printing commands. To print a file to the default printer
+on the system (or your only printer if you have only one) you just need
+to type: </P>
+<UL>
+<PRE>
+% lp filename ENTER
+</PRE>
+</UL>
+<P>or: </P>
+<UL>
+<PRE>
+% lpr filename ENTER
+</PRE>
+</UL>
+<P>CUPS understands many different types of files directly, including
+PostScript and image files. This allows you to print from inside your
+applications or at the command-line, whichever is most convenient! </P>
+<H2><A NAME=3_2>Choosing a Printer</A></H2>
+<P>Many systems will have more than one printer available to the user.
+These printers can be attached to the local system via a parallel or
+serial port, or available over the network. </P>
+<P>To see a list of available printers, use the <CODE>lpstat</CODE>
+ command: </P>
+<UL>
+<PRE>
+% lpstat -p -d ENTER
+</PRE>
+</UL>
+<P>The &quot;-p&quot; option specifies that you want to see a list of printers,
+and the &quot;-d&quot; option reports the current system default printer or
+class. </P>
+<P>To print to a specific printer, use the &quot;-d&quot; option to the <CODE>lp</CODE>
+ command: </P>
+<UL>
+<PRE>
+% lp -d printer filename ENTER
+</PRE>
+</UL>
+<P>or the &quot;-P&quot; option to the <CODE>lpr</CODE> command: </P>
+<UL>
+<PRE>
+% lpr -P printer filename ENTER
+</PRE>
+</UL>
+<H2><A NAME=3_3>Setting Printer Options</A></H2>
+<P>For many types of files, the default printer options may be
+sufficient for your needs. However, there may be times when you need to
+change the options for a particular file you are printing. </P>
+<P>The <CODE>lp</CODE> command allows you to pass printer options using
+the &quot;-o&quot; option: </P>
+<UL>
+<PRE>
+% lp -o landscape -o scaling=75 -o media=A4 filename.jpg
+</PRE>
+</UL>
+<P>The <CODE>lpr</CODE> command has no command-line option for printer
+options. </P>
+<P>The available printer options vary depending on the printer. The
+standard options are described in <A HREF=#4>Chapter 3</A>. </P>
+<H2><A NAME=3_4>Printing Multiple Copies</A></H2>
+<P>Both the <CODE>lp</CODE> and <CODE>lpr</CODE> commands have options
+for printing more than one copy of a file: </P>
+<UL>
+<PRE>
+% lp -n num-copies filename ENTER
+% lpr -#num-copies filename ENTER
+</PRE>
+</UL>
+<P>Copies are normally <I>not</I> collated for you. To get collated
+copies use the <CODE>lp</CODE> command with the &quot;-o Collate=True&quot;
+option: </P>
+<UL>
+<PRE>
+% lp -n num-copies -o Collate=True filename ENTER
+</PRE>
+</UL>
+<H2><A NAME=3_5>Checking the Printer Status from the Command-Line</A></H2>
+<P>The <CODE>lpstat</CODE> command can be used to check for jobs that
+you have submitted for printing: </P>
+<UL>
+<PRE>
+% lpstat ENTER
+Printer-1 johndoe 4427776
+Printer-2 johndoe 15786
+Printer-3 johndoe 372842
+</PRE>
+</UL>
+<P>The jobs are listed in the order they will be printed. To see which
+files and printers are active, use the &quot;-p&quot; option: </P>
+<UL>
+<PRE>
+% lpstat -p ENTER
+printer DeskJet now printing DeskJet-1.
+</PRE>
+</UL>
+<P>Or to show the jobs and the printers, use the &quot;-o&quot; and &quot;-p&quot; options: </P>
+<UL>
+<PRE>
+% lpstat -o -p ENTER
+Printer-1 johndoe 4427776
+Printer-2 johndoe 15786
+Printer-3 johndoe 372842
+printer DeskJet now printing DeskJet-1.
+</PRE>
+</UL>
+<H2><A NAME=3_6>Checking the Printer Status from the Web</A></H2>
+<P>Since CUPS uses the Internet Printing Protocol, it is also a
+full-featured web server. To use your web browser to monitor the
+printers on your system, open the URL &quot;<A HREF=http://localhost:631>
+http://localhost:631</A>&quot;. From there you can view the status of
+classes, jobs, and printers with the click of a button! </P>
+<H2><A NAME=3_7>Canceling a Print Job</A></H2>
+<P>The <CODE>cancel</CODE> command cancels a print job: </P>
+<UL>
+<PRE>
+% cancel job-id ENTER
+</PRE>
+</UL>
+<P>The <I>job-id</I> is a number that was reported to you by the <CODE>
+lp</CODE> or <CODE>lpstat</CODE> commands. </P>
+<H1 ALIGN=RIGHT><A NAME=4>3 - Standard Printer Options</A></H1>
+<P>This chapter describes the standard printer options that are
+available when printing with the <CODE>lp</CODE> command. </P>
+<H2><A NAME=4_1>General Options</A></H2>
+<P>The following options apply when printing all types of files. </P>
+<H3><A NAME=4_1_1>Selecting the Media Size, Type, and Source</A></H3>
+<P>The &quot;-o media=xyz&quot; option sets the media size, type, and/or source: </P>
+<UL>
+<PRE>
+% lp -o media=Letter filename ENTER
+% lp -o media=Letter,MultiPurpose filename ENTER
+% lp -o media=Letter,Transparency filename ENTER
+% lp -o media=Letter,MultiPurpose,Transparency filename ENTER
+</PRE>
+</UL>
+<P>The available media sizes, types, and sources depend on the printer,
+but most support the following options (case is significant): </P>
+<UL>
+<LI><CODE>Letter</CODE> - US Letter (8.5x11 inches, or 216x279mm) </LI>
+<LI><CODE>Legal</CODE> - US Legal (8.5x14 inches, or 216x356mm) </LI>
+<LI><CODE>A4</CODE> - ISO A4 (8.27x11.69 inches, or 210x297mm) </LI>
+<LI><CODE>COM10</CODE> - US #10 Envelope (9.5x4.125 inches, or
+ 241x105mm) </LI>
+<LI><CODE>DL</CODE> - ISO DL Envelope (8.66x4.33 inches, or 220x110mm) </LI>
+<LI><CODE>Transparency</CODE> - Transparency media type or source </LI>
+<LI><CODE>Upper</CODE> - Upper paper tray </LI>
+<LI><CODE>Lower</CODE> - Lower paper tray </LI>
+<LI><CODE>MultiPurpose</CODE> - Multi-purpose paper tray </LI>
+<LI><CODE>LargeCapacity</CODE> - Large capacity paper tray </LI>
+</UL>
+<P>The actual options supported are defined in the printer's PPD file
+in the <CODE>PageSize</CODE>, <CODE>InputSlot</CODE>, and <CODE>
+MediaType</CODE> options. </P>
+<H3><A NAME=4_1_2>Setting the Orientation</A></H3>
+<P>The &quot;-o landscape&quot; option will rotate the page 90 degrees to print
+in landscape orientation: </P>
+<UL>
+<PRE>
+% lp -o landscape filename ENTER
+</PRE>
+</UL>
+<H3><A NAME=4_1_3>Printing On Both Sides of the Paper</A></H3>
+<P>The &quot;-o sides=two-sided-short&quot; and &quot;-o sides=two-sided-long&quot; options
+will enable duplexing on the printer (if the printer supports it.) The
+&quot;two-sided-short&quot; option is suitable for landscape pages, while the
+&quot;two-sided-long&quot; option is suitable for portrait pages: </P>
+<UL>
+<PRE>
+% lp -o sides=two-sided-short filename ENTER
+% lp -o sides=two-sided-long filename ENTER
+</PRE>
+</UL>
+<H3><A NAME=4_1_4>Selecting a Range of Pages</A></H3>
+<P>The &quot;-o page-ranges=pages&quot; option selects a range of pages for
+printing: </P>
+<UL>
+<PRE>
+% lp -o page-ranges=1 filename ENTER
+% lp -o page-ranges=1-4 filename ENTER
+% lp -o page-ranges=1-4,7,9-12 filename ENTER
+</PRE>
+</UL>
+<P>As shown above, the <I>pages</I> value can be a single page, a range
+of pages, or a collection of page numbers and ranges separated by
+commas. The pages will always be printed in ascending order, regardless
+of the order of the pages in the &quot;page-range&quot; option. </P>
+<P>To select the even or odd pages, use the &quot;-o page-set=set&quot; option: </P>
+<UL>
+<PRE>
+% lp -o page-set=odd filename ENTER
+% lp -o page-set=even filename ENTER
+</PRE>
+</UL>
+<H3><A NAME=4_1_5>Setting the Brightness</A></H3>
+<P>You can control the overall brightness of the printed output using
+the &quot;-o brightness=percent&quot; option: </P>
+<UL>
+<PRE>
+% lp -o brightness=120 filename ENTER
+</PRE>
+</UL>
+<P>Values greater than 100 will lighten the print, while values less
+than 100 will darken it. </P>
+<H3><A NAME=4_1_6>Setting the Gamma Correction</A></H3>
+<P>You can control the overall gamma correction of the printed output
+using the &quot;-o gamma=value&quot; option: </P>
+<UL>
+<PRE>
+% lp -o gamma=1700 filename ENTER
+</PRE>
+</UL>
+<P>Values greater than 1000 will lighten the print, while values less
+than 1000 will darken it. </P>
+<H2><A NAME=4_2>Text Options</A></H2>
+<P>The following options apply when printing text files. </P>
+<H3><A NAME=4_2_1>Setting the Number of Characters Per Inch</A></H3>
+<P>The &quot;-o cpi=value&quot; option sets the number of characters per inch: </P>
+<UL>
+<PRE>
+% lp -o cpi=12 filename ENTER
+</PRE>
+</UL>
+<H3><A NAME=4_2_2>Setting the Number of Lines Per Inch</A></H3>
+<P>The &quot;-o lpi=value&quot; option sets the number of lines per inch: </P>
+<UL>
+<PRE>
+% lp -o lpi=8 filename ENTER
+</PRE>
+</UL>
+<H3><A NAME=4_2_3>Setting the Number of Columns</A></H3>
+<P>The &quot;-o columns=value&quot; option sets the number of text columns: </P>
+<UL>
+<PRE>
+% lp -o columns=2 filename ENTER
+</PRE>
+</UL>
+<H3><A NAME=4_2_4>Setting the Page Margins</A></H3>
+<P>Normally the page margins are set to the hard limits of the printer.
+To adjust the page margins use the &quot;-o page-left=value&quot;, &quot;-o
+page-right=value&quot;, &quot;-o page-top=value&quot;, and &quot;-o page-bottom=value&quot;
+options: </P>
+<UL>
+<PRE>
+% lp -o page-left=value filename ENTER
+% lp -o page-right=value filename ENTER
+% lp -o page-top=value filename ENTER
+% lp -o page-bottom=value filename ENTER
+</PRE>
+</UL>
+<P>The <I>value</I> argument is the margin in points; each point is
+1/72 inch or 0.35mm. </P>
+<H3><A NAME=4_2_5>Pretty Printing</A></H3>
+<P>The &quot;-o prettyprint&quot; option puts a header at the top of each page
+with the page number, job title (usually the filename), and the date.
+Also, C and C++ keywords are highlighted, and comment lines are
+italicized: </P>
+<UL>
+<PRE>
+% lp -o prettyprint filename ENTER
+</PRE>
+</UL>
+<H2><A NAME=4_3>Image Options</A></H2>
+<P>The following options apply when printing image files. </P>
+<H3><A NAME=4_3_1>Scaling the Image</A></H3>
+<P>The &quot;-o scaling=percent&quot; and &quot;-o ppi=value&quot; options change the size
+of a printed image: </P>
+<UL>
+<PRE>
+% lp -o scaling=percent filename ENTER
+% lp -o ppi=value filename ENTER
+</PRE>
+</UL>
+<P>The scaling <I>percent</I> is a number from 1 to 800 specifying the
+size in relation to the page (<I>not</I> the image.) A scaling of 100
+percent will fill the page as completely as the image aspect ratio
+allows. A scaling of 200 percent will print on up to 4 pages. </P>
+<P>The ppi <I>value</I> is a number from 1 to 1200 specifying the
+resolution of the image in pixels per inch. An image that is 3000x2400
+pixels will print 10x8 inches at 300 pixels per inch, for example. If
+the specified resolution makes the image larger than the page, multiple
+pages will be printed to satisfy the request. </P>
+<H3><A NAME=4_3_2>Adjusting the Hue (Tint) of an Image</A></H3>
+<P>The &quot;-o hue=value&quot; option will adjust the hue of the printed image,
+much like the tint control on your television: </P>
+<UL>
+<PRE>
+% lp -o hue=value filename ENTER
+</PRE>
+</UL>
+<P>The <I>value</I> argument is a number from -360 to 360 and
+represents the color hue rotation. The following table summarizes the
+change you'll see with different colors:
+<CENTER>
+<TABLE BORDER=1 WIDTH=50%>
+<TR><TH>Original</TH><TH>hue=-45</TH><TH>hue=45</TH></TR>
+<TR><TD>Red</TD><TD>Purple</TD><TD>Yellow-orange</TD></TR>
+<TR><TD>Green</TD><TD>Yellow-green</TD><TD>Blue-green</TD></TR>
+<TR><TD>Yellow</TD><TD>Orange</TD><TD>Green-yellow</TD></TR>
+<TR><TD>Blue</TD><TD>Sky-blue</TD><TD>Purple</TD></TR>
+<TR><TD>Magenta</TD><TD>Indigo</TD><TD>Crimson</TD></TR>
+<TR><TD>Cyan</TD><TD>Blue-green</TD><TD>Light-navy-blue</TD></TR>
+</TABLE>
+</CENTER>
+</P>
+<H3><A NAME=4_3_3>Adjusting the Saturation (Color) of an Image</A></H3>
+<P>The &quot;-o saturation=percent&quot; option adjusts the saturation of the
+colors in an image, much like the color knob on your television: </P>
+<UL>
+<PRE>
+% lp -o saturation=percent filename ENTER
+</PRE>
+</UL>
+<P>The <I>percent</I> argument specifies the color saturation from 0 to
+200. A color saturation of 0 produces a black-and-white print, while a
+value of 200 will make the colors extremely intense. </P>
+</BODY>
+</HTML>
diff --git a/doc/sum.pdf b/doc/sum.pdf
new file mode 100644
index 000000000..c2d34fef5
--- /dev/null
+++ b/doc/sum.pdf
@@ -0,0 +1,949 @@
+%PDF-1.2
+%âãÏÓ
+1 0 obj<</Producer(htmldoc 2.0b1 Copyright 1997-1999 Michael Sweet, All Rights Reserved.)/CreationDate(D:19990713124419Z)/Title(DRAFT - CUPS Software Users Manual)/Author(Easy Software Products)>>endobj
+2 0 obj<</Type/Encoding/BaseEncoding/WinAnsiEncoding>>endobj
+3 0 obj<</Type/Font/Subtype/Type1/BaseFont/Courier/Encoding 2 0 R>>endobj
+4 0 obj<</Type/Font/Subtype/Type1/BaseFont/Times-Roman/Encoding 2 0 R>>endobj
+5 0 obj<</Type/Font/Subtype/Type1/BaseFont/Times-Bold/Encoding 2 0 R>>endobj
+6 0 obj<</Type/Font/Subtype/Type1/BaseFont/Times-Italic/Encoding 2 0 R>>endobj
+7 0 obj<</Type/Font/Subtype/Type1/BaseFont/Helvetica/Encoding 2 0 R>>endobj
+8 0 obj<</Type/Font/Subtype/Type1/BaseFont/Helvetica-Bold/Encoding 2 0 R>>endobj
+9 0 obj<</Type/Font/Subtype/Type1/BaseFont/Symbol>>endobj
+10 0 obj<</Subtype/Link/Rect[468.6 232.2 506.2 245.2]/Border[0 0 0]/Dest[234 0 R/XYZ null 818 0]>>endobj
+11 0 obj<</Subtype/Link/Rect[506.2 232.2 511.7 245.2]/Border[0 0 0]/Dest[234 0 R/XYZ null 818 0]>>endobj
+12 0 obj[10 0 R
+11 0 R
+]endobj
+13 0 obj<</S/URI/URI(http://localhost:631)>>endobj
+14 0 obj<</Subtype/Link/Rect[353.9 285.8 439.5 298.8]/Border[0 0 0]/A 13 0 R>>endobj
+15 0 obj[14 0 R
+]endobj
+16 0 obj<</Subtype/Link/Rect[72.0 673.2 107.4 686.2]/Border[0 0 0]/Dest[204 0 R/XYZ null 818 0]>>endobj
+17 0 obj<</Subtype/Link/Rect[108.0 660.0 143.1 673.0]/Border[0 0 0]/Dest[204 0 R/XYZ null 416 0]>>endobj
+18 0 obj<</Subtype/Link/Rect[143.1 660.0 186.5 673.0]/Border[0 0 0]/Dest[204 0 R/XYZ null 416 0]>>endobj
+19 0 obj<</Subtype/Link/Rect[108.0 646.8 156.6 659.8]/Border[0 0 0]/Dest[207 0 R/XYZ null 800 0]>>endobj
+20 0 obj<</Subtype/Link/Rect[156.6 646.8 199.9 659.8]/Border[0 0 0]/Dest[207 0 R/XYZ null 800 0]>>endobj
+21 0 obj<</Subtype/Link/Rect[72.0 620.4 80.2 633.4]/Border[0 0 0]/Dest[210 0 R/XYZ null 818 0]>>endobj
+22 0 obj<</Subtype/Link/Rect[80.2 620.4 89.3 633.4]/Border[0 0 0]/Dest[210 0 R/XYZ null 818 0]>>endobj
+23 0 obj<</Subtype/Link/Rect[89.3 620.4 131.1 633.4]/Border[0 0 0]/Dest[210 0 R/XYZ null 818 0]>>endobj
+24 0 obj<</Subtype/Link/Rect[131.1 620.4 167.5 633.4]/Border[0 0 0]/Dest[210 0 R/XYZ null 818 0]>>endobj
+25 0 obj<</Subtype/Link/Rect[167.5 620.4 212.7 633.4]/Border[0 0 0]/Dest[210 0 R/XYZ null 818 0]>>endobj
+26 0 obj<</Subtype/Link/Rect[108.0 607.2 127.9 620.2]/Border[0 0 0]/Dest[210 0 R/XYZ null 416 0]>>endobj
+27 0 obj<</Subtype/Link/Rect[127.9 607.2 166.1 620.2]/Border[0 0 0]/Dest[210 0 R/XYZ null 416 0]>>endobj
+28 0 obj<</Subtype/Link/Rect[166.1 607.2 203.3 620.2]/Border[0 0 0]/Dest[210 0 R/XYZ null 416 0]>>endobj
+29 0 obj<</Subtype/Link/Rect[108.0 594.0 127.9 607.0]/Border[0 0 0]/Dest[213 0 R/XYZ null 800 0]>>endobj
+30 0 obj<</Subtype/Link/Rect[127.9 594.0 180.4 607.0]/Border[0 0 0]/Dest[213 0 R/XYZ null 800 0]>>endobj
+31 0 obj<</Subtype/Link/Rect[108.0 580.8 127.6 593.8]/Border[0 0 0]/Dest[213 0 R/XYZ null 522 0]>>endobj
+32 0 obj<</Subtype/Link/Rect[108.0 567.6 141.0 580.6]/Border[0 0 0]/Dest[213 0 R/XYZ null 417 0]>>endobj
+33 0 obj<</Subtype/Link/Rect[108.0 554.4 136.1 567.4]/Border[0 0 0]/Dest[213 0 R/XYZ null 325 0]>>endobj
+34 0 obj<</Subtype/Link/Rect[108.0 541.2 140.7 554.2]/Border[0 0 0]/Dest[216 0 R/XYZ null 800 0]>>endobj
+35 0 obj<</Subtype/Link/Rect[140.7 541.2 173.7 554.2]/Border[0 0 0]/Dest[216 0 R/XYZ null 800 0]>>endobj
+36 0 obj<</Subtype/Link/Rect[108.0 528.0 160.5 541.0]/Border[0 0 0]/Dest[216 0 R/XYZ null 681 0]>>endobj
+37 0 obj<</Subtype/Link/Rect[72.0 501.6 80.2 514.6]/Border[0 0 0]/Dest[222 0 R/XYZ null 818 0]>>endobj
+38 0 obj<</Subtype/Link/Rect[80.2 501.6 89.3 514.6]/Border[0 0 0]/Dest[222 0 R/XYZ null 818 0]>>endobj
+39 0 obj<</Subtype/Link/Rect[89.3 501.6 118.9 514.6]/Border[0 0 0]/Dest[222 0 R/XYZ null 818 0]>>endobj
+40 0 obj<</Subtype/Link/Rect[118.9 501.6 136.3 514.6]/Border[0 0 0]/Dest[222 0 R/XYZ null 818 0]>>endobj
+41 0 obj<</Subtype/Link/Rect[136.3 501.6 178.2 514.6]/Border[0 0 0]/Dest[222 0 R/XYZ null 818 0]>>endobj
+42 0 obj<</Subtype/Link/Rect[178.2 501.6 211.8 514.6]/Border[0 0 0]/Dest[222 0 R/XYZ null 818 0]>>endobj
+43 0 obj<</Subtype/Link/Rect[108.0 488.4 159.7 501.4]/Border[0 0 0]/Dest[222 0 R/XYZ null 416 0]>>endobj
+44 0 obj<</Subtype/Link/Rect[159.7 488.4 183.8 501.4]/Border[0 0 0]/Dest[222 0 R/XYZ null 416 0]>>endobj
+45 0 obj<</Subtype/Link/Rect[183.8 488.4 199.4 501.4]/Border[0 0 0]/Dest[222 0 R/XYZ null 416 0]>>endobj
+46 0 obj<</Subtype/Link/Rect[199.4 488.4 234.8 501.4]/Border[0 0 0]/Dest[222 0 R/XYZ null 416 0]>>endobj
+47 0 obj<</Subtype/Link/Rect[108.0 475.2 152.9 488.2]/Border[0 0 0]/Dest[225 0 R/XYZ null 800 0]>>endobj
+48 0 obj<</Subtype/Link/Rect[152.9 475.2 160.6 488.2]/Border[0 0 0]/Dest[225 0 R/XYZ null 800 0]>>endobj
+49 0 obj<</Subtype/Link/Rect[160.6 475.2 190.5 488.2]/Border[0 0 0]/Dest[225 0 R/XYZ null 800 0]>>endobj
+50 0 obj<</Subtype/Link/Rect[108.0 462.0 141.9 475.0]/Border[0 0 0]/Dest[225 0 R/XYZ null 489 0]>>endobj
+51 0 obj<</Subtype/Link/Rect[141.9 462.0 174.6 475.0]/Border[0 0 0]/Dest[225 0 R/XYZ null 489 0]>>endobj
+52 0 obj<</Subtype/Link/Rect[174.6 462.0 209.5 475.0]/Border[0 0 0]/Dest[225 0 R/XYZ null 489 0]>>endobj
+53 0 obj<</Subtype/Link/Rect[108.0 448.8 146.2 461.8]/Border[0 0 0]/Dest[225 0 R/XYZ null 281 0]>>endobj
+54 0 obj<</Subtype/Link/Rect[146.2 448.8 186.8 461.8]/Border[0 0 0]/Dest[225 0 R/XYZ null 281 0]>>endobj
+55 0 obj<</Subtype/Link/Rect[186.8 448.8 217.4 461.8]/Border[0 0 0]/Dest[225 0 R/XYZ null 281 0]>>endobj
+56 0 obj<</Subtype/Link/Rect[108.0 435.6 152.9 448.6]/Border[0 0 0]/Dest[228 0 R/XYZ null 758 0]>>endobj
+57 0 obj<</Subtype/Link/Rect[152.9 435.6 169.1 448.6]/Border[0 0 0]/Dest[228 0 R/XYZ null 758 0]>>endobj
+58 0 obj<</Subtype/Link/Rect[169.1 435.6 201.8 448.6]/Border[0 0 0]/Dest[228 0 R/XYZ null 758 0]>>endobj
+59 0 obj<</Subtype/Link/Rect[201.8 435.6 231.4 448.6]/Border[0 0 0]/Dest[228 0 R/XYZ null 758 0]>>endobj
+60 0 obj<</Subtype/Link/Rect[231.4 435.6 255.6 448.6]/Border[0 0 0]/Dest[228 0 R/XYZ null 758 0]>>endobj
+61 0 obj<</Subtype/Link/Rect[255.6 435.6 271.8 448.6]/Border[0 0 0]/Dest[228 0 R/XYZ null 758 0]>>endobj
+62 0 obj<</Subtype/Link/Rect[271.8 435.6 344.0 448.6]/Border[0 0 0]/Dest[228 0 R/XYZ null 758 0]>>endobj
+63 0 obj<</Subtype/Link/Rect[108.0 422.4 152.9 435.4]/Border[0 0 0]/Dest[228 0 R/XYZ null 426 0]>>endobj
+64 0 obj<</Subtype/Link/Rect[152.9 422.4 169.1 435.4]/Border[0 0 0]/Dest[228 0 R/XYZ null 426 0]>>endobj
+65 0 obj<</Subtype/Link/Rect[169.1 422.4 201.8 435.4]/Border[0 0 0]/Dest[228 0 R/XYZ null 426 0]>>endobj
+66 0 obj<</Subtype/Link/Rect[201.8 422.4 231.4 435.4]/Border[0 0 0]/Dest[228 0 R/XYZ null 426 0]>>endobj
+67 0 obj<</Subtype/Link/Rect[231.4 422.4 255.6 435.4]/Border[0 0 0]/Dest[228 0 R/XYZ null 426 0]>>endobj
+68 0 obj<</Subtype/Link/Rect[255.6 422.4 271.8 435.4]/Border[0 0 0]/Dest[228 0 R/XYZ null 426 0]>>endobj
+69 0 obj<</Subtype/Link/Rect[271.8 422.4 292.5 435.4]/Border[0 0 0]/Dest[228 0 R/XYZ null 426 0]>>endobj
+70 0 obj<</Subtype/Link/Rect[108.0 409.2 155.4 422.2]/Border[0 0 0]/Dest[228 0 R/XYZ null 321 0]>>endobj
+71 0 obj<</Subtype/Link/Rect[155.4 409.2 163.0 422.2]/Border[0 0 0]/Dest[228 0 R/XYZ null 321 0]>>endobj
+72 0 obj<</Subtype/Link/Rect[163.0 409.2 187.1 422.2]/Border[0 0 0]/Dest[228 0 R/XYZ null 321 0]>>endobj
+73 0 obj<</Subtype/Link/Rect[187.1 409.2 202.4 422.2]/Border[0 0 0]/Dest[228 0 R/XYZ null 321 0]>>endobj
+74 0 obj<</Subtype/Link/Rect[72.0 382.8 80.2 395.8]/Border[0 0 0]/Dest[234 0 R/XYZ null 818 0]>>endobj
+75 0 obj<</Subtype/Link/Rect[80.2 382.8 89.3 395.8]/Border[0 0 0]/Dest[234 0 R/XYZ null 818 0]>>endobj
+76 0 obj<</Subtype/Link/Rect[89.3 382.8 136.0 395.8]/Border[0 0 0]/Dest[234 0 R/XYZ null 818 0]>>endobj
+77 0 obj<</Subtype/Link/Rect[136.0 382.8 173.0 395.8]/Border[0 0 0]/Dest[234 0 R/XYZ null 818 0]>>endobj
+78 0 obj<</Subtype/Link/Rect[173.0 382.8 210.3 395.8]/Border[0 0 0]/Dest[234 0 R/XYZ null 818 0]>>endobj
+79 0 obj<</Subtype/Link/Rect[108.0 369.6 145.6 382.6]/Border[0 0 0]/Dest[234 0 R/XYZ null 416 0]>>endobj
+80 0 obj<</Subtype/Link/Rect[145.6 369.6 180.4 382.6]/Border[0 0 0]/Dest[234 0 R/XYZ null 416 0]>>endobj
+81 0 obj<</Subtype/Link/Rect[144.0 356.4 187.7 369.4]/Border[0 0 0]/Dest[234 0 R/XYZ null 322 0]>>endobj
+82 0 obj<</Subtype/Link/Rect[187.7 356.4 203.9 369.4]/Border[0 0 0]/Dest[234 0 R/XYZ null 322 0]>>endobj
+83 0 obj<</Subtype/Link/Rect[203.9 356.4 234.7 369.4]/Border[0 0 0]/Dest[234 0 R/XYZ null 322 0]>>endobj
+84 0 obj<</Subtype/Link/Rect[234.7 356.4 259.2 369.4]/Border[0 0 0]/Dest[234 0 R/XYZ null 322 0]>>endobj
+85 0 obj<</Subtype/Link/Rect[259.2 356.4 287.3 369.4]/Border[0 0 0]/Dest[234 0 R/XYZ null 322 0]>>endobj
+86 0 obj<</Subtype/Link/Rect[287.3 356.4 305.9 369.4]/Border[0 0 0]/Dest[234 0 R/XYZ null 322 0]>>endobj
+87 0 obj<</Subtype/Link/Rect[305.9 356.4 336.5 369.4]/Border[0 0 0]/Dest[234 0 R/XYZ null 322 0]>>endobj
+88 0 obj<</Subtype/Link/Rect[144.0 343.2 177.9 356.2]/Border[0 0 0]/Dest[237 0 R/XYZ null 607 0]>>endobj
+89 0 obj<</Subtype/Link/Rect[177.9 343.2 194.1 356.2]/Border[0 0 0]/Dest[237 0 R/XYZ null 607 0]>>endobj
+90 0 obj<</Subtype/Link/Rect[194.1 343.2 244.2 356.2]/Border[0 0 0]/Dest[237 0 R/XYZ null 607 0]>>endobj
+91 0 obj<</Subtype/Link/Rect[144.0 330.0 182.2 343.0]/Border[0 0 0]/Dest[237 0 R/XYZ null 511 0]>>endobj
+92 0 obj<</Subtype/Link/Rect[182.2 330.0 198.4 343.0]/Border[0 0 0]/Dest[237 0 R/XYZ null 511 0]>>endobj
+93 0 obj<</Subtype/Link/Rect[198.4 330.0 222.5 343.0]/Border[0 0 0]/Dest[237 0 R/XYZ null 511 0]>>endobj
+94 0 obj<</Subtype/Link/Rect[222.5 330.0 249.1 343.0]/Border[0 0 0]/Dest[237 0 R/XYZ null 511 0]>>endobj
+95 0 obj<</Subtype/Link/Rect[249.1 330.0 261.0 343.0]/Border[0 0 0]/Dest[237 0 R/XYZ null 511 0]>>endobj
+96 0 obj<</Subtype/Link/Rect[261.0 330.0 277.2 343.0]/Border[0 0 0]/Dest[237 0 R/XYZ null 511 0]>>endobj
+97 0 obj<</Subtype/Link/Rect[277.2 330.0 302.3 343.0]/Border[0 0 0]/Dest[237 0 R/XYZ null 511 0]>>endobj
+98 0 obj<</Subtype/Link/Rect[144.0 316.8 187.7 329.8]/Border[0 0 0]/Dest[237 0 R/XYZ null 377 0]>>endobj
+99 0 obj<</Subtype/Link/Rect[187.7 316.8 195.3 329.8]/Border[0 0 0]/Dest[237 0 R/XYZ null 377 0]>>endobj
+100 0 obj<</Subtype/Link/Rect[195.3 316.8 226.2 329.8]/Border[0 0 0]/Dest[237 0 R/XYZ null 377 0]>>endobj
+101 0 obj<</Subtype/Link/Rect[226.2 316.8 238.1 329.8]/Border[0 0 0]/Dest[237 0 R/XYZ null 377 0]>>endobj
+102 0 obj<</Subtype/Link/Rect[238.1 316.8 263.8 329.8]/Border[0 0 0]/Dest[237 0 R/XYZ null 377 0]>>endobj
+103 0 obj<</Subtype/Link/Rect[144.0 303.6 177.9 316.6]/Border[0 0 0]/Dest[240 0 R/XYZ null 782 0]>>endobj
+104 0 obj<</Subtype/Link/Rect[177.9 303.6 194.1 316.6]/Border[0 0 0]/Dest[240 0 R/XYZ null 782 0]>>endobj
+105 0 obj<</Subtype/Link/Rect[194.1 303.6 241.2 316.6]/Border[0 0 0]/Dest[240 0 R/XYZ null 782 0]>>endobj
+106 0 obj<</Subtype/Link/Rect[144.0 290.4 177.9 303.4]/Border[0 0 0]/Dest[240 0 R/XYZ null 645 0]>>endobj
+107 0 obj<</Subtype/Link/Rect[177.9 290.4 194.1 303.4]/Border[0 0 0]/Dest[240 0 R/XYZ null 645 0]>>endobj
+108 0 obj<</Subtype/Link/Rect[194.1 290.4 231.7 303.4]/Border[0 0 0]/Dest[240 0 R/XYZ null 645 0]>>endobj
+109 0 obj<</Subtype/Link/Rect[231.7 290.4 278.7 303.4]/Border[0 0 0]/Dest[240 0 R/XYZ null 645 0]>>endobj
+110 0 obj<</Subtype/Link/Rect[108.0 277.2 130.9 290.2]/Border[0 0 0]/Dest[240 0 R/XYZ null 524 0]>>endobj
+111 0 obj<</Subtype/Link/Rect[130.9 277.2 165.8 290.2]/Border[0 0 0]/Dest[240 0 R/XYZ null 524 0]>>endobj
+112 0 obj<</Subtype/Link/Rect[144.0 264.0 177.9 277.0]/Border[0 0 0]/Dest[240 0 R/XYZ null 430 0]>>endobj
+113 0 obj<</Subtype/Link/Rect[177.9 264.0 194.1 277.0]/Border[0 0 0]/Dest[240 0 R/XYZ null 430 0]>>endobj
+114 0 obj<</Subtype/Link/Rect[194.1 264.0 232.9 277.0]/Border[0 0 0]/Dest[240 0 R/XYZ null 430 0]>>endobj
+115 0 obj<</Subtype/Link/Rect[232.9 264.0 244.8 277.0]/Border[0 0 0]/Dest[240 0 R/XYZ null 430 0]>>endobj
+116 0 obj<</Subtype/Link/Rect[244.8 264.0 294.6 277.0]/Border[0 0 0]/Dest[240 0 R/XYZ null 430 0]>>endobj
+117 0 obj<</Subtype/Link/Rect[294.6 264.0 312.0 277.0]/Border[0 0 0]/Dest[240 0 R/XYZ null 430 0]>>endobj
+118 0 obj<</Subtype/Link/Rect[312.0 264.0 331.6 277.0]/Border[0 0 0]/Dest[240 0 R/XYZ null 430 0]>>endobj
+119 0 obj<</Subtype/Link/Rect[144.0 250.8 177.9 263.8]/Border[0 0 0]/Dest[240 0 R/XYZ null 334 0]>>endobj
+120 0 obj<</Subtype/Link/Rect[177.9 250.8 194.1 263.8]/Border[0 0 0]/Dest[240 0 R/XYZ null 334 0]>>endobj
+121 0 obj<</Subtype/Link/Rect[194.1 250.8 232.9 263.8]/Border[0 0 0]/Dest[240 0 R/XYZ null 334 0]>>endobj
+122 0 obj<</Subtype/Link/Rect[232.9 250.8 244.8 263.8]/Border[0 0 0]/Dest[240 0 R/XYZ null 334 0]>>endobj
+123 0 obj<</Subtype/Link/Rect[244.8 250.8 272.0 263.8]/Border[0 0 0]/Dest[240 0 R/XYZ null 334 0]>>endobj
+124 0 obj<</Subtype/Link/Rect[272.0 250.8 289.4 263.8]/Border[0 0 0]/Dest[240 0 R/XYZ null 334 0]>>endobj
+125 0 obj<</Subtype/Link/Rect[289.4 250.8 309.0 263.8]/Border[0 0 0]/Dest[240 0 R/XYZ null 334 0]>>endobj
+126 0 obj<</Subtype/Link/Rect[144.0 237.6 177.9 250.6]/Border[0 0 0]/Dest[240 0 R/XYZ null 238 0]>>endobj
+127 0 obj<</Subtype/Link/Rect[177.9 237.6 194.1 250.6]/Border[0 0 0]/Dest[240 0 R/XYZ null 238 0]>>endobj
+128 0 obj<</Subtype/Link/Rect[194.1 237.6 232.9 250.6]/Border[0 0 0]/Dest[240 0 R/XYZ null 238 0]>>endobj
+129 0 obj<</Subtype/Link/Rect[232.9 237.6 244.8 250.6]/Border[0 0 0]/Dest[240 0 R/XYZ null 238 0]>>endobj
+130 0 obj<</Subtype/Link/Rect[244.8 237.6 284.6 250.6]/Border[0 0 0]/Dest[240 0 R/XYZ null 238 0]>>endobj
+131 0 obj<</Subtype/Link/Rect[144.0 224.4 177.9 237.4]/Border[0 0 0]/Dest[243 0 R/XYZ null 782 0]>>endobj
+132 0 obj<</Subtype/Link/Rect[177.9 224.4 194.1 237.4]/Border[0 0 0]/Dest[243 0 R/XYZ null 782 0]>>endobj
+133 0 obj<</Subtype/Link/Rect[194.1 224.4 218.2 237.4]/Border[0 0 0]/Dest[243 0 R/XYZ null 782 0]>>endobj
+134 0 obj<</Subtype/Link/Rect[218.2 224.4 254.9 237.4]/Border[0 0 0]/Dest[243 0 R/XYZ null 782 0]>>endobj
+135 0 obj<</Subtype/Link/Rect[144.0 211.2 173.0 224.2]/Border[0 0 0]/Dest[243 0 R/XYZ null 599 0]>>endobj
+136 0 obj<</Subtype/Link/Rect[173.0 211.2 208.5 224.2]/Border[0 0 0]/Dest[243 0 R/XYZ null 599 0]>>endobj
+137 0 obj<</Subtype/Link/Rect[108.0 198.0 138.2 211.0]/Border[0 0 0]/Dest[243 0 R/XYZ null 504 0]>>endobj
+138 0 obj<</Subtype/Link/Rect[138.2 198.0 173.1 211.0]/Border[0 0 0]/Dest[243 0 R/XYZ null 504 0]>>endobj
+139 0 obj<</Subtype/Link/Rect[144.0 184.8 179.8 197.8]/Border[0 0 0]/Dest[243 0 R/XYZ null 411 0]>>endobj
+140 0 obj<</Subtype/Link/Rect[179.8 184.8 195.9 197.8]/Border[0 0 0]/Dest[243 0 R/XYZ null 411 0]>>endobj
+141 0 obj<</Subtype/Link/Rect[195.9 184.8 223.4 197.8]/Border[0 0 0]/Dest[243 0 R/XYZ null 411 0]>>endobj
+142 0 obj<</Subtype/Link/Rect[144.0 171.6 190.1 184.6]/Border[0 0 0]/Dest[243 0 R/XYZ null 184 0]>>endobj
+143 0 obj<</Subtype/Link/Rect[190.1 171.6 206.3 184.6]/Border[0 0 0]/Dest[243 0 R/XYZ null 184 0]>>endobj
+144 0 obj<</Subtype/Link/Rect[206.3 171.6 227.4 184.6]/Border[0 0 0]/Dest[243 0 R/XYZ null 184 0]>>endobj
+145 0 obj<</Subtype/Link/Rect[227.4 171.6 255.8 184.6]/Border[0 0 0]/Dest[243 0 R/XYZ null 184 0]>>endobj
+146 0 obj<</Subtype/Link/Rect[255.8 171.6 267.7 184.6]/Border[0 0 0]/Dest[243 0 R/XYZ null 184 0]>>endobj
+147 0 obj<</Subtype/Link/Rect[267.7 171.6 280.9 184.6]/Border[0 0 0]/Dest[243 0 R/XYZ null 184 0]>>endobj
+148 0 obj<</Subtype/Link/Rect[280.9 171.6 308.4 184.6]/Border[0 0 0]/Dest[243 0 R/XYZ null 184 0]>>endobj
+149 0 obj<</Subtype/Link/Rect[144.0 158.4 190.1 171.4]/Border[0 0 0]/Dest[246 0 R/XYZ null 510 0]>>endobj
+150 0 obj<</Subtype/Link/Rect[190.1 158.4 206.3 171.4]/Border[0 0 0]/Dest[246 0 R/XYZ null 510 0]>>endobj
+151 0 obj<</Subtype/Link/Rect[206.3 158.4 254.3 171.4]/Border[0 0 0]/Dest[246 0 R/XYZ null 510 0]>>endobj
+152 0 obj<</Subtype/Link/Rect[254.3 158.4 289.4 171.4]/Border[0 0 0]/Dest[246 0 R/XYZ null 510 0]>>endobj
+153 0 obj<</Subtype/Link/Rect[289.4 158.4 301.4 171.4]/Border[0 0 0]/Dest[246 0 R/XYZ null 510 0]>>endobj
+154 0 obj<</Subtype/Link/Rect[301.4 158.4 314.5 171.4]/Border[0 0 0]/Dest[246 0 R/XYZ null 510 0]>>endobj
+155 0 obj<</Subtype/Link/Rect[314.5 158.4 342.0 171.4]/Border[0 0 0]/Dest[246 0 R/XYZ null 510 0]>>endobj
+156 0 obj[16 0 R
+17 0 R
+18 0 R
+19 0 R
+20 0 R
+21 0 R
+22 0 R
+23 0 R
+24 0 R
+25 0 R
+26 0 R
+27 0 R
+28 0 R
+29 0 R
+30 0 R
+31 0 R
+32 0 R
+33 0 R
+34 0 R
+35 0 R
+36 0 R
+37 0 R
+38 0 R
+39 0 R
+40 0 R
+41 0 R
+42 0 R
+43 0 R
+44 0 R
+45 0 R
+46 0 R
+47 0 R
+48 0 R
+49 0 R
+50 0 R
+51 0 R
+52 0 R
+53 0 R
+54 0 R
+55 0 R
+56 0 R
+57 0 R
+58 0 R
+59 0 R
+60 0 R
+61 0 R
+62 0 R
+63 0 R
+64 0 R
+65 0 R
+66 0 R
+67 0 R
+68 0 R
+69 0 R
+70 0 R
+71 0 R
+72 0 R
+73 0 R
+74 0 R
+75 0 R
+76 0 R
+77 0 R
+78 0 R
+79 0 R
+80 0 R
+81 0 R
+82 0 R
+83 0 R
+84 0 R
+85 0 R
+86 0 R
+87 0 R
+88 0 R
+89 0 R
+90 0 R
+91 0 R
+92 0 R
+93 0 R
+94 0 R
+95 0 R
+96 0 R
+97 0 R
+98 0 R
+99 0 R
+100 0 R
+101 0 R
+102 0 R
+103 0 R
+104 0 R
+105 0 R
+106 0 R
+107 0 R
+108 0 R
+109 0 R
+110 0 R
+111 0 R
+112 0 R
+113 0 R
+114 0 R
+115 0 R
+116 0 R
+117 0 R
+118 0 R
+119 0 R
+120 0 R
+121 0 R
+122 0 R
+123 0 R
+124 0 R
+125 0 R
+126 0 R
+127 0 R
+128 0 R
+129 0 R
+130 0 R
+131 0 R
+132 0 R
+133 0 R
+134 0 R
+135 0 R
+136 0 R
+137 0 R
+138 0 R
+139 0 R
+140 0 R
+141 0 R
+142 0 R
+143 0 R
+144 0 R
+145 0 R
+146 0 R
+147 0 R
+148 0 R
+149 0 R
+150 0 R
+151 0 R
+152 0 R
+153 0 R
+154 0 R
+155 0 R
+]endobj
+157 0 obj<</Dests 158 0 R>>endobj
+158 0 obj<</Kids[159 0 R]>>endobj
+159 0 obj<</Limits[(1)(4_3_3)]/Names[(1)160 0 R(1_1)161 0 R(1_2)162 0 R(2)163 0 R(2_1)164 0 R(2_2)165 0 R(2_3)166 0 R(2_4)167 0 R(2_5)168 0 R(2_6)169 0 R(2_7)170 0 R(3)171 0 R(3_1)172 0 R(3_2)173 0 R(3_3)174 0 R(3_4)175 0 R(3_5)176 0 R(3_6)177 0 R(3_7)178 0 R(4)179 0 R(4_1)180 0 R(4_1_1)181 0 R(4_1_2)182 0 R(4_1_3)183 0 R(4_1_4)184 0 R(4_1_5)185 0 R(4_1_6)186 0 R(4_2)187 0 R(4_2_1)188 0 R(4_2_2)189 0 R(4_2_3)190 0 R(4_2_4)191 0 R(4_2_5)192 0 R(4_3)193 0 R(4_3_1)194 0 R(4_3_2)195 0 R(4_3_3)196 0 R]>>endobj
+160 0 obj<</D[204 0 R/XYZ null 818 null]>>endobj
+161 0 obj<</D[204 0 R/XYZ null 416 null]>>endobj
+162 0 obj<</D[207 0 R/XYZ null 800 null]>>endobj
+163 0 obj<</D[210 0 R/XYZ null 818 null]>>endobj
+164 0 obj<</D[210 0 R/XYZ null 416 null]>>endobj
+165 0 obj<</D[213 0 R/XYZ null 800 null]>>endobj
+166 0 obj<</D[213 0 R/XYZ null 522 null]>>endobj
+167 0 obj<</D[213 0 R/XYZ null 417 null]>>endobj
+168 0 obj<</D[213 0 R/XYZ null 325 null]>>endobj
+169 0 obj<</D[216 0 R/XYZ null 800 null]>>endobj
+170 0 obj<</D[216 0 R/XYZ null 681 null]>>endobj
+171 0 obj<</D[222 0 R/XYZ null 818 null]>>endobj
+172 0 obj<</D[222 0 R/XYZ null 416 null]>>endobj
+173 0 obj<</D[225 0 R/XYZ null 800 null]>>endobj
+174 0 obj<</D[225 0 R/XYZ null 489 null]>>endobj
+175 0 obj<</D[225 0 R/XYZ null 281 null]>>endobj
+176 0 obj<</D[228 0 R/XYZ null 758 null]>>endobj
+177 0 obj<</D[228 0 R/XYZ null 426 null]>>endobj
+178 0 obj<</D[228 0 R/XYZ null 321 null]>>endobj
+179 0 obj<</D[234 0 R/XYZ null 818 null]>>endobj
+180 0 obj<</D[234 0 R/XYZ null 416 null]>>endobj
+181 0 obj<</D[234 0 R/XYZ null 322 null]>>endobj
+182 0 obj<</D[237 0 R/XYZ null 607 null]>>endobj
+183 0 obj<</D[237 0 R/XYZ null 511 null]>>endobj
+184 0 obj<</D[237 0 R/XYZ null 377 null]>>endobj
+185 0 obj<</D[240 0 R/XYZ null 782 null]>>endobj
+186 0 obj<</D[240 0 R/XYZ null 645 null]>>endobj
+187 0 obj<</D[240 0 R/XYZ null 524 null]>>endobj
+188 0 obj<</D[240 0 R/XYZ null 430 null]>>endobj
+189 0 obj<</D[240 0 R/XYZ null 334 null]>>endobj
+190 0 obj<</D[240 0 R/XYZ null 238 null]>>endobj
+191 0 obj<</D[243 0 R/XYZ null 782 null]>>endobj
+192 0 obj<</D[243 0 R/XYZ null 599 null]>>endobj
+193 0 obj<</D[243 0 R/XYZ null 504 null]>>endobj
+194 0 obj<</D[243 0 R/XYZ null 411 null]>>endobj
+195 0 obj<</D[243 0 R/XYZ null 184 null]>>endobj
+196 0 obj<</D[246 0 R/XYZ null 510 null]>>endobj
+197 0 obj<</Type/Pages/MediaBox[0 0 595 792]/Count 20/Kids[198 0 R
+201 0 R
+252 0 R
+255 0 R
+204 0 R
+207 0 R
+210 0 R
+213 0 R
+216 0 R
+219 0 R
+222 0 R
+225 0 R
+228 0 R
+231 0 R
+234 0 R
+237 0 R
+240 0 R
+243 0 R
+246 0 R
+249 0 R
+]>>endobj
+198 0 obj<</Type/Page/Parent 197 0 R/Contents 199 0 R/Resources<</ProcSet[/PDF/Text/ImageB/ImageC/ImageI]/Font<</F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+199 0 obj<</Length 200 0 R/Filter/FlateDecode>>stream
+xÚíßoä8rÇ¥nõË<ÉÞíwº= $@Æ#{ܳ;@hlk“}8 $QÈC‡ÍíÞ%Áæön$¸ÿ>ýËÝúAJUEJ¢|懛tó#¿U$‹ê¿¼¹Ñö·â«;ñá£øýÿ¼yoùæ/âCôõÍWû_~øøñ&_º¹ww·7÷ûZß¾Ìÿíý·ï³~›Œöåþ|Ü—Oûò¸/ßìË?ýûûoů?ÿöÃû÷nßÿ‹ØÜÞ¾€Gq/¾}zS¼Ì¯\fE>Ý­V««‹]ÙþÇêþiê\òquzªrqyý4M.yw¥F*Á­î'Æ%ﺘžËåu2.<Ôí~
+\W¹ø«Äq®‡Ðã•ËÃ\w\*›æÎŒ•Ý1³Ìõ(<óòEâW~åY)þµS\wžµ2Û8Ã%…g³\;Âe¬v‡Ì—¼ò¬ÿíè\yèõQ¾™ëÁë©Ì’1¹n½ÞŠ¿˲Z·±¶åí(\2ô<ÁÀI!¬–/ç‹Àâ€Á°`à´dðŦåyë¸z÷[fšÍ50Ö,‚+ò/³¸>{#”yï\™7Jù²g®<‡‹ Šà¾Â³´¦ … 퀉huŠÑ¹roÔ²é‹+— 9Å`™ãÅ`ž‹,öD.9>–ç÷À%àòë\™çDÙXæ’¡\3Ë\‘çHYZåÊ<gJb‘Ë+D91Wì9T6Ö¸r—°:¥¦'¨¨&(˜¨&iàµË•º†Õ1`0=G LPã1ÎÇ%]ÄjëaŠ˜a0=—Œša0Üp]\®VoŸvåawñ¡ßƒA†Ë¿|Ûœä»{½ ô?\~KÒ¿|¼êgÀ ïáºì\*= ­ ¸¢~†ª<j+ëa=ô8\„ôcÉÍ Þ°¹Ø¡-]œI6çrqC zznÎZ2$L.æp±Ò©9C0¹X<7’1d>+íGÛ-ÙšÅeƒ'oZ‘z°¿«a’öÎI6Ø0¸Ä`S‹Ÿyйò°È`>‹,ò¾+’ÂX9Ú¹FÂ"‚Í©\éFÈ1Å„È%ÆÂ"‚-i\¹É \Ø[x}YX-™‘!‚µHÞ2éöՒ•ZgŒJdðá`I5üÄ>¡ žKši¦%碞 F\‡ò™mˆ`e…2+z*‚kˆ`Cf{™\´É°ÄrÅc9dž(Ï°\3œ=Á3D07Ãþ¬¢‰kWä„zà¸ÐBštø(®ldÌP° † ¯†AÑ{ÁIØú!­Ÿ!¸2~`6Þ€%Ý\±+¢A°u7WèˆÆ“útren rÀf\±cÃ…ôaIWèØp!Ÿô²ƒ+wÆ%Óºtp¥Î .Jœup ÇfZÊ’V.éàpáæüº•+sp¸pÛHA+RåýA±PV4kå
+Š ‰ÊÑÆ…Uù±PÓcÓÂ…Tù`h®‚dCÀUùÍà\Cœ·p9°·Æ6Ä™ž+sQäÑ‚–h¹ÀE‘G; µ–K8ª8SZ踤«ª›úsWF «žìü¯š±?ÿŽÏ4­¨r†õó¾cÀ
+¢–(®¥–KVnÜ–ÐJt­‰†KÐbV®¹–ëø1eÃ8‰Ÿ À™^ŽËÓsAI½r|¸)ЖœéµFr­µ\yéù
+®c‰{v1ª‹@“YAá*ŽJ¯ãÎSŠ2) ÉÆ‚ÆuTzW̯ 剀&k×Qéu\!g~IÔ³î Šë¨ô®Œw†b$HÑÆŒÈuTz WÌÛìïzþó*—dL¯N®ƒÒk¸BÞáLŒyø@’5•ë ôj®œyŠ‘b3ö6
+*×AéÕ\ 7³HªÊ3¦W7×^éÕ\‚™¹#1Ž(QT@çÚ[w¬_W®†Ø%p›
+ëP¹[)¸ö½*®ƒÝKFÈ!ÝJ•¨]R6¿ÊuîDK4YÐ 1BÀÊ"¨r­Ë¦Î®ç"œ¦`·ae.ÄžÍ\c˲¢Îj\¹†+=òätCL2
+Ñ¡Ö$‚ÀºÎ•)¹ÒSå”û"t
+®²ÞdäEà¹"–l4lb^4¹Ž¶Zá
+Ë–¼“Oí¥Ä%X²QŸ˜§Û{Šuq™«šõÑôT±Zù¬òpŠ}Œ2W\1ì´‡Ô?@Ë¡vñw~¯ÉÛBÉ%\aÅòòR5õêí$ß¿øÔ¿à
+)•+eÊás9LV÷¸º£y¿˜T¬ÌÏ&É%xQ”ó\!/ŠrKšÈ¡Ã\¹¡ºÊ•q£Cǹҗ&‡G®˜ºÍñ£C§¹ÄK“ù#WøÒäðÈÅÙšŸ
+®´©r€rË3,×\¾¬U\‡+Pp…ÍÇh™ë`°ºÜ¹Zg®Y“ëY4ü‹ðd÷€rËšk­æòU\õë|(®ÆÀ“œïüÉcx4×¢•kÿ_«ô`Qu,‰Šk©tEÕxѨìdžÛ
+õÀhvU]<Š6«ÞÈ€«@sªŸú‰¦‹õë|(®z#-Wl‘k£ÅDÝÅúu>W½‘–+⇇…Úà4Ûû.Ö¯ó¡¸êÂZšê‰KXãò W)T­t±vÇUkÄçš!¹üué§G¿|ÜöXª»X»Î‡ãª5%Å®p…v¸Jù‡åxã°í¨»X»Î‡ãª5jÄQ)–+(pë”BÅUz?”¢‹{ѦrUEõˆ(>ryýrem\Õë|H.ÅÀr#,׈+×Åó§ R¹ªÒúT \²g.©¹Å¡¸Î‡äª6Êj’-=,×Òˆ«håW¥‘¬…æ©\9‡«Ú(¬N0qäêÎ=\÷ÈU¹Î‡åª6ŠÉø[®l\®˜ÃUi”žO•Ï›¼£se®J#YîevŠ7L–•¸$‡«ÚHœÏ¿<WÒ'Wù:š«Ú(mœWºÀ•r¸ªj‘ Øs™,—Q\a;WÎáª6ª®ŒƒÈ ®Òu><W­QeÀ’x@®¾‹1‡«Ú¨šF{®Îí ßKxå-Ú…:à§rÕ}./Óa¸NoÐtQr¸êJ[¸Ü)ûüÃ/ÎïŸïäšJ¬,ß„îm¶Éåö¸Ä+×+׸æ/”+˜&WøÊõÊõÊõÊõÊ…ãZ¼r½rõÏå™smW>è{¥”ºãr=ìßaæ_cºK©;2×UýúZõeC¥7Ì)êªOÔ'Užâ¢”¶wÆ\¢‘¾QÍÓ/O5ë2¸6ÕÍО¸>7W4Õ—yåçÎϘU\ëêí‡K*N”ª/4LO[üuó¨“k9¨–Ö¢<Áj鵺 ®EQ™šýp…ª£2(σ°’&S¯Ëà
+ª–Ò Wþ|ðtQ®[~uíù´^Y—Á5«|t?\éé PÞ–>´$çì
+e]>WÖ'W\:±ŽÎÞ¥ä²ÎÉ皺ªm~õ§VwÒ>¹DI äyV5°ôBfM]—:>ÝNâYo\ayNÔ”þl<IK]×ìô8â..“x¾òk8«Åi‚eÕ$mU]×üô8¶O&†+=ÿë”Õ$mU]WP~t 3®ŽôÃÓ¯ó‹mY–Ä*)Ý‘Ò×%qíîÓ²W®™î;žRRqÛV?žK'én
+¯{ã
+ué8Ç3弶¤î๢ãßÛýåMo\‡Hp£‰g•i¤­KâŠ2´³ô¤7®ãYçåF`í3)žå\[—ÄçìW¿èë¢7z{ø⇰ä‹õu•\ÍÄÎ=Ezœ¯;÷ÕW)¤ÖÛøÙÛœ|±¾.…+;@´ûÃf\mçzåz•÷§Í,Zm]
+W~üçÎ-rµÃÆÊ{•ÅqÒY—Â%½Ó Ñí\FçËR›Y*Y-úº®âè˜÷?í«–Eë'‘ u)\ǼwË}r•^´U¥P}Éœ®.…ëà˜³½ï“ëù"l#—J•lÚZÇupÌÙ¾¾WgÞ×C¨š5BùPÔuñ~yï“Ýÿû\6òÙ¯š‹šT³ÈQÕ%p¥{ Ü»åþ¹Î½×B© ª.ëà˜÷ny®gQ˜i'M[]×Á1ïݲ!öÝ¢Y»´Õ%pÉg/¶ŠëœxÝÍU«Kà:8æƒH¶r™äcï^%³®ºb-W[]
+×Ögùyÿ´®ª{…ÎÍN]]
+×V2üìà ¹ÜþaW[]
+×n¤Sïô¢ºÞ¸fÛò[¹tu)\p¸ ëwq™Ü»•_‹Vo«KáJ7kfæ\ëöåײ¼™k¹ÚêR¸²³Sï+-gTû˜ZgÛêR¸òój •Ëäâ¡­>ÔZk¹ÚêvÄó•·mÉê© ײóö|©_,¶Õ¥p•Þ ÛWܲV«sµÔ%q —ѽ켥j«¥.ƒ+1çZàÒmê¡#VÑ×%qÅŸôÀ•ë¦Á•s÷£ªpz*­\FïߨîÅ|Y´réë’¸tïgµËUêìuÑÁ¥­KâÊN>½Ëh#{ç®BíA‰I]£bøÞžc¡|½Þ0_Ågøž%g‹á{±^¹†çê~Ýò4¹â¿Y®d’\Ý›Ir™,˜_¹†ç2ZXNšk1I.ù7ËL’«{6¡\³ir…/0ð5ü>ŽW®¸¢øâ¾Çg=I.x¡\é  ¿×l¬Òõ²×¥á÷ÐUºº¼Æ}o sTj‡Ë¹@ªKêÃïå«Ä(®prGW(ýÞ[×Ñe_Èï)vm§-ìÒä÷J»px(.˜ZÀ!;ý-òûÍ 82×ä¾>íÀD[Î9fèœ6€1WçsÔ)s€‘ç˜ètK€qÎ9°°s
+ã”Ð"H\U§„>FtCë’Ð „ir*ºчÝîë™KNHè%Fº¡À
+ýb2r¸.q‰ébŠ™1€ ¹Ä£pP`…Þ›Šúe®l:‚ˆ2,(ÐBïH„˜£">(ÐBLDe.„Ð;"€²+(ÐBïˆpD(
+Áá(
+¼Ð“Y• !ô³IÈFPåBlÝø“EKLC8Bœ
+
+LjŒrH´qu F9C
+Î|Õ,¨jk‚ œd™‘½¬ 6° ‹ÉsXC>°rHz€5EVFÔ
+œåöÐ1‡ Ï àl sd Þ$Tê#Æ„æãPê%ç1÷ï $ÆÇÜi:Ü€±¦;peu°ÃõfàB*ý@²ž1ðÿÔ0Æ €?S‡°ÖÛ3`)Ó™_‚ :S¢ÀqEX°µÃ5GreŽ ˜ ¹A8F†Xûª¥qB Õ*
+3Cìuá,ùV…™!öªõا»Dsá ±GéÈ l
+SCô“‘5^)^`&°}Zâgo†Z.ém‰¹‘tAalˆ=i¢0ò¡`<i{òÎh+TïŒñ¬íÇ;ç†Öƾ¾—)&CCÙOÌþŠ%2}¤`aâÚ÷b„.¨\)iÀljE³2—ôF“¡ù
+ 
+–ÌÁb@Eš×Kí¶å‹¥°%Ô » %«0WÅ[;&6Ö«ÁhXúx
+$ß¾Iß<È7ï¿ù$n? ùññãÍ×â>º¹òû¿{Ê>#Å;ñX@.òŸþðëÿ}÷ó¢ø凟¿ûîO¿}÷ãßËÿÞ6¾··»Æ·w_ß|ïn?ÜÜíšïš½Ë‹ß½»½‰n¢ÿ¸ÝU~w¿ýÝ»»‡H¾ûå¯ç? ?ÿôýo¿ÿõ—C½7Kê§?ÿõçÿúãþºí槯ÞmÿïÓ?ˆÏ?þ(²ÝÙÛNýïßïš&r õÿÉ„ñkendstream
+endobj
+200 0 obj
+6310
+endobj
+201 0 obj<</Type/Page/Parent 197 0 R/Contents 202 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F8 7 0 R>>>>>>endobj
+202 0 obj<</Length 203 0 R/Filter/FlateDecode>>stream
+xÚ+ä2T0
+ár á
+ä
+endobj
+203 0 obj
+31
+endobj
+204 0 obj<</Type/Page/Parent 197 0 R/Contents 205 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+205 0 obj<</Length 206 0 R/Filter/FlateDecode>>stream
+xÚ…TKoâ0¾ó+F=Ôd P(Ç¥/!µÝl“V{ÈÅI&Å­c§¶býŽ ºl¥UN±?{¾ÇŒ?Œé‹`1éŠz°Jßn—-!­`ÍÂÌ3HËa¬±bŽÒ7BÌ Š"è Át²'•n¸£*»c¡5¨ ÔL¶L@‰¦Ð<Gµ«Ü.Ø Â•ªk%áùqý bÍ¥åò’½±XC6<»zŽ“³l/tWÒCM»‚Q8¦¯cE¼/ÂKÇ‹
+endobj
+206 0 obj
+690
+endobj
+207 0 obj<</Type/Page/Parent 197 0 R/Contents 208 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F8 7 0 R/F9 8 0 R/Fc 9 0 R>>>>>>endobj
+208 0 obj<</Length 209 0 R/Filter/FlateDecode>>stream
+xÚ•ÑÍRƒ0à=Oq–u6€@Ýù×Ó*¡nÜDHÛhI4 íèÓ›@;Žé¢6Iîýr™óŒÝ"H2ÿÕmpKƒËéä**@—î.Ëó(mF÷ªîZ.-f[®·‚ï.軫MAÈP&“¡’®…QK»cš£3\´Lvlw¡ôŠIñÃiìšc©6µrÃk+”4×^ïñ¸ˆb„q¥Þ³ÿ_„˜k'z¥ü6–·GƒÖƒ’Äq' ÷*ã?Ùy–”8©´L6L7Ä5fŸýÿ¥º[óúã0•Smg°¬ß¾ð·¡£Øw’E9HZô¹=ßL©ªy‰òJÕ§òاâ›C’¦Ña}SìÏ’¢Àødô4x
+~aT¡¯endstream
+endobj
+209 0 obj
+296
+endobj
+210 0 obj<</Type/Page/Parent 197 0 R/Contents 211 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F6 6 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+211 0 obj<</Length 212 0 R/Filter/FlateDecode>>stream
+xÚUMsÛ6½ëWìÑž‘}8Šul2É´‡ÔêXnsð"—"b`PŠþ}ß‚ÔG¨L2ÓÑE$w÷½}ûøw4¥ ~Sz7£ù‚òzô~=zóiIÓ%­KšMÙœïîi]ÜLéŽV^ۨ햞!rM;ö;ÍûÛõW¤ÝÓt*iw]ÞÝü~’¥Ôu¥å•j"{j¼Ûé‚)K®Ï'WRåö+¦®®¥ç?ÿør…·wþ5dH¾Ío¨å‰ÏI+ï6†ëïÉuÁ þä<Xù@]È¢¹ͱJó£*7T©@QÛ–‹Äõeöv‘ѳ5ú•é³Î½ ®Œò–þѶpûþò³ÊŸÆ]ƒRÅ:© ^Óy6^!*[(_°/UÎ’: ´X¼*å]Û4Î÷4StÈè7È·MRgÚ¨SÏ;íÚ`¤vJ…–Æ3€~Ïþ• #ÊGÑÿ>kÑqÀè<'€Ú…˜j+Ã6f}±Ùâ4ŒÀ×É=i2Úò‰6½ÜDþÉYsx¹•~W¨ý”{ÝÄ« !·õª©t^nÇéy¯c5ÔÑÕL¹Sßt§ÇrxnÚª
+ Å~kW´Fyqª$œO @õ¶#@àäÎ&Ò'6(¹,ù„+ß\z
+Xx< UÓ«t°á¨Á=«&•ß°åR4I»X\.‘óÊ:㶇ÎÙàŠË-¯’‡óÝ°XÊòëÛîþa’-‘€Ð¹<\þýçm»endstream
+endobj
+212 0 obj
+843
+endobj
+213 0 obj<</Type/Page/Parent 197 0 R/Contents 214 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F6 6 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+214 0 obj<</Length 215 0 R/Filter/FlateDecode>>stream
+xÚWÁRÜ8½ó}$UŒa€r !,¤HílÆ©½pÑÈ2£Œ-9’ŒwòõûZ²=ŒC¨­Pa<’ZÝï½~m~ÌéÿætvÁ?²>¸ÎŽoßÓüìŠòk——ÙåÅa¾V”+¹6¶²OÛwùwl<§ù<m›½OÛ>~[,I{Z ¯
+jkHRµrOÚ<ѽ ÊÈa
+’¢ª°3 ü¸¸pÚÞ¾p6Xi«#²Žî‹Œÿ£µ@|¥ '«çgÙ)_­ê•±V[*ìOe<Ù’Ž¥²(ÆÏ^¹güª…iK!Cë”óGqG§«
+±É·Mc]HÁ87£þ ôEKg½-Ãäæ´)l‡ëåDLÜo}PuÖï;½ÈÎy'_¨RåIì hú*©D•ÍP;Šì²Áo$*"|q‘¾Û•ß«Ç6A[”[é¢ZZLôú§:"§¼­ZÞšŠõ–o ëŒø ˆ
+¿ËóÅuk<† QêÔª×-§ÒIAFùG@K’WÀ ·ô¬U‡Îm D"IøÜzÒb¨Ó×¼²S×Ø0­¿^­]Ìnåp¡rSL¢#€Êg]DÙK[7•
+Š©<žg'£à¬cë0Òawì0È ™1ùÂÖB#a7Ikr’õ‚ÈèF?)^ •?,_Hc´ñ,t%V•BT¶ìÄ,ÄŽ³åä^®6KîøÒFÙ#ŸÑ»¿5ÏOÄ—h‡T=XôíªÖ}hÏ°Òû¦ í¢vsؘÑçhÈX"@©“¯ jþÑÂÜÚzŶ„KÖ0
+±^RQœSȳ9£¿xy‚os
+¿ñÙT×BnTj¤B³3‰µý…p]³ü#‡èŒ6ÏèoßFdz©ô%z
+Ä ¢«7-yHfÏaöIÖ5Wq=¢»ÅìχãÓáqqs;|d@Ï·øœ½Fõ8»+´+-PóR:Ý„xvw }ž ¹ß`H²‰[~KC’_ïø©hbœÉÅI¨ÎÒJ‡Z4½ú7›>]ºùA·JôN°‹oE°Fvè*XhDTÍ$Á4¾9abwÖm0œE|þ¬ÂvPØ$ÿÇÃM³´¸/<¾;¢ej™/€…ºÆKÙ&E~Àëeê<¬ßUC©ã›ToW½çççÙ{º¸ºJ6póõÃm}Å"—xÏí¸%¿yVǼ,‹ŠÏÒ©Ùåé :çïÎO®2þcâÕ?>åü$ŸQendstream
+endobj
+215 0 obj
+1367
+endobj
+216 0 obj<</Type/Page/Parent 197 0 R/Contents 217 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F6 6 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+217 0 obj<</Length 218 0 R/Filter/FlateDecode>>stream
+xÚ…UÁRÛ0½ç+¶¹4Ì$'![K)Óvh‡–0½p²«È’+ɘü}w%Û$¦°´»o÷½·â÷(…üIa=‡Å
+x9ºØŒŽ¯Î!=MÎ`“ãÝj½NV°É&7Vj/,\Zù$¬;ÚüÂÈ%¤iŒ›-Îã²RÇ»›[àF;é<˜Œô«4V@.•§0W .sÉÁ`PÅ"IL•š«:/++%º{j¡ÓE2'è ¹±ðI4Jx?»aü‘Ù ®™ö‹ðÀt—Â=Òßm—ÀÏBbU_H×ö ™A<m<l…–yì¸ò²d
+Lí«Ú  3“y.¬Ð}aœ0ÊMAúX/%²àC¹Â4°35p¦¡±OðË‚itŸÞQH=#ÆV†Rо%^#oDV…Ù—¤YRä7ác¥ÞþO½ˆÉsÛ6©  ÇñÝÎyd¨«½)™—x¬và
+<Ë ‘¾
+¦qÄ _WÁ&-žRÜz¬]«[×—Û @ÍIKÔÆUÆ(ä HR!Ü ô#õGC|Ÿ8£¤öqºàYÎk‹ó`ßœuÓM)æe/6 ‰.ÎòI=·ìáKvc%F)„
+ „ýÀB:€wÅÄ3n‘‹4SÇŽ’(sJ\ reC:¡÷! K|صK¶{Þ’]È1±z?É6q›ÿŽÈKÇDæÁÑ|œÜµåæ«h´°µL9RÍ<IZÝè¸U븉Ä5–÷¢õØ¡!'Sˆ‘˸Q4‹ÄÍ!Ú«=‡c3I_’ †$ÛÁߌVÖÊKzV"*ƒÈ…ô%«bçÈêÒ]'Óà(ñ^¦)8Ä`£ßz°B‘Ñ5äÕÛœ
+…ªxšÀ…ଷw`“`ÛI{oÅñ§ý[F ¿6gŸHœ‰”ÇúvìŸ8ìmÜ6Ø ês&UmÅj­ÐÉÄ%´{³_l
+endobj
+218 0 obj
+821
+endobj
+219 0 obj<</Type/Page/Parent 197 0 R/Contents 220 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F8 7 0 R>>>>>>endobj
+220 0 obj<</Length 221 0 R/Filter/FlateDecode>>stream
+xÚ+ä2T0
+áÒw³P04TIS041ѳT072PIÑp rt QÐUp VÎO+)O,JU-N-*VðMÌ+MÌÑ ÉâÒ…èÑ…j2‰™šé™+€ùE™y%©E
+.E™e@ Y×®@.
+endobj
+221 0 obj
+124
+endobj
+222 0 obj<</Type/Page/Parent 197 0 R/Contents 223 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+223 0 obj<</Length 224 0 R/Filter/FlateDecode>>stream
+xÚ•SÁrÚ0½óÛCgÈ ¸6PÀ=¦CŽZœž¸{‹Ê’#Édü÷Ý•pÒ¤i;\¬•Þ¾·ï-“ Rúe°YÀr e;¹-&îrÈr(jX¤«$ƒõfE5]ÀîÔà„½•Úóá08íMq&Ü
+²Œqóœ/Wi°E#”è<Zpyr0˜è¼ןZégðØ£f t¥Ð%*è˜Îæäø]%ë-R%ÔѺ$ò’ÞÉ–™S"Í#ç!t ï¤Bµ±Ï²_ë¨5£>ßïÔÞ\dE“ñM7N ßá8Ðô
+ªîu¯éñ&Lp‹ö*þ„°¿CºÑÏÒ´-á]…¹Î/ ¦Ø–Ra-zå¹éΖɂu_£Ã#õ§439Íu5Œ¶¬£ýâ‚ñÂh$ \;÷΃F¬ÝÐá§Q}ÎD5¯É3é¨!ãÃ{P]ªE‹°ûRì¾½Ù
+†Ž>û_í¿w öº¢õðl$Ã/»ÃC90uèèèÂbéíÔ¥ê+Î`oœ?”Vv>D)[ñ€ñ9¥Â«,”¢~ÛçÇÌjkZêèhb
+endobj
+224 0 obj
+504
+endobj
+225 0 obj<</Type/Page/Parent 197 0 R/Contents 226 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F4 4 0 R/F6 6 0 R/F8 7 0 R/F9 8 0 R>>>>/Annots 12 0 R>>endobj
+226 0 obj<</Length 227 0 R/Filter/FlateDecode>>stream
+xÚ­VmoÓ0þÞ_qB* ÍÚõi lð([ø^â4ǶӪÿž;;I·ve›„6uss/Ï=÷Ü9 Œð'ñ”~³jð1¼»^@rÏ!-ðÙt6‹§æÃe©µj VF(ÇÍ›ôO I‚i4^Ó¯LíÀî¬ã•…­J¶áPiÃÁ•LVê؆ Éî$>Òø”Cc¹‰!-¹í,dèuÇ9Dz’ç­-!À¼É8>§¼RgL¶‰a#B­™aRr Ú
+ ƒ‚38\<·1|Ñ[¾¡æb\Áá@?NT˜q[råµJžÄi†ËcÑå£è~ÌÈÉ!Þ‰™v:±æ#ݽ@@€ Do­‰j”ñQÕߌAºSÁ •§Ab6›±šÓÿ‘ôrvA§Šç‚]~˜ôýŽÕë'À³Õ‡ëÙ‚ÒÝ1¼Ážßƒj=™Wé`ðö „>n>Ãd|Žé“Å ‹¬`2áßp’pKw š.æBx´F{B7ÌìPc5W91K#UöVþv
+Å¥aÍÝþA‹ãäý~jª¶¢íý¶ ±/SÓðƒu1oã$“I¼€é|NúɇŸn>\§Áòçênuá¶TçOK/Gø¾Õ0IîQðŠfç#ršÓwãÅ~5:ý‡#òcð7?òäendstream
+endobj
+227 0 obj
+916
+endobj
+228 0 obj<</Type/Page/Parent 197 0 R/Contents 229 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F4 4 0 R/F6 6 0 R/F8 7 0 R/F9 8 0 R>>>>/Annots 15 0 R>>endobj
+229 0 obj<</Length 230 0 R/Filter/FlateDecode>>stream
+xÚ½UmoÓ:þÞ_qî$¤^©É’4kÚI|àŽ xk&>;©C Žk;«öï9ÇNË:
+Ò@B•¢ÚçýyÎ9þ’B‚¿Š æ ¨»Éåäô*UœAÙÐÝbY@¹™þ[~™$¥)žì!R †.ªu/¸…HÃ…–’9þ¼4‡FH®XÇáò}yyCÖ§W+HÏâ%ùÐq4_Å9y»hyýU¨ÏàZ×F(Ç ¬sƒ…ÆèÎß_è®cj½Šo9P2 ¦Ožä©DÅ LFáTö=ZL¡Þ f
+*ƒåpjÊmà‹®,Æeîõ
+ƒPŠýZôñrþN[üz2ÊŸxõÓ ^ Uã°ß^¯ eëõ_“…]ï²¹6ÚéZË)’Vƒf2j8F3ØÄ[^a‹š;n|·i8ˆî)üe9Iâ·}Rúܼ‚l™Æ+Èά­CêŠøl<IXÓnLð˜Rá›ieôÖrß0VÂisÐ& Uˆgïq¦ºöcu{óÂÆ7ŸaÚ:ןŸžJ]3ÙjëÎóôP5Ù©žÄp5Bj|i´Í×à¡“mà@7PKfÙ™oîÙáÐn…k½z-.BÔfP ÎiõÏcÚ“¤3äL/,Potõ”]{¿ØÑ(E’Cž”÷f,X“Q$6£Ÿ.à ^ìò–“£¦£ç¯ò ‡xË,ÞkãÂSB|T÷Ì^¤ÇΰsžòjÙq—£ ÍslÚE–
+C´ÖÛÒ.¾µDð;¦æ‘Ž‚UTdÉ~C…ïéD/éÁ¥wTóe‚š^m5NÎÇÉ7<x`Qendstream
+endobj
+230 0 obj
+814
+endobj
+231 0 obj<</Type/Page/Parent 197 0 R/Contents 232 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F8 7 0 R>>>>>>endobj
+232 0 obj<</Length 233 0 R/Filter/FlateDecode>>stream
+xÚ-‹A‚0÷=Å[ê¢Ø/(ºT” J9@£ÅÔÔ‚´ÄëkÕ¼·šÌ<A|FH×ñ—ÛK¶(7 ‚ì@Y–l‘/äuv8ïJ Ž¢­4}^jÔh½=*å&eçòÎø¯áÿˆD„iNÉ
+_PÆãn¨&Ì`5Š~0ÚGí(Ù‰½5'-endstream
+endobj
+233 0 obj
+133
+endobj
+234 0 obj<</Type/Page/Parent 197 0 R/Contents 235 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F4 4 0 R/F8 7 0 R/F9 8 0 R/Fc 9 0 R>>>>>>endobj
+235 0 obj<</Length 236 0 R/Filter/FlateDecode>>stream
+xÚ¥TMÓ0½÷WŒVBJ¥&Û|4m‘¸ .,,$Üzq·1rlc»´Ý_Ï8N¶Ý²° ”S<3ï½y3ö÷Q Süb˜'æPµ£·åèöÝâ%”[HâY”B>Ï ¬ƒB(,5Ñ5Ük&,ÕðIY&…—ß°,ƒ8ve¡¯ Ólu¥eà T Q®¤¦¦ÒlC ؆‚U(="‰¢)„q²á >‰˜m:O>íÉ®žª  ’m‹,‘?ÇæfÑÂE¦(q龧‚jŸïÇçå¾
+[ɹ<8 ƒV¢?]É#œƒ=)lSnaË85gþ4JÜÄã”Óª«sÝÑš(Ø@‰@ùPȽ®è¥Ý„ZWüæxz¸éõ¡Ö›Ý…Àt¸vÀ½•LýzpréõáF„^k¸’9Ö:x\Á#ÕjÝà\‚´VËÕ—ó'w{nÙý^+iè?—š£p=Duú/æ—.¶ÚÙqiöy1Ͼo¬ñó¶\yEñGá¦Ðoú6{ ­4Ì^)©mýe»†~úA¬ƒŠ ax¡ Û ¶ev=îgWõb“f‡Iî·{äóëk⽸¾*!|- è:XD³#F˜¨×.JçÇd¾lÛõø)á¥ÂgÙv„ÿ† #WvÍ•Îò3×âñ}éºÃ»³ø›‡)›g˜8u©qìVåèóè'½G{®endstream
+endobj
+236 0 obj
+539
+endobj
+237 0 obj<</Type/Page/Parent 197 0 R/Contents 238 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F4 4 0 R/F6 6 0 R/F8 7 0 R/F9 8 0 R/Fc 9 0 R>>>>>>endobj
+238 0 obj<</Length 239 0 R/Filter/FlateDecode>>stream
+xÚV]oë6 }ϯ 2 kس×Iô¡·í.ЮYí¾õE±•Äƒ#y’Ü$ûõ#e;7‰=Ü›¢HE‘‡<<"ûÏÀ¿|Gôn_’Áï_Sð}H–LÝ
+½';ßw£ä"]s=© ð½]0›l6ï×µo‹æãð î_ž}¯‹öÃ/¾âƒ²ä;sov¡ë7'¨¡¿ó½›‹Qžú |x:†œºQ„˜ãñ dàaõÞʼnbB—Lq‘î»àÇVØð,g`ö˜jY©”_„õV–\õJÇP2úmÛ_óInûbÚãÏÆ|®
+“Ï+UJÍ»¡­Õ)kó§ÓfjÅïYÉÒÜôðnÍ6ö3Ǿg<s#
+Ÿ¬9°ÔT¬
+ º*K© Ï
+LH#«|Ø À6/
+PýyMÖ 39[)Î5YóE¼QþÌ?Úgu–8Ø%’‰*öé_¡(á8K½`&¯çå:©-xN9PÅ/¾H³†8Ï0;¹´IÏYç]õ¯ÉåÖl¥CŸ2G¯Q"CêQ¯¹b5<(Êò„Ù.P-YU|GÙH«›¶Ì¦êF_8¨òå±àZUjÈû~ 6¯N:M_rqn,Þ§ÍwÖ¨C8ò¶kn}Ø_A' e¢Xnêxw±—ÌžŽþБ’¼D 1/xj¥Àà• T+Š€^ªþ¡
+“„¬ÿà‚J–YÖPéz¼d¨¹¹ÅŸ6àç´EAäguB÷mr}ò˜6½õ½©;ƒè&r'àáõîk‚ùþmC,—fK+õMSÛž™À­kW²†èåLœü€ƒÉ¼Ó7KØÅ´XG@ûÏ®I Ýþ;õ˜ þüˆ@Rendstream
+endobj
+239 0 obj
+973
+endobj
+240 0 obj<</Type/Page/Parent 197 0 R/Contents 241 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F4 4 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+241 0 obj<</Length 242 0 R/Filter/FlateDecode>>stream
+xÚ¥U[o›0~çWUª”Iƒa`„LêÚ¥Ó¤­k:i.˜ÀflfL³ýûÛi.R›¦©òás¾«IþxBüG§P´Þyî½»˜
+ÏÒŒ˜¯åhδnÄtÍà\5‹Z Ö÷oò_¸
+* B+Éíš¼gŠrwëu•=éT#4+Aº4 ýÓ‰/·ÆÏ:¦
+&ô ÈN7R|pô!Lœ\ôà;éåÈ¡#¬ï`ŠD!T g‚¶ f—ùìf׋oÀÜü |`=,£š)T†ÎH²A;Ü`2±1ò–5"ýÛâÆèîJIÕoÜhtàH·÷ãÈ‘n'þ™¶-…©TŠÆùQ¹/,H±9(}»tf­»Ã ãðõ‘“ùÓ¡¿²M~‰¡ÍÙ_ ߭ͧ/wŽ¬•ä\.MR.”h×ñ(9¬$›¢3žûC›¾Ú;ô‹ÕLkªhæ{¸Â'_DQïUdÚ*ºf·+è™î-°X`|¡ AàWjˆHôh‹®Ð½&¿6ø6îâ[Ì£­ŽìõΦ’í3—ÈVææž·eïÒjúå}­X¯,[É#aL MR'ñÓÍÇ‹|˜Þ^Ía.+½¤ŠÁmonÎ7*ÊͺO’·ü1þ¢îý£HÆ ¾ovˆÄæÁ,÷®½ÿMÚòendstream
+endobj
+242 0 obj
+521
+endobj
+243 0 obj<</Type/Page/Parent 197 0 R/Contents 244 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F4 4 0 R/F6 6 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+244 0 obj<</Length 245 0 R/Filter/FlateDecode>>stream
+xÚ•VÁrÛ8 ½û+0éŒ3µTÉv§;=dÓtÚC»ÙF½åÂX°Å„U’ªã~}R²%M±I
+—îýO¡|5â3Þ82rU<±ìt½]U¾Û¸ÑÎé²Ý];©+û.äœÀi`‰¨ÛÆî ¤üòT « XJ…•(.¾fßþl²‡ö`›m*[ìçø„Ñ®¸'Ú•7#êÃö¬ÝzM†TìUSbå@Z_®P> §ÖTdû Xá…ϤoOÆ´KKÚ@OŽË2>÷eMÆÆ¥!]nà’Còü£³ RŸ÷úêê uCÊP ÈÑ€Z#Yën-]±SaÕ”7hFp«oÀI§®‡m:Á÷Ù±z}ôÅnrá0†3eõÎýêù›7p‡›µ6yh’‚ê¯X˜»….=—JVŽH'”\È_˜¿\•;*ž,;ñ}Ï÷ ?eóÏ%çÿ_h…gù^j¥ôš‡FÛ8 êšøYX…ç=éý1
+VÑÉ8a£tÊ‹“Ù$>†äo—µ‹lðÿà7öEÝendstream
+endobj
+245 0 obj
+959
+endobj
+246 0 obj<</Type/Page/Parent 197 0 R/Contents 247 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F0 3 0 R/F4 4 0 R/F5 5 0 R/F6 6 0 R/F8 7 0 R/F9 8 0 R>>>>>>endobj
+247 0 obj<</Length 248 0 R/Filter/FlateDecode>>stream
+xÚ¥VQoÚ0~çWœ&M£R“:i0©m×v•ÖµƒôaR_ \‚Û$fNRÆ~ýÎq¡0iB
+äøüù¾óÙŸu`ôq ï©Ó¤stN® m‚PÇüA‚Y÷(xî0°G¿|„x–„yg¯<.BcÊ„«ïÁÕHƒO®=Ðè,b±N‡¶¯‡só·_ýÝ-¶‡t«¨H0ÍAdÀ!-’ *•L4ƒ\‚þâé .fÍ 'ò©Œ¥Ò™’9Ï…LmГ†2ŽåR¤ä|ãZÏ©:ëfE’p%þ`E2çi„°’ŧ8† –"ŸÃL„!*U9KöÙdÝ«²v<ÇÖR™íiÎ{%"‘òXƒºÌÓ³¼^êÙÎ&hbWA‡ÙŒ°õctŽëØ zßÀ@ÿt%­Æ{À.s7`öxÐ
+µ0å\kü‰ºÒ–Tº†ï©ò}bm«Ê€[ª2àU¥–žk–èF!¦M5UîQ¯4]P‹n¢‡y>Q·dÀ-ð~A§®éc“SÑ}½ k-¥hkU#©q}âm«Æ€[ª1àýjw]ó¦’ñËÊš¬cìm÷RÁ|z¶UaÀ-Uð^ÞСmE9ÞñˆŽ Þ”r›ÎD$›B.•H2ù^wyth´>*p;%x¿’¾c¶þåŠoí–í]±–òMDóÜJùëf¹Iò{íO€
+ÜR’ï9׆`ÎýöXåLç³ç"ËKƒ ó¼P¥}ÀS÷RŸöOG Cr¸MxôƯضÁ} oÌj‚³ª)uÀ‹’—3§ÙÀ4{m`ˆTÏ%ô\ÇÓ9Äâ÷’ÊÉ#²,9Æø*2"­ªiè5z¯¥ï¦ý_Þ^qüÓݳNE(°éÝ’”†_º½Ë˜ 绩‹¿PrVL‰„¿)È$æÓ‹n Ör.r$ Hóc —éZaî.DAìäðdõ oÖ8ü+L0^Ñzä˜fh)ƒÚò={žÇÌ|_Fç×Xpùø0†± ó%§®{̨îxZ˜Ë€eFY}—í6ÞWÊè©Ðt» çõ=Ú¤å g}_øÑù 3ü}µendstream
+endobj
+248 0 obj
+839
+endobj
+249 0 obj<</Type/Page/Parent 197 0 R/Contents 250 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F8 7 0 R>>>>>>endobj
+250 0 obj<</Length 251 0 R/Filter/FlateDecode>>stream
+xÚ-Ë»‚@„á~ŸbJ(À=BPJ¼-LTÍF„Èa‰¯¯¨™©þä{
+‚üŒDóoØ°X¤k+Pú1VK .Ý5I¶ù9CÖWö¥|ÔÈ“2“z¸Ü
+ïg¼?¢hŽI?À7$e;¶15ì]ã0i7Æ.ú
+ÊàØ©ZÏhÏâ"Þɉ*ëendstream
+endobj
+251 0 obj
+143
+endobj
+252 0 obj<</Type/Page/Parent 197 0 R/Contents 253 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F4 4 0 R/F5 5 0 R/F8 7 0 R/F9 8 0 R>>>>/Annots 156 0 R>>endobj
+253 0 obj<</Length 254 0 R/Filter/FlateDecode>>stream
+xÚÍ›QsÜ6€ßý+ôØÎœ7")JâcãÔ¹v.­/v¦/y‘mÙÞvµòiw“Ëýú#HP"•dfj{Ó7Ÿ,
+ðƒÿ œn‰9Ú#hßaâóp~äõzì%pé1|Ë1éèÍG™•e™':¹,¯µ!°GP{ó°í7ýý—o›Q2>im/"æ,¤Y ô;–8 ÍY”vaøµ¿Þ}ÛLu3ŠàÚ]Vªq@Et¦9Û4»]»[ªúŽØ*1ƒh§Bú/M>1Ò <·«Áùz³o‡ƒèÚCu ÄŒ’—&ï–5,‰f@%¯¼´ÞËùÈB>bs<Î#îÍ°þ´hc‘Y:Ò$mÖB “ˆëZ‡‡Pf-”‰´ßÚýç~øK¯– yùhJÕTc@áZ”¥IDPGÚ¿'®EYÙ„ÄéÂÕáM}ðí,Rb…«ÃèµA…œF·YÌxê&ÓÌ~%œFÖžZÐ=eø€Ê¹õpQñ2ËG@¥ô¾'
+ Ñ–Ô _äQUä”Ân˜*iì ­
+.d»ŠÃu·ÞÓfõè°¹‡Mhð,œ/Ƶ¤Ý>òôíƶiN
+¤c¸)Ë/ÛÈ7çá 5BiÀq¾N*T|}È‹PÐ4'`{Iûu¿]*TøQl?cNaÂì–ªÒ½NaŪN[b<¢mtÖ!ç‘÷î°Ù¯7-•'´m`w
+C˜
+^õöz!ÙæÏÓ‰E«Ypt´BÑkd´ºsÖ³f{Ónèp:\Ùø0®vZF CO–L(²bÆ„*µZNÚPÈäó—vˆûµ_ð+Ž¤‹®È¾‚(*{¼¨_Çþ=©¯ ŠÚV ‚î+8¼Ùþ|;‹”X_Áaôv=186¶·ÍpK6¦—†ŽÚŒW-¡êe>MÔó—ÔŽ®z¡›þÂáÂ"GÑBfÙ­L24ƒ´^€Ò.ÇoÛm;4¢øÐî
+{…v›»¾%ÛÄž:$5Ús,-¬€õwTÎQž/x.ç¾ÐŽCÆ­¯
+Ù¾ñ
+ î¬ÈM‹eÂómâXär/ðöb Æ-x¤kà wg±ý/Ññh]¦à !@ã4¢èÝYñòûÝïþH¸»h"–«Xµ%òïùD݆°OG‹Kù@ªÌç¿(
+XÔo‡îšìoxú¶Ž®g4+`iäZæéBSC’°¤³‡fhnà¾Q+y M,òÑA®V²–xA½¦¯¬QRÍ9-à@¿lo~pë ":•Lb3Ë “±B‹•«ú{âÒñmäø|'€À”Èt08 –›ŽgÃ'à¡@™PiyÚ°ä£YÃèt,8M¢BÓ@zÿN L0:4=eØTóßÌ ŒÏ'¾Œ Óœû_Îc_.¾¯âs|F>ß ˆôŠÏÁl 0@Xb˜:ž£€‡ÈKSdqfò[ÀBw ¹ßºÅX=Ó5ÈX`Àîy*=íˆ
+ ¦òï*=G¾q\ÀGLŒVÀ5¿f vcÍӇ柿:
+8Ú»f¸_/ºR>ÿõiVD¼ZM—–݈ôj5Þ‰Öný;sñºØ•!Î\<yïåOèXäj9üãUsùË„C»ÍJ.¿tdôy0ë!¢q¾H8c9®>•…ÂZ é숌J™c­tÓl"¹f"Ú–”
+ù(à€‘\3©Ã6ŽB˜p0ãî…ÎÏ ï³¢x•Z—Ï G¤GÞ¬þéöÏÃ.Ö&œ˜*à®wð'r“ÜWŸ@— „¡
+K=ô?C"
+T u†§ å0ìêC
+ «ÙRµð¤ l97›@ÊO}y:«Ü]P7aÃE¬Ráx)<=VÝl,ñàÒº(’Žh'u[]Ìa(à®è7ûÃ`Ž¿¨
+ ó#”Ÿý@ŬéÌŽ{†tˆ¤bÖ×îLWJÎX(P¸ƒZª0iÃ%Ã|ôýØ°È¢b¶úûw]ÌüK¡Wç5^°8e&ÉK…·"Þ¼ÿéü*;ÍÎ>\\f—ýÝþs3´Ù‡´SÞ5ÛC³}! ÍqZqóÛ­Ñÿ>ù?7±½endstream
+endobj
+254 0 obj
+2716
+endobj
+255 0 obj<</Type/Page/Parent 197 0 R/Contents 256 0 R/Resources<</ProcSet[/PDF/Text]/Font<</F8 7 0 R>>>>>>endobj
+256 0 obj<</Length 257 0 R/Filter/FlateDecode>>stream
+xÚ+ä2T0
+áÒw³P04TIS041ѳT072PIÑp rt QÐUp VÎO+)O,JU-N-*VðMÌ+MÌÑ ÉâÒ…èÑ…jÊÌ º†pr
+endobj
+257 0 obj
+102
+endobj
+258 0 obj<</Count 5/First 259 0 R/Last 279 0 R>>endobj
+259 0 obj<</Parent 258 0 R/Title(Table of Contents)/Dest[252 0 R/XYZ null 756 null]/Next 260 0 R>>endobj
+260 0 obj<</Parent 258 0 R/Count -2/First 261 0 R/Last 262 0 R/Title(Preface)/Dest[204 0 R/XYZ null 743 null]/Prev 259 0 R/Next 263 0 R>>endobj
+261 0 obj<</Parent 260 0 R/Title(System Overview)/Dest[204 0 R/XYZ null 371 null]/Next 262 0 R>>endobj
+262 0 obj<</Parent 260 0 R/Title(Document Overview)/Dest[207 0 R/XYZ null 736 null]/Prev 261 0 R>>endobj
+263 0 obj<</Parent 258 0 R/Count -7/First 264 0 R/Last 270 0 R/Title(1 - Printing System Overview)/Dest[210 0 R/XYZ null 743 null]/Prev 260 0 R/Next 271 0 R>>endobj
+264 0 obj<</Parent 263 0 R/Title(The Printing Problem)/Dest[210 0 R/XYZ null 371 null]/Next 265 0 R>>endobj
+265 0 obj<</Parent 263 0 R/Title(The Technology)/Dest[213 0 R/XYZ null 736 null]/Prev 264 0 R/Next 266 0 R>>endobj
+266 0 obj<</Parent 263 0 R/Title(Jobs)/Dest[213 0 R/XYZ null 478 null]/Prev 265 0 R/Next 267 0 R>>endobj
+267 0 obj<</Parent 263 0 R/Title(Classes)/Dest[213 0 R/XYZ null 372 null]/Prev 266 0 R/Next 268 0 R>>endobj
+268 0 obj<</Parent 263 0 R/Title(Filters)/Dest[213 0 R/XYZ null 280 null]/Prev 267 0 R/Next 269 0 R>>endobj
+269 0 obj<</Parent 263 0 R/Title(Printer Drivers)/Dest[216 0 R/XYZ null 736 null]/Prev 268 0 R/Next 270 0 R>>endobj
+270 0 obj<</Parent 263 0 R/Title(Networking)/Dest[216 0 R/XYZ null 636 null]/Prev 269 0 R>>endobj
+271 0 obj<</Parent 258 0 R/Count -7/First 272 0 R/Last 278 0 R/Title(2 - Using the Printing System)/Dest[222 0 R/XYZ null 743 null]/Prev 263 0 R/Next 279 0 R>>endobj
+272 0 obj<</Parent 271 0 R/Title(Submitting Files for Printing)/Dest[222 0 R/XYZ null 371 null]/Next 273 0 R>>endobj
+273 0 obj<</Parent 271 0 R/Title(Choosing a Printer)/Dest[225 0 R/XYZ null 736 null]/Prev 272 0 R/Next 274 0 R>>endobj
+274 0 obj<</Parent 271 0 R/Title(Setting Printer Options)/Dest[225 0 R/XYZ null 445 null]/Prev 273 0 R/Next 275 0 R>>endobj
+275 0 obj<</Parent 271 0 R/Title(Printing Multiple Copies)/Dest[225 0 R/XYZ null 236 null]/Prev 274 0 R/Next 276 0 R>>endobj
+276 0 obj<</Parent 271 0 R/Title(Checking the Printer Status from the Command-Line)/Dest[228 0 R/XYZ null 714 null]/Prev 275 0 R/Next 277 0 R>>endobj
+277 0 obj<</Parent 271 0 R/Title(Checking the Printer Status from the Web)/Dest[228 0 R/XYZ null 382 null]/Prev 276 0 R/Next 278 0 R>>endobj
+278 0 obj<</Parent 271 0 R/Title(Canceling a Print Job)/Dest[228 0 R/XYZ null 276 null]/Prev 277 0 R>>endobj
+279 0 obj<</Parent 258 0 R/Count -3/First 280 0 R/Last 293 0 R/Title(3 - Standard Printer Options)/Dest[234 0 R/XYZ null 743 null]/Prev 271 0 R>>endobj
+280 0 obj<</Parent 279 0 R/Count -6/First 281 0 R/Last 286 0 R/Title(General Options)/Dest[234 0 R/XYZ null 371 null]/Next 287 0 R>>endobj
+281 0 obj<</Parent 280 0 R/Title(Selecting the Media Size, Type, and Source)/Dest[234 0 R/XYZ null 286 null]/Next 282 0 R>>endobj
+282 0 obj<</Parent 280 0 R/Title(Setting the Orientation)/Dest[237 0 R/XYZ null 571 null]/Prev 281 0 R/Next 283 0 R>>endobj
+283 0 obj<</Parent 280 0 R/Title(Printing On Both Sides of the Paper)/Dest[237 0 R/XYZ null 474 null]/Prev 282 0 R/Next 284 0 R>>endobj
+284 0 obj<</Parent 280 0 R/Title(Selecting a Range of Pages)/Dest[237 0 R/XYZ null 340 null]/Prev 283 0 R/Next 285 0 R>>endobj
+285 0 obj<</Parent 280 0 R/Title(Setting the Brightness)/Dest[240 0 R/XYZ null 729 null]/Prev 284 0 R/Next 286 0 R>>endobj
+286 0 obj<</Parent 280 0 R/Title(Setting the Gamma Correction)/Dest[240 0 R/XYZ null 609 null]/Prev 285 0 R>>endobj
+287 0 obj<</Parent 279 0 R/Count -5/First 288 0 R/Last 292 0 R/Title(Text Options)/Dest[240 0 R/XYZ null 479 null]/Prev 280 0 R/Next 293 0 R>>endobj
+288 0 obj<</Parent 287 0 R/Title(Setting the Number of Characters Per Inch)/Dest[240 0 R/XYZ null 394 null]/Next 289 0 R>>endobj
+289 0 obj<</Parent 287 0 R/Title(Setting the Number of Lines Per Inch)/Dest[240 0 R/XYZ null 297 null]/Prev 288 0 R/Next 290 0 R>>endobj
+290 0 obj<</Parent 287 0 R/Title(Setting the Number of Columns)/Dest[240 0 R/XYZ null 201 null]/Prev 289 0 R/Next 291 0 R>>endobj
+291 0 obj<</Parent 287 0 R/Title(Setting the Page Margins)/Dest[243 0 R/XYZ null 729 null]/Prev 290 0 R/Next 292 0 R>>endobj
+292 0 obj<</Parent 287 0 R/Title(Pretty Printing)/Dest[243 0 R/XYZ null 563 null]/Prev 291 0 R>>endobj
+293 0 obj<</Parent 279 0 R/Count -3/First 294 0 R/Last 296 0 R/Title(Image Options)/Dest[243 0 R/XYZ null 459 null]/Prev 287 0 R>>endobj
+294 0 obj<</Parent 293 0 R/Title(Scaling the Image)/Dest[243 0 R/XYZ null 374 null]/Next 295 0 R>>endobj
+295 0 obj<</Parent 293 0 R/Title(Adjusting the Hue \(Tint\) of an Image)/Dest[243 0 R/XYZ null 148 null]/Prev 294 0 R/Next 296 0 R>>endobj
+296 0 obj<</Parent 293 0 R/Title(Adjusting the Saturation \(Color\) of an Image)/Dest[246 0 R/XYZ null 473 null]/Prev 295 0 R>>endobj
+297 0 obj<</Type/Catalog/Pages 197 0 R/Names 157 0 R/ViewerPreferences<</PageLayout/TwoColumnRight>>/Outlines 258 0 R/PageMode/UseOutlines/OpenAction[204 0 R/XYZ null null null]>>endobj
+xref
+0 298
+0000000000 65535 f
+0000000015 00000 n
+0000000218 00000 n
+0000000279 00000 n
+0000000353 00000 n
+0000000431 00000 n
+0000000508 00000 n
+0000000587 00000 n
+0000000663 00000 n
+0000000744 00000 n
+0000000802 00000 n
+0000000907 00000 n
+0000001012 00000 n
+0000001043 00000 n
+0000001094 00000 n
+0000001179 00000 n
+0000001203 00000 n
+0000001307 00000 n
+0000001412 00000 n
+0000001517 00000 n
+0000001622 00000 n
+0000001727 00000 n
+0000001830 00000 n
+0000001933 00000 n
+0000002037 00000 n
+0000002142 00000 n
+0000002247 00000 n
+0000002352 00000 n
+0000002457 00000 n
+0000002562 00000 n
+0000002667 00000 n
+0000002772 00000 n
+0000002877 00000 n
+0000002982 00000 n
+0000003087 00000 n
+0000003192 00000 n
+0000003297 00000 n
+0000003402 00000 n
+0000003505 00000 n
+0000003608 00000 n
+0000003712 00000 n
+0000003817 00000 n
+0000003922 00000 n
+0000004027 00000 n
+0000004132 00000 n
+0000004237 00000 n
+0000004342 00000 n
+0000004447 00000 n
+0000004552 00000 n
+0000004657 00000 n
+0000004762 00000 n
+0000004867 00000 n
+0000004972 00000 n
+0000005077 00000 n
+0000005182 00000 n
+0000005287 00000 n
+0000005392 00000 n
+0000005497 00000 n
+0000005602 00000 n
+0000005707 00000 n
+0000005812 00000 n
+0000005917 00000 n
+0000006022 00000 n
+0000006127 00000 n
+0000006232 00000 n
+0000006337 00000 n
+0000006442 00000 n
+0000006547 00000 n
+0000006652 00000 n
+0000006757 00000 n
+0000006862 00000 n
+0000006967 00000 n
+0000007072 00000 n
+0000007177 00000 n
+0000007282 00000 n
+0000007385 00000 n
+0000007488 00000 n
+0000007592 00000 n
+0000007697 00000 n
+0000007802 00000 n
+0000007907 00000 n
+0000008012 00000 n
+0000008117 00000 n
+0000008222 00000 n
+0000008327 00000 n
+0000008432 00000 n
+0000008537 00000 n
+0000008642 00000 n
+0000008747 00000 n
+0000008852 00000 n
+0000008957 00000 n
+0000009062 00000 n
+0000009167 00000 n
+0000009272 00000 n
+0000009377 00000 n
+0000009482 00000 n
+0000009587 00000 n
+0000009692 00000 n
+0000009797 00000 n
+0000009902 00000 n
+0000010007 00000 n
+0000010113 00000 n
+0000010219 00000 n
+0000010325 00000 n
+0000010431 00000 n
+0000010537 00000 n
+0000010643 00000 n
+0000010749 00000 n
+0000010855 00000 n
+0000010961 00000 n
+0000011067 00000 n
+0000011173 00000 n
+0000011279 00000 n
+0000011385 00000 n
+0000011491 00000 n
+0000011597 00000 n
+0000011703 00000 n
+0000011809 00000 n
+0000011915 00000 n
+0000012021 00000 n
+0000012127 00000 n
+0000012233 00000 n
+0000012339 00000 n
+0000012445 00000 n
+0000012551 00000 n
+0000012657 00000 n
+0000012763 00000 n
+0000012869 00000 n
+0000012975 00000 n
+0000013081 00000 n
+0000013187 00000 n
+0000013293 00000 n
+0000013399 00000 n
+0000013505 00000 n
+0000013611 00000 n
+0000013717 00000 n
+0000013823 00000 n
+0000013929 00000 n
+0000014035 00000 n
+0000014141 00000 n
+0000014247 00000 n
+0000014353 00000 n
+0000014459 00000 n
+0000014565 00000 n
+0000014671 00000 n
+0000014777 00000 n
+0000014883 00000 n
+0000014989 00000 n
+0000015095 00000 n
+0000015201 00000 n
+0000015307 00000 n
+0000015413 00000 n
+0000015519 00000 n
+0000015625 00000 n
+0000015731 00000 n
+0000015837 00000 n
+0000015943 00000 n
+0000016997 00000 n
+0000017031 00000 n
+0000017065 00000 n
+0000017576 00000 n
+0000017625 00000 n
+0000017674 00000 n
+0000017723 00000 n
+0000017772 00000 n
+0000017821 00000 n
+0000017870 00000 n
+0000017919 00000 n
+0000017968 00000 n
+0000018017 00000 n
+0000018066 00000 n
+0000018115 00000 n
+0000018164 00000 n
+0000018213 00000 n
+0000018262 00000 n
+0000018311 00000 n
+0000018360 00000 n
+0000018409 00000 n
+0000018458 00000 n
+0000018507 00000 n
+0000018556 00000 n
+0000018605 00000 n
+0000018654 00000 n
+0000018703 00000 n
+0000018752 00000 n
+0000018801 00000 n
+0000018850 00000 n
+0000018899 00000 n
+0000018948 00000 n
+0000018997 00000 n
+0000019046 00000 n
+0000019095 00000 n
+0000019144 00000 n
+0000019193 00000 n
+0000019242 00000 n
+0000019291 00000 n
+0000019340 00000 n
+0000019389 00000 n
+0000019618 00000 n
+0000019770 00000 n
+0000026151 00000 n
+0000026173 00000 n
+0000026286 00000 n
+0000026388 00000 n
+0000026408 00000 n
+0000026539 00000 n
+0000027300 00000 n
+0000027321 00000 n
+0000027461 00000 n
+0000027828 00000 n
+0000027849 00000 n
+0000027989 00000 n
+0000028903 00000 n
+0000028924 00000 n
+0000029064 00000 n
+0000030502 00000 n
+0000030524 00000 n
+0000030664 00000 n
+0000031556 00000 n
+0000031577 00000 n
+0000031690 00000 n
+0000031885 00000 n
+0000031906 00000 n
+0000032046 00000 n
+0000032621 00000 n
+0000032642 00000 n
+0000032805 00000 n
+0000033792 00000 n
+0000033813 00000 n
+0000033976 00000 n
+0000034861 00000 n
+0000034882 00000 n
+0000034995 00000 n
+0000035199 00000 n
+0000035220 00000 n
+0000035369 00000 n
+0000035979 00000 n
+0000036000 00000 n
+0000036158 00000 n
+0000037202 00000 n
+0000037223 00000 n
+0000037363 00000 n
+0000037955 00000 n
+0000037976 00000 n
+0000038125 00000 n
+0000039155 00000 n
+0000039176 00000 n
+0000039334 00000 n
+0000040244 00000 n
+0000040265 00000 n
+0000040378 00000 n
+0000040592 00000 n
+0000040613 00000 n
+0000040768 00000 n
+0000043555 00000 n
+0000043577 00000 n
+0000043690 00000 n
+0000043863 00000 n
+0000043884 00000 n
+0000043939 00000 n
+0000044044 00000 n
+0000044188 00000 n
+0000044291 00000 n
+0000044396 00000 n
+0000044561 00000 n
+0000044669 00000 n
+0000044784 00000 n
+0000044889 00000 n
+0000044997 00000 n
+0000045105 00000 n
+0000045221 00000 n
+0000045319 00000 n
+0000045485 00000 n
+0000045602 00000 n
+0000045721 00000 n
+0000045845 00000 n
+0000045970 00000 n
+0000046120 00000 n
+0000046261 00000 n
+0000046370 00000 n
+0000046522 00000 n
+0000046661 00000 n
+0000046791 00000 n
+0000046915 00000 n
+0000047051 00000 n
+0000047178 00000 n
+0000047301 00000 n
+0000047417 00000 n
+0000047566 00000 n
+0000047695 00000 n
+0000047832 00000 n
+0000047962 00000 n
+0000048087 00000 n
+0000048190 00000 n
+0000048327 00000 n
+0000048432 00000 n
+0000048571 00000 n
+0000048705 00000 n
+trailer
+<</Size 298/Root 297 0 R/Info 1 0 R>>
+startxref
+48891
+%%EOF
diff --git a/doc/sum.shtml b/doc/sum.shtml
new file mode 100644
index 000000000..4883760bf
--- /dev/null
+++ b/doc/sum.shtml
@@ -0,0 +1,562 @@
+<HTML>
+<HEAD>
+ <META NAME="Description" CONTENT="Common UNIX Printing System Software Users Manual">
+ <META NAME="COPYRIGHT" CONTENT="Copyright 1997-1999, All Rights Reserved">
+ <META NAME="DOCNUMBER" CONTENT="CUPS-SUM-1.0.0b1">
+ <META NAME="Author" CONTENT="Easy Software Products">
+ <TITLE>DRAFT - CUPS Software Users Manual</TITLE>
+</HEAD>
+<BODY>
+
+<H1 ALIGN=RIGHT>Preface</H1>
+
+This software users manual describes how to use the Common UNIX Printing
+System ("CUPS") Version 1.0.0.
+
+<H2>System Overview</H2>
+
+The Common UNIX Printing System provides a portable printing layer for
+UNIX&reg; operating systems. It has been developed by Easy Software
+Products to promote a standard printing solution for all UNIX vendors
+and users. CUPS provides the System V and Berkeley command-line interfaces.
+
+<P>CUPS uses the Internet Printing Protocol (IETF-IPP) as the basis
+for managing print jobs and queues. The Line Printer Daemon (LPD,
+RFC1179), Server Message Block (SMB), and AppSocket protocols are also
+supported with reduced functionality.
+
+<P>CUPS adds network printer browsing and PostScript Printer
+Description (&quot;PPD&quot;)-based printing options to support real
+world applications under UNIX.
+
+<P>CUPS also includes a customized version of GNU GhostScript
+(currently based off GNU GhostScript 4.03) and an image file RIP that
+can be used to support non-PostScript printers.
+
+<H2>Document Overview</H2>
+
+<P>This software users manual is organized into the following sections:</P>
+
+<UL>
+ <LI>1 - Printing System Overview</LI>
+ <LI>2 - Using the Printing System</LI>
+ <LI>3 - Standard Printer Options</LI>
+ <LI>4 - Checking the Status Via the Web</LI>
+</UL>
+
+<H1 ALIGN=RIGHT>1 - Printing System Overview</H1>
+
+<P>This chapter provides an overview of how the Common UNIX Printing System
+works.
+
+<H2>The Printing Problem</H2>
+
+<P>For years <I>the printing problem</I> has plagued UNIX&reg;. Unlike
+Microsoft&reg; Windows&reg; or MacOS, UNIX has no standard interface or
+system in place for supporting printers. Among the solutions previously
+available, the Berkeley and System V printing systems are the most
+prevalent.
+
+<P>These printing systems support line printers (text only) or
+PostScript printers (text and graphics), and with some coaxing they can
+be made to support a full range of printers and file formats. However,
+because each varient of the UNIX operating system uses a different
+printing system than the next, developing printer drivers for a wide
+range of printers is extremely difficult. That combined with the
+limited volume of customers for each UNIX varient has forced most
+printer vendors to give up supporting UNIX entirely.
+
+<P>The Common UNIX Printing System, or CUPS, is designed to eliminate
+<I>the printing problem</I>. One common printing system can be used by
+all UNIX varients to support the printing needs of users. Printer
+vendors can use its modular filter interface to develop a single driver
+program that supports a wide range of file formats with little or no
+effort. Since CUPS provides both the System V and Berkeley printing
+commands, users (and applications) can reap the benefits of this new
+technology with no changes.
+
+<H2>The Technology</H2>
+
+<P>CUPS is based upon an emerging Internet standard called the Internet
+Printing Protocol, or IPP. IPP has been embraced by dozens of printer
+and printer server manufacturers, and will be supported by the next
+Microsoft Windows operating system.
+
+<P>IPP defines a standard protocol for printing as well as managing print
+jobs and printer options like media size, resolution, and so forth. Like all
+IP-based protocols, IPP can be used locally or over the Internet to printers
+hundreds or thousands of miles away. Unlike other protocols, however, IPP
+also supports access control, authentication, and encryption, making it a
+much more secure printing solution than older ones.
+
+<P>IPP is layered on top of the Hyper-Text Transport Protocol, or HTTP,
+which is the basis of web servers on the Internet. This allows the user to
+view documentation and status information on a printer or server using their
+web browser.
+
+<P>CUPS provides a complete IPP/1.0-based printing system that provides Basic
+authentication and domain or IP-based access control. Digest authentication
+and TLS encryption will be available in future versions of CUPS.
+
+<H2>Jobs</H2>
+
+<P>Each file that is submitted for printing is called a <I>job</I>.
+Jobs are identified by a unique number starting at 1 and are assigned
+to a particular destination (usually a printer). Jobs can also have
+options associated with them such as media size, number of copies, and
+priority.
+
+<H2>Classes</H2>
+
+<P>CUPS supports collections of printers known as <I>classes</I>. Jobs sent
+to a class are forwarded to the first available printer in the class.
+
+<H2>Filters</H2>
+
+<P>Filters allow a user or application to print many types of files
+without extra effort. Print jobs sent to a CUPS server are filtered
+before sending them to a printer. Some filters convert job files to
+different formats that the printer can understand. Others perform page
+selection and ordering tasks. <I>Backend</I> filters perform the most
+important task of all - they send the filtered print data to the
+printer.
+
+<P>CUPS provides filters for printing many types of image files,
+HP-GL/2 files, PDF files, and text files. CUPS also supplies
+PostScript and image file Raster Image Processors, or RIPs, that
+convert PostScript or image files into bitmaps that can be sent to a
+raster printer.
+
+<P>CUPS provides backends for printing over parallel and serial ports,
+and over the network via the JetDirect (AppSocket), Server Message
+Block, and Line Printer Daemon protocols.
+
+<H2>Printer Drivers</H2>
+
+<P>Printer drivers in CUPS consist of one of more filters specific to a
+printer. CUPS includes a sample printer driver for Hewlett-Packard
+LaserJet and DeskJet printers. While this driver does not generate
+optimal output for different printer models, it does demonstrate how
+you can write your own printer drivers and incorporate them into CUPS.
+
+<H2>Networking</H2>
+
+<P>Printers and classes on the local system are automatically shared with
+other systems on the network. This allows you to setup one system to print
+to a printer and use this system as a printer server or spool host for all
+of the others. If there is only one occurrence of a printer on a network,
+then that printer can be accessed using its name alone. If more than one
+printer exists with the same name, users must select the printer by specifying
+which server to use (e.g. "printer@host1" or "printer@host2".)
+
+<P>CUPS also provides <I>implicit classes</I>, which are collections of
+printers and/or classes with the same name. This allows you to setup multiple
+servers pointing to the same physical network printer, for example, so that
+you aren't relying on a single system for printing. Because this also works
+with printer classes, you can setup multiple servers and printers and never
+worry about a "single point of failure" unless all of the printers and servers
+goes down!
+
+<H1 ALIGN=RIGHT>2 - Using the Printing System</H1>
+
+<P>This chapter shows you how to submit, query, and cancel print jobs to
+different printers.
+
+<H2>Submitting Files for Printing</H2>
+
+<P>CUPS provides both the System V (<CODE>lp</CODE>) and Berkeley
+(<CODE>lpr</CODE>) printing commands. To print a file to the default printer
+on the system (or your only printer if you have only one) you just need to
+type:
+
+<UL><PRE>
+% lp filename ENTER
+</PRE></UL>
+
+<P>or:
+
+<UL><PRE>
+% lpr filename ENTER
+</PRE></UL>
+
+<P>CUPS understands many different types of files directly, including PostScript
+and image files. This allows you to print from inside your applications or at
+the command-line, whichever is most convenient!
+
+<H2>Choosing a Printer</H2>
+
+<P>Many systems will have more than one printer available to the user. These
+printers can be attached to the local system via a parallel or serial port,
+or available over the network.
+
+<P>To see a list of available printers, use the <CODE>lpstat</CODE> command:
+
+<UL><PRE>
+% lpstat -p -d ENTER
+</PRE></UL>
+
+<P>The "-p" option specifies that you want to see a list of printers, and the
+"-d" option reports the current system default printer or class.
+
+<P>To print to a specific printer, use the "-d" option to the <CODE>lp</CODE>
+command:
+
+<UL><PRE>
+% lp -d printer filename ENTER
+</PRE></UL>
+
+<P>or the "-P" option to the <CODE>lpr</CODE> command:
+
+<UL><PRE>
+% lpr -P printer filename ENTER
+</PRE></UL>
+
+<H2>Setting Printer Options</H2>
+
+<P>For many types of files, the default printer options may be sufficient for
+your needs. However, there may be times when you need to change the options
+for a particular file you are printing.
+
+<P>The <CODE>lp</CODE> command allows you to pass printer options using the
+"-o" option:
+
+<UL><PRE>
+% lp -o landscape -o scaling=75 -o media=A4 filename.jpg
+</PRE></UL>
+
+<P>The <CODE>lpr</CODE> command has no command-line option for printer options.
+
+<P>The available printer options vary depending on the printer. The standard
+options are described in <A HREF="#4">Chapter 3</A>.
+
+<H2>Printing Multiple Copies</H2>
+
+<P>Both the <CODE>lp</CODE> and <CODE>lpr</CODE> commands have options for
+printing more than one copy of a file:
+
+<UL><PRE>
+% lp -n num-copies filename ENTER
+% lpr -#num-copies filename ENTER
+</PRE></UL>
+
+<P>Copies are normally <I>not</I> collated for you. To get collated copies
+use the <CODE>lp</CODE> command with the "-o Collate=True" option:
+
+<UL><PRE>
+% lp -n num-copies -o Collate=True filename ENTER
+</PRE></UL>
+
+<H2>Checking the Printer Status from the Command-Line</H2>
+
+<P>The <CODE>lpstat</CODE> command can be used to check for jobs that you
+have submitted for printing:
+
+<UL><PRE>
+% lpstat ENTER
+Printer-1 johndoe 4427776
+Printer-2 johndoe 15786
+Printer-3 johndoe 372842
+</PRE></UL>
+
+<P>The jobs are listed in the order they will be printed. To see which files
+and printers are active, use the "-p" option:
+
+<UL><PRE>
+% lpstat -p ENTER
+printer DeskJet now printing DeskJet-1.
+</PRE></UL>
+
+<P>Or to show the jobs and the printers, use the "-o" and "-p" options:
+
+<UL><PRE>
+% lpstat -o -p ENTER
+Printer-1 johndoe 4427776
+Printer-2 johndoe 15786
+Printer-3 johndoe 372842
+printer DeskJet now printing DeskJet-1.
+</PRE></UL>
+
+<H2>Checking the Printer Status from the Web</H2>
+
+<P>Since CUPS uses the Internet Printing Protocol, it is also a
+full-featured web server. To use your web browser to monitor the
+printers on your system, open the URL
+"<A HREF="http://localhost:631">http://localhost:631</A>". From there
+you can view the status of classes, jobs, and printers with the click
+of a button!
+
+<H2>Canceling a Print Job</H2>
+
+<P>The <CODE>cancel</CODE> command cancels a print job:
+
+<UL><PRE>
+% cancel job-id ENTER
+</PRE></UL>
+
+<P>The <I>job-id</I> is a number that was reported to you by the
+<CODE>lp</CODE> or <CODE>lpstat</CODE> commands.
+
+<H1 ALIGN=RIGHT>3 - Standard Printer Options</H1>
+
+<P>This chapter describes the standard printer options that are available
+when printing with the <CODE>lp</CODE> command.
+
+<H2>General Options</H2>
+
+<P>The following options apply when printing all types of files.
+
+<H3>Selecting the Media Size, Type, and Source</H3>
+
+<P>The "-o media=xyz" option sets the media size, type, and/or source:
+
+<UL><PRE>
+% lp -o media=Letter filename ENTER
+% lp -o media=Letter,MultiPurpose filename ENTER
+% lp -o media=Letter,Transparency filename ENTER
+% lp -o media=Letter,MultiPurpose,Transparency filename ENTER
+</PRE></UL>
+
+<P>The available media sizes, types, and sources depend on the printer, but
+most support the following options (case is significant):
+
+<UL>
+
+ <LI><CODE>Letter</CODE> - US Letter (8.5x11 inches, or 216x279mm)
+
+ <LI><CODE>Legal</CODE> - US Legal (8.5x14 inches, or 216x356mm)
+
+ <LI><CODE>A4</CODE> - ISO A4 (8.27x11.69 inches, or 210x297mm)
+
+ <LI><CODE>COM10</CODE> - US #10 Envelope (9.5x4.125 inches, or
+ 241x105mm)
+
+ <LI><CODE>DL</CODE> - ISO DL Envelope (8.66x4.33 inches, or 220x110mm)
+
+ <LI><CODE>Transparency</CODE> - Transparency media type or source
+
+ <LI><CODE>Upper</CODE> - Upper paper tray
+
+ <LI><CODE>Lower</CODE> - Lower paper tray
+
+ <LI><CODE>MultiPurpose</CODE> - Multi-purpose paper tray
+
+ <LI><CODE>LargeCapacity</CODE> - Large capacity paper tray
+
+</UL>
+
+<P>The actual options supported are defined in the printer's PPD file in the
+<CODE>PageSize</CODE>, <CODE>InputSlot</CODE>, and <CODE>MediaType</CODE>
+options.
+
+<H3>Setting the Orientation</H3>
+
+<P>The "-o landscape" option will rotate the page 90 degrees to print in
+landscape orientation:
+
+<UL><PRE>
+% lp -o landscape filename ENTER
+</PRE></UL>
+
+<H3>Printing On Both Sides of the Paper</H3>
+
+<P>The "-o sides=two-sided-short" and "-o sides=two-sided-long" options will
+enable duplexing on the printer (if the printer supports it.) The "two-sided-short"
+option is suitable for landscape pages, while the "two-sided-long" option is
+suitable for portrait pages:
+
+<UL><PRE>
+% lp -o sides=two-sided-short filename ENTER
+% lp -o sides=two-sided-long filename ENTER
+</PRE></UL>
+
+<H3>Selecting a Range of Pages</H3>
+
+<P>The "-o page-ranges=pages" option selects a range of pages for printing:
+
+<UL><PRE>
+% lp -o page-ranges=1 filename ENTER
+% lp -o page-ranges=1-4 filename ENTER
+% lp -o page-ranges=1-4,7,9-12 filename ENTER
+</PRE></UL>
+
+<P>As shown above, the <I>pages</I> value can be a single page, a range of
+pages, or a collection of page numbers and ranges separated by commas. The
+pages will always be printed in ascending order, regardless of the order of
+the pages in the "page-range" option.
+
+<P>To select the even or odd pages, use the "-o page-set=set" option:
+
+<UL><PRE>
+% lp -o page-set=odd filename ENTER
+% lp -o page-set=even filename ENTER
+</PRE></UL>
+
+<H3>Setting the Brightness</H3>
+
+<P>You can control the overall brightness of the printed output using the
+"-o brightness=percent" option:
+
+<UL><PRE>
+% lp -o brightness=120 filename ENTER
+</PRE></UL>
+
+<P>Values greater than 100 will lighten the print, while values less than
+100 will darken it.
+
+<H3>Setting the Gamma Correction</H3>
+
+<P>You can control the overall gamma correction of the printed output
+using the "-o gamma=value" option:
+
+<UL><PRE>
+% lp -o gamma=1700 filename ENTER
+</PRE></UL>
+
+<P>Values greater than 1000 will lighten the print, while values less
+than 1000 will darken it.
+
+<H2>Text Options</H2>
+
+<P>The following options apply when printing text files.
+
+<H3>Setting the Number of Characters Per Inch</H3>
+
+<P>The "-o cpi=value" option sets the number of characters per inch:
+
+<UL><PRE>
+% lp -o cpi=12 filename ENTER
+</PRE></UL>
+
+<H3>Setting the Number of Lines Per Inch</H3>
+
+<P>The "-o lpi=value" option sets the number of lines per inch:
+
+<UL><PRE>
+% lp -o lpi=8 filename ENTER
+</PRE></UL>
+
+<H3>Setting the Number of Columns</H3>
+
+<P>The "-o columns=value" option sets the number of text columns:
+
+<UL><PRE>
+% lp -o columns=2 filename ENTER
+</PRE></UL>
+
+<H3>Setting the Page Margins</H3>
+
+<P>Normally the page margins are set to the hard limits of the printer. To
+adjust the page margins use the "-o page-left=value", "-o page-right=value",
+"-o page-top=value", and "-o page-bottom=value" options:
+
+<UL><PRE>
+% lp -o page-left=value filename ENTER
+% lp -o page-right=value filename ENTER
+% lp -o page-top=value filename ENTER
+% lp -o page-bottom=value filename ENTER
+</PRE></UL>
+
+<P>The <I>value</I> argument is the margin in points; each point is 1/72 inch
+or 0.35mm.
+
+<H3>Pretty Printing</H3>
+
+<P>The "-o prettyprint" option puts a header at the top of each page with the
+page number, job title (usually the filename), and the date. Also, C and C++
+keywords are highlighted, and comment lines are italicized:
+
+<UL><PRE>
+% lp -o prettyprint filename ENTER
+</PRE></UL>
+
+<H2>Image Options</H2>
+
+<P>The following options apply when printing image files.
+
+<H3>Scaling the Image</H3>
+
+<P>The "-o scaling=percent" and "-o ppi=value" options change the size of a
+printed image:
+
+<UL><PRE>
+% lp -o scaling=percent filename ENTER
+% lp -o ppi=value filename ENTER
+</PRE></UL>
+
+<P>The scaling <I>percent</I> is a number from 1 to 800 specifying the size
+in relation to the page (<I>not</I> the image.) A scaling of 100 percent will
+fill the page as completely as the image aspect ratio allows. A scaling of
+200 percent will print on up to 4 pages.
+
+<P>The ppi <I>value</I> is a number from 1 to 1200 specifying the resolution
+of the image in pixels per inch. An image that is 3000x2400 pixels will print
+10x8 inches at 300 pixels per inch, for example. If the specified resolution
+makes the image larger than the page, multiple pages will be printed to
+satisfy the request.
+
+<H3>Adjusting the Hue (Tint) of an Image</H3>
+
+<P>The "-o hue=value" option will adjust the hue of the printed image, much
+like the tint control on your television:
+
+<UL><PRE>
+% lp -o hue=value filename ENTER
+</PRE></UL>
+
+<P>The <I>value</I> argument is a number from -360 to 360 and represents the
+color hue rotation. The following table summarizes the change you'll see with
+different colors:
+
+<CENTER><TABLE WIDTH="50%" BORDER="1">
+<TR>
+ <TH>Original</TH>
+ <TH>hue=-45</TH>
+ <TH>hue=45</TH>
+</TR>
+<TR>
+ <TD>Red</TD>
+ <TD>Purple</TD>
+ <TD>Yellow-orange</TD>
+</TR>
+<TR>
+ <TD>Green</TD>
+ <TD>Yellow-green</TD>
+ <TD>Blue-green</TD>
+</TR>
+<TR>
+ <TD>Yellow</TD>
+ <TD>Orange</TD>
+ <TD>Green-yellow</TD>
+</TR>
+<TR>
+ <TD>Blue</TD>
+ <TD>Sky-blue</TD>
+ <TD>Purple</TD>
+</TR>
+<TR>
+ <TD>Magenta</TD>
+ <TD>Indigo</TD>
+ <TD>Crimson</TD>
+</TR>
+<TR>
+ <TD>Cyan</TD>
+ <TD>Blue-green</TD>
+ <TD>Light-navy-blue</TD>
+</TR>
+</TABLE></CENTER>
+
+<H3>Adjusting the Saturation (Color) of an Image</H3>
+
+<P>The "-o saturation=percent" option adjusts the saturation of the colors in
+an image, much like the color knob on your television:
+
+<UL><PRE>
+% lp -o saturation=percent filename ENTER
+</PRE></UL>
+
+<P>The <I>percent</I> argument specifies the color saturation from 0 to 200.
+A color saturation of 0 produces a black-and-white print, while a value of 200
+will make the colors extremely intense.
+
+</BODY>
+</HTML>
diff --git a/doc/svd.html b/doc/svd.html
new file mode 100644
index 000000000..780f84302
--- /dev/null
+++ b/doc/svd.html
@@ -0,0 +1,149 @@
+<HTML>
+<HEAD>
+<TITLE>CUPS Software Version Description</TITLE>
+<META NAME="AUTHOR" CONTENT="Easy Software Products">
+<META NAME="COPYRIGHT" CONTENT="Copyright 1997-1999, All Rights Reserved">
+<META NAME="DOCNUMBER" CONTENT="CUPS-SVD-1.0.0">
+</HEAD>
+<BODY>
+<CENTER><A HREF=#contents><IMG SRC="images/cups-large.gif" BORDER=0><BR>
+<H1>CUPS Software Version Description</H1></A><BR>
+CUPS-SVD-1.0.0<BR>
+Easy Software Products<BR>
+Copyright 1997-1999, All Rights Reserved<BR>
+</CENTER>
+<HR>
+<H1 ALIGN=CENTER><A NAME=CONTENTS>Table of Contents</A></H1>
+<BR>
+<BR><B><A HREF=#1>1 Scope</A></B>
+<UL>
+<LI><A HREF=#1_1>1.1 Identification</A></LI>
+<LI><A HREF=#1_2>1.2 System Overview</A></LI>
+<LI><A HREF=#1_3>1.3 Document Overview</A></LI>
+</UL>
+<B><A HREF=#2>2 References</A></B>
+<UL>
+<LI><A HREF=#2_1>2.1 CUPS Documentation</A></LI>
+<LI><A HREF=#2_2>2.2 Other Documents</A></LI>
+</UL>
+<B><A HREF=#3>3 Additions</A></B>
+<BR>
+<BR><B><A HREF=#4>4 Changes</A></B>
+<BR>
+<BR><B><A HREF=#5>A Glossary</A></B>
+<UL>
+<LI><A HREF=#5_1>A.1 Terms</A></LI>
+<LI><A HREF=#5_2>A.2 Acronyms</A></LI>
+</UL>
+<HR>
+<H1><A NAME=1>1 Scope</A></H1>
+<H2><A NAME=1_1>1.1 Identification</A></H2>
+ This software version description document provides release
+information for the Common UNIX Printing System (&quot;CUPS&quot;) Version 1.0.
+<H2><A NAME=1_2>1.2 System Overview</A></H2>
+ The Common UNIX Printing System provides a portable printing layer for
+ UNIX&reg; operating systems. It has been developed by Easy Software
+ Products to promote a standard printing solution for all UNIX vendors
+ and users. CUPS provides the System V and Berkeley command-line
+interfaces.
+<P>CUPS uses the Internet Printing Protocol (IETF-IPP) as the basis for
+managing print jobs and queues. The Line Printer Daemon (LPD, RFC1179),
+Server Message Block (SMB), and AppSocket protocols are also supported
+with reduced functionality. </P>
+<P>CUPS adds network printer browsing and PostScript Printer
+Description (&quot;PPD&quot;)-based printing options to support real world
+applications under UNIX. </P>
+<P>CUPS also includes a customized version of GNU GhostScript
+(currently based off GNU GhostScript 4.03) and an image file RIP that
+can be used to support non-PostScript printers. </P>
+<H2><A NAME=1_3>1.3 Document Overview</A></H2>
+<P>This Something Something Something document is organized into the
+following sections:</P>
+<UL>
+<LI>1 - Scope</LI>
+<LI>2 - References</LI>
+<LI>3 - Additions</LI>
+<LI>4 - Changes</LI>
+<LI>A - Glossary</LI>
+</UL>
+<H1><A NAME=2>2 References</A></H1>
+<H2><A NAME=2_1>2.1 CUPS Documentation</A></H2>
+ The following CUPS documentation is referenced by this document:
+<UL>
+<LI>CUPS-CMP-1.0: CUPS Configuration Management Plan </LI>
+<LI>CUPS-IDD-1.0: CUPS System Interface Design Description </LI>
+<LI>CUPS-SAM-1.0.x: CUPS Software Administrators Manual </LI>
+<LI>CUPS-SDD-1.0: CUPS Software Design Description </LI>
+<LI>CUPS-SPM-1.0: CUPS Software Programming Manual </LI>
+<LI>CUPS-SSR-1.0: CUPS Software Security Report </LI>
+<LI>CUPS-STP-1.0: CUPS Software Test Plan </LI>
+<LI>CUPS-SUM-1.0.x: CUPS Software Users Manual </LI>
+<LI>CUPS-SVD-1.0.x: CUPS Software Version Description </LI>
+</UL>
+<H2><A NAME=2_2>2.2 Other Documents</A></H2>
+ The following non-CUPS documents are referenced by this document:
+<UL>
+<LI>IEEE 1387.4, System Administration: Printing (draft) </LI>
+<LI>IPP/1.0: Additional Optional Operations - Set 1 </LI>
+<LI>IPP/1.0: Encoding and Transport </LI>
+<LI>IPP/1.0: Implementers Guide </LI>
+<LI>IPP/1.0: Model and Semantics </LI>
+<LI>RFC 1179, Line Printer Daemon Protocol </LI>
+</UL>
+<H1><A NAME=3>3 Additions</A></H1>
+ Since this is the first release of CUPS, there are no additions to
+report.
+<H1><A NAME=4>4 Changes</A></H1>
+ Since this is the first release of CUPS, there are no changes to
+report.
+<H1 TYPE=A VALUE=1><A NAME=5>A Glossary</A></H1>
+<H2><A NAME=5_1>A.1 Terms</A></H2>
+<DL>
+<DT>C </DT>
+<DD>A computer language. </DD>
+<DT>parallel </DT>
+<DD>Sending or receiving data more than 1 bit at a time. </DD>
+<DT>pipe </DT>
+<DD>A one-way communications channel between two programs. </DD>
+<DT>serial </DT>
+<DD>Sending or receiving data 1 bit at a time. </DD>
+<DT>socket </DT>
+<DD>A two-way network communications channel. </DD>
+</DL>
+<H2><A NAME=5_2>A.2 Acronyms</A></H2>
+<DL>
+<DT>ASCII </DT>
+<DD>American Standard Code for Information Interchange </DD>
+<DT>CUPS </DT>
+<DD>Common UNIX Printing System </DD>
+<DT>ESC/P </DT>
+<DD>EPSON Standard Code for Printers </DD>
+<DT>FTP </DT>
+<DD>File Transfer Protocol </DD>
+<DT>HP-GL </DT>
+<DD>Hewlett-Packard Graphics Language </DD>
+<DT>HP-PCL </DT>
+<DD>Hewlett-Packard Printer Control Language </DD>
+<DT>HP-PJL </DT>
+<DD>Hewlett-Packard Printer Job Language </DD>
+<DT>IETF </DT>
+<DD>Internet Engineering Task Force </DD>
+<DT>IPP </DT>
+<DD>Internet Printing Protocol </DD>
+<DT>ISO </DT>
+<DD>International Standards Organization </DD>
+<DT>LPD </DT>
+<DD>Line Printer Daemon </DD>
+<DT>MIME </DT>
+<DD>Multimedia Internet Mail Exchange </DD>
+<DT>PCL </DT>
+<DD>Page Control Language </DD>
+<DT>PPD </DT>
+<DD>PostScript Printer Description </DD>
+<DT>SMB </DT>
+<DD>Server Message Block </DD>
+<DT>TFTP </DT>
+<DD>Trivial File Transfer Protocol </DD>
+</DL>
+</BODY>
+</HTML>
diff --git a/doc/svd.pdf b/doc/svd.pdf
new file mode 100644
index 000000000..4491433ea
--- /dev/null
+++ b/doc/svd.pdf
Binary files differ
diff --git a/doc/svd.shtml b/doc/svd.shtml
new file mode 100644
index 000000000..1f9c34c2a
--- /dev/null
+++ b/doc/svd.shtml
@@ -0,0 +1,167 @@
+<HTML>
+<HEAD>
+ <META NAME="COPYRIGHT" CONTENT="Copyright 1997-1999, All Rights Reserved">
+ <META NAME="DOCNUMBER" CONTENT="CUPS-SVD-1.0.0">
+ <META NAME="Author" CONTENT="Easy Software Products">
+ <TITLE>CUPS Software Version Description</TITLE>
+</HEAD>
+<BODY>
+
+<H1>Scope</H1>
+
+<H2>Identification</H2>
+
+This software version description document provides release information for the
+Common UNIX Printing System ("CUPS") Version 1.0.
+
+<H2>System Overview</H2>
+
+The Common UNIX Printing System provides a portable printing layer for
+UNIX&reg; operating systems. It has been developed by Easy Software
+Products to promote a standard printing solution for all UNIX vendors
+and users. CUPS provides the System V and Berkeley command-line interfaces.
+
+<P>CUPS uses the Internet Printing Protocol (IETF-IPP) as the basis
+for managing print jobs and queues. The Line Printer Daemon (LPD,
+RFC1179), Server Message Block (SMB), and AppSocket protocols are also
+supported with reduced functionality.
+
+<P>CUPS adds network printer browsing and PostScript Printer
+Description (&quot;PPD&quot;)-based printing options to support real
+world applications under UNIX.
+
+<P>CUPS also includes a customized version of GNU GhostScript
+(currently based off GNU GhostScript 4.03) and an image file RIP that
+can be used to support non-PostScript printers.
+
+<H2>Document Overview</H2>
+
+<P>This Something Something Something document is organized into the following
+sections:</P>
+
+<UL>
+ <LI>1 - Scope</LI>
+ <LI>2 - References</LI>
+ <LI>3 - Additions</LI>
+ <LI>4 - Changes</LI>
+ <LI>A - Glossary</LI>
+</UL>
+
+<H1>References</H1>
+
+<H2>CUPS Documentation</H2>
+
+The following CUPS documentation is referenced by this document:
+
+<UL>
+ <LI>CUPS-CMP-1.0: CUPS Configuration Management Plan
+ <LI>CUPS-IDD-1.0: CUPS System Interface Design Description
+ <LI>CUPS-SAM-1.0.x: CUPS Software Administrators Manual
+ <LI>CUPS-SDD-1.0: CUPS Software Design Description
+ <LI>CUPS-SPM-1.0: CUPS Software Programming Manual
+ <LI>CUPS-SSR-1.0: CUPS Software Security Report
+ <LI>CUPS-STP-1.0: CUPS Software Test Plan
+ <LI>CUPS-SUM-1.0.x: CUPS Software Users Manual
+ <LI>CUPS-SVD-1.0.x: CUPS Software Version Description
+</UL>
+
+<H2>Other Documents</H2>
+
+The following non-CUPS documents are referenced by this document:
+
+<UL>
+ <LI>IEEE 1387.4, System Administration: Printing (draft)
+ <LI>IPP/1.0: Additional Optional Operations - Set 1
+ <LI>IPP/1.0: Encoding and Transport
+ <LI>IPP/1.0: Implementers Guide
+ <LI>IPP/1.0: Model and Semantics
+ <LI>RFC 1179, Line Printer Daemon Protocol
+</UL>
+
+<H1>Additions</H1>
+
+Since this is the first release of CUPS, there are no additions to report.
+
+<H1>Changes</H1>
+
+Since this is the first release of CUPS, there are no changes to report.
+
+<H1 TYPE=A VALUE=1>Glossary</H1>
+
+<H2>Terms</H2>
+
+<DL>
+
+ <DT>C
+ <DD>A computer language.
+
+ <DT>parallel
+ <DD>Sending or receiving data more than 1 bit at a time.
+
+ <DT>pipe
+ <DD>A one-way communications channel between two programs.
+
+ <DT>serial
+ <DD>Sending or receiving data 1 bit at a time.
+
+ <DT>socket
+ <DD>A two-way network communications channel.
+
+</DL>
+
+<H2>Acronyms</H2>
+
+<DL>
+
+ <DT>ASCII
+ <DD>American Standard Code for Information Interchange
+
+ <DT>CUPS
+ <DD>Common UNIX Printing System
+
+ <DT>ESC/P
+ <DD>EPSON Standard Code for Printers
+
+ <DT>FTP
+ <DD>File Transfer Protocol
+
+ <DT>HP-GL
+ <DD>Hewlett-Packard Graphics Language
+
+ <DT>HP-PCL
+ <DD>Hewlett-Packard Printer Control Language
+
+ <DT>HP-PJL
+ <DD>Hewlett-Packard Printer Job Language
+
+ <DT>IETF
+ <DD>Internet Engineering Task Force
+
+ <DT>IPP
+ <DD>Internet Printing Protocol
+
+ <DT>ISO
+ <DD>International Standards Organization
+
+ <DT>LPD
+ <DD>Line Printer Daemon
+
+ <DT>MIME
+ <DD>Multimedia Internet Mail Exchange
+
+ <DT>PCL
+ <DD>Page Control Language
+
+ <DT>PPD
+ <DD>PostScript Printer Description
+
+ <DT>SMB
+ <DD>Server Message Block
+
+ <DT>TFTP
+ <DD>Trivial File Transfer Protocol
+
+</DL>
+
+</BODY>
+</HTML>
diff --git a/filter/Makefile b/filter/Makefile
new file mode 100644
index 000000000..bdf5892d2
--- /dev/null
+++ b/filter/Makefile
@@ -0,0 +1,150 @@
+#
+# "$Id$"
+#
+# Filter makefile for the Common UNIX Printing System (CUPS).
+#
+# Copyright 1997-1999 by Easy Software Products.
+#
+# These coded instructions, statements, and computer programs are the
+# property of Easy Software Products and are protected by Federal
+# copyright law. Distribution and use rights are outlined in the file
+# "LICENSE.txt" which should have been included with this file. If this
+# file is missing or damaged please contact Easy Software Products
+# at:
+#
+# Attn: CUPS Licensing Information
+# Easy Software Products
+# 44141 Airport View Drive, Suite 204
+# Hollywood, Maryland 20636-3111 USA
+#
+# Voice: (301) 373-9603
+# EMail: cups-info@cups.org
+# WWW: http://www.cups.org
+#
+
+include ../Makedefs
+
+TARGETS = hpgltops texttops pstops imagetops imagetoraster \
+ rastertohp
+
+HPGLOBJS = hpgl-attr.o hpgl-config.o hpgl-main.o hpgl-prolog.o \
+ hpgl-char.o hpgl-input.o hpgl-polygon.o hpgl-vector.o
+IMAGEOBJS = image-colorspace.o image-photocd.o image-sgilib.o \
+ image-tiff.o image-gif.o image-png.o image-sgi.o image-zoom.o \
+ image-jpeg.o image-pnm.o image-sun.o image.o
+OBJS = $(HPGLOBJS) $(IMAGEOBJS) imagetops.o imagetoraster.o \
+ common.o pstops.o rastertohp.o texttops.o textcommon.o
+
+#
+# Make all targets...
+#
+
+all: $(TARGETS)
+
+#
+# Clean all object files...
+#
+
+clean:
+ rm -f $(OBJS) $(TARGETS) $(LIBCUPSIMAGE)
+
+#
+# Install all targets...
+#
+
+install:
+ -$(MKDIR) $(SERVERROOT)/filter
+ $(CP) $(TARGETS) $(SERVERROOT)/filter
+ -$(MKDIR) $(LIBDIR)
+ $(CP) $(LIBCUPSIMAGE) $(LIBDIR)
+ -if test $(LIBCUPSIMAGE) != "libcupsimage.a"; then \
+ $(LN) $(LIBCUPSIMAGE) `basename $(LIBCUPSIMAGE) .1`; \
+ fi
+
+#
+# hpgltops
+#
+
+hpgltops: $(HPGLOBJS) common.o ../Makedefs ../cups/$(LIBCUPS)
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o $@ $(HPGLOBJS) common.o $(LIBS) -lm
+$(HPGLOBJS): hpgltops.h
+
+#
+# libcupsimage.so.1, libcupsimage.sl.1
+#
+
+libcupsimage.so.1 libcupsimage.sl.1: $(IMAGEOBJS) ../Makedefs
+ echo Linking $@...
+ $(DSO) $@ $(IMAGEOBJS) $(DSOLIBS) -lm
+ -$(LN) $@ `basename $@ .1`
+
+#
+# libcupsimage.a
+#
+
+libcupsimage.a: $(IMAGEOBJS) ../Makedefs
+ echo Archiving $@...
+ $(RM) $@
+ $(AR) $(ARFLAGS) $@ $(IMAGEOBJS)
+ $(RANLIB) $@
+
+$(IMAGEOBJS): image.h
+
+#
+# imagetops
+#
+
+imagetops: imagetops.o common.o $(LIBCUPSIMAGE) ../Makedefs \
+ ../cups/$(LIBCUPS)
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o $@ imagetops.o common.o -L. -lcupsimage \
+ $(IMGLIBS) $(LIBS)
+imagetops: common.h image.h
+
+#
+# imagetoraster
+#
+
+imagetoraster: imagetoraster.o common.o $(LIBCUPSIMAGE) ../Makedefs \
+ ../cups/$(LIBCUPS)
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o $@ imagetoraster.o common.o -L. -lcupsimage \
+ $(IMGLIBS) $(LIBS)
+imagetoraster: common.h image.h
+
+#
+# pstops
+#
+
+pstops: pstops.o common.o ../Makedefs ../cups/$(LIBCUPS)
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o $@ pstops.o common.o $(LIBS)
+pstops.o: common.h
+
+#
+# rastertohp
+#
+
+rastertohp: rastertohp.o ../Makedefs ../cups/$(LIBCUPS)
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o $@ rastertohp.o $(LIBS)
+rastertohp.o: ../cups/raster.h
+
+#
+# texttops
+#
+
+texttops: texttops.o textcommon.o common.o ../Makedefs \
+ ../cups/$(LIBCUPS)
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o $@ texttops.o textcommon.o common.o $(LIBS)
+texttops.o: common.h textcommon.h
+
+common.o: common.h
+textcommon.o: textcommon.h common.h
+$(OBJS): ../Makedefs ../cups/cups.h ../cups/ppd.h ../cups/language.h
+
+#
+# End of "$Id$".
+#
diff --git a/filter/common.c b/filter/common.c
new file mode 100644
index 000000000..65ddc529e
--- /dev/null
+++ b/filter/common.c
@@ -0,0 +1,252 @@
+/*
+ * "$Id$"
+ *
+ * Common filter routines for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-1999 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * Contents:
+ *
+ * SetCommonOptions() - Set common filter options for media size, etc.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "common.h"
+
+
+/*
+ * Globals...
+ */
+
+int Orientation = 0, /* 0 = portrait, 1 = landscape, etc. */
+ Duplex = 0, /* Duplexed? */
+ LanguageLevel = 1, /* Language level of printer */
+ ColorDevice = 1; /* Do color text? */
+float PageLeft = 18.0f, /* Left margin */
+ PageRight = 594.0f, /* Right margin */
+ PageBottom = 36.0f, /* Bottom margin */
+ PageTop = 756.0f, /* Top margin */
+ PageWidth = 612.0f, /* Total page width */
+ PageLength = 792.0f; /* Total page length */
+
+
+/*
+ * 'SetCommonOptions()' - Set common filter options for media size, etc.
+ */
+
+ppd_file_t * /* O - PPD file */
+SetCommonOptions(int num_options, /* I - Number of options */
+ cups_option_t *options, /* I - Options */
+ int change_size) /* I - Change page size? */
+{
+ float temp; /* Swapping variable */
+ ppd_file_t *ppd; /* PPD file */
+ ppd_size_t *pagesize; /* Current page size */
+ const char *val; /* Option value */
+
+
+ ppd = ppdOpenFile(getenv("PPD"));
+
+ ppdMarkDefaults(ppd);
+ cupsMarkOptions(ppd, num_options, options);
+
+ if ((pagesize = ppdPageSize(ppd, NULL)) != NULL)
+ {
+ PageWidth = pagesize->width;
+ PageLength = pagesize->length;
+ PageTop = pagesize->top;
+ PageBottom = pagesize->bottom;
+ PageLeft = pagesize->left;
+ PageRight = pagesize->right;
+ }
+
+ if (ppd != NULL)
+ {
+ ColorDevice = ppd->color_device;
+ LanguageLevel = ppd->language_level;
+ }
+
+ if ((val = cupsGetOption("landscape", num_options, options)) != NULL)
+ Orientation = 1;
+
+ if ((val = cupsGetOption("orientation-requested", num_options, options)) != NULL)
+ {
+ /*
+ * Map IPP orientation values to 0 to 3:
+ *
+ * 3 = 0 degrees = 0
+ * 4 = 90 degrees = 1
+ * 5 = -90 degrees = 3
+ * 6 = 180 degrees = 2
+ */
+
+ Orientation = atoi(val) - 3;
+ if (Orientation >= 2)
+ Orientation ^= 1;
+ }
+
+ if ((val = cupsGetOption("page-left", num_options, options)) != NULL)
+ {
+ switch (Orientation)
+ {
+ case 0 :
+ PageLeft = (float)atof(val);
+ break;
+ case 1 :
+ PageBottom = (float)atof(val);
+ break;
+ case 2 :
+ PageRight = PageWidth - (float)atof(val);
+ break;
+ case 3 :
+ PageTop = PageLength - (float)atof(val);
+ break;
+ }
+ }
+
+ if ((val = cupsGetOption("page-right", num_options, options)) != NULL)
+ {
+ switch (Orientation)
+ {
+ case 0 :
+ PageRight = PageWidth - (float)atof(val);
+ break;
+ case 1 :
+ PageTop = PageLength - (float)atof(val);
+ break;
+ case 2 :
+ PageLeft = (float)atof(val);
+ break;
+ case 3 :
+ PageBottom = (float)atof(val);
+ break;
+ }
+ }
+
+ if ((val = cupsGetOption("page-bottom", num_options, options)) != NULL)
+ {
+ switch (Orientation)
+ {
+ case 0 :
+ PageBottom = (float)atof(val);
+ break;
+ case 1 :
+ PageRight = PageWidth - (float)atof(val);
+ break;
+ case 2 :
+ PageTop = PageLength - (float)atof(val);
+ break;
+ case 3 :
+ PageLeft = (float)atof(val);
+ break;
+ }
+ }
+
+ if ((val = cupsGetOption("page-top", num_options, options)) != NULL)
+ {
+ switch (Orientation)
+ {
+ case 0 :
+ PageTop = PageLength - (float)atof(val);
+ break;
+ case 1 :
+ PageLeft = (float)atof(val);
+ break;
+ case 2 :
+ PageBottom = (float)atof(val);
+ break;
+ case 3 :
+ PageRight = PageWidth - (float)atof(val);
+ break;
+ }
+ }
+
+ if (change_size)
+ switch (Orientation)
+ {
+ case 0 : /* Portait */
+ break;
+
+ case 1 : /* Landscape */
+ temp = PageLeft;
+ PageLeft = PageBottom;
+ PageBottom = temp;
+
+ temp = PageRight;
+ PageRight = PageTop;
+ PageTop = temp;
+
+ temp = PageWidth;
+ PageWidth = PageLength;
+ PageLength = temp;
+ break;
+
+ case 2 : /* Reverse Portrait */
+ temp = PageWidth - PageLeft;
+ PageLeft = PageWidth - PageRight;
+ PageRight = temp;
+
+ temp = PageLength - PageBottom;
+ PageBottom = PageLength - PageTop;
+ PageTop = temp;
+ break;
+
+ case 3 : /* Reverse Landscape */
+ temp = PageWidth - PageLeft;
+ PageLeft = PageWidth - PageRight;
+ PageRight = temp;
+
+ temp = PageLength - PageBottom;
+ PageBottom = PageLength - PageTop;
+ PageTop = temp;
+
+ temp = PageLeft;
+ PageLeft = PageBottom;
+ PageBottom = temp;
+
+ temp = PageRight;
+ PageRight = PageTop;
+ PageTop = temp;
+
+ temp = PageWidth;
+ PageWidth = PageLength;
+ PageLength = temp;
+ break;
+ }
+
+ if ((val = cupsGetOption("sides", num_options, options)) != NULL &&
+ strncmp(val, "two-", 4) == 0)
+ Duplex = 1;
+ else if ((val = cupsGetOption("Duplex", num_options, options)) != NULL &&
+ strcmp(val, "DuplexNoTumble") == 0)
+ Duplex = 1;
+ else if (ppdIsMarked(ppd, "Duplex", "DuplexNoTumble") ||
+ ppdIsMarked(ppd, "Duplex", "DuplexTumble"))
+ Duplex = 1;
+
+ return (ppd);
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/filter/common.h b/filter/common.h
new file mode 100644
index 000000000..16e9ac6ca
--- /dev/null
+++ b/filter/common.h
@@ -0,0 +1,67 @@
+/*
+ * "$Id$"
+ *
+ * Common filter definitions for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-1999 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <time.h>
+
+#include <cups/cups.h>
+#include <cups/language.h>
+#include <cups/string.h>
+
+
+/*
+ * Globals...
+ */
+
+extern int Orientation, /* 0 = portrait, 1 = landscape, etc. */
+ Duplex, /* Duplexed? */
+ LanguageLevel, /* Language level of printer */
+ ColorDevice; /* Do color text? */
+extern float PageLeft, /* Left margin */
+ PageRight, /* Right margin */
+ PageBottom, /* Bottom margin */
+ PageTop, /* Top margin */
+ PageWidth, /* Total page width */
+ PageLength; /* Total page length */
+
+
+/*
+ * Prototypes...
+ */
+
+extern ppd_file_t *SetCommonOptions(int num_options, cups_option_t *options,
+ int change_size);
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/filter/hpgl-attr.c b/filter/hpgl-attr.c
new file mode 100644
index 000000000..99a1b9fa5
--- /dev/null
+++ b/filter/hpgl-attr.c
@@ -0,0 +1,405 @@
+/*
+ * "$Id$"
+ *
+ * HP-GL/2 attribute processing for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1993-1999 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * Contents:
+ *
+ * CR_color_range() - Set the range for color values.
+ * AC_anchor_corner() - Set the anchor corner.
+ * FT_fill_type() - Set the fill type or pattern.
+ * LA_line_attributes() - Set the line drawing attributes.
+ * LT_line_type() - Set the line type (style)...
+ * NP_number_pens() - Set the number of pens to be used.
+ * PC_pen_color() - Set the pen color...
+ * PW_pen_width() - Set the pen width.
+ * RF_raster_fill() - Set the raster fill pattern.
+ * SM_symbol_mode() - Set where symbols are drawn.
+ * SP_select_pen() - Select a pen for drawing.
+ * UL_user_line_type() - Set a user-defined line type.
+ * WU_width_units() - Set the units used for pen widths.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "hpgltops.h"
+
+
+/*
+ * 'CR_color_range()' - Set the range for color values.
+ */
+
+void
+CR_color_range(int num_params, /* I - Number of parameters */
+ param_t *params) /* I - Parameters */
+{
+ if (num_params == 0)
+ {
+ /*
+ * Default to 0 to 255 for all color values.
+ */
+
+ ColorRange[0][0] = 0.0;
+ ColorRange[0][1] = 255.0;
+ ColorRange[1][0] = 0.0;
+ ColorRange[1][1] = 255.0;
+ ColorRange[2][0] = 0.0;
+ ColorRange[2][1] = 255.0;
+ }
+ else if (num_params == 6)
+ {
+ /*
+ * Set the range based on the parameters...
+ */
+ ColorRange[0][0] = params[0].value.number;
+ ColorRange[0][1] = params[1].value.number - params[0].value.number;
+ ColorRange[1][0] = params[2].value.number;
+ ColorRange[1][1] = params[3].value.number - params[2].value.number;
+ ColorRange[2][0] = params[4].value.number;
+ ColorRange[2][1] = params[5].value.number - params[4].value.number;
+ }
+ else
+ fprintf(stderr, "WARNING: HP-GL/2 \'CR\' command with invalid number of parameters (%d)!\n",
+ num_params);
+}
+
+
+/*
+ * 'AC_anchor_corner()' - Set the anchor corner.
+ */
+
+void
+AC_anchor_corner(int num_params, /* I - Number of parameters */
+ param_t *params) /* I - Parameters */
+{
+ (void)num_params;
+ (void)params;
+}
+
+
+/*
+ * 'FT_fill_type()' - Set the fill type or pattern.
+ *
+ * Note:
+ *
+ * This needs to be updated to support non-solid fill.
+ */
+
+void
+FT_fill_type(int num_params, /* I - Number of parameters */
+ param_t *params) /* I - Parameters */
+{
+ if (num_params == 0 ||
+ params[0].value.number == 1 ||
+ params[0].value.number == 2)
+ {
+ /**** SOLID PATTERN ****/
+ }
+}
+
+
+/*
+ * 'LA_line_attributes()' - Set the line drawing attributes.
+ */
+
+void
+LA_line_attributes(int num_params, /* I - Number of parameters */
+ param_t *params) /* I - Parameters */
+{
+ int i; /* Looping var */
+
+
+ if (num_params == 0)
+ {
+ Outputf("3.0 setmiterlimit\n");
+ Outputf("0 setlinecap\n");
+ Outputf("0 setlinejoin\n");
+ }
+ else for (i = 0; i < (num_params - 1); i += 2)
+ switch ((int)params[i].value.number)
+ {
+ case 1 :
+ Outputf("%d setlinecap\n",
+ params[i + 1].value.number == 1 ? 0 :
+ params[i + 1].value.number == 4 ? 1 : 2);
+ break;
+ case 2 :
+ switch ((int)params[i + 1].value.number)
+ {
+ case 1 :
+ case 2 :
+ case 3 :
+ Outputf("0 setlinejoin\n");
+ break;
+ case 5 :
+ Outputf("2 setlinejoin\n");
+ break;
+ default :
+ Outputf("1 setlinejoin\n");
+ break;
+ }
+ break;
+ case 3 :
+ Outputf("%f setmiterlimit\n",
+ 1.0 + 0.5 * (params[i + 1].value.number - 1.0));
+ break;
+ }
+}
+
+
+/*
+ * 'LT_line_type()' - Set the line type (style)...
+ *
+ * Note:
+ *
+ * This needs to be updated to support line types.
+ */
+
+void
+LT_line_type(int num_params, /* I - Number of parameters */
+ param_t *params) /* I - Parameters */
+{
+ (void)num_params;
+ (void)params;
+}
+
+
+/*
+ * 'NP_number_pens()' - Set the number of pens to be used.
+ */
+
+void
+NP_number_pens(int num_params, /* I - Number of parameters */
+ param_t *params) /* I - Parameters */
+{
+ int i; /* Looping var */
+
+
+ if (num_params == 0)
+ PenCount = 8;
+ else if (num_params == 1)
+ PenCount = (int)params[0].value.number;
+ else
+ fprintf(stderr, "WARNING: HP-GL/2 \'NP\' command with invalid number of parameters (%d)!\n",
+ num_params);
+
+ PC_pen_color(0, NULL);
+
+ for (i = 0; i <= PenCount; i ++)
+ Outputf("/W%d { DefaultPenWidth PenScaling mul setlinewidth } bind def\n", i);
+}
+
+
+/*
+ * 'PC_pen_color()' - Set the pen color...
+ */
+
+void
+PC_pen_color(int num_params, /* I - Number of parameters */
+ param_t *params) /* I - Parameters */
+{
+ int i; /* Looping var */
+ static float standard_colors[8][3] = /* Standard colors for first 8 pens */
+ {
+ { 1.0, 1.0, 1.0 }, /* White */
+ { 0.0, 0.0, 0.0 }, /* Black */
+ { 1.0, 0.0, 0.0 }, /* Red */
+ { 0.0, 1.0, 0.0 }, /* Green */
+ { 1.0, 1.0, 0.0 }, /* Yellow */
+ { 0.0, 0.0, 1.0 }, /* Blue */
+ { 1.0, 0.0, 1.0 }, /* Magenta */
+ { 0.0, 1.0, 1.0 } /* Cyan */
+ };
+
+ if (num_params == 0)
+ {
+ for (i = 0; i <= PenCount; i ++)
+ if (i < 8)
+ Outputf("/P%d { %.3f %.3f %.3f setrgbcolor } bind def\n",
+ i, standard_colors[i][0],
+ standard_colors[i][1], standard_colors[i][2]);
+ else
+ Outputf("/P%d { 0.0 0.0 0.0 setrgbcolor } bind def\n", i);
+ }
+ else if (num_params == 1)
+ {
+ i = (int)params[0].value.number;
+
+ Outputf("/P%d { %.3f %.3f %.3f setrgbcolor } bind def\n",
+ i, standard_colors[i & 7][0], standard_colors[i & 7][1],
+ standard_colors[i & 7][2]);
+ }
+ else if (num_params == 4)
+ Outputf("/P%d { %.3f %.3f %.3f setrgbcolor } bind def\n",
+ (int)params[0].value.number,
+ (params[1].value.number - ColorRange[0][0]) / ColorRange[0][1],
+ (params[2].value.number - ColorRange[1][0]) / ColorRange[1][1],
+ (params[3].value.number - ColorRange[2][0]) / ColorRange[2][1]);
+ else
+ fprintf(stderr, "WARNING: HP-GL/2 \'PC\' command with invalid number of parameters (%d)!\n",
+ num_params);
+}
+
+
+/*
+ * 'PW_pen_width()' - Set the pen width.
+ */
+
+void
+PW_pen_width(int num_params, /* I - Number of parameters */
+ param_t *params) /* I - Parameters */
+{
+ int pen; /* Pen number */
+ float w; /* Width value */
+
+
+ if (WidthUnits == 0)
+ {
+ /*
+ * Metric...
+ */
+
+ if (num_params == 0)
+ w = 0.35f / 25.4f * 72.0f;
+ else
+ w = params[0].value.number / 25.4f * 72.0f;
+ }
+ else
+ {
+ /*
+ * Relative...
+ */
+
+ w = (float)hypot(PlotSize[0], PlotSize[1]) / 1016.0f * 72.0f;
+
+ if (num_params == 0)
+ w *= 0.01f;
+ else
+ w *= params[0].value.number;
+ }
+
+ if (num_params == 2)
+ Outputf("/W%d { %.1f PenScaling mul setlinewidth } bind def W%d\n",
+ (int)params[1].value.number, w, (int)params[1].value.number);
+ else if (num_params < 2)
+ {
+ /*
+ * Set width for all pens...
+ */
+
+ for (pen = 0; pen <= PenCount; pen ++)
+ Outputf("/W%d { %.1f PenScaling mul setlinewidth } bind def\n",
+ pen, w);
+
+ Outputf("W%d\n", PenNumber);
+ }
+ else
+ fprintf(stderr, "WARNING: HP-GL/2 \'PW\' command with invalid number of parameters (%d)!\n",
+ num_params);
+}
+
+
+/*
+ * 'RF_raster_fill()' - Set the raster fill pattern.
+ *
+ * Note:
+ *
+ * This needs to be implemented.
+ */
+
+void
+RF_raster_fill(int num_params, /* I - Number of parameters */
+ param_t *params) /* I - Parameters */
+{
+ (void)num_params;
+ (void)params;
+}
+
+
+/*
+ * 'SM_symbol_mode()' - Set where symbols are drawn.
+ */
+
+void
+SM_symbol_mode(int num_params, /* I - Number of parameters */
+ param_t *params) /* I - Parameters */
+{
+ (void)num_params;
+ (void)params;
+}
+
+
+/*
+ * 'SP_select_pen()' - Select a pen for drawing.
+ */
+
+void
+SP_select_pen(int num_params, /* I - Number of parameters */
+ param_t *params) /* I - Parameters */
+{
+ if (num_params == 0)
+ PenNumber = 1;
+ else if (params[0].value.number <= PenCount)
+ PenNumber = (int)params[0].value.number;
+ else
+ fprintf(stderr, "WARNING: HP-GL/2 \'SP\' command with invalid number or value of parameters (%d, %d)!\n",
+ num_params, (int)params[0].value.number);
+
+ Outputf("P%d W%d\n", PenNumber, PenNumber);
+}
+
+
+/*
+ * 'UL_user_line_type()' - Set a user-defined line type.
+ */
+
+void
+UL_user_line_type(int num_params, /* I - Number of parameters */
+ param_t *params) /* I - Parameters */
+{
+ (void)num_params;
+ (void)params;
+}
+
+
+/*
+ * 'WU_width_units()' - Set the units used for pen widths.
+ */
+
+void
+WU_width_units(int num_params, /* I - Number of parameters */
+ param_t *params) /* I - Parameters */
+{
+ if (num_params == 0)
+ WidthUnits = 0;
+ else if (num_params == 1)
+ WidthUnits = (int)params[0].value.number;
+ else
+ fprintf(stderr, "WARNING: HP-GL/2 \'WU\' command with invalid number of parameters (%d)!\n",
+ num_params);
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/filter/hpgl-char.c b/filter/hpgl-char.c
new file mode 100644
index 000000000..75c72d899
--- /dev/null
+++ b/filter/hpgl-char.c
@@ -0,0 +1,433 @@
+/*
+ * "$Id$"
+ *
+ * HP-GL/2 character processing for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1993-1999 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * Contents:
+ *
+ * AD_define_alternate() - Define the alternate font.
+ * CF_character_fill() - Set whether or not to fill or outline
+ * characters.
+ * CP_character_plot() - Move the current pen position for the given
+ * number of columns and rows.
+ * DI_absolute_direction() - Set the direction vector for text.
+ * DR_relative_direction() - Set the relative direction vector for text.
+ * DT_define_label_term() - Set the label string terminator.
+ * DV_define_variable_path() - Define a path for text.
+ * ES_extra_space() - Set extra spacing (kerning) between characters.
+ * LB_label() - Display a label string.
+ * LO_label_origin() - Set the label origin.
+ * SA_select_alternate() - Select the alternate font.
+ * SD_define_standard() - Define the standard font...
+ * SI_absolute_size() - Set the absolute size of text.
+ * SL_character_slant() - Set the slant of text.
+ * SR_relative_size() - Set the relative size of text.
+ * SS_select_standard() - Select the standard font for text.
+ * TD_transparent_data() - Send transparent print data.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "hpgltops.h"
+
+
+/*
+ * 'AD_define_alternate()' - Define the alternate font.
+ */
+
+void
+AD_define_alternate(int num_params, /* I - Number of parameters */
+ param_t *params) /* I - Parameters */
+{
+ int i; /* Looping var */
+ int typeface, /* Typeface number */
+ posture, /* Posture number */
+ weight; /* Weight number */
+ float height; /* Height/size of font */
+
+
+ /*
+ * Set default font attributes...
+ */
+
+ typeface = 48;
+ posture = 0;
+ weight = 0;
+ height = 11.5;
+
+ /*
+ * Loop through parameter value pairs...
+ */
+
+ for (i = 0; i < (num_params - 1); i += 2)
+ switch ((int)params[i].value.number)
+ {
+ case 4 :
+ height = params[i + 1].value.number;
+ break;
+ case 5 :
+ posture = (int)params[i + 1].value.number;
+ break;
+ case 6 :
+ weight = (int)params[i + 1].value.number;
+ break;
+ case 7 :
+ typeface = (int)params[i + 1].value.number;
+ break;
+ }
+
+ /*
+ * Define the font...
+ */
+
+ Outputf("/SA { /%s%s%s%s findfont %.1f scalefont setfont } def\n",
+ typeface == 48 ? "Courier" : "Helvetica",
+ (weight != 0 || posture != 0) ? "-" : "",
+ weight != 0 ? "Bold" : "",
+ posture != 0 ? "Oblique" : "",
+ height);
+
+ CharHeight[1] = height;
+}
+
+
+/*
+ * 'CF_character_fill()' - Set whether or not to fill or outline characters.
+ */
+
+void
+CF_character_fill(int num_params, /* I - Number of parameters */
+ param_t *params) /* I - Parameters */
+{
+ if (num_params == 0)
+ CharFillMode = 0;
+ else
+ CharFillMode = (int)params[0].value.number;
+
+ if (num_params == 2)
+ CharPen = (int)params[1].value.number;
+}
+
+
+/*
+ * 'CP_character_plot()' - Move the current pen position for the given number
+ * of columns and rows.
+ */
+
+void
+CP_character_plot(int num_params,
+ param_t *params)
+{
+ if (num_params < 2)
+ return;
+
+ switch (Rotation)
+ {
+ case 0:
+ PenPosition[0] += params[0].value.number * 1.2f / CharHeight[CharFont];
+ PenPosition[1] += params[1].value.number * CharHeight[CharFont];
+ break;
+ case 90:
+ PenPosition[0] -= params[1].value.number * 1.2f / CharHeight[CharFont];
+ PenPosition[1] += params[0].value.number * CharHeight[CharFont];
+ break;
+ case 180:
+ PenPosition[0] -= params[0].value.number * 1.2f / CharHeight[CharFont];
+ PenPosition[1] -= params[1].value.number * CharHeight[CharFont];
+ break;
+ case 270:
+ PenPosition[0] += params[1].value.number * 1.2f / CharHeight[CharFont];
+ PenPosition[1] -= params[0].value.number * CharHeight[CharFont];
+ break;
+ }
+}
+
+
+/*
+ * 'DI_absolute_direction()' - Set the direction vector for text.
+ */
+
+void
+DI_absolute_direction(int num_params, /* I - Number of parameters */
+ param_t *params) /* I - Parameters */
+{
+ Outputf(CharFont == 0 ? "SS\n" : "SA\n");
+
+ if (num_params == 2)
+ Outputf("currentfont [ %f %f %f %f 0.0 0.0 ] makefont setfont\n",
+ params[0].value.number, -params[1].value.number,
+ params[1].value.number, params[0].value.number);
+}
+
+
+/*
+ * 'DR_relative_direction()' - Set the relative direction vector for text.
+ */
+
+void
+DR_relative_direction(int num_params, /* I - Number of parameters */
+ param_t *params) /* I - Parameters */
+{
+ (void)num_params;
+ (void)params;
+}
+
+
+/*
+ * 'DT_define_label_term()' - Set the label string terminator.
+ */
+
+void
+DT_define_label_term(int num_params, /* I - Number of parameters */
+ param_t *params) /* I - Parameters */
+{
+ if (num_params == 0)
+ StringTerminator = '\003';
+ else
+ StringTerminator = params[0].value.string[0];
+}
+
+
+/*
+ * 'DV_define_variable_path()' - Define a path for text.
+ */
+
+void
+DV_define_variable_path(int num_params, /* I - Number of parameters */
+ param_t *params) /* I - Parameters */
+{
+ (void)num_params;
+ (void)params;
+}
+
+
+/*
+ * 'ES_extra_space()' - Set extra spacing (kerning) between characters.
+ */
+
+void
+ES_extra_space(int num_params, /* I - Number of parameters */
+ param_t *params) /* I - Parameters */
+{
+ (void)num_params;
+ (void)params;
+}
+
+
+/*
+ * 'LB_label()' - Display a label string.
+ */
+
+void
+LB_label(int num_params, /* I - Number of parameters */
+ param_t *params) /* I - Parameters */
+{
+ char *s; /* Pointer into string */
+
+
+ if (num_params == 0)
+ return;
+
+ Outputf("gsave\n");
+ Outputf("currentmiterlimit 1.0 setmiterlimit\n");
+ Outputf("MP\n");
+ Outputf("%.3f %.3f MO\n", PenPosition[0], PenPosition[1]);
+
+ Outputf("(");
+ for (s = params[0].value.string; *s != '\0'; s ++)
+ if (strchr("()\\", *s) != NULL)
+ Outputf("\\%c", *s);
+ else
+ Outputf("%c", *s);
+ Outputf(") true charpath\n");
+
+ if (CharFillMode != 1)
+ Outputf("FI\n");
+ if (CharFillMode == 1 || CharFillMode == 3)
+ Outputf("P%d ST\n", CharPen);
+
+ Outputf("setmiterlimit\n");
+ Outputf("grestore\n");
+}
+
+
+/*
+ * 'LO_label_origin()' - Set the label origin.
+ */
+
+void
+LO_label_origin(int num_params, /* I - Number of parameters */
+ param_t *params) /* I - Parameters */
+{
+ (void)num_params;
+ (void)params;
+}
+
+
+/*
+ * 'SA_select_alternate()' - Select the alternate font.
+ */
+
+void
+SA_select_alternate(int num_params, /* I - Number of parameters */
+ param_t *params) /* I - Parameters */
+{
+ (void)num_params;
+ (void)params;
+
+ Outputf("SA\n");
+ CharFont = 1;
+}
+
+
+/*
+ * 'SD_define_standard()' - Define the standard font...
+ */
+
+void
+SD_define_standard(int num_params, /* I - Number of parameters */
+ param_t *params) /* I - Parameters */
+{
+ int i; /* Looping var */
+ int typeface, /* Typeface number */
+ posture, /* Posture number */
+ weight; /* Weight number */
+ float height; /* Height/size of font */
+
+
+ /*
+ * Set default font attributes...
+ */
+
+ typeface = 48;
+ posture = 0;
+ weight = 0;
+ height = 11.5;
+
+ /*
+ * Loop through parameter value pairs...
+ */
+
+ for (i = 0; i < (num_params - 1); i += 2)
+ switch ((int)params[i].value.number)
+ {
+ case 4 :
+ height = params[i + 1].value.number;
+ break;
+ case 5 :
+ posture = (int)params[i + 1].value.number;
+ break;
+ case 6 :
+ weight = (int)params[i + 1].value.number;
+ break;
+ case 7 :
+ typeface = (int)params[i + 1].value.number;
+ break;
+ }
+
+ /*
+ * Define the font...
+ */
+
+ Outputf("/SS { /%s%s%s%s findfont %.1f scalefont setfont } def\n",
+ typeface == 48 ? "Courier" : "Helvetica",
+ (weight != 0 || posture != 0) ? "-" : "",
+ weight != 0 ? "Bold" : "",
+ posture != 0 ? "Oblique" : "",
+ height);
+
+ CharHeight[0] = height;
+}
+
+
+/*
+ * 'SI_absolute_size()' - Set the absolute size of text.
+ */
+
+void
+SI_absolute_size(int num_params, /* I - Number of parameters */
+ param_t *params) /* I - Parameters */
+{
+ (void)num_params;
+ (void)params;
+}
+
+
+/*
+ * 'SL_character_slant()' - Set the slant of text.
+ */
+
+void
+SL_character_slant(int num_params, /* I - Number of parameters */
+ param_t *params) /* I - Parameters */
+{
+ (void)num_params;
+ (void)params;
+}
+
+
+/*
+ * 'SR_relative_size()' - Set the relative size of text.
+ */
+
+void
+SR_relative_size(int num_params, /* I - Number of parameters */
+ param_t *params) /* I - Parameters */
+{
+ (void)num_params;
+ (void)params;
+}
+
+
+/*
+ * 'SS_select_standard()' - Select the standard font for text.
+ */
+
+void
+SS_select_standard(int num_params, /* I - Number of parameters */
+ param_t *params) /* I - Parameters */
+{
+ (void)num_params;
+ (void)params;
+
+ Outputf("SS\n");
+ CharFont = 0;
+}
+
+
+/*
+ * 'TD_transparent_data()' - Send transparent print data.
+ */
+
+void
+TD_transparent_data(int num_params, /* I - Number of parameters */
+ param_t *params) /* I - Parameters */
+{
+ (void)num_params;
+ (void)params;
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/filter/hpgl-config.c b/filter/hpgl-config.c
new file mode 100644
index 000000000..729a67aa6
--- /dev/null
+++ b/filter/hpgl-config.c
@@ -0,0 +1,473 @@
+/*
+ * "$Id$"
+ *
+ * HP-GL/2 configuration routines for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1993-1999 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * Contents:
+ *
+ * update_transform() - Update the page transformation matrix as needed.
+ * BP_begin_plot() - Start a plot...
+ * DF_default_values() - Set all state info to the default values.
+ * IN_initialize() - Initialize the plotter.
+ * IP_input_absolute() - Set P1 and P2 values for the plot.
+ * IR_input_relative() - Update P1 and P2.
+ * IW_input_window() - Setup an input window.
+ * PG_advance_page() - Eject the current page.
+ * PS_plot_size() - Set the plot size.
+ * RO_rotate() - Rotate the plot.
+ * RP_replot() - Replot the current page.
+ * SC_scale() - Set user-defined scaling.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "hpgltops.h"
+
+
+/*
+ * 'update_transform()' - Update the page transformation matrix as needed.
+ */
+
+void
+update_transform(void)
+{
+ float width, /* Plot width */
+ height; /* Plot height */
+ float page_width, /* Actual page width in points */
+ page_length; /* Actual page length in points */
+ float scaling; /* Scaling factor */
+
+
+ /*
+ * Get the page and input window sizes...
+ */
+
+ width = IW2[0] - IW1[0];
+ height = IW2[1] - IW1[1];
+
+ if (width == 0 || height == 0)
+ return;
+
+ /*
+ * Scale the plot as needed...
+ */
+
+ if (FitPlot)
+ {
+ page_width = PageRight - PageLeft;
+ page_length = PageTop - PageBottom;
+
+ if (Rotation == 0 || Rotation == 180)
+ {
+ scaling = page_width / width;
+ if (scaling > (page_length / width))
+ scaling = page_length / width;
+ }
+ else
+ {
+ scaling = page_width / height;
+ if (scaling > (page_length / height))
+ scaling = page_length / height;
+ }
+ }
+ else
+ {
+ page_width = PlotSize[0];
+ page_length = PlotSize[1];
+
+ if (Rotation == 0 || Rotation == 180)
+ scaling = page_width / width;
+ else
+ scaling = page_width / height;
+ }
+
+ /*
+ * Generate a new transformation matrix...
+ */
+
+ switch (Rotation)
+ {
+ case 0 :
+ Transform[0][0] = scaling;
+ Transform[0][1] = 0.0;
+ Transform[0][2] = -IW1[0] * scaling;
+ Transform[1][0] = 0.0;
+ Transform[1][1] = scaling;
+ Transform[1][2] = -IW1[1] * scaling;
+ break;
+
+ case 90 :
+ Transform[0][0] = 0.0;
+ Transform[0][1] = -scaling;
+ Transform[0][2] = (height - IW1[0]) * scaling;
+ Transform[1][0] = scaling;
+ Transform[1][1] = 0.0;
+ Transform[1][2] = -IW1[1] * scaling;
+ break;
+
+ case 180 :
+ Transform[0][0] = -scaling;
+ Transform[0][1] = 0.0;
+ Transform[0][2] = (height - IW1[0]) * scaling;
+ Transform[1][0] = 0.0;
+ Transform[1][1] = -scaling;
+ Transform[1][2] = (width - IW1[1]) * scaling;
+ break;
+
+ case 270 :
+ Transform[0][0] = 0.0;
+ Transform[0][1] = scaling;
+ Transform[0][2] = -IW1[0] * scaling;
+ Transform[1][0] = -scaling;
+ Transform[1][1] = 0.0;
+ Transform[1][2] = (width - IW1[1]) * scaling;
+ break;
+ }
+
+ PenScaling = Transform[0][0] + Transform[0][1];
+
+ if (PenScaling < 0.0)
+ PenScaling = -PenScaling;
+
+ if (PageDirty)
+ printf("/PenScaling %.3f def W%d\n", PenScaling, PenNumber);
+}
+
+
+/*
+ * 'BP_begin_plot()' - Start a plot...
+ */
+
+void
+BP_begin_plot(int num_params, /* I - Number of parameters */
+ param_t *params) /* I - Parameters */
+{
+ (void)num_params;
+ (void)params;
+}
+
+
+/*
+ * 'DF_default_values()' - Set all state info to the default values.
+ */
+
+void
+DF_default_values(int num_params, /* I - Number of parameters */
+ param_t *params) /* I - Parameters */
+{
+ (void)num_params;
+ (void)params;
+
+ AC_anchor_corner(0, NULL);
+ AD_define_alternate(0, NULL);
+ SD_define_standard(0, NULL);
+ CF_character_fill(0, NULL);
+ DI_absolute_direction(0, NULL);
+ DT_define_label_term(0, NULL);
+ DV_define_variable_path(0, NULL);
+ ES_extra_space(0, NULL);
+ FT_fill_type(0, NULL);
+ IW_input_window(0, NULL);
+ LA_line_attributes(0, NULL);
+ LO_label_origin(0, NULL);
+ LT_line_type(0, NULL);
+ PA_plot_absolute(0, NULL);
+ PolygonMode = 0;
+ RF_raster_fill(0, NULL);
+ SC_scale(0, NULL);
+ SM_symbol_mode(0, NULL);
+ SS_select_standard(0, NULL);
+ TD_transparent_data(0, NULL);
+ UL_user_line_type(0, NULL);
+}
+
+
+/*
+ * 'IN_initialize()' - Initialize the plotter.
+ */
+
+void
+IN_initialize(int num_params, /* I - Number of parameters */
+ param_t *params) /* I - Parameters */
+{
+ (void)num_params;
+ (void)params;
+
+ DF_default_values(0, NULL);
+ PU_pen_up(0, NULL);
+ RO_rotate(0, NULL);
+ PS_plot_size(0, NULL);
+ WU_width_units(0, NULL);
+ PW_pen_width(0, NULL);
+ SP_select_pen(0, NULL);
+
+ PenPosition[0] = PenPosition[1] = 0.0;
+}
+
+
+/*
+ * 'IP_input_absolute()' - Set P1 and P2 values for the plot.
+ */
+
+void
+IP_input_absolute(int num_params, /* I - Number of parameters */
+ param_t *params) /* I - Parameters */
+{
+ if (num_params == 0)
+ {
+ P1[0] = 0.0;
+ P1[1] = 0.0;
+ P2[0] = PlotSize[0] / 72.0f * 1016.0f;
+ P2[1] = PlotSize[1] / 72.0f * 1016.0f;
+ }
+ else if (num_params == 2)
+ {
+ P2[0] -= P1[0];
+ P2[1] -= P1[1];
+ P1[0] = params[0].value.number;
+ P1[1] = params[1].value.number;
+ P2[0] += P1[0];
+ P2[1] += P1[1];
+ }
+ else if (num_params == 4)
+ {
+ P1[0] = params[0].value.number;
+ P1[1] = params[1].value.number;
+ P2[0] = params[2].value.number;
+ P2[1] = params[3].value.number;
+ }
+
+ IW1[0] = P1[0];
+ IW1[1] = P1[1];
+ IW2[0] = P2[0];
+ IW2[1] = P2[1];
+
+ update_transform();
+}
+
+
+/*
+ * 'IR_input_relative()' - Update P1 and P2.
+ */
+
+void
+IR_input_relative(int num_params, /* I - Number of parameters */
+ param_t *params) /* I - Parameters */
+{
+ if (num_params == 0)
+ {
+ P1[0] = PageLeft / 72.0f * 1016.0f;
+ P1[1] = PageBottom / 72.0f * 1016.0f;
+ P2[0] = PageRight / 72.0f * 1016.0f;
+ P2[1] = PageTop / 72.0f * 1016.0f;
+ }
+ else if (num_params == 2)
+ {
+ P2[0] -= P1[0];
+ P2[1] -= P1[1];
+ P1[0] = params[0].value.number * PlotSize[0] / 72.0f * 1016.0f / 100.0f;
+ P1[1] = params[1].value.number * PlotSize[1] / 72.0f * 1016.0f / 100.0f;
+ P2[0] += P1[0];
+ P2[1] += P1[1];
+ }
+ else if (num_params == 4)
+ {
+ P1[0] = params[0].value.number * PlotSize[0] / 72.0f * 1016.0f / 100.0f;
+ P1[1] = params[1].value.number * PlotSize[1] / 72.0f * 1016.0f / 100.0f;
+ P2[0] = params[2].value.number * PlotSize[0] / 72.0f * 1016.0f / 100.0f;
+ P2[1] = params[3].value.number * PlotSize[1] / 72.0f * 1016.0f / 100.0f;
+ }
+
+ IW1[0] = P1[0];
+ IW1[1] = P1[1];
+ IW2[0] = P2[0];
+ IW2[1] = P2[1];
+
+ update_transform();
+}
+
+
+/*
+ * 'IW_input_window()' - Setup an input window.
+ */
+
+void
+IW_input_window(int num_params, /* I - Number of parameters */
+ param_t *params) /* I - Parameters */
+{
+ if (num_params == 0)
+ {
+ IW1[0] = P1[0];
+ IW1[1] = P1[1];
+ IW2[0] = P2[0];
+ IW2[1] = P2[1];
+ }
+ else if (num_params == 4)
+ {
+ IW1[0] = params[0].value.number;
+ IW1[1] = params[1].value.number;
+ IW2[0] = params[2].value.number;
+ IW2[1] = params[3].value.number;
+ }
+
+ update_transform();
+}
+
+
+/*
+ * 'PG_advance_page()' - Eject the current page.
+ */
+
+void
+PG_advance_page(int num_params, /* I - Number of parameters */
+ param_t *params) /* I - Parameters */
+{
+ (void)num_params;
+ (void)params;
+
+ if (PageDirty)
+ {
+ puts("grestore");
+ puts("showpage");
+
+ PageDirty = 0;
+ }
+}
+
+
+/*
+ * 'PS_plot_size()' - Set the plot size.
+ */
+
+void
+PS_plot_size(int num_params, /* I - Number of parameters */
+ param_t *params) /* I - Parameters */
+{
+ switch (num_params)
+ {
+ case 0 :
+ if (Rotation == 0 || Rotation == 180)
+ {
+ PlotSize[0] = PageRight - PageLeft;
+ PlotSize[1] = PageTop - PageBottom;
+ }
+ else
+ {
+ PlotSize[0] = PageTop - PageBottom;
+ PlotSize[1] = PageRight - PageLeft;
+ }
+ break;
+ case 1 :
+ if (Rotation == 0 || Rotation == 180)
+ {
+ PlotSize[1] = 72.0f * params[0].value.number / 1016.0f;
+ PlotSize[0] = 0.75f * PlotSize[1];
+ }
+ else
+ {
+ PlotSize[0] = 72.0f * params[0].value.number / 1016.0f;
+ PlotSize[1] = 0.75f * PlotSize[0];
+ }
+ break;
+ case 2 :
+ if (Rotation == 0 || Rotation == 180)
+ {
+ PlotSize[0] = 72.0f * params[1].value.number / 1016.0f;
+ PlotSize[1] = 72.0f * params[0].value.number / 1016.0f;
+ }
+ else
+ {
+ PlotSize[0] = 72.0f * params[0].value.number / 1016.0f;
+ PlotSize[1] = 72.0f * params[1].value.number / 1016.0f;
+ }
+ break;
+ }
+
+ /*
+ * This is required for buggy files that don't set the input window.
+ */
+
+ IP_input_absolute(0, NULL);
+}
+
+
+/*
+ * 'RO_rotate()' - Rotate the plot.
+ */
+
+void
+RO_rotate(int num_params, /* I - Number of parameters */
+ param_t *params) /* I - Parameters */
+{
+ if (num_params == 0)
+ Rotation = 0;
+ else
+ Rotation = (int)params[0].value.number;
+
+ update_transform();
+}
+
+
+/*
+ * 'RP_replot()' - Replot the current page.
+ */
+
+void
+RP_replot(int num_params, /* I - Number of parameters */
+ param_t *params) /* I - Parameters */
+{
+ (void)num_params;
+ (void)params;
+}
+
+
+/*
+ * 'SC_scale()' - Set user-defined scaling.
+ */
+
+void
+SC_scale(int num_params, /* I - Number of parameters */
+ param_t *params) /* I - Parameters */
+{
+ if (num_params == 0)
+ ScalingType = -1;
+ else if (num_params > 3)
+ {
+ Scaling1[0] = params[0].value.number;
+ Scaling2[0] = params[1].value.number;
+ Scaling1[1] = params[2].value.number;
+ Scaling2[1] = params[3].value.number;
+
+ if (num_params > 4)
+ ScalingType = (int)params[4].value.number;
+ else
+ ScalingType = 0;
+ }
+
+ update_transform();
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/filter/hpgl-input.c b/filter/hpgl-input.c
new file mode 100644
index 000000000..212dabc4a
--- /dev/null
+++ b/filter/hpgl-input.c
@@ -0,0 +1,232 @@
+/*
+ * "$Id$"
+ *
+ * HP-GL/2 input processing for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1993-1999 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * Contents:
+ *
+ * ParseCommand() - Parse an HPGL/2 command.
+ * FreeParameters() - Free all string parameter values.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "hpgltops.h"
+#include <ctype.h>
+
+#define MAX_PARAMS 16384
+
+
+/*
+ * 'ParseCommand()' - Parse an HPGL/2 command.
+ *
+ * Returns the number of parameters seen or -1 on EOF.
+ */
+
+int /* O - -1 on EOF, # params otherwise */
+ParseCommand(FILE *fp, /* I - File to read from */
+ char *name, /* O - Name of command */
+ param_t **params) /* O - Parameter list */
+{
+ int num_params, /* Number of parameters seen */
+ ch, /* Current char */
+ done, /* Non-zero when the current command is read */
+ i; /* Looping var */
+ char buf[262144]; /* String buffer */
+ static param_t p[MAX_PARAMS]; /* Parameter buffer */
+
+
+ num_params = 0;
+ done = 0;
+
+ do
+ {
+ while ((ch = getc(fp)) != EOF)
+ if (strchr(" \t\r\n,;", ch) == NULL)
+ break;
+
+ if (ch == EOF)
+ return (-1);
+
+ if (ch == 0x1b)
+ switch (getc(fp))
+ {
+ case '.' : /* HP-GL/2 job control */
+ i = getc(fp);
+
+ if (strchr(")Z", i) != NULL)
+ {
+ /*
+ * 'Printer Off' command - look for next 'Printer On' command...
+ */
+
+ for (;;)
+ {
+ while ((i = getc(fp)) != EOF && i != 0x1b);
+
+ if (i == EOF)
+ return (-1);
+
+ if (getc(fp) != '.')
+ continue;
+
+ if ((i = getc(fp)) == '(' ||
+ i == 'Y')
+ break;
+ }
+ }
+ else if (strchr("@HIMNTI\003", i) != NULL)
+ {
+ while ((i = getc(fp)) != EOF && i != ':');
+ }
+ break;
+
+ default : /* HP RTL/PCL control */
+ while ((i = getc(fp)) != EOF && !isupper(i));
+ break;
+ }
+ } while (ch == 0x1b);
+
+ name[0] = ch;
+ name[1] = getc(fp);
+ name[2] = '\0';
+
+ if (strcasecmp(name, "LB") == 0)
+ {
+ for (i = 0; (ch = getc(fp)) != StringTerminator; i ++)
+ buf[i] = ch;
+ buf[i] = '\0';
+ p[num_params].type = PARAM_STRING;
+ p[num_params].value.string = strdup(buf);
+ num_params ++;
+ }
+ else if (strcasecmp(name, "SM") == 0)
+ {
+ buf[0] = getc(fp);
+ buf[1] = '\0';
+ p[num_params].type = PARAM_STRING;
+ p[num_params].value.string = strdup(buf);
+ num_params ++;
+ }
+ else if (strcasecmp(name, "DT") == 0)
+ {
+ if ((buf[0] = getc(fp)) != ';')
+ {
+ buf[1] = '\0';
+ p[num_params].type = PARAM_STRING;
+ p[num_params].value.string = strdup(buf);
+ num_params ++;
+ }
+ }
+ else if (strcasecmp(name, "PE") == 0)
+ {
+ for (i = 0; i < (sizeof(buf) - 1); i ++)
+ if ((buf[i] = getc(fp)) == ';')
+ break;
+
+ buf[i] = '\0';
+ p[num_params].type = PARAM_STRING;
+ p[num_params].value.string = strdup(buf);
+ num_params ++;
+ }
+
+ while (!done)
+ switch (ch = getc(fp))
+ {
+ case ',' :
+ case ' ' :
+ case '\n' :
+ case '\r' :
+ case '\t' :
+ break;
+
+ case '\"' :
+ fscanf(fp, "%[^\"]\"", buf);
+ if (num_params < MAX_PARAMS)
+ {
+ p[num_params].type = PARAM_STRING;
+ p[num_params].value.string = strdup(buf);
+ num_params ++;
+ };
+ break;
+
+ case '-' :
+ case '+' :
+ ungetc(ch, fp);
+ fscanf(fp, "%f", &(p[num_params].value.number));
+ if (num_params < MAX_PARAMS)
+ {
+ p[num_params].type = PARAM_RELATIVE;
+ num_params ++;
+ }
+ break;
+ case '0' :
+ case '1' :
+ case '2' :
+ case '3' :
+ case '4' :
+ case '5' :
+ case '6' :
+ case '7' :
+ case '8' :
+ case '9' :
+ case '.' :
+ ungetc(ch, fp);
+ fscanf(fp, "%f", &(p[num_params].value.number));
+ if (num_params < MAX_PARAMS)
+ {
+ p[num_params].type = PARAM_ABSOLUTE;
+ num_params ++;
+ }
+ break;
+ default :
+ ungetc(ch, fp);
+ done = 1;
+ break;
+ }
+
+ *params = p;
+ return (num_params);
+}
+
+
+/*
+ * 'FreeParameters()' - Free all string parameter values.
+ */
+
+void
+FreeParameters(int num_params, /* I - Number of parameters */
+ param_t *params) /* I - Parameter values */
+{
+ int i; /* Looping var */
+
+
+ for (i = 0; i < num_params; i ++)
+ if (params[i].type == PARAM_STRING)
+ free(params[i].value.string);
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/filter/hpgl-main.c b/filter/hpgl-main.c
new file mode 100644
index 000000000..9c460985f
--- /dev/null
+++ b/filter/hpgl-main.c
@@ -0,0 +1,255 @@
+/*
+ * "$Id$"
+ *
+ * HP-GL/2 filter main entry for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1993-1999 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * Contents:
+ *
+ * main() - Main entry for HP-GL/2 filter.
+ * compare_names() - Compare two command names.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+/*#define DEBUG*/
+#define _HPGL_MAIN_C_
+#include "hpgltops.h"
+
+
+/*
+ * HP-GL/2 command table...
+ */
+
+typedef struct
+{
+ char name[4]; /* Name of command */
+ void (*func)(int, param_t *); /* Function to call */
+} name_t;
+
+static name_t commands[] =
+{
+ { "BP", BP_begin_plot },
+ { "DF", DF_default_values },
+ { "IN", IN_initialize },
+ { "IP", IP_input_absolute },
+ { "IR", IR_input_relative },
+ { "IW", IW_input_window },
+ { "PG", PG_advance_page },
+ { "RO", RO_rotate },
+ { "RP", RP_replot },
+ { "SC", SC_scale },
+ { "AA", AA_arc_absolute },
+ { "AR", AR_arc_relative },
+ { "AT", AT_arc_absolute3 },
+ { "CI", CI_circle },
+ { "PA", PA_plot_absolute },
+ { "PD", PD_pen_down },
+ { "PE", PE_polyline_encoded },
+ { "PR", PR_plot_relative },
+ { "PS", PS_plot_size },
+ { "PU", PU_pen_up },
+ { "RT", RT_arc_relative3 },
+ { "EA", EA_edge_rect_absolute },
+ { "EP", EP_edge_polygon },
+ { "ER", ER_edge_rect_relative },
+ { "EW", EW_edge_wedge },
+ { "FP", FP_fill_polygon },
+ { "PM", PM_polygon_mode },
+ { "RA", RA_fill_rect_absolute },
+ { "RR", RR_fill_rect_relative },
+ { "WG", WG_fill_wedge },
+ { "AD", AD_define_alternate },
+ { "CF", CF_character_fill },
+ { "CP", CP_character_plot },
+ { "DI", DI_absolute_direction },
+ { "DR", DR_relative_direction },
+ { "DT", DT_define_label_term },
+ { "DV", DV_define_variable_path },
+ { "ES", ES_extra_space },
+ { "LB", LB_label },
+ { "LO", LO_label_origin },
+ { "SA", SA_select_alternate },
+ { "SD", SD_define_standard },
+ { "SI", SI_absolute_size },
+ { "SL", SL_character_slant },
+ { "SR", SR_relative_size },
+ { "SS", SS_select_standard },
+ { "TD", TD_transparent_data },
+ { "AC", AC_anchor_corner },
+ { "FT", FT_fill_type },
+ { "LA", LA_line_attributes },
+ { "LT", LT_line_type },
+ { "NP", NP_number_pens },
+ { "PC", PC_pen_color },
+ { "CR", CR_color_range },
+ { "PW", PW_pen_width },
+ { "RF", RF_raster_fill },
+ { "SM", SM_symbol_mode },
+ { "SP", SP_select_pen },
+ { "UL", UL_user_line_type },
+ { "WU", WU_width_units }
+};
+#define NUM_COMMANDS (sizeof(commands) / sizeof(name_t))
+
+
+/*
+ * Local functions...
+ */
+
+static int compare_names(const void *p1, const void *p2);
+
+
+/*
+ * 'main()' - Main entry for HP-GL/2 filter.
+ */
+
+int /* O - Exit status */
+main(int argc, /* I - Number of command-line arguments */
+ char *argv[]) /* I - Command-line arguments */
+{
+ FILE *fp; /* Input file */
+ ppd_file_t *ppd; /* PPD file */
+ int num_params; /* Number of parameters */
+ param_t *params; /* Command parameters */
+ name_t *command, /* Command */
+ name; /* Name of command */
+ int num_options; /* Number of print options */
+ cups_option_t *options; /* Print options */
+ const char *val; /* Option value */
+ int shading; /* -1 = black, 0 = grey, 1 = color */
+ float penwidth; /* Default pen width */
+
+
+ if (argc < 6 || argc > 7)
+ {
+ fputs("ERROR: hpgltops job-id user title copies options [file]\n", stderr);
+ return (1);
+ }
+
+ /*
+ * If we have 7 arguments, print the file named on the command-line.
+ * Otherwise, send stdin instead...
+ */
+
+ if (argc == 6)
+ fp = stdin;
+ else
+ {
+ /*
+ * Try to open the print file...
+ */
+
+ if ((fp = fopen(argv[6], "rb")) == NULL)
+ {
+ perror("ERROR: unable to open print file - ");
+ return (1);
+ }
+ }
+
+ /*
+ * Process command-line options and write the prolog...
+ */
+
+ options = NULL;
+ num_options = cupsParseOptions(argv[5], 0, &options);
+
+ if ((ppd = SetCommonOptions(num_options, options, 1)) != NULL)
+ ppdClose(ppd);
+
+ shading = 1;
+ penwidth = 1.0;
+
+ if ((val = cupsGetOption("blackplot", num_options, options)) != NULL)
+ shading = 0;
+
+ if ((val = cupsGetOption("fitplot", num_options, options)) != NULL)
+ FitPlot = 1;
+
+ if ((val = cupsGetOption("penwidth", num_options, options)) != NULL)
+ penwidth = (float)atof(val);
+
+ /*
+ * Write the PostScript prolog and initialize the plotting "engine"...
+ */
+
+ OutputProlog(argv[3], argv[2], shading, penwidth);
+
+ IP_input_absolute(0, NULL);
+
+ /*
+ * Sort the command array...
+ */
+
+ qsort(commands, NUM_COMMANDS, sizeof(name_t),
+ (int (*)(const void *, const void *))compare_names);
+
+ /*
+ * Read commands until we reach the end of file.
+ */
+
+ while ((num_params = ParseCommand(fp, name.name, &params)) >= 0)
+ {
+#ifdef DEBUG
+ {
+ int i;
+ fprintf(stderr, "DEBUG: %s(%d)", name.name, num_params);
+ for (i = 0; i < num_params; i ++)
+ if (params[i].type == PARAM_STRING)
+ fprintf(stderr, " \'%s\'", params[i].value.string);
+ else
+ fprintf(stderr, " %f", params[i].value.number);
+ fputs("\n", stderr);
+ }
+#endif /* DEBUG */
+
+ if ((command = bsearch(&name, commands, NUM_COMMANDS, sizeof(name_t),
+ (int (*)(const void *, const void *))compare_names)) != NULL)
+ (*command->func)(num_params, params);
+
+ FreeParameters(num_params, params);
+ }
+
+ OutputTrailer();
+
+ if (fp != stdin)
+ fclose(fp);
+
+ return (0);
+}
+
+
+/*
+ * 'compare_names()' - Compare two command names.
+ */
+
+static int /* O - Result of strcasecmp() on names */
+compare_names(const void *p1, /* I - First name */
+ const void *p2) /* I - Second name */
+{
+ return (strcasecmp(((name_t *)p1)->name, ((name_t *)p2)->name));
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/filter/hpgl-polygon.c b/filter/hpgl-polygon.c
new file mode 100644
index 000000000..f16f8649d
--- /dev/null
+++ b/filter/hpgl-polygon.c
@@ -0,0 +1,380 @@
+/*
+ * "$Id$"
+ *
+ * HP-GL/2 polygon routines for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1993-1999 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * Contents:
+ *
+ * EA_edge_rect_absolute() - Draw a rectangle.
+ * EP_edge_polygon() - Stroke the edges of a polygon.
+ * ER_edge_rect_relative() - Draw a rectangle relative to the current
+ * EW_edge_wedge() - Draw a pie wedge.
+ * FP_fill_polygon() - Fill a polygon.
+ * PM_polygon_mode() - Set the polygon drawing mode.
+ * RA_fill_rect_absolute() - Fill a rectangle.
+ * RR_fill_rect_relative() - Fill a rectangle relative to the current
+ * WG_fill_wedge() - Fill a pie wedge.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "hpgltops.h"
+
+
+/*
+ * 'EA_edge_rect_absolute()' - Draw a rectangle.
+ */
+
+void
+EA_edge_rect_absolute(int num_params, /* I - Number of parameters */
+ param_t *params) /* I - Parameters */
+{
+ float x, y; /* Transformed coordinates */
+
+
+ if (num_params < 2)
+ return;
+
+ x = Transform[0][0] * params[0].value.number +
+ Transform[0][1] * params[1].value.number +
+ Transform[0][2];
+ y = Transform[1][0] * params[0].value.number +
+ Transform[1][1] * params[1].value.number +
+ Transform[1][2];
+
+ if (!PolygonMode)
+ Outputf("MP\n");
+
+ Outputf("%.3f %.3f MO\n", PenPosition[0], PenPosition[1]);
+ Outputf("%.3f %.3f LI\n", PenPosition[0], y);
+ Outputf("%.3f %.3f LI\n", x, y);
+ Outputf("%.3f %.3f LI\n", x, PenPosition[1]);
+
+ Outputf("CP\n");
+ if (!PolygonMode)
+ Outputf("ST\n");
+}
+
+
+/*
+ * 'EP_edge_polygon()' - Stroke the edges of a polygon.
+ */
+
+void
+EP_edge_polygon(int num_params, /* I - Number of parameters */
+ param_t *params) /* I - Parameters */
+{
+ (void)num_params;
+ (void)params;
+
+ Outputf("ST\n");
+}
+
+
+/*
+ * 'ER_edge_rect_relative()' - Draw a rectangle relative to the current
+ * pen position.
+ */
+
+void
+ER_edge_rect_relative(int num_params, /* I - Number of parameters */
+ param_t *params) /* I - Parameters */
+{
+ float x, y; /* Transformed coordinates */
+
+
+ if (num_params < 2)
+ return;
+
+ x = Transform[0][0] * params[0].value.number +
+ Transform[0][1] * params[1].value.number +
+ PenPosition[0];
+ y = Transform[1][0] * params[0].value.number +
+ Transform[1][1] * params[1].value.number +
+ PenPosition[1];
+
+ if (!PolygonMode)
+ Outputf("MP\n");
+
+ Outputf("%.3f %.3f MO\n", PenPosition[0], PenPosition[1]);
+ Outputf("%.3f %.3f LI\n", PenPosition[0], y);
+ Outputf("%.3f %.3f LI\n", x, y);
+ Outputf("%.3f %.3f LI\n", x, PenPosition[1]);
+
+ Outputf("CP\n");
+ if (!PolygonMode)
+ Outputf("ST\n");
+}
+
+
+/*
+ * 'EW_edge_wedge()' - Draw a pie wedge.
+ */
+
+void
+EW_edge_wedge(int num_params, /* I - Number of parameters */
+ param_t *params) /* I - Parameters */
+{
+ float x, y; /* Transformed coordinates */
+ float start, end, /* Start and end of arc */
+ theta, /* Current angle */
+ dt, /* Step between points */
+ radius; /* Radius of arc */
+
+
+ if (num_params < 3)
+ return;
+
+ radius = params[0].value.number;
+ start = params[1].value.number;
+ end = start + params[2].value.number;
+
+ if (num_params > 3)
+ dt = (float)fabs(params[3].value.number);
+ else
+ dt = 5.0f;
+
+ if (!PolygonMode)
+ Outputf("MP\n");
+
+ Outputf("%.3f %.3f MO\n", PenPosition[0], PenPosition[1]);
+
+ if (start < end)
+ for (theta = start + dt; theta < end; theta += dt)
+ {
+ x = (float)(PenPosition[0] +
+ radius * cos(M_PI * theta / 180.0) * Transform[0][0] +
+ radius * sin(M_PI * theta / 180.0) * Transform[0][1]);
+ y = (float)(PenPosition[1] +
+ radius * cos(M_PI * theta / 180.0) * Transform[1][0] +
+ radius * sin(M_PI * theta / 180.0) * Transform[1][1]);
+
+ Outputf("%.3f %.3f LI\n", x, y);
+ }
+ else
+ for (theta = start - dt; theta > end; theta -= dt)
+ {
+ x = (float)(PenPosition[0] +
+ radius * cos(M_PI * theta / 180.0) * Transform[0][0] +
+ radius * sin(M_PI * theta / 180.0) * Transform[0][1]);
+ y = (float)(PenPosition[1] +
+ radius * cos(M_PI * theta / 180.0) * Transform[1][0] +
+ radius * sin(M_PI * theta / 180.0) * Transform[1][1]);
+
+ Outputf("%.3f %.3f LI\n", x, y);
+ }
+
+ x = (float)(PenPosition[0] +
+ radius * cos(M_PI * end / 180.0) * Transform[0][0] +
+ radius * sin(M_PI * end / 180.0) * Transform[0][1]);
+ y = (float)(PenPosition[1] +
+ radius * cos(M_PI * end / 180.0) * Transform[1][0] +
+ radius * sin(M_PI * end / 180.0) * Transform[1][1]);
+ Outputf("%.3f %.3f LI\n", x, y);
+
+ Outputf("CP\n");
+ if (!PolygonMode)
+ Outputf("ST\n");
+}
+
+
+/*
+ * 'FP_fill_polygon()' - Fill a polygon.
+ */
+
+void
+FP_fill_polygon(int num_params, /* I - Number of parameters */
+ param_t *params) /* I - Parameters */
+{
+ (void)num_params;
+ (void)params;
+
+ Outputf("FI\n");
+}
+
+
+/*
+ * 'PM_polygon_mode()' - Set the polygon drawing mode.
+ */
+
+void
+PM_polygon_mode(int num_params, /* I - Number of parameters */
+ param_t *params) /* I - Parameters */
+{
+ if (num_params == 0 ||
+ params[0].value.number == 0)
+ {
+ Outputf("MP\n");
+ Outputf("%.3f %.3f MO\n", PenPosition[0], PenPosition[1]);
+ PolygonMode = 1;
+ }
+ else if (params[0].value.number == 2)
+ PolygonMode = 0;
+}
+
+
+/*
+ * 'RA_fill_rect_absolute()' - Fill a rectangle.
+ */
+
+void
+RA_fill_rect_absolute(int num_params, /* I - Number of parameters */
+ param_t *params) /* I - Parameters */
+{
+ float x, y; /* Transformed coordinates */
+
+
+ if (num_params < 2)
+ return;
+
+ x = Transform[0][0] * params[0].value.number +
+ Transform[0][1] * params[1].value.number +
+ Transform[0][2];
+ y = Transform[1][0] * params[0].value.number +
+ Transform[1][1] * params[1].value.number +
+ Transform[1][2];
+
+ if (!PolygonMode)
+ Outputf("MP\n");
+
+ Outputf("%.3f %.3f MO\n", PenPosition[0], PenPosition[1]);
+ Outputf("%.3f %.3f LI\n", PenPosition[0], y);
+ Outputf("%.3f %.3f LI\n", x, y);
+ Outputf("%.3f %.3f LI\n", x, PenPosition[1]);
+
+ Outputf("CP\n");
+ if (!PolygonMode)
+ Outputf("FI\n");
+}
+
+
+/*
+ * 'RR_fill_rect_relative()' - Fill a rectangle relative to the current
+ * pen position.
+ */
+
+void
+RR_fill_rect_relative(int num_params, /* I - Number of parameters */
+ param_t *params) /* I - Parameters */
+{
+ float x, y; /* Transformed coordinates */
+
+
+ if (num_params < 2)
+ return;
+
+ x = Transform[0][0] * params[0].value.number +
+ Transform[0][1] * params[1].value.number +
+ PenPosition[0];
+ y = Transform[1][0] * params[0].value.number +
+ Transform[1][1] * params[1].value.number +
+ PenPosition[1];
+
+ if (!PolygonMode)
+ Outputf("MP\n");
+
+ Outputf("%.3f %.3f MO\n", PenPosition[0], PenPosition[1]);
+ Outputf("%.3f %.3f LI\n", PenPosition[0], y);
+ Outputf("%.3f %.3f LI\n", x, y);
+ Outputf("%.3f %.3f LI\n", x, PenPosition[1]);
+
+ Outputf("CP\n");
+ if (!PolygonMode)
+ Outputf("FI\n");
+}
+
+
+/*
+ * 'WG_fill_wedge()' - Fill a pie wedge.
+ */
+
+void
+WG_fill_wedge(int num_params, /* I - Number of parameters */
+ param_t *params) /* I - Parameters */
+{
+ float x, y; /* Transformed coordinates */
+ float start, end, /* Start and end angles */
+ theta, /* Current angle */
+ dt, /* Step between points */
+ radius; /* Radius of arc */
+
+
+ if (num_params < 3)
+ return;
+
+ radius = params[0].value.number;
+ start = params[1].value.number;
+ end = start + params[2].value.number;
+
+ if (num_params > 3)
+ dt = (float)fabs(params[3].value.number);
+ else
+ dt = 5.0;
+
+ if (!PolygonMode)
+ Outputf("MP\n");
+
+ Outputf("%.3f %.3f MO\n", PenPosition[0], PenPosition[1]);
+
+ if (start < end)
+ for (theta = start + dt; theta < end; theta += dt)
+ {
+ x = (float)(PenPosition[0] +
+ radius * cos(M_PI * theta / 180.0) * Transform[0][0] +
+ radius * sin(M_PI * theta / 180.0) * Transform[0][1]);
+ y = (float)(PenPosition[1] +
+ radius * cos(M_PI * theta / 180.0) * Transform[1][0] +
+ radius * sin(M_PI * theta / 180.0) * Transform[1][1]);
+
+ Outputf("%.3f %.3f LI\n", x, y);
+ }
+ else
+ for (theta = start - dt; theta > end; theta -= dt)
+ {
+ x = (float)(PenPosition[0] +
+ radius * cos(M_PI * theta / 180.0) * Transform[0][0] +
+ radius * sin(M_PI * theta / 180.0) * Transform[0][1]);
+ y = (float)(PenPosition[1] +
+ radius * cos(M_PI * theta / 180.0) * Transform[1][0] +
+ radius * sin(M_PI * theta / 180.0) * Transform[1][1]);
+
+ Outputf("%.3f %.3f LI\n", x, y);
+ }
+
+ x = (float)(PenPosition[0] +
+ radius * cos(M_PI * end / 180.0) * Transform[0][0] +
+ radius * sin(M_PI * end / 180.0) * Transform[0][1]);
+ y = (float)(PenPosition[1] +
+ radius * cos(M_PI * end / 180.0) * Transform[1][0] +
+ radius * sin(M_PI * end / 180.0) * Transform[1][1]);
+ Outputf("%.3f %.3f LI\n", x, y);
+
+ Outputf("CP\n");
+ if (!PolygonMode)
+ Outputf("FI\n");
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/filter/hpgl-prolog.c b/filter/hpgl-prolog.c
new file mode 100644
index 000000000..0d8aac704
--- /dev/null
+++ b/filter/hpgl-prolog.c
@@ -0,0 +1,192 @@
+/*
+ * "$Id$"
+ *
+ * HP-GL/2 prolog routines for for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1993-1999 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * Contents:
+ *
+ * OutputProlog() - Output the PostScript prolog...
+ * OutputTrailer() - Output the PostScript trailer...
+ * Outputf() - Write a formatted string to the output file, creating the
+ * page header as needed...
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "hpgltops.h"
+#include <stdarg.h>
+
+
+/*
+ * 'OutputProlog()' - Output the PostScript prolog...
+ */
+
+void
+OutputProlog(char *title, /* I - Job title */
+ char *user, /* I - Username */
+ int shading, /* I - Type of shading */
+ float penwidth) /* I - Default pen width */
+{
+ FILE *prolog; /* Prolog file */
+ char line[255]; /* Line from prolog file */
+ time_t curtime; /* Current time */
+ struct tm *curtm; /* Current date */
+
+
+ curtime = time(NULL);
+ curtm = localtime(&curtime);
+
+ puts("%!PS-Adobe-3.0");
+ printf("%%%%BoundingBox: %.0f %.0f %.0f %.0f\n",
+ PageLeft, PageBottom, PageRight, PageTop);
+ puts("%%Pages: (atend)");
+ printf("%%%%LanguageLevel: %d\n", LanguageLevel);
+ puts("%%DocumentData: Clean7Bit");
+ puts("%%DocumentSuppliedResources: procset hpgltops 1.0 0");
+ puts("%%DocumentNeededResources: font Courier Helvetica");
+ puts("%%Creator: hpgltops/" CUPS_SVERSION);
+ strftime(line, sizeof(line), "%%%%CreationDate: %c", curtm);
+ puts(line);
+ printf("%%%%Title: %s\n", title);
+ printf("%%%%For: %s\n", user);
+ if (Orientation & 1)
+ puts("%%Orientation: Landscape");
+ puts("%%EndComments");
+ puts("%%BeginProlog");
+ printf("/DefaultPenWidth %.2f def\n", penwidth * 72.0 / 25.4);
+ puts("3.0 setmiterlimit");
+ if (!shading) /* Black only */
+ puts("/setrgbcolor { pop pop pop } bind def");
+ else if (!ColorDevice) /* Greyscale */
+ puts("/setrgbcolor { 0.08 mul exch 0.61 mul add exch 0.31 mul add setgray } bind def\n");
+
+ if ((prolog = fopen(CUPS_DATADIR "/data/HPGLprolog", "r")) == NULL)
+ {
+ perror("ERROR: Unable to open HPGL prolog \"" CUPS_DATADIR "/data/HPGLprolog\" for reading");
+ exit(1);
+ }
+
+ while (fgets(line, sizeof(line), prolog) != NULL)
+ fputs(line, stdout);
+
+ fclose(prolog);
+
+ puts("%%EndProlog");
+
+ IN_initialize(0, NULL);
+}
+
+
+/*
+ * 'OutputTrailer()' - Output the PostScript trailer...
+ */
+
+void
+OutputTrailer(void)
+{
+ if (PageDirty)
+ PG_advance_page(0, NULL);
+
+ puts("%%BeginTrailer");
+ printf("%%%%Pages: %d\n", PageCount);
+ puts("%%EOF");
+}
+
+
+/*
+ * 'Outputf()' - Write a formatted string to the output file, creating the
+ * page header as needed...
+ */
+
+int /* O - Number of bytes written */
+Outputf(const char *format, /* I - Printf-style string */
+ ...) /* I - Additional args as needed */
+{
+ va_list ap; /* Argument pointer */
+ int bytes; /* Number of bytes written */
+
+
+ /*
+ * Write the page header as needed...
+ */
+
+ if (!PageDirty)
+ {
+ PageDirty = 1;
+ PageCount ++;
+
+ printf("%%%%Page: %d %d\n", PageCount, PageCount);
+ printf("/PenScaling %.3f def\n", PenScaling);
+ puts("gsave");
+
+ if (Duplex && (PageCount & 1) == 0)
+ switch ((PageRotation / 90) & 3)
+ {
+ case 0 :
+ printf("%.1f %.1f translate\n", PageWidth - PageRight, PageBottom);
+ break;
+ case 1 :
+ printf("%.1f %.1f translate\n", PageLength - PageTop,
+ PageWidth - PageRight);
+ break;
+ case 2 :
+ printf("%.1f %.1f translate\n", PageLeft, PageLength - PageTop);
+ break;
+ case 3 :
+ printf("%.1f %.1f translate\n", PageBottom, PageLeft);
+ break;
+ }
+ else
+ switch ((PageRotation / 90) & 3)
+ {
+ case 0 :
+ printf("%.1f %.1f translate\n", PageLeft, PageBottom);
+ break;
+ case 1 :
+ printf("%.1f %.1f translate\n", PageBottom, PageWidth - PageRight);
+ break;
+ case 2 :
+ printf("%.1f %.1f translate\n", PageWidth - PageRight,
+ PageLength - PageTop);
+ break;
+ case 3 :
+ printf("%.1f %.1f translate\n", PageLength - PageTop, PageLeft);
+ break;
+ }
+ }
+
+ /*
+ * Write the string to the output file...
+ */
+
+ va_start(ap, format);
+ bytes = vprintf(format, ap);
+ va_end(ap);
+
+ return (bytes);
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/filter/hpgl-vector.c b/filter/hpgl-vector.c
new file mode 100644
index 000000000..d900307b2
--- /dev/null
+++ b/filter/hpgl-vector.c
@@ -0,0 +1,704 @@
+/*
+ * "$Id$"
+ *
+ * HP-GL/2 vector routines for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1993-1999 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * Contents:
+ *
+ * AA_arc_absolute() - Draw an arc.
+ * AR_arc_relative() - Draw an arc relative to the current pen
+ * AT_arc_absolute3() - Draw an arc using 3 points.
+ * CI_circle() - Draw a circle.
+ * PA_plot_absolute() - Plot a line using absolute coordinates.
+ * PD_pen_down() - Start drawing.
+ * PE_polygon_encoded() - Draw an encoded polyline.
+ * PR_plot_relative() - Plot a line using relative coordinates.
+ * PU_pen_up() - Stop drawing.
+ * RT_arc_relative3() - Draw an arc through 3 points relative to the
+ * decode_number() - Decode an encoded number.
+ * plot_points() - Plot the specified points.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "hpgltops.h"
+
+
+/*
+ * Local functions...
+ */
+
+static double decode_number(unsigned char **, int, double);
+static void plot_points(int, param_t *);
+
+
+/*
+ * 'AA_arc_absolute()' - Draw an arc.
+ */
+
+void
+AA_arc_absolute(int num_params, /* I - Number of parameters */
+ param_t *params) /* I - Parameters */
+{
+ float x, y, /* Transformed coordinates */
+ dx, dy; /* Distance from current pen */
+ float start, end, /* Start and end angles */
+ theta, /* Current angle */
+ dt, /* Step between points */
+ radius; /* Radius of arc */
+
+
+ if (num_params < 3)
+ return;
+
+ x = Transform[0][0] * params[0].value.number +
+ Transform[0][1] * params[1].value.number +
+ Transform[0][2];
+ y = Transform[1][0] * params[0].value.number +
+ Transform[1][1] * params[1].value.number +
+ Transform[1][2];
+
+ dx = PenPosition[0] - x;
+ dy = PenPosition[1] - y;
+
+ start = (float)(180.0 * atan2(dy, dx) / M_PI);
+ if (start < 0.0)
+ start += 360.0f;
+
+ end = start + params[2].value.number;
+ radius = (float)hypot(dx, dy);
+
+ if (PenDown)
+ {
+ if (num_params > 3 && params[3].value.number > 0.0)
+ dt = (float)fabs(params[3].value.number);
+ else
+ dt = 5.0;
+
+ if (!PolygonMode)
+ Outputf("MP\n");
+
+ Outputf("%.3f %.3f MO\n", PenPosition[0], PenPosition[1]);
+
+ if (start < end)
+ for (theta = start + dt; theta < end; theta += dt)
+ {
+ PenPosition[0] = (float)(x + radius * cos(M_PI * theta / 180.0));
+ PenPosition[1] = (float)(y + radius * sin(M_PI * theta / 180.0));
+
+ Outputf("%.3f %.3f LI\n", PenPosition[0], PenPosition[1]);
+ }
+ else
+ for (theta = start - dt; theta > end; theta -= dt)
+ {
+ PenPosition[0] = (float)(x + radius * cos(M_PI * theta / 180.0));
+ PenPosition[1] = (float)(y + radius * sin(M_PI * theta / 180.0));
+
+ Outputf("%.3f %.3f LI\n", PenPosition[0], PenPosition[1]);
+ }
+ }
+
+ PenPosition[0] = (float)(x + radius * cos(M_PI * end / 180.0));
+ PenPosition[1] = (float)(y + radius * sin(M_PI * end / 180.0));
+
+ if (PenDown)
+ {
+ Outputf("%.3f %.3f LI\n", PenPosition[0], PenPosition[1]);
+
+ if (!PolygonMode)
+ Outputf("ST\n");
+ }
+}
+
+
+/*
+ * 'AR_arc_relative()' - Draw an arc relative to the current pen
+ * position.
+ */
+
+void
+AR_arc_relative(int num_params, /* I - Number of parameters */
+ param_t *params) /* I - Parameters */
+{
+ float x, y, /* Transformed coordinates */
+ dx, dy; /* Distance from current pen */
+ float start, end, /* Start and end angles */
+ theta, /* Current angle */
+ dt, /* Step between points */
+ radius; /* Radius of arc */
+
+
+ if (num_params < 3)
+ return;
+
+ x = Transform[0][0] * params[0].value.number +
+ Transform[0][1] * params[1].value.number +
+ PenPosition[0];
+ y = Transform[1][0] * params[0].value.number +
+ Transform[1][1] * params[1].value.number +
+ PenPosition[1];
+
+ dx = PenPosition[0] - x;
+ dy = PenPosition[1] - y;
+
+ start = (float)(180.0 * atan2(dy, dx) / M_PI);
+ if (start < 0.0)
+ start += 360.0f;
+
+ end = start + params[2].value.number;
+ radius = (float)hypot(dx, dy);
+
+ if (PenDown)
+ {
+ if (num_params > 3 && params[3].value.number > 0.0)
+ dt = (float)fabs(params[3].value.number);
+ else
+ dt = 5.0;
+
+ if (!PolygonMode)
+ Outputf("MP\n");
+
+ Outputf("%.3f %.3f MO\n", PenPosition[0], PenPosition[1]);
+
+ if (start < end)
+ for (theta = start + dt; theta < end; theta += dt)
+ {
+ PenPosition[0] = (float)(x + radius * cos(M_PI * theta / 180.0));
+ PenPosition[1] = (float)(y + radius * sin(M_PI * theta / 180.0));
+
+ Outputf("%.3f %.3f LI\n", PenPosition[0], PenPosition[1]);
+ }
+ else
+ for (theta = start - dt; theta > end; theta -= dt)
+ {
+ PenPosition[0] = (float)(x + radius * cos(M_PI * theta / 180.0));
+ PenPosition[1] = (float)(y + radius * sin(M_PI * theta / 180.0));
+
+ Outputf("%.3f %.3f LI\n", PenPosition[0], PenPosition[1]);
+ }
+ }
+
+ PenPosition[0] = (float)(x + radius * cos(M_PI * end / 180.0));
+ PenPosition[1] = (float)(y + radius * sin(M_PI * end / 180.0));
+
+ if (PenDown)
+ {
+ Outputf("%.3f %.3f LI\n", PenPosition[0], PenPosition[1]);
+
+ if (!PolygonMode)
+ Outputf("ST\n");
+ }
+}
+
+
+/*
+ * 'AT_arc_absolute3()' - Draw an arc using 3 points.
+ *
+ * Note:
+ *
+ * Currently this only draws two line segments through the
+ * specified points.
+ */
+
+void
+AT_arc_absolute3(int num_params, /* I - Number of parameters */
+ param_t *params) /* I - Parameters */
+{
+ if (num_params < 4)
+ return;
+
+ if (PenDown)
+ {
+ if (!PolygonMode)
+ Outputf("MP\n");
+
+ Outputf("%.3f %.3f MO\n", PenPosition[0], PenPosition[1]);
+
+ PenPosition[0] = Transform[0][0] * params[0].value.number +
+ Transform[0][1] * params[1].value.number +
+ Transform[0][2];
+ PenPosition[1] = Transform[1][0] * params[0].value.number +
+ Transform[1][1] * params[1].value.number +
+ Transform[1][2];
+
+ Outputf("%.3f %.3f LI\n", PenPosition[0], PenPosition[1]);
+ }
+
+ PenPosition[0] = Transform[0][0] * params[2].value.number +
+ Transform[0][1] * params[3].value.number +
+ Transform[0][2];
+ PenPosition[1] = Transform[1][0] * params[2].value.number +
+ Transform[1][1] * params[3].value.number +
+ Transform[1][2];
+
+ if (PenDown)
+ {
+ Outputf("%.3f %.3f LI\n", PenPosition[0], PenPosition[1]);
+
+ if (!PolygonMode)
+ Outputf("ST\n");
+ }
+}
+
+
+/*
+ * 'CI_circle()' - Draw a circle.
+ */
+
+void
+CI_circle(int num_params, /* I - Number of parameters */
+ param_t *params) /* I - Parameters */
+{
+ float x, y; /* Transformed coordinates */
+ float theta, /* Current angle */
+ dt, /* Step between points */
+ radius; /* Radius of circle */
+
+
+ if (num_params < 1)
+ return;
+
+ if (!PenDown)
+ return;
+
+ radius = params[0].value.number;
+
+ if (num_params > 1)
+ dt = (float)fabs(params[1].value.number);
+ else
+ dt = 5.0;
+
+ if (!PolygonMode)
+ Outputf("MP\n");
+
+ for (theta = 0.0; theta < 360.0; theta += dt)
+ {
+ x = (float)(PenPosition[0] +
+ radius * cos(M_PI * theta / 180.0) * Transform[0][0] +
+ radius * sin(M_PI * theta / 180.0) * Transform[0][1]);
+ y = (float)(PenPosition[1] +
+ radius * cos(M_PI * theta / 180.0) * Transform[1][0] +
+ radius * sin(M_PI * theta / 180.0) * Transform[1][1]);
+
+ Outputf("%.3f %.3f %s\n", x, y, theta == 0.0 ? "MO" : "LI");
+ }
+
+ Outputf("CP\n");
+ if (!PolygonMode)
+ Outputf("ST\n");
+}
+
+
+/*
+ * 'PA_plot_absolute()' - Plot a line using absolute coordinates.
+ */
+
+void
+PA_plot_absolute(int num_params, /* I - Number of parameters */
+ param_t *params) /* I - Parameters */
+{
+ PenMotion = 0;
+
+ if (num_params > 1)
+ plot_points(num_params, params);
+}
+
+
+/*
+ * 'PD_pen_down()' - Start drawing.
+ */
+
+void
+PD_pen_down(int num_params, /* I - Number of parameters */
+ param_t *params) /* I - Parameters */
+{
+ PenDown = 1;
+
+ if (num_params > 1)
+ plot_points(num_params, params);
+}
+
+
+/*
+ * 'PE_polygon_encoded()' - Draw an encoded polyline.
+ */
+
+void
+PE_polyline_encoded(int num_params, /* I - Number of parameters */
+ param_t *params) /* I - Parameters */
+{
+ unsigned char *s; /* Pointer into string */
+ int temp, /* Temporary value */
+ base_bits, /* Data bits per byte */
+ draw, /* Draw or move */
+ abscoords; /* Use absolute coordinates */
+ double tx, ty, /* Transformed coordinates */
+ x, y, /* Raw coordinates */
+ frac_bits; /* Multiplier for encoded number */
+
+
+ base_bits = 6;
+ frac_bits = 1.0;
+ draw = 1;
+ abscoords = 0;
+
+ if (num_params == 0)
+ return;
+
+ if (!PolygonMode)
+ {
+ Outputf("MP\n");
+ Outputf("%.3f %.3f MO\n", PenPosition[0], PenPosition[1]);
+ }
+
+ for (s = (unsigned char *)params[0].value.string; *s != '\0';)
+ switch (*s)
+ {
+ case '7' :
+ s ++;
+ base_bits = 5;
+ break;
+ case ':' : /* Select pen */
+ s ++;
+ temp = (int)decode_number(&s, base_bits, 1.0);
+ Outputf("P%d W%d\n", temp, temp);
+ break;
+ case '<' : /* Next coords are a move-to */
+ draw = 0;
+ s ++;
+ break;
+ case '>' : /* Set fractional bits */
+ s ++;
+ temp = (int)decode_number(&s, base_bits, 1.0);
+ frac_bits = 1.0 / (1 << temp);
+ break;
+ case '=' : /* Next coords are absolute */
+ s ++;
+ abscoords = 1;
+ break;
+ default :
+ if (*s >= 63)
+ {
+ /*
+ * Coordinate...
+ */
+
+ x = decode_number(&s, base_bits, frac_bits);
+ y = decode_number(&s, base_bits, frac_bits);
+
+ if (abscoords)
+ {
+ tx = Transform[0][0] * x + Transform[0][1] * y +
+ Transform[0][2];
+ ty = Transform[1][0] * x + Transform[1][1] * y +
+ Transform[1][2];
+ }
+ else if (x == 0.0 && y == 0.0)
+ {
+ draw = 1;
+ continue;
+ }
+ else
+ {
+ tx = Transform[0][0] * x + Transform[0][1] * y +
+ PenPosition[0];
+ ty = Transform[1][0] * x + Transform[1][1] * y +
+ PenPosition[1];
+ }
+
+ if (draw)
+ Outputf("%.3f %.3f LI\n", tx, ty);
+ else
+ Outputf("%.3f %.3f MO\n", tx, ty);
+
+ PenPosition[0] = (float)tx;
+ PenPosition[1] = (float)ty;
+
+ draw = 1;
+ abscoords = 0;
+ }
+ else
+ {
+ /*
+ * Junk - ignore...
+ */
+
+ if (*s != '\n' && *s != '\r')
+ fprintf(stderr, "WARNING: ignoring illegal PE char \'%c\'...\n", *s);
+ s ++;
+ }
+ break;
+ }
+
+ if (!PolygonMode)
+ Outputf("ST\n");
+}
+
+
+/*
+ * 'PR_plot_relative()' - Plot a line using relative coordinates.
+ */
+
+void
+PR_plot_relative(int num_params, /* I - Number of parameters */
+ param_t *params) /* I - Parameters */
+{
+ PenMotion = 1;
+
+ if (num_params > 1)
+ plot_points(num_params, params);
+}
+
+
+/*
+ * 'PU_pen_up()' - Stop drawing.
+ */
+
+void
+PU_pen_up(int num_params, /* I - Number of parameters */
+ param_t *params) /* I - Parameters */
+{
+ PenDown = 0;
+
+ if (num_params > 1)
+ plot_points(num_params, params);
+}
+
+
+/*
+ * 'RT_arc_relative3()' - Draw an arc through 3 points relative to the
+ * current pen position.
+ *
+ * Note:
+ *
+ * This currently only draws two line segments through the specified
+ * points.
+ */
+
+void
+RT_arc_relative3(int num_params, /* I - Number of parameters */
+ param_t *params) /* I - Parameters */
+{
+ if (num_params < 4)
+ return;
+
+ if (PenDown)
+ {
+ if (!PolygonMode)
+ Outputf("MP\n");
+
+ Outputf("%.3f %.3f MO\n", PenPosition[0], PenPosition[1]);
+
+ PenPosition[0] = Transform[0][0] * params[0].value.number +
+ Transform[0][1] * params[1].value.number +
+ PenPosition[0];
+ PenPosition[1] = Transform[1][0] * params[0].value.number +
+ Transform[1][1] * params[1].value.number +
+ PenPosition[1];
+
+ Outputf("%.3f %.3f LI\n", PenPosition[0], PenPosition[1]);
+ }
+
+ PenPosition[0] = Transform[0][0] * params[2].value.number +
+ Transform[0][1] * params[3].value.number +
+ PenPosition[0];
+ PenPosition[1] = Transform[1][0] * params[2].value.number +
+ Transform[1][1] * params[3].value.number +
+ PenPosition[1];
+
+ if (PenDown)
+ {
+ Outputf("%.3f %.3f LI\n", PenPosition[0], PenPosition[1]);
+
+ if (!PolygonMode)
+ Outputf("ST\n");
+ }
+}
+
+
+/*
+ * 'decode_number()' - Decode an encoded number.
+ */
+
+static double /* O - Value */
+decode_number(unsigned char **s, /* IO - String to decode */
+ int base_bits, /* I - Number of data bits per byte */
+ double frac_bits) /* I - Multiplier for fractional data */
+{
+ double temp, /* Current value */
+ shift; /* Multiplier */
+ int sign; /* Sign of result */
+
+
+ sign = 0;
+
+ if (base_bits == 5)
+ {
+ for (temp = 0.0, shift = frac_bits * 0.5; **s != '\0'; (*s) ++)
+ if (**s >= 95 && **s < 127)
+ {
+ if (sign == 0)
+ {
+ if ((**s - 95) & 1)
+ sign = -1;
+ else
+ sign = 1;
+
+ temp += ((**s - 95) & ~1) * shift;
+ }
+ else
+ temp += (**s - 95) * shift;
+ break;
+ }
+ else if (**s < 63)
+ {
+ if (**s != '\r' && **s != '\n')
+ fprintf(stderr, "hpgl2ps: Bad PE character \'%c\'!\n", **s);
+
+ continue;
+ }
+ else
+ {
+ if (sign == 0)
+ {
+ if ((**s - 63) & 1)
+ sign = -1;
+ else
+ sign = 1;
+
+ temp += ((**s - 63) & ~1) * shift;
+ }
+ else
+ temp += (**s - 63) * shift;
+
+ shift *= 32.0;
+ }
+ }
+ else
+ {
+ for (temp = 0.0, shift = frac_bits * 0.5; **s != '\0'; (*s) ++)
+ if (**s >= 191 && **s < 255)
+ {
+ if (sign == 0)
+ {
+ if ((**s - 191) & 1)
+ sign = -1;
+ else
+ sign = 1;
+
+ temp += ((**s - 191) & ~1) * shift;
+ }
+ else
+ temp += (**s - 191) * shift;
+ break;
+ }
+ else if (**s < 63)
+ {
+ if (**s != '\r' && **s != '\n')
+ fprintf(stderr, "hpgl2ps: Bad PE character \'%c\'!\n", **s);
+
+ continue;
+ }
+ else
+ {
+ if (sign == 0)
+ {
+ if ((**s - 63) & 1)
+ sign = -1;
+ else
+ sign = 1;
+
+ temp += ((**s - 63) & ~1) * shift;
+ }
+ else
+ temp += (**s - 63) * shift;
+
+ shift *= 64.0;
+ }
+ }
+
+ (*s) ++;
+
+ return (temp * sign);
+}
+
+
+/*
+ * 'plot_points()' - Plot the specified points.
+ */
+
+static void
+plot_points(int num_params, /* I - Number of parameters */
+ param_t *params) /* I - Parameters */
+{
+ int i; /* Looping var */
+ float x, y; /* Transformed coordinates */
+
+
+ if (PenDown)
+ {
+ if (!PolygonMode)
+ Outputf("MP\n");
+
+ Outputf("%.3f %.3f MO\n", PenPosition[0], PenPosition[1]);
+ }
+
+ for (i = 0; i < num_params; i += 2)
+ {
+ if (PenMotion == 0)
+ {
+ x = Transform[0][0] * params[i + 0].value.number +
+ Transform[0][1] * params[i + 1].value.number +
+ Transform[0][2];
+ y = Transform[1][0] * params[i + 0].value.number +
+ Transform[1][1] * params[i + 1].value.number +
+ Transform[1][2];
+ }
+ else
+ {
+ x = Transform[0][0] * params[i + 0].value.number +
+ Transform[0][1] * params[i + 1].value.number +
+ PenPosition[0];
+ y = Transform[1][0] * params[i + 0].value.number +
+ Transform[1][1] * params[i + 1].value.number +
+ PenPosition[1];
+ }
+
+ if (PenDown)
+ Outputf("%.3f %.3f LI\n", x, y);
+
+ PenPosition[0] = x;
+ PenPosition[1] = y;
+ }
+
+ if (PenDown)
+ {
+ if (!PolygonMode)
+ Outputf("ST\n");
+ }
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/filter/hpgltops.h b/filter/hpgltops.h
new file mode 100644
index 000000000..4cd32e0f0
--- /dev/null
+++ b/filter/hpgltops.h
@@ -0,0 +1,196 @@
+/*
+ * "$Id$"
+ *
+ * HP-GL/2 to PostScript filter for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1993-1999 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "common.h"
+#include <math.h>
+
+#ifndef M_PI
+# define M_PI 3.14159265358979323846
+#endif /* M_PI */
+
+/*
+ * Parameter value structure...
+ */
+
+typedef struct
+{
+ int type;
+ union
+ {
+ float number;
+ char *string;
+ } value;
+} param_t;
+
+#define PARAM_ABSOLUTE 0
+#define PARAM_RELATIVE 1
+#define PARAM_STRING 2
+
+
+/*
+ * Globals...
+ */
+
+#ifdef _HPGL_MAIN_C_
+# define VAR
+# define VALUE(x) =x
+# define VALUE2(x,y) ={x,y}
+#else
+# define VAR extern
+# define VALUE(x)
+# define VALUE2(x,y)
+#endif /* _HPGL_MAIN_C_ */
+
+VAR float P1[2], /* Lower-lefthand physical limit */
+ P2[2], /* Upper-righthand physical limit */
+ IW1[2], /* Window lower-lefthand limit */
+ IW2[2]; /* Window upper-righthand limit */
+VAR int Rotation VALUE(0); /* Page rotation */
+VAR int ScalingType VALUE(-1); /* Type of scaling (-1 for none) */
+VAR float Scaling1[2], /* Lower-lefthand user limit */
+ Scaling2[2]; /* Upper-righthand user limit */
+VAR float Transform[2][3]; /* Transform matrix */
+VAR int PageRotation VALUE(0); /* Page/plot rotation */
+
+VAR char StringTerminator VALUE('\003'); /* Terminator for labels */
+VAR float PenPosition[2] VALUE2(0.0f, 0.0f),
+ /* Current pen position */
+ PenScaling VALUE(1.0f); /* Pen width scaling factor */
+VAR int PenMotion VALUE(0), /* 0 = absolute, 1 = relative */
+ PenNumber VALUE(1), /* Current pen number */
+ PenCount VALUE(8), /* Number of pens */
+ PenDown VALUE(0), /* 0 = pen up, 1 = pen down */
+ PolygonMode VALUE(0), /* Drawing polygons? */
+ PageCount VALUE(1), /* Number of pages in plot */
+ PageDirty VALUE(0), /* Current page written on? */
+ WidthUnits VALUE(0); /* 0 = mm, 1 = proportionate */
+VAR float PlotSize[2] VALUE2(2592.0f, 3456.0f);
+ /* Plot size */
+VAR int CharFillMode VALUE(0), /* Where to draw labels */
+ CharPen VALUE(0), /* Pen to use for labels */
+ CharFont VALUE(0); /* Font to use for labels */
+VAR float CharHeight[2] VALUE2(11.5f,11.5f);
+ /* Size of font for labels */
+VAR int FitPlot VALUE(0); /* 1 = fit to page */
+VAR float ColorRange[3][2] /* Range of color values */
+#ifdef _HPGL_MAIN_C_
+ = {
+ { 0.0, 255.0 },
+ { 0.0, 255.0 },
+ { 0.0, 255.0 }
+ }
+#endif /* _HPGL_MAIN_C_ */
+;
+
+/*
+ * Prototypes...
+ */
+
+/* hpgl-input.c */
+extern int ParseCommand(FILE *fp, char *name, param_t **params);
+extern void FreeParameters(int num_params, param_t *params);
+
+/* hpgl-config.c */
+extern void update_transform(void);
+extern void BP_begin_plot(int num_params, param_t *params);
+extern void DF_default_values(int num_params, param_t *params);
+extern void IN_initialize(int num_params, param_t *params);
+extern void IP_input_absolute(int num_params, param_t *params);
+extern void IR_input_relative(int num_params, param_t *params);
+extern void IW_input_window(int num_params, param_t *params);
+extern void PG_advance_page(int num_params, param_t *params);
+extern void PS_plot_size(int num_params, param_t *params);
+extern void RO_rotate(int num_params, param_t *params);
+extern void RP_replot(int num_params, param_t *params);
+extern void SC_scale(int num_params, param_t *params);
+
+/* hpgl-vector.c */
+extern void AA_arc_absolute(int num_params, param_t *params);
+extern void AR_arc_relative(int num_params, param_t *params);
+extern void AT_arc_absolute3(int num_params, param_t *params);
+extern void CI_circle(int num_params, param_t *params);
+extern void PA_plot_absolute(int num_params, param_t *params);
+extern void PD_pen_down(int num_params, param_t *params);
+extern void PE_polyline_encoded(int num_params, param_t *params);
+extern void PR_plot_relative(int num_params, param_t *params);
+extern void PU_pen_up(int num_params, param_t *params);
+extern void RT_arc_relative3(int num_params, param_t *params);
+
+/* hpgl-polygon.c */
+extern void EA_edge_rect_absolute(int num_params, param_t *params);
+extern void EP_edge_polygon(int num_params, param_t *params);
+extern void ER_edge_rect_relative(int num_params, param_t *params);
+extern void EW_edge_wedge(int num_params, param_t *params);
+extern void FP_fill_polygon(int num_params, param_t *params);
+extern void PM_polygon_mode(int num_params, param_t *params);
+extern void RA_fill_rect_absolute(int num_params, param_t *params);
+extern void RR_fill_rect_relative(int num_params, param_t *params);
+extern void WG_fill_wedge(int num_params, param_t *params);
+
+/* hpgl-char.c */
+extern void AD_define_alternate(int num_params, param_t *params);
+extern void CF_character_fill(int num_params, param_t *params);
+extern void CP_character_plot(int num_params, param_t *params);
+extern void DI_absolute_direction(int num_params, param_t *params);
+extern void DR_relative_direction(int num_params, param_t *params);
+extern void DT_define_label_term(int num_params, param_t *params);
+extern void DV_define_variable_path(int num_params, param_t *params);
+extern void ES_extra_space(int num_params, param_t *params);
+extern void LB_label(int num_params, param_t *params);
+extern void LO_label_origin(int num_params, param_t *params);
+extern void SA_select_alternate(int num_params, param_t *params);
+extern void SD_define_standard(int num_params, param_t *params);
+extern void SI_absolute_size(int num_params, param_t *params);
+extern void SL_character_slant(int num_params, param_t *params);
+extern void SR_relative_size(int num_params, param_t *params);
+extern void SS_select_standard(int num_params, param_t *params);
+extern void TD_transparent_data(int num_params, param_t *params);
+
+/* hpgl-attr.c */
+extern void AC_anchor_corner(int num_params, param_t *params);
+extern void CR_color_range(int num_params, param_t *params);
+extern void FT_fill_type(int num_params, param_t *params);
+extern void LA_line_attributes(int num_params, param_t *params);
+extern void LT_line_type(int num_params, param_t *params);
+extern void NP_number_pens(int num_params, param_t *params);
+extern void PC_pen_color(int num_params, param_t *params);
+extern void PW_pen_width(int num_params, param_t *params);
+extern void RF_raster_fill(int num_params, param_t *params);
+extern void SM_symbol_mode(int num_params, param_t *params);
+extern void SP_select_pen(int num_params, param_t *params);
+extern void UL_user_line_type(int num_params, param_t *params);
+extern void WU_width_units(int num_params, param_t *params);
+
+/* hpgl-prolog.c */
+extern void OutputProlog(char *title, char *user, int shading, float penwidth);
+extern void OutputTrailer(void);
+extern int Outputf(const char *format, ...);
+
+/*
+ * End of "$Id$".
+ */
diff --git a/filter/image-colorspace.c b/filter/image-colorspace.c
new file mode 100644
index 000000000..a9518d090
--- /dev/null
+++ b/filter/image-colorspace.c
@@ -0,0 +1,910 @@
+/*
+ * "$Id$"
+ *
+ * Colorspace conversions for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1993-1999 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * Contents:
+ *
+ * ImageWhiteToWhite() - Convert luminance colors to device-dependent
+ * ImageWhiteToRGB() - Convert luminance data to RGB.
+ * ImageWhiteToBlack() - Convert luminance colors to black.
+ * ImageWhiteToCMY() - Convert luminance colors to CMY.
+ * ImageWhiteToCMYK() - Convert luminance colors to CMYK.
+ * ImageRGBToBlack() - Convert RGB data to black.
+ * ImageRGBToCMY() - Convert RGB colors to CMY.
+ * ImageRGBToCMYK() - Convert RGB colors to CMYK.
+ * ImageRGBToWhite() - Convert RGB colors to luminance.
+ * ImageRGBToRGB() - Convert RGB colors to device-dependent RGB.
+ * ImageLut() - Adjust all pixel values with the given LUT.
+ * ImageRGBAdjust() - Adjust the hue and saturation of the given RGB
+ * colors.
+ * huerotate() - Rotate the hue, maintaining luminance.
+ * ident() - Make an identity matrix.
+ * mult() - Multiply two matrices.
+ * saturate() - Make a saturation matrix.
+ * xform() - Transform a 3D point using a matrix...
+ * xrotate() - Rotate about the x (red) axis...
+ * yrotate() - Rotate about the y (green) axis...
+ * zrotate() - Rotate about the z (blue) axis...
+ * zshear() - Shear z using x and y...
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "image.h"
+#include <math.h>
+
+
+/*
+ * Globals...
+ */
+
+extern int ImageHaveProfile;
+extern int ImageDensity[256];
+extern int ImageMatrix[3][3][256];
+
+/*
+ * Local functions...
+ */
+
+static void huerotate(float [3][3], float);
+static void ident(float [3][3]);
+static void mult(float [3][3], float [3][3], float [3][3]);
+static void saturate(float [3][3], float);
+static void xform(float [3][3], float, float, float, float *, float *, float *);
+static void xrotate(float [3][3], float, float);
+static void yrotate(float [3][3], float, float);
+static void zrotate(float [3][3], float, float);
+static void zshear(float [3][3], float, float);
+
+
+/*
+ * 'ImageWhiteToWhite()' - Convert luminance colors to device-dependent
+ * luminance.
+ */
+
+void
+ImageWhiteToWhite(ib_t *in, /* I - Input pixels */
+ ib_t *out, /* I - Output pixels */
+ int count) /* I - Number of pixels */
+{
+ if (ImageHaveProfile)
+ while (count > 0)
+ {
+ *out++ = 255 - ImageDensity[255 - *in++];
+ count --;
+ }
+ else if (in != out)
+ memcpy(out, in, count);
+}
+
+
+/*
+ * 'ImageWhiteToRGB()' - Convert luminance data to RGB.
+ */
+
+void
+ImageWhiteToRGB(ib_t *in, /* I - Input pixels */
+ ib_t *out, /* I - Output pixels */
+ int count) /* I - Number of pixels */
+{
+ if (ImageHaveProfile)
+ while (count > 0)
+ {
+ out[0] = 255 - ImageDensity[255 - *in++];
+ out[1] = out[0];
+ out[2] = out[0];
+ out += 3;
+ count --;
+ }
+ else
+ while (count > 0)
+ {
+ *out++ = *in;
+ *out++ = *in;
+ *out++ = *in++;
+ count --;
+ }
+}
+
+
+/*
+ * 'ImageWhiteToBlack()' - Convert luminance colors to black.
+ */
+
+void
+ImageWhiteToBlack(ib_t *in, /* I - Input pixels */
+ ib_t *out, /* I - Output pixels */
+ int count) /* I - Number of pixels */
+{
+ if (ImageHaveProfile)
+ while (count > 0)
+ {
+ *out++ = ImageDensity[255 - *in++];
+ count --;
+ }
+ else
+ while (count > 0)
+ {
+ *out++ = 255 - *in++;
+ count --;
+ }
+}
+
+
+/*
+ * 'ImageWhiteToCMY()' - Convert luminance colors to CMY.
+ */
+
+void
+ImageWhiteToCMY(ib_t *in, /* I - Input pixels */
+ ib_t *out, /* I - Output pixels */
+ int count) /* I - Number of pixels */
+{
+ if (ImageHaveProfile)
+ while (count > 0)
+ {
+ out[0] = ImageDensity[255 - *in++];
+ out[1] = out[0];
+ out[2] = out[0];
+ out += 3;
+ count --;
+ }
+ else
+ while (count > 0)
+ {
+ *out++ = 255 - *in;
+ *out++ = 255 - *in;
+ *out++ = 255 - *in++;
+ count --;
+ }
+}
+
+
+/*
+ * 'ImageWhiteToCMYK()' - Convert luminance colors to CMYK.
+ */
+
+void
+ImageWhiteToCMYK(ib_t *in, /* I - Input pixels */
+ ib_t *out, /* I - Output pixels */
+ int count) /* I - Number of pixels */
+{
+ if (ImageHaveProfile)
+ while (count > 0)
+ {
+ *out++ = 0;
+ *out++ = 0;
+ *out++ = 0;
+ *out++ = ImageDensity[255 - *in++];
+ count --;
+ }
+ else
+ while (count > 0)
+ {
+ *out++ = 0;
+ *out++ = 0;
+ *out++ = 0;
+ *out++ = 255 - *in++;
+ count --;
+ }
+}
+
+
+/*
+ * 'ImageRGBToBlack()' - Convert RGB data to black.
+ */
+
+void
+ImageRGBToBlack(ib_t *in, /* I - Input pixels */
+ ib_t *out, /* I - Output pixels */
+ int count) /* I - Number of pixels */
+{
+ if (ImageHaveProfile)
+ while (count > 0)
+ {
+ *out++ = ImageDensity[255 - (31 * in[0] + 61 * in[1] + 8 * in[2]) / 100];
+ in += 3;
+ count --;
+ }
+ else
+ while (count > 0)
+ {
+ *out++ = 255 - (31 * in[0] + 61 * in[1] + 8 * in[2]) / 100;
+ in += 3;
+ count --;
+ }
+}
+
+
+/*
+ * 'ImageRGBToCMY()' - Convert RGB colors to CMY.
+ */
+
+void
+ImageRGBToCMY(ib_t *in, /* I - Input pixels */
+ ib_t *out, /* I - Output pixels */
+ int count) /* I - Number of pixels */
+{
+ int c, m, y, k; /* CMYK values */
+ int cc, cm, cy; /* Calibrated CMY values */
+
+
+ if (ImageHaveProfile)
+ while (count > 0)
+ {
+ c = 255 - *in++;
+ m = 255 - *in++;
+ y = 255 - *in++;
+ k = min(c, min(m, y));
+ c -= k;
+ m -= k;
+ y -= k;
+
+ cc = ImageMatrix[0][0][c] +
+ ImageMatrix[0][1][m] +
+ ImageMatrix[0][2][y] + k;
+ cm = ImageMatrix[1][0][c] +
+ ImageMatrix[1][1][m] +
+ ImageMatrix[1][2][y] + k;
+ cy = ImageMatrix[2][0][c] +
+ ImageMatrix[2][1][m] +
+ ImageMatrix[2][2][y] + k;
+
+ if (cc < 0)
+ *out++ = 0;
+ else if (cc > 255)
+ *out++ = ImageDensity[255];
+ else
+ *out++ = ImageDensity[cc];
+
+ if (cm < 0)
+ *out++ = 0;
+ else if (cm > 255)
+ *out++ = ImageDensity[255];
+ else
+ *out++ = ImageDensity[cm];
+
+ if (cy < 0)
+ *out++ = 0;
+ else if (cy > 255)
+ *out++ = ImageDensity[255];
+ else
+ *out++ = ImageDensity[cy];
+
+ count --;
+ }
+ else
+ while (count > 0)
+ {
+ c = 255 - in[0];
+ m = 255 - in[1];
+ y = 255 - in[2];
+ k = min(c, min(m, y));
+
+ *out++ = (255 - in[1] / 4) * (c - k) / 255 + k;
+ *out++ = (255 - in[2] / 4) * (m - k) / 255 + k;
+ *out++ = (255 - in[0] / 4) * (y - k) / 255 + k;
+ in += 3;
+ count --;
+ }
+}
+
+
+/*
+ * 'ImageRGBToCMYK()' - Convert RGB colors to CMYK.
+ */
+
+void
+ImageRGBToCMYK(ib_t *in, /* I - Input pixels */
+ ib_t *out, /* I - Output pixels */
+ int count) /* I - Number of pixels */
+{
+ int c, m, y, k, /* CMYK values */
+ diff, /* Color differences */
+ divk; /* Color divisor */
+ int cc, cm, cy; /* Calibrated CMY values */
+
+
+ if (ImageHaveProfile)
+ while (count > 0)
+ {
+ c = 255 - *in++;
+ m = 255 - *in++;
+ y = 255 - *in++;
+ k = min(c, min(m, y));
+
+ diff = 255 - (max(c, max(m, y)) - k);
+ k = k * diff / 255;
+
+ if (k == 255)
+ c = m = y = 0;
+ else if (k > 0)
+ {
+ divk = 255 - k;
+ c = 255 * (c - k) / divk;
+ m = 255 * (m - k) / divk;
+ y = 255 * (y - k) / divk;
+
+ if (c > 255)
+ c = 255;
+
+ if (m > 255)
+ m = 255;
+
+ if (y > 255)
+ y = 255;
+ }
+
+ cc = (ImageMatrix[0][0][c] +
+ ImageMatrix[0][1][m] +
+ ImageMatrix[0][2][y]);
+ cm = (ImageMatrix[1][0][c] +
+ ImageMatrix[1][1][m] +
+ ImageMatrix[1][2][y]);
+ cy = (ImageMatrix[2][0][c] +
+ ImageMatrix[2][1][m] +
+ ImageMatrix[2][2][y]);
+
+ if (cc < 0)
+ *out++ = 0;
+ else if (cc > 255)
+ *out++ = ImageDensity[255];
+ else
+ *out++ = ImageDensity[cc];
+
+ if (cm < 0)
+ *out++ = 0;
+ else if (cm > 255)
+ *out++ = ImageDensity[255];
+ else
+ *out++ = ImageDensity[cm];
+
+ if (cy < 0)
+ *out++ = 0;
+ else if (cy > 255)
+ *out++ = ImageDensity[255];
+ else
+ *out++ = cy;
+
+ *out++ = ImageDensity[k];
+
+ count --;
+ }
+ else
+ while (count > 0)
+ {
+ c = 255 - *in++;
+ m = 255 - *in++;
+ y = 255 - *in++;
+ k = min(c, min(m, y));
+
+ if (k == 255)
+ c = m = y = 0;
+ else if (k > 0)
+ {
+ divk = 255 - k;
+ c = 255 * (c - k) / divk;
+ m = 255 * (m - k) / divk;
+ y = 255 * (y - k) / divk;
+
+ if (c > 255)
+ c = 255;
+
+ if (m > 255)
+ m = 255;
+
+ if (y > 255)
+ y = 255;
+ }
+
+ *out++ = c;
+ *out++ = m;
+ *out++ = y;
+ *out++ = k;
+
+ count --;
+ }
+}
+
+
+/*
+ * 'ImageRGBToWhite()' - Convert RGB colors to luminance.
+ */
+
+void
+ImageRGBToWhite(ib_t *in, /* I - Input pixels */
+ ib_t *out, /* I - Output pixels */
+ int count) /* I - Number of pixels */
+{
+ if (ImageHaveProfile)
+ while (count > 0)
+ {
+ *out++ = 255 - ImageDensity[255 - (31 * in[0] + 61 * in[1] + 8 * in[2]) / 100];
+ in += 3;
+ count --;
+ }
+ else
+ while (count > 0)
+ {
+ *out++ = (31 * in[0] + 61 * in[1] + 8 * in[2]) / 100;
+ in += 3;
+ count --;
+ }
+}
+
+
+/*
+ * 'ImageRGBToRGB()' - Convert RGB colors to device-dependent RGB.
+ */
+
+void
+ImageRGBToRGB(ib_t *in, /* I - Input pixels */
+ ib_t *out, /* I - Output pixels */
+ int count) /* I - Number of pixels */
+{
+ int c, m, y, k; /* CMYK values */
+ int cr, cg, cb; /* Calibrated RGB values */
+
+
+ if (ImageHaveProfile)
+ while (count > 0)
+ {
+ c = 255 - *in++;
+ m = 255 - *in++;
+ y = 255 - *in++;
+ k = min(c, min(m, y));
+ c -= k;
+ m -= k;
+ y -= k;
+
+ cr = ImageMatrix[0][0][c] +
+ ImageMatrix[0][1][m] +
+ ImageMatrix[0][2][y] + k;
+ cg = ImageMatrix[1][0][c] +
+ ImageMatrix[1][1][m] +
+ ImageMatrix[1][2][y] + k;
+ cb = ImageMatrix[2][0][c] +
+ ImageMatrix[2][1][m] +
+ ImageMatrix[2][2][y] + k;
+
+ if (cr < 0)
+ *out++ = 255;
+ else if (cr > 255)
+ *out++ = 255 - ImageDensity[255];
+ else
+ *out++ = 255 - ImageDensity[cr];
+
+ if (cg < 0)
+ *out++ = 255;
+ else if (cg > 255)
+ *out++ = 255 - ImageDensity[255];
+ else
+ *out++ = 255 - ImageDensity[cg];
+
+ if (cb < 0)
+ *out++ = 255;
+ else if (cb > 255)
+ *out++ = 255 - ImageDensity[255];
+ else
+ *out++ = 255 - ImageDensity[cb];
+
+ count --;
+ }
+ else if (in != out)
+ memcpy(out, in, count * 3);
+}
+
+
+/*
+ * 'ImageLut()' - Adjust all pixel values with the given LUT.
+ */
+
+void
+ImageLut(ib_t *pixels, /* I - Input/output pixels */
+ int count, /* I - Number of pixels/bytes to adjust */
+ ib_t *lut) /* I - Lookup table */
+{
+ while (count > 0)
+ {
+ *pixels = lut[*pixels];
+ pixels ++;
+ count --;
+ }
+}
+
+
+/*
+ * 'ImageRGBAdjust()' - Adjust the hue and saturation of the given RGB colors.
+ */
+
+void
+ImageRGBAdjust(ib_t *pixels, /* I - Input/output pixels */
+ int count, /* I - Number of pixels to adjust */
+ int saturation, /* I - Color saturation (%) */
+ int hue) /* I - Color hue (degrees) */
+{
+ int i, j, k; /* Looping vars */
+ float mat[3][3]; /* Color adjustment matrix */
+ static int last_sat = 100, /* Last saturation used */
+ last_hue = 0; /* Last hue used */
+ static int lut[3][3][256]; /* Lookup table for matrix */
+
+
+ if (saturation != last_sat ||
+ hue != last_hue)
+ {
+ /*
+ * Build the color adjustment matrix...
+ */
+
+ ident(mat);
+ saturate(mat, saturation * 0.01);
+ huerotate(mat, (float)hue);
+
+ /*
+ * Convert the matrix into a 3x3 array of lookup tables...
+ */
+
+ for (i = 0; i < 3; i ++)
+ for (j = 0; j < 3; j ++)
+ for (k = 0; k < 256; k ++)
+ lut[i][j][k] = mat[i][j] * k + 0.5;
+
+ /*
+ * Save the saturation and hue to compare later...
+ */
+
+ last_sat = saturation;
+ last_hue = hue;
+ }
+
+ /*
+ * Adjust each pixel in the given buffer.
+ */
+
+ while (count > 0)
+ {
+ i = lut[0][0][pixels[0]] +
+ lut[1][0][pixels[1]] +
+ lut[2][0][pixels[2]];
+ if (i < 0)
+ pixels[0] = 0;
+ else if (i > 255)
+ pixels[0] = 255;
+ else
+ pixels[0] = i;
+
+ i = lut[0][1][pixels[0]] +
+ lut[1][1][pixels[1]] +
+ lut[2][1][pixels[2]];
+ if (i < 0)
+ pixels[1] = 0;
+ else if (i > 255)
+ pixels[1] = 255;
+ else
+ pixels[1] = i;
+
+ i = lut[0][2][pixels[0]] +
+ lut[1][2][pixels[1]] +
+ lut[2][2][pixels[2]];
+ if (i < 0)
+ pixels[2] = 0;
+ else if (i > 255)
+ pixels[2] = 255;
+ else
+ pixels[2] = i;
+
+ count --;
+ pixels += 3;
+ }
+}
+
+
+/*
+ * The color saturation/hue matrix stuff is provided thanks to Mr. Paul
+ * Haeberli at "http://www.sgi.com/grafica/matrix/index.html".
+ */
+
+/*
+ * 'huerotate()' - Rotate the hue, maintaining luminance.
+ */
+
+static void
+huerotate(float mat[3][3], /* I - Matrix to append to */
+ float rot) /* I - Hue rotation in degrees */
+{
+ float hmat[3][3]; /* Hue matrix */
+ float lx, ly, lz; /* Luminance vector */
+ float xrs, xrc; /* X rotation sine/cosine */
+ float yrs, yrc; /* Y rotation sine/cosine */
+ float zrs, zrc; /* Z rotation sine/cosine */
+ float zsx, zsy; /* Z shear x/y */
+
+
+ /*
+ * Load the identity matrix...
+ */
+
+ ident(hmat);
+
+ /*
+ * Rotate the grey vector into positive Z...
+ */
+
+ xrs = M_SQRT1_2;
+ xrc = M_SQRT1_2;
+ xrotate(hmat,xrs,xrc);
+
+ yrs = -1.0 / sqrt(3.0);
+ yrc = -M_SQRT2 * yrs;
+ yrotate(hmat,yrs,yrc);
+
+ /*
+ * Shear the space to make the luminance plane horizontal...
+ */
+
+ xform(hmat, 0.3086, 0.6094, 0.0820, &lx, &ly, &lz);
+ zsx = lx / lz;
+ zsy = ly / lz;
+ zshear(hmat, zsx, zsy);
+
+ /*
+ * Rotate the hue...
+ */
+
+ zrs = sin(rot * M_PI / 180.0);
+ zrc = cos(rot * M_PI / 180.0);
+
+ zrotate(hmat, zrs, zrc);
+
+ /*
+ * Unshear the space to put the luminance plane back...
+ */
+
+ zshear(hmat, -zsx, -zsy);
+
+ /*
+ * Rotate the grey vector back into place...
+ */
+
+ yrotate(hmat, -yrs, yrc);
+ xrotate(hmat, -xrs, xrc);
+
+ /*
+ * Append it to the current matrix...
+ */
+
+ mult(hmat, mat, mat);
+}
+
+
+/*
+ * 'ident()' - Make an identity matrix.
+ */
+
+static void
+ident(float mat[3][3]) /* I - Matrix to identify */
+{
+ mat[0][0] = 1.0;
+ mat[0][1] = 0.0;
+ mat[0][2] = 0.0;
+ mat[1][0] = 0.0;
+ mat[1][1] = 1.0;
+ mat[1][2] = 0.0;
+ mat[2][0] = 0.0;
+ mat[2][1] = 0.0;
+ mat[2][2] = 1.0;
+}
+
+
+/*
+ * 'mult()' - Multiply two matrices.
+ */
+
+static void
+mult(float a[3][3], /* I - First matrix */
+ float b[3][3], /* I - Second matrix */
+ float c[3][3]) /* I - Destination matrix */
+{
+ int x, y; /* Looping vars */
+ float temp[3][3]; /* Temporary matrix */
+
+
+ /*
+ * Multiply a and b, putting the result in temp...
+ */
+
+ for (y = 0; y < 3; y ++)
+ for (x = 0; x < 3; x ++)
+ temp[y][x] = b[y][0] * a[0][x] +
+ b[y][1] * a[1][x] +
+ b[y][2] * a[2][x];
+
+ /*
+ * Copy temp to c (that way c can be a pointer to a or b).
+ */
+
+ memcpy(c, temp, sizeof(temp));
+}
+
+
+/*
+ * 'saturate()' - Make a saturation matrix.
+ */
+
+static void
+saturate(float mat[3][3], /* I - Matrix to append to */
+ float sat) /* I - Desired color saturation */
+{
+ float smat[3][3]; /* Saturation matrix */
+
+
+ smat[0][0] = (1.0 - sat) * 0.3086 + sat;
+ smat[0][1] = (1.0 - sat) * 0.3086;
+ smat[0][2] = (1.0 - sat) * 0.3086;
+ smat[1][0] = (1.0 - sat) * 0.6094;
+ smat[1][1] = (1.0 - sat) * 0.6094 + sat;
+ smat[1][2] = (1.0 - sat) * 0.6094;
+ smat[2][0] = (1.0 - sat) * 0.0820;
+ smat[2][1] = (1.0 - sat) * 0.0820;
+ smat[2][2] = (1.0 - sat) * 0.0820 + sat;
+
+ mult(smat, mat, mat);
+}
+
+
+/*
+ * 'xform()' - Transform a 3D point using a matrix...
+ */
+
+static void
+xform(float mat[3][3], /* I - Matrix */
+ float x, /* I - Input X coordinate */
+ float y, /* I - Input Y coordinate */
+ float z, /* I - Input Z coordinate */
+ float *tx, /* O - Output X coordinate */
+ float *ty, /* O - Output Y coordinate */
+ float *tz) /* O - Output Z coordinate */
+{
+ *tx = x * mat[0][0] + y * mat[1][0] + z * mat[2][0];
+ *ty = x * mat[0][1] + y * mat[1][1] + z * mat[2][1];
+ *tz = x * mat[0][2] + y * mat[1][2] + z * mat[2][2];
+}
+
+
+/*
+ * 'xrotate()' - Rotate about the x (red) axis...
+ */
+
+static void
+xrotate(float mat[3][3], /* I - Matrix */
+ float rs, /* I - Rotation angle sine */
+ float rc) /* I - Rotation angle cosine */
+{
+ float rmat[3][3]; /* I - Rotation matrix */
+
+
+ rmat[0][0] = 1.0;
+ rmat[0][1] = 0.0;
+ rmat[0][2] = 0.0;
+
+ rmat[1][0] = 0.0;
+ rmat[1][1] = rc;
+ rmat[1][2] = rs;
+
+ rmat[2][0] = 0.0;
+ rmat[2][1] = -rs;
+ rmat[2][2] = rc;
+
+ mult(rmat, mat, mat);
+}
+
+
+/*
+ * 'yrotate()' - Rotate about the y (green) axis...
+ */
+
+static void
+yrotate(float mat[3][3], /* I - Matrix */
+ float rs, /* I - Rotation angle sine */
+ float rc) /* I - Rotation angle cosine */
+{
+ float rmat[3][3]; /* I - Rotation matrix */
+
+
+ rmat[0][0] = rc;
+ rmat[0][1] = 0.0;
+ rmat[0][2] = -rs;
+
+ rmat[1][0] = 0.0;
+ rmat[1][1] = 1.0;
+ rmat[1][2] = 0.0;
+
+ rmat[2][0] = rs;
+ rmat[2][1] = 0.0;
+ rmat[2][2] = rc;
+
+ mult(rmat,mat,mat);
+}
+
+
+/*
+ * 'zrotate()' - Rotate about the z (blue) axis...
+ */
+
+static void
+zrotate(float mat[3][3], /* I - Matrix */
+ float rs, /* I - Rotation angle sine */
+ float rc) /* I - Rotation angle cosine */
+{
+ float rmat[3][3]; /* I - Rotation matrix */
+
+
+ rmat[0][0] = rc;
+ rmat[0][1] = rs;
+ rmat[0][2] = 0.0;
+
+ rmat[1][0] = -rs;
+ rmat[1][1] = rc;
+ rmat[1][2] = 0.0;
+
+ rmat[2][0] = 0.0;
+ rmat[2][1] = 0.0;
+ rmat[2][2] = 1.0;
+
+ mult(rmat,mat,mat);
+}
+
+
+/*
+ * 'zshear()' - Shear z using x and y...
+ */
+
+static void
+zshear(float mat[3][3], /* I - Matrix */
+ float dx, /* I - X shear */
+ float dy) /* I - Y shear */
+{
+ float smat[3][3]; /* Shear matrix */
+
+
+ smat[0][0] = 1.0;
+ smat[0][1] = 0.0;
+ smat[0][2] = dx;
+
+ smat[1][0] = 0.0;
+ smat[1][1] = 1.0;
+ smat[1][2] = dy;
+
+ smat[2][0] = 0.0;
+ smat[2][1] = 0.0;
+ smat[2][2] = 1.0;
+
+ mult(smat, mat, mat);
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/filter/image-gif.c b/filter/image-gif.c
new file mode 100644
index 000000000..5f18ff085
--- /dev/null
+++ b/filter/image-gif.c
@@ -0,0 +1,644 @@
+/*
+ * "$Id$"
+ *
+ * GIF image routines for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1993-1999 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * Contents:
+ *
+ * ImageReadGIF() - Read a GIF image file.
+ * gif_read_cmap() - Read the colormap from a GIF file...
+ * gif_get_block() - Read a GIF data block...
+ * gif_get_code() - Get a LZW code from the file...
+ * gif_read_lzw() - Read a byte from the LZW stream...
+ * gif_read_image() - Read a GIF image stream...
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "image.h"
+
+
+/*
+ * GIF definitions...
+ */
+
+#define GIF_INTERLACE 0x40
+#define GIF_COLORMAP 0x80
+
+typedef ib_t gif_cmap_t[256][4];
+
+
+/*
+ * Local globals...
+ */
+
+static int gif_eof = 0; /* Did we hit EOF? */
+
+
+/*
+ * Local functions...
+ */
+
+static int gif_read_cmap(FILE *fp, int ncolors, gif_cmap_t cmap,
+ int *gray);
+static int gif_get_block(FILE *fp, unsigned char *buffer);
+static int gif_get_code (FILE *fp, int code_size, int first_time);
+static int gif_read_lzw(FILE *fp, int first_time, int input_code_size);
+static int gif_read_image(FILE *fp, image_t *img, gif_cmap_t cmap,
+ int interlace);
+
+
+/*
+ * 'ImageReadGIF()' - Read a GIF image file.
+ */
+
+int /* O - Read status */
+ImageReadGIF(image_t *img, /* IO - Image */
+ FILE *fp, /* I - Image file */
+ int primary, /* I - Primary choice for colorspace */
+ int secondary, /* I - Secondary choice for colorspace */
+ int saturation,/* I - Color saturation (%) */
+ int hue, /* I - Color hue (degrees) */
+ ib_t *lut) /* I - Lookup table for gamma/brightness */
+{
+ unsigned char buf[1024]; /* Input buffer */
+ gif_cmap_t cmap; /* Colormap */
+ int i, /* Looping var */
+ bpp, /* Bytes per pixel */
+ gray, /* Grayscale image? */
+ ncolors, /* Bits per pixel */
+ transparent; /* Transparent color index */
+
+
+ /*
+ * Read the header; we already know it is a GIF file...
+ */
+
+ fread(buf, 13, 1, fp);
+
+ img->xsize = (buf[7] << 8) | buf[6];
+ img->ysize = (buf[9] << 8) | buf[8];
+ ncolors = 2 << (buf[10] & 0x07);
+ gray = primary == IMAGE_BLACK || primary == IMAGE_WHITE;
+
+ if (buf[10] & GIF_COLORMAP)
+ if (gif_read_cmap(fp, ncolors, cmap, &gray))
+ {
+ fclose(fp);
+ return (-1);
+ }
+
+ transparent = -1;
+
+ for (;;)
+ {
+ switch (getc(fp))
+ {
+ case ';' : /* End of image */
+ fclose(fp);
+ return (-1); /* Early end of file */
+
+ case '!' : /* Extension record */
+ buf[0] = getc(fp);
+ if (buf[0] == 0xf9) /* Graphic Control Extension */
+ {
+ gif_get_block(fp, buf);
+ if (buf[0] & 1) /* Get transparent color index */
+ transparent = buf[3];
+ }
+
+ while (gif_get_block(fp, buf) != 0);
+ break;
+
+ case ',' : /* Image data */
+ fread(buf, 9, 1, fp);
+
+ if (buf[8] & GIF_COLORMAP)
+ {
+ ncolors = 2 << (buf[8] & 0x07);
+ gray = primary == IMAGE_BLACK || primary == IMAGE_WHITE;
+
+ if (gif_read_cmap(fp, ncolors, cmap, &gray))
+ {
+ fclose(fp);
+ return (-1);
+ }
+ }
+
+ if (transparent >= 0)
+ {
+ /*
+ * Make transparent color white...
+ */
+
+ cmap[transparent][0] = 255;
+ cmap[transparent][1] = 255;
+ cmap[transparent][2] = 255;
+ }
+
+ if (gray)
+ {
+ switch (secondary)
+ {
+ case IMAGE_CMYK :
+ for (i = ncolors - 1; i >= 0; i --)
+ ImageWhiteToCMYK(cmap[i], cmap[i], 1);
+ break;
+ case IMAGE_CMY :
+ for (i = ncolors - 1; i >= 0; i --)
+ ImageWhiteToCMY(cmap[i], cmap[i], 1);
+ break;
+ case IMAGE_BLACK :
+ for (i = ncolors - 1; i >= 0; i --)
+ ImageWhiteToBlack(cmap[i], cmap[i], 1);
+ break;
+ case IMAGE_WHITE :
+ break;
+ case IMAGE_RGB :
+ for (i = ncolors - 1; i >= 0; i --)
+ ImageWhiteToRGB(cmap[i], cmap[i], 1);
+ break;
+ }
+
+ img->colorspace = secondary;
+ }
+ else
+ {
+ if (hue != 0 || saturation != 100)
+ for (i = ncolors - 1; i >= 0; i --)
+ ImageRGBAdjust(cmap[i], 1, saturation, hue);
+
+ switch (primary)
+ {
+ case IMAGE_CMYK :
+ for (i = ncolors - 1; i >= 0; i --)
+ ImageRGBToCMYK(cmap[i], cmap[i], 1);
+ break;
+ case IMAGE_CMY :
+ for (i = ncolors - 1; i >= 0; i --)
+ ImageRGBToCMY(cmap[i], cmap[i], 1);
+ break;
+ case IMAGE_BLACK :
+ for (i = ncolors - 1; i >= 0; i --)
+ ImageRGBToBlack(cmap[i], cmap[i], 1);
+ break;
+ case IMAGE_WHITE :
+ for (i = ncolors - 1; i >= 0; i --)
+ ImageRGBToWhite(cmap[i], cmap[i], 1);
+ break;
+ case IMAGE_RGB :
+ break;
+ }
+
+ img->colorspace = primary;
+ }
+
+ if (lut)
+ {
+ bpp = ImageGetDepth(img);
+
+ for (i = ncolors - 1; i >= 0; i --)
+ ImageLut(cmap[i], bpp, lut);
+ }
+
+ img->xsize = (buf[5] << 8) | buf[4];
+ img->ysize = (buf[7] << 8) | buf[6];
+
+ i = gif_read_image(fp, img, cmap, buf[8] & GIF_INTERLACE);
+ fclose(fp);
+ return (i);
+ }
+ }
+}
+
+
+/*
+ * 'gif_read_cmap()' - Read the colormap from a GIF file...
+ */
+
+static int
+gif_read_cmap(FILE *fp,
+ int ncolors,
+ gif_cmap_t cmap,
+ int *gray)
+{
+ int i;
+
+
+ /*
+ * Read the colormap...
+ */
+
+ for (i = 0; i < ncolors; i ++)
+ if (fread(cmap[i], 3, 1, fp) < 1)
+ return (-1);
+
+ /*
+ * Check to see if the colormap is a grayscale ramp...
+ */
+
+ for (i = 0; i < ncolors; i ++)
+ if (cmap[i][0] != cmap[i][1] || cmap[i][1] != cmap[i][2])
+ break;
+
+ if (i == ncolors)
+ {
+ *gray = 1;
+ return (0);
+ }
+
+ /*
+ * If this needs to be a grayscale image, convert the RGB values to
+ * luminance values...
+ */
+
+ if (*gray)
+ for (i = 0; i < ncolors; i ++)
+ cmap[i][0] = (cmap[i][0] * 31 + cmap[i][1] * 61 + cmap[i][2] * 8) / 100;
+
+ return (0);
+}
+
+
+/*
+ * 'gif_get_block()' - Read a GIF data block...
+ */
+
+static int /* O - Number characters read */
+gif_get_block(FILE *fp, /* I - File to read from */
+ unsigned char *buf) /* I - Input buffer */
+{
+ int count; /* Number of character to read */
+
+
+ /*
+ * Read the count byte followed by the data from the file...
+ */
+
+ if ((count = getc(fp)) == EOF)
+ {
+ gif_eof = 1;
+ return (-1);
+ }
+ else if (count == 0)
+ gif_eof = 1;
+ else if (fread(buf, 1, count, fp) < count)
+ {
+ gif_eof = 1;
+ return (-1);
+ }
+ else
+ gif_eof = 0;
+
+ return (count);
+}
+
+
+/*
+ * 'gif_get_code()' - Get a LZW code from the file...
+ */
+
+static int /* O - LZW code */
+gif_get_code(FILE *fp, /* I - File to read from */
+ int code_size, /* I - Size of code in bits */
+ int first_time) /* I - 1 = first time, 0 = not first time */
+{
+ unsigned i, j, /* Looping vars */
+ ret; /* Return value */
+ int count; /* Number of bytes read */
+ static unsigned char buf[280]; /* Input buffer */
+ static unsigned curbit, /* Current bit */
+ lastbit, /* Last bit in buffer */
+ done, /* Done with this buffer? */
+ last_byte; /* Last byte in buffer */
+ static unsigned char bits[8] = /* Bit masks for codes */
+ {
+ 0x01, 0x02, 0x04, 0x08,
+ 0x10, 0x20, 0x40, 0x80
+ };
+
+
+ if (first_time)
+ {
+ /*
+ * Just initialize the input buffer...
+ */
+
+ curbit = 0;
+ lastbit = 0;
+ done = 0;
+
+ return (0);
+ }
+
+
+ if ((curbit + code_size) >= lastbit)
+ {
+ /*
+ * Don't have enough bits to hold the code...
+ */
+
+ if (done)
+ return (-1); /* Sorry, no more... */
+
+ /*
+ * Move last two bytes to front of buffer...
+ */
+
+ buf[0] = buf[last_byte - 2];
+ buf[1] = buf[last_byte - 1];
+
+ /*
+ * Read in another buffer...
+ */
+
+ if ((count = gif_get_block (fp, buf + 2)) <= 0)
+ {
+ /*
+ * Whoops, no more data!
+ */
+
+ done = 1;
+ return (-1);
+ }
+
+ /*
+ * Update buffer state...
+ */
+
+ last_byte = 2 + count;
+ curbit = (curbit - lastbit) + 16;
+ lastbit = last_byte * 8;
+ }
+
+ ret = 0;
+ for (ret = 0, i = curbit + code_size - 1, j = code_size;
+ j > 0;
+ i --, j --)
+ ret = (ret << 1) | ((buf[i / 8] & bits[i & 7]) != 0);
+
+ curbit += code_size;
+
+ return ret;
+}
+
+
+/*
+ * 'gif_read_lzw()' - Read a byte from the LZW stream...
+ */
+
+static int /* I - Byte from stream */
+gif_read_lzw(FILE *fp, /* I - File to read from */
+ int first_time, /* I - 1 = first time, 0 = not first time */
+ int input_code_size) /* I - Code size in bits */
+{
+ int i, /* Looping var */
+ code, /* Current code */
+ incode; /* Input code */
+ static short fresh = 0, /* 1 = empty buffers */
+ code_size, /* Current code size */
+ set_code_size, /* Initial code size set */
+ max_code, /* Maximum code used */
+ max_code_size, /* Maximum code size */
+ firstcode, /* First code read */
+ oldcode, /* Last code read */
+ clear_code, /* Clear code for LZW input */
+ end_code, /* End code for LZW input */
+ table[2][4096], /* String table */
+ stack[8192], /* Output stack */
+ *sp; /* Current stack pointer */
+
+
+ if (first_time)
+ {
+ /*
+ * Setup LZW state...
+ */
+
+ set_code_size = input_code_size;
+ code_size = set_code_size + 1;
+ clear_code = 1 << set_code_size;
+ end_code = clear_code + 1;
+ max_code_size = 2 * clear_code;
+ max_code = clear_code + 2;
+
+ /*
+ * Initialize input buffers...
+ */
+
+ gif_get_code(fp, 0, 1);
+
+ /*
+ * Wipe the decompressor table...
+ */
+
+ fresh = 1;
+
+ for (i = 0; i < clear_code; i ++)
+ {
+ table[0][i] = 0;
+ table[1][i] = i;
+ }
+
+ for (; i < 4096; i ++)
+ table[0][i] = table[1][0] = 0;
+
+ sp = stack;
+
+ return (0);
+ }
+ else if (fresh)
+ {
+ fresh = 0;
+
+ do
+ firstcode = oldcode = gif_get_code(fp, code_size, 0);
+ while (firstcode == clear_code);
+
+ return (firstcode);
+ }
+
+ if (sp > stack)
+ return (*--sp);
+
+ while ((code = gif_get_code (fp, code_size, 0)) >= 0)
+ {
+ if (code == clear_code)
+ {
+ for (i = 0; i < clear_code; i ++)
+ {
+ table[0][i] = 0;
+ table[1][i] = i;
+ }
+
+ for (; i < 4096; i ++)
+ table[0][i] = table[1][i] = 0;
+
+ code_size = set_code_size + 1;
+ max_code_size = 2 * clear_code;
+ max_code = clear_code + 2;
+
+ sp = stack;
+
+ firstcode = oldcode = gif_get_code(fp, code_size, 0);
+
+ return (firstcode);
+ }
+ else if (code == end_code)
+ {
+ unsigned char buf[260];
+
+
+ if (!gif_eof)
+ while (gif_get_block(fp, buf) > 0);
+
+ return (-2);
+ }
+
+ incode = code;
+
+ if (code >= max_code)
+ {
+ *sp++ = firstcode;
+ code = oldcode;
+ }
+
+ while (code >= clear_code)
+ {
+ *sp++ = table[1][code];
+ if (code == table[0][code])
+ return (255);
+
+ code = table[0][code];
+ }
+
+ *sp++ = firstcode = table[1][code];
+ code = max_code;
+
+ if (code < 4096)
+ {
+ table[0][code] = oldcode;
+ table[1][code] = firstcode;
+ max_code ++;
+
+ if (max_code >= max_code_size && max_code_size < 4096)
+ {
+ max_code_size *= 2;
+ code_size ++;
+ }
+ }
+
+ oldcode = incode;
+
+ if (sp > stack)
+ return (*--sp);
+ }
+
+ return (code);
+}
+
+
+/*
+ * 'gif_read_image()' - Read a GIF image stream...
+ */
+
+static int /* I - 0 = success, -1 = failure */
+gif_read_image(FILE *fp, /* I - Input file */
+ image_t *img, /* I - Image pointer */
+ gif_cmap_t cmap, /* I - Colormap */
+ int interlace) /* I - Non-zero = interlaced image */
+{
+ unsigned char code_size; /* Code size */
+ ib_t *pixels, /* Pixel buffer */
+ *temp; /* Current pixel */
+ int xpos, /* Current X position */
+ ypos, /* Current Y position */
+ pass; /* Current pass */
+ int pixel; /* Current pixel */
+ int bpp; /* Bytes per pixel */
+ static int xpasses[4] = { 8, 8, 4, 2 },
+ ypasses[5] = { 0, 4, 2, 1, 999999 };
+
+
+ bpp = ImageGetDepth(img);
+ pixels = calloc(bpp, img->xsize);
+ xpos = 0;
+ ypos = 0;
+ pass = 0;
+ code_size = getc(fp);
+
+ if (gif_read_lzw(fp, 1, code_size) < 0)
+ return (-1);
+
+ temp = pixels;
+ while ((pixel = gif_read_lzw(fp, 0, code_size)) >= 0)
+ {
+ switch (bpp)
+ {
+ case 4 :
+ temp[3] = cmap[pixel][3];
+ case 3 :
+ temp[2] = cmap[pixel][2];
+ case 2 :
+ temp[1] = cmap[pixel][1];
+ default :
+ temp[0] = cmap[pixel][0];
+ }
+
+ xpos ++;
+ temp += bpp;
+ if (xpos == img->xsize)
+ {
+ ImagePutRow(img, 0, ypos, img->xsize, pixels);
+
+ xpos = 0;
+ temp = pixels;
+
+ if (interlace)
+ {
+ ypos += xpasses[pass];
+
+ if (ypos >= img->ysize)
+ {
+ pass ++;
+
+ ypos = ypasses[pass];
+ }
+ }
+ else
+ ypos ++;
+ }
+
+ if (ypos >= img->ysize)
+ break;
+ }
+
+ free(pixels);
+
+ return (0);
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/filter/image-jpeg.c b/filter/image-jpeg.c
new file mode 100644
index 000000000..a08051b30
--- /dev/null
+++ b/filter/image-jpeg.c
@@ -0,0 +1,190 @@
+/*
+ * "$Id$"
+ *
+ * JPEG image routines for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1993-1999 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * Contents:
+ *
+ * ImageReadJPEG() - Read a JPEG image file.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "image.h"
+
+#ifdef HAVE_LIBJPEG
+# include <jpeglib.h> /* JPEG/JFIF image definitions */
+
+
+/*
+ * 'ImageReadJPEG()' - Read a JPEG image file.
+ */
+
+int /* O - Read status */
+ImageReadJPEG(image_t *img, /* IO - Image */
+ FILE *fp, /* I - Image file */
+ int primary, /* I - Primary choice for colorspace */
+ int secondary, /* I - Secondary choice for colorspace */
+ int saturation, /* I - Color saturation (%) */
+ int hue, /* I - Color hue (degrees) */
+ ib_t *lut) /* I - Lookup table for gamma/brightness */
+{
+ struct jpeg_decompress_struct cinfo; /* Decompressor info */
+ struct jpeg_error_mgr jerr; /* Error handler info */
+ ib_t *in, /* Input pixels */
+ *out; /* Output pixels */
+
+
+ (void)secondary;
+
+ cinfo.err = jpeg_std_error(&jerr);
+ jpeg_create_decompress(&cinfo);
+ jpeg_stdio_src(&cinfo, fp);
+ jpeg_read_header(&cinfo, 1);
+
+ cinfo.quantize_colors = 0;
+
+ if (cinfo.num_components == 1)
+ {
+ cinfo.out_color_space = JCS_GRAYSCALE;
+ cinfo.out_color_components = 1;
+ cinfo.output_components = 1;
+ }
+ else
+ {
+ cinfo.out_color_space = JCS_RGB;
+ cinfo.out_color_components = 3;
+ cinfo.output_components = 3;
+ }
+
+ jpeg_calc_output_dimensions(&cinfo);
+
+ img->xsize = cinfo.output_width;
+ img->ysize = cinfo.output_height;
+ img->colorspace = primary;
+
+ if (cinfo.X_density > 0 && cinfo.Y_density > 0 && cinfo.density_unit > 0)
+ {
+ if (cinfo.density_unit == 1)
+ {
+ img->xppi = cinfo.X_density;
+ img->yppi = cinfo.Y_density;
+ }
+ else
+ {
+ img->xppi = (int)((float)cinfo.X_density * 2.54);
+ img->yppi = (int)((float)cinfo.Y_density * 2.54);
+ }
+ }
+
+ ImageSetMaxTiles(img, 0);
+
+ in = malloc(img->xsize * cinfo.output_components);
+ if (primary < 0)
+ out = malloc(-img->xsize * primary);
+ else
+ out = malloc(img->xsize * primary);
+
+ jpeg_start_decompress(&cinfo);
+
+ while (cinfo.output_scanline < cinfo.output_height)
+ {
+ jpeg_read_scanlines(&cinfo, (JSAMPROW *)&in, (JDIMENSION)1);
+
+ if ((saturation != 100 || hue != 0) && cinfo.output_components > 1)
+ ImageRGBAdjust(in, img->xsize, saturation, hue);
+
+ if ((primary == IMAGE_WHITE && cinfo.out_color_space == JCS_GRAYSCALE) ||
+ (primary == IMAGE_RGB && cinfo.out_color_space == JCS_RGB))
+ {
+ if (lut)
+ ImageLut(in, img->xsize * ImageGetDepth(img), lut);
+
+ ImagePutRow(img, 0, cinfo.output_scanline - 1, img->xsize, in);
+ }
+ else if (cinfo.out_color_space == JCS_GRAYSCALE)
+ {
+ switch (primary)
+ {
+ case IMAGE_BLACK :
+ ImageWhiteToBlack(in, out, img->xsize);
+ break;
+ case IMAGE_RGB :
+ ImageWhiteToRGB(in, out, img->xsize);
+ break;
+ case IMAGE_CMY :
+ ImageWhiteToCMY(in, out, img->xsize);
+ break;
+ case IMAGE_CMYK :
+ ImageWhiteToCMYK(in, out, img->xsize);
+ break;
+ }
+
+ if (lut)
+ ImageLut(out, img->xsize * ImageGetDepth(img), lut);
+
+ ImagePutRow(img, 0, cinfo.output_scanline - 1, img->xsize, out);
+ }
+ else
+ {
+ switch (primary)
+ {
+ case IMAGE_WHITE :
+ ImageRGBToWhite(in, out, img->xsize);
+ break;
+ case IMAGE_BLACK :
+ ImageRGBToBlack(in, out, img->xsize);
+ break;
+ case IMAGE_CMY :
+ ImageRGBToCMY(in, out, img->xsize);
+ break;
+ case IMAGE_CMYK :
+ ImageRGBToCMYK(in, out, img->xsize);
+ break;
+ }
+
+ if (lut)
+ ImageLut(out, img->xsize * ImageGetDepth(img), lut);
+
+ ImagePutRow(img, 0, cinfo.output_scanline - 1, img->xsize, out);
+ }
+ }
+
+ free(in);
+ free(out);
+
+ jpeg_finish_decompress(&cinfo);
+ jpeg_destroy_decompress(&cinfo);
+
+ fclose(fp);
+
+ return (0);
+}
+
+
+#endif /* HAVE_LIBJPEG */
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/filter/image-photocd.c b/filter/image-photocd.c
new file mode 100644
index 000000000..e86b5467f
--- /dev/null
+++ b/filter/image-photocd.c
@@ -0,0 +1,319 @@
+/*
+ * "$Id$"
+ *
+ * PhotoCD routines for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1993-1999 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * Contents:
+ *
+ * ImageReadPhotoCD() - Read a PhotoCD image file.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "image.h"
+
+
+/*
+ * PhotoCD support is currently limited to the 768x512 base image, which
+ * is only YCC encoded. Support for the higher resolution images will
+ * require a lot of extra code...
+ */
+
+/*
+ * 'ImageReadPhotoCD()' - Read a PhotoCD image file.
+ */
+
+int /* O - Read status */
+ImageReadPhotoCD(image_t *img, /* IO - Image */
+ FILE *fp, /* I - Image file */
+ int primary, /* I - Primary choice for colorspace */
+ int secondary, /* I - Secondary choice for colorspace */
+ int saturation, /* I - Color saturation (%) */
+ int hue, /* I - Color hue (degrees) */
+ ib_t *lut) /* I - Lookup table for gamma/brightness */
+{
+ int x, y; /* Looping vars */
+ int xdir, /* X direction */
+ xstart; /* X starting point */
+ int bpp; /* Bytes per pixel */
+ int pass; /* Pass number */
+ int rotation; /* 0 for 768x512, 1 for 512x768 */
+ int temp, /* Adjusted luminance */
+ temp2, /* Red, green, and blue values */
+ cb, cr; /* Adjusted chroma values */
+ ib_t *in, /* Input (YCC) pixels */
+ *iy, /* Luminance */
+ *icb, /* Blue chroma */
+ *icr, /* Red chroma */
+ *rgb, /* RGB */
+ *rgbptr, /* Pointer into RGB data */
+ *out; /* Output pixels */
+
+
+ (void)secondary;
+
+ /*
+ * Get the image orientation...
+ */
+
+ fseek(fp, 72, SEEK_SET);
+ rotation = (getc(fp) & 63) != 8;
+
+ /*
+ * Seek to the start of the base image...
+ */
+
+ fseek(fp, 0x30000, SEEK_SET);
+
+ /*
+ * Allocate and initialize...
+ */
+
+ img->colorspace = primary;
+ img->xppi = 128;
+ img->yppi = 128;
+
+ if (rotation)
+ {
+ img->xsize = 512;
+ img->ysize = 768;
+ }
+ else
+ {
+ img->xsize = 768;
+ img->ysize = 512;
+ }
+
+ ImageSetMaxTiles(img, 0);
+
+ bpp = ImageGetDepth(img);
+ in = malloc(768 * 3);
+ out = malloc(768 * bpp);
+
+ if (bpp > 1)
+ rgb = malloc(768 * 3);
+
+ if (rotation)
+ {
+ xstart = 767 * bpp;
+ xdir = -2 * bpp;
+ }
+ else
+ {
+ xstart = 0;
+ xdir = 0;
+ }
+
+ /*
+ * Read the image file...
+ */
+
+ for (y = 0; y < 512; y += 2)
+ {
+ /*
+ * Grab the next two scanlines:
+ *
+ * YYYYYYYYYYYYYYY...
+ * YYYYYYYYYYYYYYY...
+ * CbCbCb...CrCrCr...
+ */
+
+ if (fread(in, 1, 768 * 3, fp) < (768 * 3))
+ {
+ /*
+ * Couldn't read a row of data - return an error!
+ */
+
+ free(in);
+ free(out);
+
+ return (-1);
+ }
+
+ /*
+ * Process the two scanlines...
+ */
+
+ for (pass = 0, iy = in; pass < 2; pass ++)
+ {
+ if (bpp == 1)
+ {
+ /*
+ * Just extract the luminance channel from the line and put it
+ * in the image...
+ */
+
+ if (primary == IMAGE_BLACK)
+ {
+ if (rotation)
+ {
+ for (rgbptr = out + xstart, x = 0; x < 768; x ++)
+ *rgbptr-- = 255 - *iy++;
+
+ if (lut)
+ ImageLut(out, 768, lut);
+
+ ImagePutCol(img, 511 - y - pass, 0, 768, out);
+ }
+ else
+ {
+ ImageWhiteToBlack(iy, out, 768);
+
+ if (lut)
+ ImageLut(out, 768, lut);
+
+ ImagePutRow(img, 0, y + pass, 768, out);
+ iy += 768;
+ }
+ }
+ else if (rotation)
+ {
+ for (rgbptr = out + xstart, x = 0; x < 768; x ++)
+ *rgbptr-- = 255 - *iy++;
+
+ if (lut)
+ ImageLut(out, 768, lut);
+
+ ImagePutCol(img, 511 - y - pass, 0, 768, out);
+ }
+ else
+ {
+ if (lut)
+ ImageLut(iy, 768, lut);
+
+ ImagePutRow(img, 0, y + pass, 768, iy);
+ iy += 768;
+ }
+ }
+ else
+ {
+ /*
+ * Convert YCbCr to RGB... While every pixel gets a luminance
+ * value, adjacent pixels share chroma information.
+ */
+
+ for (x = 0, rgbptr = rgb + xstart, icb = in + 1536, icr = in + 1920;
+ x < 768;
+ x ++, iy ++, rgbptr += xdir)
+ {
+ if (!(x & 1))
+ {
+ cb = (float)(*icb - 156);
+ cr = (float)(*icr - 137);
+ }
+
+ temp = 92241 * (*iy);
+
+ temp2 = (temp + 86706 * cr) / 65536;
+ if (temp2 < 0)
+ *rgbptr++ = 0;
+ else if (temp2 > 255)
+ *rgbptr++ = 255;
+ else
+ *rgbptr++ = temp2;
+
+ temp2 = (temp - 25914 * cb - 44166 * cr) / 65536;
+ if (temp2 < 0)
+ *rgbptr++ = 0;
+ else if (temp2 > 255)
+ *rgbptr++ = 255;
+ else
+ *rgbptr++ = temp2;
+
+ temp2 = (temp + 133434 * cb) / 65536;
+ if (temp2 < 0)
+ *rgbptr++ = 0;
+ else if (temp2 > 255)
+ *rgbptr++ = 255;
+ else
+ *rgbptr++ = temp2;
+
+ if (x & 1)
+ {
+ icb ++;
+ icr ++;
+ }
+ }
+
+ /*
+ * Adjust the hue and saturation if needed...
+ */
+
+ if (saturation != 100 || hue != 0)
+ ImageRGBAdjust(rgb, 768, saturation, hue);
+
+ /*
+ * Then convert the RGB data to the appropriate colorspace and
+ * put it in the image...
+ */
+
+ if (img->colorspace == IMAGE_RGB)
+ {
+ if (lut)
+ ImageLut(rgb, 768 * 3, lut);
+
+ if (rotation)
+ ImagePutCol(img, 511 - y - pass, 0, 768, rgb);
+ else
+ ImagePutRow(img, 0, y + pass, 768, rgb);
+ }
+ else
+ {
+ switch (img->colorspace)
+ {
+ case IMAGE_CMY :
+ ImageRGBToCMY(rgb, out, 768);
+ break;
+ case IMAGE_CMYK :
+ ImageRGBToCMYK(rgb, out, 768);
+ break;
+ }
+
+ if (lut)
+ ImageLut(out, 768 * bpp, lut);
+
+ if (rotation)
+ ImagePutCol(img, 511 - y - pass, 0, 768, out);
+ else
+ ImagePutRow(img, 0, y + pass, 768, out);
+ }
+ }
+ }
+ }
+
+ /*
+ * Free memory and return...
+ */
+
+ free(in);
+ free(out);
+ if (bpp > 1)
+ free(rgb);
+
+ return (0);
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/filter/image-png.c b/filter/image-png.c
new file mode 100644
index 000000000..b74558157
--- /dev/null
+++ b/filter/image-png.c
@@ -0,0 +1,205 @@
+/*
+ * "$Id$"
+ *
+ * PNG image routines for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1993-1999 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * Contents:
+ *
+ * ImageReadPNG() - Read a PNG image file.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "image.h"
+
+#if defined(HAVE_LIBPNG) && defined(HAVE_LIBZ)
+#include <png.h> /* Portable Network Graphics (PNG) definitions */
+
+
+/*
+ * 'ImageReadPNG()' - Read a PNG image file.
+ */
+
+int /* O - Read status */
+ImageReadPNG(image_t *img, /* IO - Image */
+ FILE *fp, /* I - Image file */
+ int primary, /* I - Primary choice for colorspace */
+ int secondary, /* I - Secondary choice for colorspace */
+ int saturation,/* I - Color saturation (%) */
+ int hue, /* I - Color hue (degrees) */
+ ib_t *lut) /* I - Lookup table for gamma/brightness */
+{
+ int y; /* Looping var */
+ png_structp pp; /* PNG read pointer */
+ png_infop info; /* PNG info pointers */
+ int bpp; /* Bytes per pixel */
+ ib_t *in, /* Input pixels */
+ *out; /* Output pixels */
+
+
+ /*
+ * Setup the PNG data structures...
+ */
+
+ pp = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ info = png_create_info_struct(pp);
+
+ /*
+ * Initialize the PNG read "engine"...
+ */
+
+ png_init_io(pp, fp);
+
+ /*
+ * Get the image dimensions and load the output image...
+ */
+
+ png_read_info(pp, info);
+
+ if (info->color_type == PNG_COLOR_TYPE_PALETTE)
+ png_set_expand(pp);
+
+ if (info->color_type == PNG_COLOR_TYPE_GRAY ||
+ info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+ img->colorspace = secondary;
+ else
+ img->colorspace = primary;
+
+ img->xsize = info->width;
+ img->ysize = info->height;
+
+ if (info->valid & PNG_INFO_pHYs &&
+ info->phys_unit_type == PNG_RESOLUTION_METER)
+ {
+ img->xppi = (int)((float)info->x_pixels_per_unit * 0.0254);
+ img->yppi = (int)((float)info->y_pixels_per_unit * 0.0254);
+ }
+
+ ImageSetMaxTiles(img, 0);
+
+ if (info->bit_depth < 8)
+ {
+ png_set_packing(pp);
+
+ if (info->valid & PNG_INFO_sBIT)
+ png_set_shift(pp, &(info->sig_bit));
+ }
+ else if (info->bit_depth == 16)
+ png_set_strip_16(pp);
+
+ if (info->color_type == PNG_COLOR_TYPE_GRAY ||
+ info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+ in = malloc(img->xsize);
+ else
+ in = malloc(img->xsize * 3);
+
+ bpp = ImageGetDepth(img);
+ out = malloc(img->xsize * bpp);
+
+ /*
+ * This doesn't work for interlaced PNG files... :(
+ */
+
+ for (y = 0; y < img->ysize; y ++)
+ {
+ if (info->color_type == PNG_COLOR_TYPE_GRAY ||
+ info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+ {
+ if (img->colorspace == IMAGE_WHITE)
+ png_read_row(pp, (png_bytep)out, NULL);
+ else
+ {
+ png_read_row(pp, (png_bytep)in, NULL);
+
+ switch (img->colorspace)
+ {
+ case IMAGE_RGB :
+ ImageWhiteToRGB(in, out, img->xsize);
+ break;
+ case IMAGE_BLACK :
+ ImageWhiteToBlack(in, out, img->xsize);
+ break;
+ case IMAGE_CMY :
+ ImageWhiteToCMY(in, out, img->xsize);
+ break;
+ case IMAGE_CMYK :
+ ImageWhiteToCMYK(in, out, img->xsize);
+ break;
+ }
+ }
+ }
+ else
+ {
+ if (img->colorspace == IMAGE_RGB)
+ {
+ png_read_row(pp, (png_bytep)out, NULL);
+
+ if (saturation != 100 || hue != 0)
+ ImageRGBAdjust(out, img->xsize, saturation, hue);
+ }
+ else
+ {
+ png_read_row(pp, (png_bytep)in, NULL);
+
+ if ((saturation != 100 || hue != 0) && bpp > 1)
+ ImageRGBAdjust(in, img->xsize, saturation, hue);
+
+ switch (img->colorspace)
+ {
+ case IMAGE_WHITE :
+ ImageRGBToWhite(in, out, img->xsize);
+ break;
+ case IMAGE_BLACK :
+ ImageRGBToBlack(in, out, img->xsize);
+ break;
+ case IMAGE_CMY :
+ ImageRGBToCMY(in, out, img->xsize);
+ break;
+ case IMAGE_CMYK :
+ ImageRGBToCMYK(in, out, img->xsize);
+ break;
+ }
+ }
+ }
+
+ if (lut)
+ ImageLut(out, img->xsize * bpp, lut);
+
+ ImagePutRow(img, 0, y, img->xsize, out);
+ }
+
+ png_read_end(pp, info);
+ png_read_destroy(pp, info, NULL);
+
+ fclose(fp);
+
+ return (0);
+}
+
+
+#endif /* HAVE_LIBPNG && HAVE_LIBZ */
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/filter/image-pnm.c b/filter/image-pnm.c
new file mode 100644
index 000000000..d827812fe
--- /dev/null
+++ b/filter/image-pnm.c
@@ -0,0 +1,288 @@
+/*
+ * "$Id$"
+ *
+ * Portable Any Map file routines for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1993-1999 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * Contents:
+ *
+ * ImageReadPNM() - Read a PNM image file.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "image.h"
+#include <ctype.h>
+
+
+/*
+ * 'ImageReadPNM()' - Read a PNM image file.
+ */
+
+int /* O - Read status */
+ImageReadPNM(image_t *img, /* IO - Image */
+ FILE *fp, /* I - Image file */
+ int primary, /* I - Primary choice for colorspace */
+ int secondary, /* I - Secondary choice for colorspace */
+ int saturation,/* I - Color saturation (%) */
+ int hue, /* I - Color hue (degrees) */
+ ib_t *lut) /* I - Lookup table for gamma/brightness */
+{
+ int x, y; /* Looping vars */
+ int bpp; /* Bytes per pixel */
+ ib_t *in, /* Input pixels */
+ *inptr, /* Current input pixel */
+ *out, /* Output pixels */
+ *outptr, /* Current output pixel */
+ bit; /* Bit in input line */
+ char line[255], /* Input line */
+ *lineptr; /* Pointer in line */
+ int format, /* Format of PNM file */
+ val, /* Pixel value */
+ maxval; /* Maximum pixel value */
+
+
+ /*
+ * Read the file header in the format:
+ *
+ * Pformat
+ * # comment1
+ * # comment2
+ * ...
+ * # commentN
+ * width
+ * height
+ * max sample
+ */
+
+ lineptr = fgets(line, sizeof(line), fp);
+ lineptr ++;
+
+ format = atoi(lineptr);
+ while (isdigit(*lineptr))
+ lineptr ++;
+
+ while (lineptr != NULL && img->xsize == 0)
+ {
+ if (*lineptr == '\0' || *lineptr == '#')
+ lineptr = fgets(line, sizeof(line), fp);
+ else if (isdigit(*lineptr))
+ {
+ img->xsize = atoi(lineptr);
+ while (isdigit(*lineptr))
+ lineptr ++;
+ }
+ else
+ lineptr ++;
+ }
+
+ while (lineptr != NULL && img->ysize == 0)
+ {
+ if (*lineptr == '\0' || *lineptr == '#')
+ lineptr = fgets(line, sizeof(line), fp);
+ else if (isdigit(*lineptr))
+ {
+ img->ysize = atoi(lineptr);
+ while (isdigit(*lineptr))
+ lineptr ++;
+ }
+ else
+ lineptr ++;
+ }
+
+ if (format != 1 && format != 4)
+ {
+ maxval = 0;
+
+ while (lineptr != NULL && maxval == 0)
+ {
+ if (*lineptr == '\0' || *lineptr == '#')
+ lineptr = fgets(line, sizeof(line), fp);
+ else if (isdigit(*lineptr))
+ {
+ maxval = atoi(lineptr);
+ while (isdigit(*lineptr))
+ lineptr ++;
+ }
+ else
+ lineptr ++;
+ }
+ }
+ else
+ maxval = 1;
+
+ if (format == 1 || format == 2 || format == 4 || format == 5)
+ img->colorspace = secondary;
+ else
+ img->colorspace = primary;
+
+ ImageSetMaxTiles(img, 0);
+
+ bpp = ImageGetDepth(img);
+ in = malloc(img->xsize * 3);
+ out = malloc(img->xsize * bpp);
+
+ /*
+ * Read the image file...
+ */
+
+ for (y = 0; y < img->ysize; y ++)
+ {
+ switch (format)
+ {
+ case 1 :
+ case 2 :
+ for (x = img->xsize, inptr = in; x > 0; x --, inptr ++)
+ if (fscanf(fp, "%d", &val) == 1)
+ *inptr = 255 * val / maxval;
+ break;
+
+ case 3 :
+ for (x = img->xsize, inptr = in; x > 0; x --, inptr += 3)
+ {
+ if (fscanf(fp, "%d", &val) == 1)
+ inptr[0] = 255 * val / maxval;
+ if (fscanf(fp, "%d", &val) == 1)
+ inptr[1] = 255 * val / maxval;
+ if (fscanf(fp, "%d", &val) == 1)
+ inptr[2] = 255 * val / maxval;
+ }
+ break;
+
+ case 4 :
+ fread(out, (img->xsize + 7) / 8, 1, fp);
+ for (x = img->xsize, inptr = in, outptr = out, bit = 128;
+ x > 0;
+ x --, inptr ++)
+ {
+ if (*outptr & bit)
+ *inptr = 255;
+ else
+ *inptr = 0;
+
+ if (bit > 1)
+ bit >>= 1;
+ else
+ {
+ bit = 128;
+ inptr ++;
+ }
+ }
+ break;
+
+ case 5 :
+ fread(in, img->xsize, 1, fp);
+ break;
+
+ case 6 :
+ fread(in, img->xsize, 3, fp);
+ break;
+ }
+
+ switch (format)
+ {
+ case 1 :
+ case 2 :
+ case 4 :
+ case 5 :
+ if (img->colorspace == IMAGE_WHITE)
+ {
+ if (lut)
+ ImageLut(in, img->xsize, lut);
+
+ ImagePutRow(img, 0, y, img->xsize, in);
+ }
+ else
+ {
+ switch (img->colorspace)
+ {
+ case IMAGE_RGB :
+ ImageWhiteToRGB(in, out, img->xsize);
+ break;
+ case IMAGE_BLACK :
+ ImageWhiteToBlack(in, out, img->xsize);
+ break;
+ case IMAGE_CMY :
+ ImageWhiteToCMY(in, out, img->xsize);
+ break;
+ case IMAGE_CMYK :
+ ImageWhiteToCMYK(in, out, img->xsize);
+ break;
+ }
+
+ if (lut)
+ ImageLut(out, img->xsize * bpp, lut);
+
+ ImagePutRow(img, 0, y, img->xsize, out);
+ }
+ break;
+
+ default :
+ if ((saturation != 100 || hue != 0) && bpp > 1)
+ ImageRGBAdjust(in, img->xsize, saturation, hue);
+
+ if (img->colorspace == IMAGE_RGB)
+ {
+ if (lut)
+ ImageLut(in, img->xsize * 3, lut);
+
+ ImagePutRow(img, 0, y, img->xsize, in);
+ }
+ else
+ {
+ switch (img->colorspace)
+ {
+ case IMAGE_WHITE :
+ ImageRGBToWhite(in, out, img->xsize);
+ break;
+ case IMAGE_BLACK :
+ ImageRGBToBlack(in, out, img->xsize);
+ break;
+ case IMAGE_CMY :
+ ImageRGBToCMY(in, out, img->xsize);
+ break;
+ case IMAGE_CMYK :
+ ImageRGBToCMYK(in, out, img->xsize);
+ break;
+ }
+
+ if (lut)
+ ImageLut(out, img->xsize * bpp, lut);
+
+ ImagePutRow(img, 0, y, img->xsize, out);
+ }
+ break;
+ }
+ }
+
+ free(in);
+ free(out);
+
+ fclose(fp);
+
+ return (0);
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/filter/image-sgi.c b/filter/image-sgi.c
new file mode 100644
index 000000000..59661b872
--- /dev/null
+++ b/filter/image-sgi.c
@@ -0,0 +1,267 @@
+/*
+ * "$Id$"
+ *
+ * SGI image file routines for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1993-1999 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * Contents:
+ *
+ * ImageReadSGI() - Read a SGI image file.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "image.h"
+#include "image-sgi.h"
+
+
+/*
+ * 'ImageReadSGI()' - Read a SGI image file.
+ */
+
+int /* O - Read status */
+ImageReadSGI(image_t *img, /* IO - Image */
+ FILE *fp, /* I - Image file */
+ int primary, /* I - Primary choice for colorspace */
+ int secondary, /* I - Secondary choice for colorspace */
+ int saturation,/* I - Color saturation (%) */
+ int hue, /* I - Color hue (degrees) */
+ ib_t *lut) /* I - Lookup table for gamma/brightness */
+{
+ int i, y; /* Looping vars */
+ int bpp; /* Bytes per pixel */
+ sgi_t *sgip; /* SGI image file */
+ ib_t *in, /* Input pixels */
+ *inptr, /* Current input pixel */
+ *out; /* Output pixels */
+ unsigned short *rows[4], /* Row pointers for image data */
+ *red,
+ *green,
+ *blue,
+ *gray,
+ *alpha;
+
+
+ /*
+ * Setup the SGI file...
+ */
+
+ sgip = sgiOpenFile(fp, SGI_READ, 0, 0, 0, 0, 0);
+
+ /*
+ * Get the image dimensions and load the output image...
+ */
+
+ if (sgip->zsize < 3)
+ img->colorspace = secondary;
+ else
+ img->colorspace = primary;
+
+ img->xsize = sgip->xsize;
+ img->ysize = sgip->ysize;
+
+ ImageSetMaxTiles(img, 0);
+
+ bpp = ImageGetDepth(img);
+ in = malloc(img->xsize * sgip->zsize);
+ out = malloc(img->xsize * bpp);
+
+ rows[0] = calloc(img->xsize * sgip->zsize, sizeof(unsigned short));
+ for (i = 1; i < sgip->zsize; i ++)
+ rows[i] = rows[0] + i * img->xsize;
+
+ /*
+ * Read the SGI image file...
+ */
+
+ for (y = 0; y < img->ysize; y ++)
+ {
+ for (i = 0; i < sgip->zsize; i ++)
+ sgiGetRow(sgip, rows[i], img->ysize - 1 - y, i);
+
+ switch (sgip->zsize)
+ {
+ case 1 :
+ if (sgip->bpp == 1)
+ for (i = img->xsize - 1, gray = rows[0], inptr = in;
+ i >= 0;
+ i --)
+ {
+ *inptr++ = *gray++;
+ }
+ else
+ for (i = img->xsize - 1, gray = rows[0], inptr = in;
+ i >= 0;
+ i --)
+ {
+ *inptr++ = (*gray++) / 256 + 128;
+ }
+ break;
+ case 2 :
+ if (sgip->bpp == 1)
+ for (i = img->xsize - 1, gray = rows[0], alpha = rows[1], inptr = in;
+ i >= 0;
+ i --)
+ {
+ *inptr++ = (*gray++) * (*alpha++) / 255;
+ }
+ else
+ for (i = img->xsize - 1, gray = rows[0], alpha = rows[1], inptr = in;
+ i >= 0;
+ i --)
+ {
+ *inptr++ = ((*gray++) / 256 + 128) * (*alpha++) / 32767;
+ }
+ break;
+ case 3 :
+ if (sgip->bpp == 1)
+ for (i = img->xsize - 1, red = rows[0], green = rows[1],
+ blue = rows[2], inptr = in;
+ i >= 0;
+ i --)
+ {
+ *inptr++ = *red++;
+ *inptr++ = *green++;
+ *inptr++ = *blue++;
+ }
+ else
+ for (i = img->xsize - 1, red = rows[0], green = rows[1],
+ blue = rows[2], inptr = in;
+ i >= 0;
+ i --)
+ {
+ *inptr++ = (*red++) / 256 + 128;
+ *inptr++ = (*green++) / 256 + 128;
+ *inptr++ = (*blue++) / 256 + 128;
+ }
+ break;
+ case 4 :
+ if (sgip->bpp == 1)
+ for (i = img->xsize - 1, red = rows[0], green = rows[1],
+ blue = rows[2], alpha = rows[3], inptr = in;
+ i >= 0;
+ i --)
+ {
+ *inptr++ = (*red++) * (*alpha) / 255;
+ *inptr++ = (*green++) * (*alpha) / 255;
+ *inptr++ = (*blue++) * (*alpha++) / 255;
+ }
+ else
+ for (i = img->xsize - 1, red = rows[0], green = rows[1],
+ blue = rows[2], inptr = in;
+ i >= 0;
+ i --)
+ {
+ *inptr++ = ((*red++) / 256 + 128) * (*alpha) / 32767;
+ *inptr++ = ((*green++) / 256 + 128) * (*alpha) / 32767;
+ *inptr++ = ((*blue++) / 256 + 128) * (*alpha++) / 32767;
+ }
+ break;
+ }
+
+ if (sgip->zsize < 3)
+ {
+ if (img->colorspace == IMAGE_WHITE)
+ {
+ if (lut)
+ ImageLut(in, img->xsize, lut);
+
+ ImagePutRow(img, 0, y, img->xsize, in);
+ }
+ else
+ {
+ switch (img->colorspace)
+ {
+ case IMAGE_RGB :
+ ImageWhiteToRGB(in, out, img->xsize);
+ break;
+ case IMAGE_BLACK :
+ ImageWhiteToBlack(in, out, img->xsize);
+ break;
+ case IMAGE_CMY :
+ ImageWhiteToCMY(in, out, img->xsize);
+ break;
+ case IMAGE_CMYK :
+ ImageWhiteToCMYK(in, out, img->xsize);
+ break;
+ }
+
+ if (lut)
+ ImageLut(out, img->xsize * bpp, lut);
+
+ ImagePutRow(img, 0, y, img->xsize, out);
+ }
+ }
+ else
+ {
+ if (img->colorspace == IMAGE_RGB)
+ {
+ if (saturation != 100 || hue != 0)
+ ImageRGBAdjust(in, img->xsize, saturation, hue);
+
+ if (lut)
+ ImageLut(in, img->xsize * 3, lut);
+
+ ImagePutRow(img, 0, y, img->xsize, in);
+ }
+ else
+ {
+ if ((saturation != 100 || hue != 0) && bpp > 1)
+ ImageRGBAdjust(in, img->xsize, saturation, hue);
+
+ switch (img->colorspace)
+ {
+ case IMAGE_WHITE :
+ ImageRGBToWhite(in, out, img->xsize);
+ break;
+ case IMAGE_BLACK :
+ ImageRGBToBlack(in, out, img->xsize);
+ break;
+ case IMAGE_CMY :
+ ImageRGBToCMY(in, out, img->xsize);
+ break;
+ case IMAGE_CMYK :
+ ImageRGBToCMYK(in, out, img->xsize);
+ break;
+ }
+
+ if (lut)
+ ImageLut(out, img->xsize * bpp, lut);
+
+ ImagePutRow(img, 0, y, img->xsize, out);
+ }
+ }
+ }
+
+ free(in);
+ free(out);
+ free(rows[0]);
+
+ sgiClose(sgip);
+
+ return (0);
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/filter/image-sgi.h b/filter/image-sgi.h
new file mode 100644
index 000000000..0b8220d32
--- /dev/null
+++ b/filter/image-sgi.h
@@ -0,0 +1,94 @@
+/*
+ * "$Id$"
+ *
+ * SGI image file format library definitions for the Common UNIX Printing
+ * System (CUPS).
+ *
+ * Copyright 1993-1999 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ */
+
+#ifndef _SGI_H_
+# define _SGI_H_
+
+# include <stdio.h>
+# include <stdlib.h>
+# include <string.h>
+
+# ifdef __cplusplus
+extern "C" {
+# endif
+
+
+/*
+ * Constants...
+ */
+
+# define SGI_MAGIC 474 /* Magic number in image file */
+
+# define SGI_READ 0 /* Read from an SGI image file */
+# define SGI_WRITE 1 /* Write to an SGI image file */
+
+# define SGI_COMP_NONE 0 /* No compression */
+# define SGI_COMP_RLE 1 /* Run-length encoding */
+# define SGI_COMP_ARLE 2 /* Agressive run-length encoding */
+
+
+/*
+ * Image structure...
+ */
+
+typedef struct
+{
+ FILE *file; /* Image file */
+ int mode, /* File open mode */
+ bpp, /* Bytes per pixel/channel */
+ comp; /* Compression */
+ unsigned short xsize, /* Width in pixels */
+ ysize, /* Height in pixels */
+ zsize; /* Number of channels */
+ long firstrow, /* File offset for first row */
+ nextrow, /* File offset for next row */
+ **table, /* Offset table for compression */
+ **length; /* Length table for compression */
+ unsigned short *arle_row; /* Advanced RLE compression buffer */
+ long arle_offset, /* Advanced RLE buffer offset */
+ arle_length; /* Advanced RLE buffer length */
+} sgi_t;
+
+
+/*
+ * Prototypes...
+ */
+
+extern int sgiClose(sgi_t *sgip);
+extern int sgiGetRow(sgi_t *sgip, unsigned short *row, int y, int z);
+extern sgi_t *sgiOpen(char *filename, int mode, int comp, int bpp,
+ int xsize, int ysize, int zsize);
+extern sgi_t *sgiOpenFile(FILE *file, int mode, int comp, int bpp,
+ int xsize, int ysize, int zsize);
+extern int sgiPutRow(sgi_t *sgip, unsigned short *row, int y, int z);
+
+# ifdef __cplusplus
+}
+# endif
+#endif /* !_SGI_H_ */
+
+/*
+ * End of "$Id$".
+ */
diff --git a/filter/image-sgilib.c b/filter/image-sgilib.c
new file mode 100644
index 000000000..c9f3d57d4
--- /dev/null
+++ b/filter/image-sgilib.c
@@ -0,0 +1,857 @@
+/*
+ * "$Id$"
+ *
+ * SGI image file format library routines for the Common UNIX Printing
+ * System (CUPS).
+ *
+ * Copyright 1993-1999 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * Contents:
+ *
+ * sgiClose() - Close an SGI image file.
+ * sgiGetRow() - Get a row of image data from a file.
+ * sgiOpen() - Open an SGI image file for reading or writing.
+ * sgiOpenFile() - Open an SGI image file for reading or writing.
+ * sgiPutRow() - Put a row of image data to a file.
+ * getlong() - Get a 32-bit big-endian integer.
+ * getshort() - Get a 16-bit big-endian integer.
+ * putlong() - Put a 32-bit big-endian integer.
+ * putshort() - Put a 16-bit big-endian integer.
+ * read_rle8() - Read 8-bit RLE data.
+ * read_rle16() - Read 16-bit RLE data.
+ * write_rle8() - Write 8-bit RLE data.
+ * write_rle16() - Write 16-bit RLE data.
+ */
+
+#include "image-sgi.h"
+
+
+/*
+ * Local functions...
+ */
+
+static int getlong(FILE *);
+static int getshort(FILE *);
+static int putlong(long, FILE *);
+static int putshort(unsigned short, FILE *);
+static int read_rle8(FILE *, unsigned short *, int);
+static int read_rle16(FILE *, unsigned short *, int);
+static int write_rle8(FILE *, unsigned short *, int);
+static int write_rle16(FILE *, unsigned short *, int);
+
+
+/*
+ * 'sgiClose()' - Close an SGI image file.
+ */
+
+int
+sgiClose(sgi_t *sgip) /* I - SGI image */
+{
+ int i; /* Return status */
+ long *offset; /* Looping var for offset table */
+
+
+ if (sgip == NULL)
+ return (-1);
+
+ if (sgip->mode == SGI_WRITE && sgip->comp != SGI_COMP_NONE)
+ {
+ /*
+ * Write the scanline offset table to the file...
+ */
+
+ fseek(sgip->file, 512, SEEK_SET);
+
+ for (i = sgip->ysize * sgip->zsize, offset = sgip->table[0];
+ i > 0;
+ i --, offset ++)
+ if (putlong(offset[0], sgip->file) < 0)
+ return (-1);
+
+ for (i = sgip->ysize * sgip->zsize, offset = sgip->length[0];
+ i > 0;
+ i --, offset ++)
+ if (putlong(offset[0], sgip->file) < 0)
+ return (-1);
+ }
+
+ if (sgip->table != NULL)
+ {
+ free(sgip->table[0]);
+ free(sgip->table);
+ }
+
+ if (sgip->length != NULL)
+ {
+ free(sgip->length[0]);
+ free(sgip->length);
+ }
+
+ if (sgip->comp == SGI_COMP_ARLE)
+ free(sgip->arle_row);
+
+ i = fclose(sgip->file);
+ free(sgip);
+
+ return (i);
+}
+
+
+/*
+ * 'sgiGetRow()' - Get a row of image data from a file.
+ */
+
+int
+sgiGetRow(sgi_t *sgip, /* I - SGI image */
+ unsigned short *row, /* O - Row to read */
+ int y, /* I - Line to read */
+ int z) /* I - Channel to read */
+{
+ int x; /* X coordinate */
+ long offset; /* File offset */
+
+
+ if (sgip == NULL ||
+ row == NULL ||
+ y < 0 || y >= sgip->ysize ||
+ z < 0 || z >= sgip->zsize)
+ return (-1);
+
+ switch (sgip->comp)
+ {
+ case SGI_COMP_NONE :
+ /*
+ * Seek to the image row - optimize buffering by only seeking if
+ * necessary...
+ */
+
+ offset = 512 + (y + z * sgip->ysize) * sgip->xsize * sgip->bpp;
+ if (offset != ftell(sgip->file))
+ fseek(sgip->file, offset, SEEK_SET);
+
+ if (sgip->bpp == 1)
+ {
+ for (x = sgip->xsize; x > 0; x --, row ++)
+ *row = getc(sgip->file);
+ }
+ else
+ {
+ for (x = sgip->xsize; x > 0; x --, row ++)
+ *row = getshort(sgip->file);
+ }
+ break;
+
+ case SGI_COMP_RLE :
+ offset = sgip->table[z][y];
+ if (offset != ftell(sgip->file))
+ fseek(sgip->file, offset, SEEK_SET);
+
+ if (sgip->bpp == 1)
+ return (read_rle8(sgip->file, row, sgip->xsize));
+ else
+ return (read_rle16(sgip->file, row, sgip->xsize));
+ }
+
+ return (0);
+}
+
+
+/*
+ * 'sgiOpen()' - Open an SGI image file for reading or writing.
+ */
+
+sgi_t *
+sgiOpen(char *filename, /* I - File to open */
+ int mode, /* I - Open mode (SGI_READ or SGI_WRITE) */
+ int comp, /* I - Type of compression */
+ int bpp, /* I - Bytes per pixel */
+ int xsize, /* I - Width of image in pixels */
+ int ysize, /* I - Height of image in pixels */
+ int zsize) /* I - Number of channels */
+{
+ sgi_t *sgip; /* New SGI image file */
+ FILE *file; /* Image file pointer */
+
+
+ if (mode == SGI_READ)
+ file = fopen(filename, "rb");
+ else
+ file = fopen(filename, "wb+");
+
+ if (file == NULL)
+ return (NULL);
+
+ if ((sgip = sgiOpenFile(file, mode, comp, bpp, xsize, ysize, zsize)) == NULL)
+ fclose(file);
+
+ return (sgip);
+}
+
+
+/*
+ * 'sgiOpenFile()' - Open an SGI image file for reading or writing.
+ */
+
+sgi_t *
+sgiOpenFile(FILE *file, /* I - File to open */
+ int mode, /* I - Open mode (SGI_READ or SGI_WRITE) */
+ int comp, /* I - Type of compression */
+ int bpp, /* I - Bytes per pixel */
+ int xsize, /* I - Width of image in pixels */
+ int ysize, /* I - Height of image in pixels */
+ int zsize) /* I - Number of channels */
+{
+ int i, j; /* Looping var */
+ char name[80]; /* Name of file in image header */
+ short magic; /* Magic number */
+ sgi_t *sgip; /* New image pointer */
+
+
+ if ((sgip = calloc(sizeof(sgi_t), 1)) == NULL)
+ return (NULL);
+
+ sgip->file = file;
+
+ switch (mode)
+ {
+ case SGI_READ :
+ sgip->mode = SGI_READ;
+
+ magic = getshort(sgip->file);
+ if (magic != SGI_MAGIC)
+ {
+ free(sgip);
+ return (NULL);
+ }
+
+ sgip->comp = getc(sgip->file);
+ sgip->bpp = getc(sgip->file);
+ getshort(sgip->file); /* Dimensions */
+ sgip->xsize = getshort(sgip->file);
+ sgip->ysize = getshort(sgip->file);
+ sgip->zsize = getshort(sgip->file);
+ getlong(sgip->file); /* Minimum pixel */
+ getlong(sgip->file); /* Maximum pixel */
+
+ if (sgip->comp)
+ {
+ /*
+ * This file is compressed; read the scanline tables...
+ */
+
+ fseek(sgip->file, 512, SEEK_SET);
+
+ sgip->table = calloc(sgip->zsize, sizeof(long *));
+ sgip->table[0] = calloc(sgip->ysize * sgip->zsize, sizeof(long));
+ for (i = 1; i < sgip->zsize; i ++)
+ sgip->table[i] = sgip->table[0] + i * sgip->ysize;
+
+ for (i = 0; i < sgip->zsize; i ++)
+ for (j = 0; j < sgip->ysize; j ++)
+ sgip->table[i][j] = getlong(sgip->file);
+ }
+ break;
+
+ case SGI_WRITE :
+ if (xsize < 1 ||
+ ysize < 1 ||
+ zsize < 1 ||
+ bpp < 1 || bpp > 2 ||
+ comp < SGI_COMP_NONE || comp > SGI_COMP_ARLE)
+ {
+ free(sgip);
+ return (NULL);
+ }
+
+ sgip->mode = SGI_WRITE;
+
+ putshort(SGI_MAGIC, sgip->file);
+ putc((sgip->comp = comp) != 0, sgip->file);
+ putc(sgip->bpp = bpp, sgip->file);
+ putshort(3, sgip->file); /* Dimensions */
+ putshort(sgip->xsize = xsize, sgip->file);
+ putshort(sgip->ysize = ysize, sgip->file);
+ putshort(sgip->zsize = zsize, sgip->file);
+ if (bpp == 1)
+ {
+ putlong(0, sgip->file); /* Minimum pixel */
+ putlong(255, sgip->file); /* Maximum pixel */
+ }
+ else
+ {
+ putlong(-32768, sgip->file); /* Minimum pixel */
+ putlong(32767, sgip->file); /* Maximum pixel */
+ }
+ putlong(0, sgip->file); /* Reserved */
+
+ memset(name, 0, sizeof(name));
+ fwrite(name, sizeof(name), 1, sgip->file);
+
+ for (i = 0; i < 102; i ++)
+ putlong(0, sgip->file);
+
+ switch (comp)
+ {
+ case SGI_COMP_NONE : /* No compression */
+ /*
+ * This file is uncompressed. To avoid problems with sparse files,
+ * we need to write blank pixels for the entire image...
+ */
+
+ if (bpp == 1)
+ {
+ for (i = xsize * ysize * zsize; i > 0; i --)
+ putc(0, sgip->file);
+ }
+ else
+ {
+ for (i = xsize * ysize * zsize; i > 0; i --)
+ putshort(0, sgip->file);
+ }
+ break;
+
+ case SGI_COMP_ARLE : /* Aggressive RLE */
+ sgip->arle_row = calloc(xsize, sizeof(unsigned short));
+ sgip->arle_offset = 0;
+
+ case SGI_COMP_RLE : /* Run-Length Encoding */
+ /*
+ * This file is compressed; write the (blank) scanline tables...
+ */
+
+ for (i = 2 * ysize * zsize; i > 0; i --)
+ putlong(0, sgip->file);
+
+ sgip->firstrow = ftell(sgip->file);
+ sgip->nextrow = ftell(sgip->file);
+ sgip->table = calloc(sgip->zsize, sizeof(long *));
+ sgip->table[0] = calloc(sgip->ysize * sgip->zsize, sizeof(long));
+ for (i = 1; i < sgip->zsize; i ++)
+ sgip->table[i] = sgip->table[0] + i * sgip->ysize;
+ sgip->length = calloc(sgip->zsize, sizeof(long *));
+ sgip->length[0] = calloc(sgip->ysize * sgip->zsize, sizeof(long));
+ for (i = 1; i < sgip->zsize; i ++)
+ sgip->length[i] = sgip->length[0] + i * sgip->ysize;
+ break;
+ }
+ break;
+
+ default :
+ free(sgip);
+ return (NULL);
+ }
+
+ return (sgip);
+}
+
+
+/*
+ * 'sgiPutRow()' - Put a row of image data to a file.
+ */
+
+int
+sgiPutRow(sgi_t *sgip, /* I - SGI image */
+ unsigned short *row, /* I - Row to write */
+ int y, /* I - Line to write */
+ int z) /* I - Channel to write */
+{
+ int x; /* X coordinate */
+ long offset; /* File offset */
+
+
+ if (sgip == NULL ||
+ row == NULL ||
+ y < 0 || y >= sgip->ysize ||
+ z < 0 || z >= sgip->zsize)
+ return (-1);
+
+ switch (sgip->comp)
+ {
+ case SGI_COMP_NONE :
+ /*
+ * Seek to the image row - optimize buffering by only seeking if
+ * necessary...
+ */
+
+ offset = 512 + (y + z * sgip->ysize) * sgip->xsize * sgip->bpp;
+ if (offset != ftell(sgip->file))
+ fseek(sgip->file, offset, SEEK_SET);
+
+ if (sgip->bpp == 1)
+ {
+ for (x = sgip->xsize; x > 0; x --, row ++)
+ putc(*row, sgip->file);
+ }
+ else
+ {
+ for (x = sgip->xsize; x > 0; x --, row ++)
+ putshort(*row, sgip->file);
+ }
+ break;
+
+ case SGI_COMP_ARLE :
+ if (sgip->table[z][y] != 0)
+ return (-1);
+
+ /*
+ * First check the last row written...
+ */
+
+ if (sgip->arle_offset > 0)
+ {
+ for (x = 0; x < sgip->xsize; x ++)
+ if (row[x] != sgip->arle_row[x])
+ break;
+
+ if (x == sgip->xsize)
+ {
+ sgip->table[z][y] = sgip->arle_offset;
+ sgip->length[z][y] = sgip->arle_length;
+ return (0);
+ }
+ }
+
+ /*
+ * If that didn't match, search all the previous rows...
+ */
+
+ fseek(sgip->file, sgip->firstrow, SEEK_SET);
+
+ if (sgip->bpp == 1)
+ {
+ for (;;)
+ {
+ sgip->arle_offset = ftell(sgip->file);
+ if ((sgip->arle_length = read_rle8(sgip->file, sgip->arle_row, sgip->xsize)) < 0)
+ {
+ x = 0;
+ break;
+ }
+
+ if (memcmp(row, sgip->arle_row, sgip->xsize * sizeof(unsigned short)) == 0)
+ {
+ x = sgip->xsize;
+ break;
+ }
+ }
+ }
+ else
+ {
+ for (;;)
+ {
+ sgip->arle_offset = ftell(sgip->file);
+ if ((sgip->arle_length = read_rle16(sgip->file, sgip->arle_row, sgip->xsize)) < 0)
+ {
+ x = 0;
+ break;
+ }
+
+ if (memcmp(row, sgip->arle_row, sgip->xsize * sizeof(unsigned short)) == 0)
+ {
+ x = sgip->xsize;
+ break;
+ }
+ }
+ }
+
+ if (x == sgip->xsize)
+ {
+ sgip->table[z][y] = sgip->arle_offset;
+ sgip->length[z][y] = sgip->arle_length;
+ return (0);
+ }
+ else
+ fseek(sgip->file, 0, SEEK_END); /* Clear EOF */
+
+ case SGI_COMP_RLE :
+ if (sgip->table[z][y] != 0)
+ return (-1);
+
+ offset = sgip->table[z][y] = sgip->nextrow;
+
+ if (offset != ftell(sgip->file))
+ fseek(sgip->file, offset, SEEK_SET);
+
+ if (sgip->bpp == 1)
+ x = write_rle8(sgip->file, row, sgip->xsize);
+ else
+ x = write_rle16(sgip->file, row, sgip->xsize);
+
+ if (sgip->comp == SGI_COMP_ARLE)
+ {
+ sgip->arle_offset = offset;
+ sgip->arle_length = x;
+ memcpy(sgip->arle_row, row, sgip->xsize * sizeof(unsigned short));
+ }
+
+ sgip->nextrow = ftell(sgip->file);
+ sgip->length[z][y] = x;
+
+ return (x);
+ }
+
+ return (0);
+}
+
+
+/*
+ * 'getlong()' - Get a 32-bit big-endian integer.
+ */
+
+static int
+getlong(FILE *fp) /* I - File to read from */
+{
+ unsigned char b[4];
+
+
+ fread(b, 4, 1, fp);
+ return ((b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3]);
+}
+
+
+/*
+ * 'getshort()' - Get a 16-bit big-endian integer.
+ */
+
+static int
+getshort(FILE *fp) /* I - File to read from */
+{
+ unsigned char b[2];
+
+
+ fread(b, 2, 1, fp);
+ return ((b[0] << 8) | b[1]);
+}
+
+
+/*
+ * 'putlong()' - Put a 32-bit big-endian integer.
+ */
+
+static int
+putlong(long n, /* I - Long to write */
+ FILE *fp) /* I - File to write to */
+{
+ if (putc(n >> 24, fp) == EOF)
+ return (EOF);
+ if (putc(n >> 16, fp) == EOF)
+ return (EOF);
+ if (putc(n >> 8, fp) == EOF)
+ return (EOF);
+ if (putc(n, fp) == EOF)
+ return (EOF);
+ else
+ return (0);
+}
+
+
+/*
+ * 'putshort()' - Put a 16-bit big-endian integer.
+ */
+
+static int
+putshort(unsigned short n, /* I - Short to write */
+ FILE *fp) /* I - File to write to */
+{
+ if (putc(n >> 8, fp) == EOF)
+ return (EOF);
+ if (putc(n, fp) == EOF)
+ return (EOF);
+ else
+ return (0);
+}
+
+
+/*
+ * 'read_rle8()' - Read 8-bit RLE data.
+ */
+
+static int
+read_rle8(FILE *fp, /* I - File to read from */
+ unsigned short *row, /* O - Data */
+ int xsize) /* I - Width of data in pixels */
+{
+ int i, /* Looping var */
+ ch, /* Current character */
+ count, /* RLE count */
+ length; /* Number of bytes read... */
+
+
+ length = 0;
+
+ while (xsize > 0)
+ {
+ if ((ch = getc(fp)) == EOF)
+ return (-1);
+ length ++;
+
+ count = ch & 127;
+ if (count == 0)
+ break;
+
+ if (ch & 128)
+ {
+ for (i = 0; i < count; i ++, row ++, xsize --, length ++)
+ *row = getc(fp);
+ }
+ else
+ {
+ ch = getc(fp);
+ length ++;
+ for (i = 0; i < count; i ++, row ++, xsize --)
+ *row = ch;
+ }
+ }
+
+ return (xsize > 0 ? -1 : length);
+}
+
+
+/*
+ * 'read_rle16()' - Read 16-bit RLE data.
+ */
+
+static int
+read_rle16(FILE *fp, /* I - File to read from */
+ unsigned short *row, /* O - Data */
+ int xsize)/* I - Width of data in pixels */
+{
+ int i, /* Looping var */
+ ch, /* Current character */
+ count, /* RLE count */
+ length; /* Number of bytes read... */
+
+
+ length = 0;
+
+ while (xsize > 0)
+ {
+ if ((ch = getshort(fp)) == EOF)
+ return (-1);
+ length ++;
+
+ count = ch & 127;
+ if (count == 0)
+ break;
+
+ if (ch & 128)
+ {
+ for (i = 0; i < count; i ++, row ++, xsize --, length ++)
+ *row = getshort(fp);
+ }
+ else
+ {
+ ch = getshort(fp);
+ length ++;
+ for (i = 0; i < count; i ++, row ++, xsize --)
+ *row = ch;
+ }
+ }
+
+ return (xsize > 0 ? -1 : length * 2);
+}
+
+
+/*
+ * 'write_rle8()' - Write 8-bit RLE data.
+ */
+
+static int
+write_rle8(FILE *fp, /* I - File to write to */
+ unsigned short *row, /* I - Data */
+ int xsize)/* I - Width of data in pixels */
+{
+ int length,
+ count,
+ i,
+ x;
+ unsigned short *start,
+ repeat;
+
+
+ for (x = xsize, length = 0; x > 0;)
+ {
+ start = row;
+ row += 2;
+ x -= 2;
+
+ while (x > 0 && (row[-2] != row[-1] || row[-1] != row[0]))
+ {
+ row ++;
+ x --;
+ }
+
+ row -= 2;
+ x += 2;
+
+ count = row - start;
+ while (count > 0)
+ {
+ i = count > 126 ? 126 : count;
+ count -= i;
+
+ if (putc(128 | i, fp) == EOF)
+ return (-1);
+ length ++;
+
+ while (i > 0)
+ {
+ if (putc(*start, fp) == EOF)
+ return (-1);
+ start ++;
+ i --;
+ length ++;
+ }
+ }
+
+ if (x <= 0)
+ break;
+
+ start = row;
+ repeat = row[0];
+
+ row ++;
+ x --;
+
+ while (x > 0 && *row == repeat)
+ {
+ row ++;
+ x --;
+ }
+
+ count = row - start;
+ while (count > 0)
+ {
+ i = count > 126 ? 126 : count;
+ count -= i;
+
+ if (putc(i, fp) == EOF)
+ return (-1);
+ length ++;
+
+ if (putc(repeat, fp) == EOF)
+ return (-1);
+ length ++;
+ }
+ }
+
+ length ++;
+
+ if (putc(0, fp) == EOF)
+ return (-1);
+ else
+ return (length);
+}
+
+
+/*
+ * 'write_rle16()' - Write 16-bit RLE data.
+ */
+
+static int
+write_rle16(FILE *fp, /* I - File to write to */
+ unsigned short *row, /* I - Data */
+ int xsize)/* I - Width of data in pixels */
+{
+ int length,
+ count,
+ i,
+ x;
+ unsigned short *start,
+ repeat;
+
+
+ for (x = xsize, length = 0; x > 0;)
+ {
+ start = row;
+ row += 2;
+ x -= 2;
+
+ while (x > 0 && (row[-2] != row[-1] || row[-1] != row[0]))
+ {
+ row ++;
+ x --;
+ }
+
+ row -= 2;
+ x += 2;
+
+ count = row - start;
+ while (count > 0)
+ {
+ i = count > 126 ? 126 : count;
+ count -= i;
+
+ if (putshort(128 | i, fp) == EOF)
+ return (-1);
+ length ++;
+
+ while (i > 0)
+ {
+ if (putshort(*start, fp) == EOF)
+ return (-1);
+ start ++;
+ i --;
+ length ++;
+ }
+ }
+
+ if (x <= 0)
+ break;
+
+ start = row;
+ repeat = row[0];
+
+ row ++;
+ x --;
+
+ while (x > 0 && *row == repeat)
+ {
+ row ++;
+ x --;
+ }
+
+ count = row - start;
+ while (count > 0)
+ {
+ i = count > 126 ? 126 : count;
+ count -= i;
+
+ if (putshort(i, fp) == EOF)
+ return (-1);
+ length ++;
+
+ if (putshort(repeat, fp) == EOF)
+ return (-1);
+ length ++;
+ }
+ }
+
+ length ++;
+
+ if (putshort(0, fp) == EOF)
+ return (-1);
+ else
+ return (2 * length);
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/filter/image-sun.c b/filter/image-sun.c
new file mode 100644
index 000000000..f155dff26
--- /dev/null
+++ b/filter/image-sun.c
@@ -0,0 +1,376 @@
+/*
+ * "$Id$"
+ *
+ * Sun Raster image file routines for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1993-1999 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * Contents:
+ *
+ * ImageReadSunRaster() - Read a SunRaster image file.
+ * read_unsigned() - Read a 32-bit unsigned integer.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "image.h"
+
+
+#define RAS_MAGIC 0x59a66a95
+
+ /* Sun supported ras_type's */
+#define RT_OLD 0 /* Raw pixrect image in 68000 byte order */
+#define RT_STANDARD 1 /* Raw pixrect image in 68000 byte order */
+#define RT_BYTE_ENCODED 2 /* Run-length compression of bytes */
+#define RT_FORMAT_RGB 3 /* XRGB or RGB instead of XBGR or BGR */
+#define RT_EXPERIMENTAL 0xffff /* Reserved for testing */
+
+ /* Sun registered ras_maptype's */
+#define RMT_RAW 2
+ /* Sun supported ras_maptype's */
+#define RMT_NONE 0 /* ras_maplength is expected to be 0 */
+#define RMT_EQUAL_RGB 1 /* red[ras_maplength/3],green[],blue[] */
+
+#define RAS_RLE 0x80
+
+/*
+ * NOTES:
+ * Each line of the image is rounded out to a multiple of 16 bits.
+ * This corresponds to the rounding convention used by the memory pixrect
+ * package (/usr/include/pixrect/memvar.h) of the SunWindows system.
+ * The ras_encoding field (always set to 0 by Sun's supported software)
+ * was renamed to ras_length in release 2.0. As a result, rasterfiles
+ * of type 0 generated by the old software claim to have 0 length; for
+ * compatibility, code reading rasterfiles must be prepared to compute the
+ * true length from the width, height, and depth fields.
+ */
+
+/*
+ * Local functions...
+ */
+
+static unsigned read_unsigned(FILE *fp);
+
+
+/*
+ * 'ImageReadSunRaster()' - Read a SunRaster image file.
+ */
+
+int /* O - Read status */
+ImageReadSunRaster(image_t *img, /* IO - Image */
+ FILE *fp, /* I - Image file */
+ int primary, /* I - Primary choice for colorspace */
+ int secondary, /* I - Secondary choice for colorspace */
+ int saturation, /* I - Color saturation (%) */
+ int hue, /* I - Color hue (degrees) */
+ ib_t *lut) /* I - Lookup table for gamma/brightness */
+{
+ int i, x, y,
+ bpp, /* Bytes per pixel */
+ scanwidth,
+ run_count,
+ run_value;
+ ib_t *in,
+ *out,
+ *scanline,
+ *scanptr,
+ *p,
+ bit;
+ unsigned ras_depth, /* depth (1, 8, or 24 bits) of pixel */
+ ras_type, /* type of file; see RT_* below */
+ ras_maplength; /* length (bytes) of following map */
+ unsigned char cmap[3][256]; /* colormap */
+
+
+ /*
+ * Read the header; we already know that this is a raster file (ImageOpen
+ * checks this) so we don't need to check the magic number again.
+ */
+
+ read_unsigned(fp); /* Skip magic */
+ img->xsize = read_unsigned(fp);
+ img->ysize = read_unsigned(fp);
+ ras_depth = read_unsigned(fp);
+ /* ras_length */read_unsigned(fp);
+ ras_type = read_unsigned(fp);
+ /* ras_maptype*/read_unsigned(fp);
+ ras_maplength = read_unsigned(fp);
+
+ if (ras_maplength > 0)
+ {
+ fread(cmap[0], 1, ras_maplength / 3, fp);
+ fread(cmap[1], 1, ras_maplength / 3, fp);
+ fread(cmap[2], 1, ras_maplength / 3, fp);
+ }
+
+ /*
+ * Compute the width of each line and allocate memory as needed...
+ */
+
+ scanwidth = (img->xsize * ras_depth + 7) / 8;
+ if (scanwidth & 1)
+ scanwidth ++;
+
+ if (ras_depth < 24 && ras_maplength == 0)
+ {
+ img->colorspace = secondary;
+ in = malloc(img->xsize + 1);
+ }
+ else
+ {
+ img->colorspace = primary;
+ in = malloc(img->xsize * 3 + 1);
+ }
+
+ bpp = ImageGetDepth(img);
+ out = malloc(img->xsize * bpp);
+ scanline = malloc(scanwidth);
+ run_count = 0;
+
+ for (y = 0; y < img->ysize; y ++)
+ {
+ if (ras_depth != 8 || ras_maplength > 0)
+ p = scanline;
+ else
+ p = in;
+
+ if (ras_type != RT_BYTE_ENCODED)
+ fread(p, scanwidth, 1, fp);
+ else
+ {
+ for (i = scanwidth; i > 0; i --, p ++)
+ {
+ if (run_count > 0)
+ {
+ *p = run_value;
+ run_count --;
+ }
+ else
+ {
+ run_value = getc(fp);
+
+ if (run_value == RAS_RLE)
+ {
+ run_count = getc(fp);
+ if (run_count == 0)
+ *p = RAS_RLE;
+ else
+ run_value = *p = getc(fp);
+ }
+ else
+ *p = run_value;
+ }
+ }
+ }
+
+ if (ras_depth == 1 && ras_maplength == 0)
+ {
+ /*
+ * 1-bit B&W image...
+ */
+
+ for (x = img->xsize, bit = 128, scanptr = scanline, p = in;
+ x > 0;
+ x --, p ++)
+ {
+ if (*scanptr & bit)
+ *p = 255;
+ else
+ *p = 0;
+
+ if (bit > 1)
+ {
+ bit = 128;
+ scanptr ++;
+ }
+ else
+ bit >>= 1;
+ }
+ }
+ else if (ras_depth == 1)
+ {
+ /*
+ * 1-bit colormapped image...
+ */
+
+ for (x = img->xsize, bit = 128, scanptr = scanline, p = in;
+ x > 0;
+ x --)
+ {
+ if (*scanptr & bit)
+ {
+ *p++ = cmap[0][1];
+ *p++ = cmap[1][1];
+ *p++ = cmap[2][1];
+ }
+ else
+ {
+ *p++ = cmap[0][0];
+ *p++ = cmap[1][0];
+ *p++ = cmap[2][0];
+ }
+
+ if (bit > 1)
+ {
+ bit = 128;
+ scanptr ++;
+ }
+ else
+ bit >>= 1;
+ }
+ }
+ else if (ras_depth == 8 && ras_maplength > 0)
+ {
+ /*
+ * 8-bit colormapped image.
+ */
+
+ for (x = img->xsize, scanptr = scanline, p = in;
+ x > 0;
+ x --)
+ {
+ *p++ = cmap[0][*scanptr];
+ *p++ = cmap[1][*scanptr];
+ *p++ = cmap[2][*scanptr++];
+ }
+ }
+ else if (ras_depth == 24 && ras_type != RT_FORMAT_RGB)
+ {
+ /*
+ * Convert BGR to RGB...
+ */
+
+ for (x = img->xsize, scanptr = scanline, p = in;
+ x > 0;
+ x --, scanptr += 3)
+ {
+ *p++ = scanptr[2];
+ *p++ = scanptr[1];
+ *p++ = scanptr[0];
+ }
+ }
+
+ if (bpp == 1)
+ {
+ if (img->colorspace == IMAGE_WHITE)
+ {
+ if (lut)
+ ImageLut(in, img->xsize, lut);
+
+ ImagePutRow(img, 0, y, img->xsize, in);
+ }
+ else
+ {
+ switch (img->colorspace)
+ {
+ case IMAGE_RGB :
+ ImageWhiteToRGB(in, out, img->xsize);
+ break;
+ case IMAGE_BLACK :
+ ImageWhiteToBlack(in, out, img->xsize);
+ break;
+ case IMAGE_CMY :
+ ImageWhiteToCMY(in, out, img->xsize);
+ break;
+ case IMAGE_CMYK :
+ ImageWhiteToCMYK(in, out, img->xsize);
+ break;
+ }
+
+ if (lut)
+ ImageLut(out, img->xsize * bpp, lut);
+
+ ImagePutRow(img, 0, y, img->xsize, out);
+ }
+ }
+ else
+ {
+ if (img->colorspace == IMAGE_RGB)
+ {
+ if (saturation != 100 || hue != 0)
+ ImageRGBAdjust(in, img->xsize, saturation, hue);
+
+ if (lut)
+ ImageLut(in, img->xsize * 3, lut);
+
+ ImagePutRow(img, 0, y, img->xsize, in);
+ }
+ else
+ {
+ if ((saturation != 100 || hue != 0) && bpp > 1)
+ ImageRGBAdjust(in, img->xsize, saturation, hue);
+
+ switch (img->colorspace)
+ {
+ case IMAGE_WHITE :
+ ImageRGBToWhite(in, out, img->xsize);
+ break;
+ case IMAGE_BLACK :
+ ImageRGBToBlack(in, out, img->xsize);
+ break;
+ case IMAGE_CMY :
+ ImageRGBToCMY(in, out, img->xsize);
+ break;
+ case IMAGE_CMYK :
+ ImageRGBToCMYK(in, out, img->xsize);
+ break;
+ }
+
+ if (lut)
+ ImageLut(out, img->xsize * bpp, lut);
+
+ ImagePutRow(img, 0, y, img->xsize, out);
+ }
+ }
+ }
+
+ free(scanline);
+ free(in);
+ free(out);
+
+ fclose(fp);
+
+ return (0);
+}
+
+
+/*
+ * 'read_unsigned()' - Read a 32-bit unsigned integer.
+ */
+
+static unsigned /* O - Integer from file */
+read_unsigned(FILE *fp) /* I - File to read from */
+{
+ unsigned v; /* Integer from file */
+
+
+ v = getc(fp);
+ v = (v << 8) | getc(fp);
+ v = (v << 8) | getc(fp);
+ v = (v << 8) | getc(fp);
+
+ return (v);
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/filter/image-tiff.c b/filter/image-tiff.c
new file mode 100644
index 000000000..36455a71f
--- /dev/null
+++ b/filter/image-tiff.c
@@ -0,0 +1,1622 @@
+/*
+ * "$Id$"
+ *
+ * TIFF file routines for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1993-1999 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * Contents:
+ *
+ * ImageReadTIFF() - Read a TIFF image file.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "image.h"
+
+#ifdef HAVE_LIBTIFF
+# include <tiff.h> /* TIFF image definitions */
+# include <tiffio.h>
+
+
+/*
+ * 'ImageReadTIFF()' - Read a TIFF image file.
+ */
+
+int /* O - Read status */
+ImageReadTIFF(image_t *img, /* IO - Image */
+ FILE *fp, /* I - Image file */
+ int primary, /* I - Primary choice for colorspace */
+ int secondary, /* I - Secondary choice for colorspace */
+ int saturation, /* I - Color saturation (%) */
+ int hue, /* I - Color hue (degrees) */
+ ib_t *lut) /* I - Lookup table for gamma/brightness */
+{
+ TIFF *tif; /* TIFF file */
+ uint32 width, height; /* Size of image */
+ uint16 photometric, /* Colorspace */
+ orientation, /* Orientation */
+ resunit, /* Units for resolution */
+ samples, /* Number of samples/pixel */
+ bits, /* Number of bits/pixel */
+ inkset; /* Ink set for color separations */
+ float xres, /* Horizontal resolution */
+ yres; /* Vertical resolution */
+ uint16 *redcmap, /* Red colormap information */
+ *greencmap, /* Green colormap information */
+ *bluecmap; /* Blue colormap information */
+ int c, /* Color index */
+ num_colors, /* Number of colors */
+ bpp, /* Bytes per pixel */
+ x, y, /* Current x & y */
+ xstart, ystart, /* Starting x & y */
+ xdir, ydir, /* X & y direction */
+ xcount, ycount, /* X & Y counters */
+ pstep, /* Pixel step (= bpp or -2 * bpp) */
+ scanwidth, /* Width of scanline */
+ r, g, b, k, /* Red, green, blue, and black values */
+ alpha; /* Image includes alpha? */
+ ib_t *in, /* Input buffer */
+ *out, /* Output buffer */
+ *p, /* Pointer into buffer */
+ *scanline, /* Scanline buffer */
+ *scanptr, /* Pointer into scanline buffer */
+ bit, /* Current bit */
+ pixel, /* Current pixel */
+ zero, /* Zero value (bitmaps) */
+ one; /* One value (bitmaps) */
+
+
+ /*
+ * Open the TIFF file and get the required parameters...
+ */
+
+#ifdef __hpux
+ lseek(fileno(fp), 0, SEEK_SET); /* Work around "feature" in HP-UX stdio */
+#endif /* __hpux */
+
+ if ((tif = TIFFFdOpen(fileno(fp), "", "r")) == NULL)
+ {
+ fclose(fp);
+ return (-1);
+ }
+
+ if (!TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &width) ||
+ !TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &height) ||
+ !TIFFGetField(tif, TIFFTAG_PHOTOMETRIC, &photometric) ||
+ !TIFFGetField(tif, TIFFTAG_SAMPLESPERPIXEL, &samples) ||
+ !TIFFGetField(tif, TIFFTAG_BITSPERSAMPLE, &bits))
+ {
+ TIFFClose(tif);
+ fclose(fp);
+ return (-1);
+ }
+
+ /*
+ * Get the image orientation...
+ */
+
+ if (!TIFFGetField(tif, TIFFTAG_ORIENTATION, &orientation))
+ orientation = 0;
+
+ /*
+ * Get the image resolution...
+ */
+
+ if (TIFFGetField(tif, TIFFTAG_XRESOLUTION, &xres) &&
+ TIFFGetField(tif, TIFFTAG_YRESOLUTION, &yres) &&
+ TIFFGetField(tif, TIFFTAG_RESOLUTIONUNIT, &resunit))
+ {
+ if (resunit == RESUNIT_INCH)
+ {
+ img->xppi = xres;
+ img->yppi = yres;
+ }
+ else if (resunit == RESUNIT_CENTIMETER)
+ {
+ img->xppi = xres * 2.54;
+ img->yppi = yres * 2.54;
+ }
+ else
+ {
+ img->xppi = xres * 0.0254;
+ img->yppi = yres * 0.0254;
+ }
+ }
+
+ /*
+ * See if the image has an alpha channel...
+ */
+
+ if (samples == 2 || (samples == 4 && photometric == PHOTOMETRIC_RGB))
+ alpha = 1;
+ else
+ alpha = 0;
+
+ /*
+ * Setup the image size and colorspace...
+ */
+
+ img->xsize = width;
+ img->ysize = height;
+ if (photometric == PHOTOMETRIC_MINISBLACK ||
+ photometric == PHOTOMETRIC_MINISWHITE)
+ img->colorspace = secondary;
+ else
+ img->colorspace = primary;
+
+ bpp = ImageGetDepth(img);
+
+ ImageSetMaxTiles(img, 0);
+
+ /*
+ * Set the X & Y start and direction according to the image orientation...
+ */
+
+ switch (orientation)
+ {
+ case ORIENTATION_TOPRIGHT :
+ case ORIENTATION_RIGHTTOP :
+ xstart = img->xsize - 1;
+ xdir = -1;
+ ystart = 0;
+ ydir = 1;
+ break;
+ default :
+ case ORIENTATION_TOPLEFT :
+ case ORIENTATION_LEFTTOP :
+ xstart = 0;
+ xdir = 1;
+ ystart = 0;
+ ydir = 1;
+ break;
+ case ORIENTATION_BOTLEFT :
+ case ORIENTATION_LEFTBOT :
+ xstart = 0;
+ xdir = 1;
+ ystart = img->ysize - 1;
+ ydir = -1;
+ break;
+ case ORIENTATION_BOTRIGHT :
+ case ORIENTATION_RIGHTBOT :
+ xstart = img->xsize - 1;
+ xdir = -1;
+ ystart = img->ysize - 1;
+ ydir = -1;
+ break;
+ }
+
+ /*
+ * Allocate a scanline buffer...
+ */
+
+ scanwidth = TIFFScanlineSize(tif);
+ scanline = _TIFFmalloc(scanwidth);
+
+ /*
+ * Allocate input and output buffers...
+ */
+
+ if (orientation < ORIENTATION_LEFTTOP)
+ {
+ if (samples > 1 || photometric == PHOTOMETRIC_PALETTE)
+ pstep = xdir * 3;
+ else
+ pstep = xdir;
+
+ in = malloc(img->xsize * 3 + 3);
+ out = malloc(img->xsize * bpp);
+ }
+ else
+ {
+ if (samples > 1 || photometric == PHOTOMETRIC_PALETTE)
+ pstep = ydir * 3;
+ else
+ pstep = ydir;
+
+ in = malloc(img->ysize * 3 + 3);
+ out = malloc(img->ysize * bpp);
+ }
+
+ /*
+ * Read the image. This is greatly complicated by the fact that TIFF
+ * supports literally hundreds of different colorspaces and orientations,
+ * each which must be handled separately...
+ */
+
+ switch (photometric)
+ {
+ case PHOTOMETRIC_MINISWHITE :
+ case PHOTOMETRIC_MINISBLACK :
+ if (photometric == PHOTOMETRIC_MINISBLACK)
+ {
+ zero = 0;
+ one = 255;
+ }
+ else
+ {
+ zero = 255;
+ one = 0;
+ }
+
+ if (orientation < ORIENTATION_LEFTTOP)
+ {
+ /*
+ * Row major order...
+ */
+
+ for (y = ystart, ycount = img->ysize;
+ ycount > 0;
+ ycount --, y += ydir)
+ {
+ if (bits == 1)
+ {
+ TIFFReadScanline(tif, scanline, y, 0);
+ for (xcount = img->xsize, scanptr = scanline, p = in + xstart, bit = 128;
+ xcount > 0;
+ xcount --, p += pstep)
+ {
+ if (*scanptr & bit)
+ *p = one;
+ else
+ *p = zero;
+
+ if (bit > 1)
+ bit >>= 1;
+ else
+ {
+ bit = 128;
+ scanptr ++;
+ }
+ }
+ }
+ else if (bits == 2)
+ {
+ TIFFReadScanline(tif, scanline, y, 0);
+ for (xcount = img->xsize, scanptr = scanline, p = in + xstart, bit = 0xc0;
+ xcount > 0;
+ xcount --, p += pstep)
+ {
+ pixel = *scanptr & bit;
+ while (pixel > 3)
+ pixel >>= 2;
+ *p = (255 * pixel / 3) ^ zero;
+
+ if (bit > 3)
+ bit >>= 2;
+ else
+ {
+ bit = 0xc0;
+ scanptr ++;
+ }
+ }
+ }
+ else if (bits == 4)
+ {
+ TIFFReadScanline(tif, scanline, y, 0);
+ for (xcount = img->xsize, scanptr = scanline, p = in + xstart, bit = 0xf0;
+ xcount > 0;
+ xcount --, p += pstep)
+ {
+ if (bit == 0xf0)
+ {
+ *p = (255 * ((*scanptr & 0xf0) >> 4) / 15) ^ zero;
+ bit = 0x0f;
+ }
+ else
+ {
+ *p = (255 * (*scanptr & 0x0f) / 15) ^ zero;
+ bit = 0xf0;
+ scanptr ++;
+ }
+ }
+ }
+ else if (xdir < 0 || zero || alpha)
+ {
+ TIFFReadScanline(tif, scanline, y, 0);
+
+ if (alpha)
+ {
+ if (zero)
+ {
+ for (xcount = img->xsize, p = in + xstart, scanptr = scanline;
+ xcount > 0;
+ xcount --, p += pstep, scanptr += 2)
+ *p = (scanptr[1] * (255 - scanptr[0]) +
+ (255 - scanptr[1]) * 255) / 255;
+ }
+ else
+ {
+ for (xcount = img->xsize, p = in + xstart, scanptr = scanline;
+ xcount > 0;
+ xcount --, p += pstep, scanptr += 2)
+ *p = (scanptr[1] * scanptr[0] +
+ (255 - scanptr[1]) * 255) / 255;
+ }
+ }
+ else
+ {
+ if (zero)
+ {
+ for (xcount = img->xsize, p = in + xstart, scanptr = scanline;
+ xcount > 0;
+ xcount --, p += pstep, scanptr ++)
+ *p = 255 - *scanptr;
+ }
+ else
+ {
+ for (xcount = img->xsize, p = in + xstart, scanptr = scanline;
+ xcount > 0;
+ xcount --, p += pstep, scanptr ++)
+ *p = *scanptr;
+ }
+ }
+ }
+ else
+ TIFFReadScanline(tif, in, y, 0);
+
+ if (img->colorspace == IMAGE_WHITE)
+ {
+ if (lut)
+ ImageLut(in, img->xsize, lut);
+
+ ImagePutRow(img, 0, y, img->xsize, in);
+ }
+ else
+ {
+ switch (img->colorspace)
+ {
+ case IMAGE_RGB :
+ ImageWhiteToRGB(in, out, img->xsize);
+ break;
+ case IMAGE_BLACK :
+ ImageWhiteToBlack(in, out, img->xsize);
+ break;
+ case IMAGE_CMY :
+ ImageWhiteToCMY(in, out, img->xsize);
+ break;
+ case IMAGE_CMYK :
+ ImageWhiteToCMYK(in, out, img->xsize);
+ break;
+ }
+
+ if (lut)
+ ImageLut(out, img->xsize * bpp, lut);
+
+ ImagePutRow(img, 0, y, img->xsize, out);
+ }
+ }
+ }
+ else
+ {
+ /*
+ * Column major order...
+ */
+
+ for (x = xstart, xcount = img->xsize;
+ xcount > 0;
+ xcount --, x += xdir)
+ {
+ if (bits == 1)
+ {
+ TIFFReadScanline(tif, scanline, x, 0);
+ for (ycount = img->ysize, scanptr = scanline, p = in + ystart, bit = 128;
+ ycount > 0;
+ ycount --, p += ydir)
+ {
+ if (*scanptr & bit)
+ *p = one;
+ else
+ *p = zero;
+
+ if (bit > 1)
+ bit >>= 1;
+ else
+ {
+ bit = 128;
+ scanptr ++;
+ }
+ }
+ }
+ else if (bits == 2)
+ {
+ TIFFReadScanline(tif, scanline, x, 0);
+ for (ycount = img->ysize, scanptr = scanline, p = in + ystart, bit = 0xc0;
+ ycount > 0;
+ ycount --, p += ydir)
+ {
+ pixel = *scanptr & 0xc0;
+ while (pixel > 3)
+ pixel >>= 2;
+
+ *p = (255 * pixel / 3) ^ zero;
+
+ if (bit > 3)
+ bit >>= 2;
+ else
+ {
+ bit = 0xc0;
+ scanptr ++;
+ }
+ }
+ }
+ else if (bits == 4)
+ {
+ TIFFReadScanline(tif, scanline, x, 0);
+ for (ycount = img->ysize, scanptr = scanline, p = in + ystart, bit = 0xf0;
+ ycount > 0;
+ ycount --, p += ydir)
+ {
+ if (bit == 0xf0)
+ {
+ *p = (255 * ((*scanptr & 0xf0) >> 4) / 15) ^ zero;
+ bit = 0x0f;
+ }
+ else
+ {
+ *p = (255 * (*scanptr & 0x0f) / 15) ^ zero;
+ bit = 0xf0;
+ scanptr ++;
+ }
+ }
+ }
+ else if (ydir < 0 || zero || alpha)
+ {
+ TIFFReadScanline(tif, scanline, x, 0);
+
+ if (alpha)
+ {
+ if (zero)
+ {
+ for (ycount = img->ysize, p = in + ystart, scanptr = scanline;
+ ycount > 0;
+ ycount --, p += ydir, scanptr += 2)
+ *p = (scanptr[1] * (255 - scanptr[0]) +
+ (255 - scanptr[1]) * 255) / 255;
+ }
+ else
+ {
+ for (ycount = img->ysize, p = in + ystart, scanptr = scanline;
+ ycount > 0;
+ ycount --, p += ydir, scanptr += 2)
+ *p = (scanptr[1] * scanptr[0] +
+ (255 - scanptr[1]) * 255) / 255;
+ }
+ }
+ else
+ {
+ if (zero)
+ {
+ for (ycount = img->ysize, p = in + ystart, scanptr = scanline;
+ ycount > 0;
+ ycount --, p += ydir, scanptr ++)
+ *p = 255 - *scanptr;
+ }
+ else
+ {
+ for (ycount = img->ysize, p = in + ystart, scanptr = scanline;
+ ycount > 0;
+ ycount --, p += ydir, scanptr ++)
+ *p = *scanptr;
+ }
+ }
+ }
+ else
+ TIFFReadScanline(tif, in, x, 0);
+
+ if (img->colorspace == IMAGE_WHITE)
+ {
+ if (lut)
+ ImageLut(in, img->ysize, lut);
+
+ ImagePutCol(img, x, 0, img->ysize, in);
+ }
+ else
+ {
+ switch (img->colorspace)
+ {
+ case IMAGE_RGB :
+ ImageWhiteToRGB(in, out, img->ysize);
+ break;
+ case IMAGE_BLACK :
+ ImageWhiteToBlack(in, out, img->ysize);
+ break;
+ case IMAGE_CMY :
+ ImageWhiteToCMY(in, out, img->ysize);
+ break;
+ case IMAGE_CMYK :
+ ImageWhiteToCMYK(in, out, img->ysize);
+ break;
+ }
+
+ if (lut)
+ ImageLut(out, img->ysize * bpp, lut);
+
+ ImagePutCol(img, x, 0, img->ysize, out);
+ }
+ }
+ }
+ break;
+
+ case PHOTOMETRIC_PALETTE :
+ if (!TIFFGetField(tif, TIFFTAG_COLORMAP, &redcmap, &greencmap, &bluecmap))
+ {
+ fclose(fp);
+ return (-1);
+ }
+
+ num_colors = 1 << bits;
+
+ for (c = 0; c < num_colors; c ++)
+ {
+ redcmap[c] >>= 8;
+ greencmap[c] >>= 8;
+ bluecmap[c] >>= 8;
+ }
+
+ if (orientation < ORIENTATION_LEFTTOP)
+ {
+ /*
+ * Row major order...
+ */
+
+ for (y = ystart, ycount = img->ysize;
+ ycount > 0;
+ ycount --, y += ydir)
+ {
+ if (bits == 1)
+ {
+ TIFFReadScanline(tif, scanline, y, 0);
+ for (xcount = img->xsize, scanptr = scanline,
+ p = in + xstart * 3, bit = 128;
+ xcount > 0;
+ xcount --, p += pstep)
+ {
+ if (*scanptr & bit)
+ {
+ p[0] = redcmap[1];
+ p[1] = greencmap[1];
+ p[2] = bluecmap[1];
+ }
+ else
+ {
+ p[0] = redcmap[0];
+ p[1] = greencmap[0];
+ p[2] = bluecmap[0];
+ }
+
+ if (bit > 1)
+ bit >>= 1;
+ else
+ {
+ bit = 128;
+ scanptr ++;
+ }
+ }
+ }
+ else if (bits == 2)
+ {
+ TIFFReadScanline(tif, scanline, y, 0);
+ for (xcount = img->xsize, scanptr = scanline,
+ p = in + xstart * 3, bit = 0xc0;
+ xcount > 0;
+ xcount --, p += pstep)
+ {
+ pixel = *scanptr & bit;
+ while (pixel > 3)
+ pixel >>= 2;
+
+ p[0] = redcmap[pixel];
+ p[1] = greencmap[pixel];
+ p[2] = bluecmap[pixel];
+
+ if (bit > 3)
+ bit >>= 2;
+ else
+ {
+ bit = 0xc0;
+ scanptr ++;
+ }
+ }
+ }
+ else if (bits == 4)
+ {
+ TIFFReadScanline(tif, scanline, y, 0);
+ for (xcount = img->xsize, scanptr = scanline,
+ p = in + 3 * xstart, bit = 0xf0;
+ xcount > 0;
+ xcount --, p += pstep)
+ {
+ if (bit == 0xf0)
+ {
+ pixel = (*scanptr & 0xf0) >> 4;
+ p[0] = redcmap[pixel];
+ p[1] = greencmap[pixel];
+ p[2] = bluecmap[pixel];
+ bit = 0x0f;
+ }
+ else
+ {
+ pixel = *scanptr++ & 0x0f;
+ p[0] = redcmap[pixel];
+ p[1] = greencmap[pixel];
+ p[2] = bluecmap[pixel];
+ bit = 0xf0;
+ }
+ }
+ }
+ else
+ {
+ TIFFReadScanline(tif, scanline, y, 0);
+
+ for (xcount = img->xsize, p = in + 3 * xstart, scanptr = scanline;
+ xcount > 0;
+ xcount --, p += pstep)
+ {
+ p[0] = redcmap[*scanptr];
+ p[1] = greencmap[*scanptr];
+ p[2] = bluecmap[*scanptr++];
+ }
+ }
+
+ if (img->colorspace == IMAGE_RGB)
+ {
+ if (lut)
+ ImageLut(in, img->xsize * 3, lut);
+
+ ImagePutRow(img, 0, y, img->xsize, in);
+ }
+ else
+ {
+ switch (img->colorspace)
+ {
+ case IMAGE_WHITE :
+ ImageRGBToWhite(in, out, img->xsize);
+ break;
+ case IMAGE_BLACK :
+ ImageRGBToBlack(in, out, img->xsize);
+ break;
+ case IMAGE_CMY :
+ ImageRGBToCMY(in, out, img->xsize);
+ break;
+ case IMAGE_CMYK :
+ ImageRGBToCMYK(in, out, img->xsize);
+ break;
+ }
+
+ if (lut)
+ ImageLut(out, img->xsize * bpp, lut);
+
+ ImagePutRow(img, 0, y, img->xsize, out);
+ }
+ }
+ }
+ else
+ {
+ /*
+ * Column major order...
+ */
+
+ for (x = xstart, xcount = img->xsize;
+ xcount > 0;
+ xcount --, x += xdir)
+ {
+ if (bits == 1)
+ {
+ TIFFReadScanline(tif, scanline, x, 0);
+ for (ycount = img->ysize, scanptr = scanline,
+ p = in + 3 * ystart, bit = 128;
+ ycount > 0;
+ ycount --, p += ydir)
+ {
+ if (*scanptr & bit)
+ {
+ p[0] = redcmap[1];
+ p[1] = greencmap[1];
+ p[2] = bluecmap[1];
+ }
+ else
+ {
+ p[0] = redcmap[0];
+ p[1] = greencmap[0];
+ p[2] = bluecmap[0];
+ }
+
+ if (bit > 1)
+ bit >>= 1;
+ else
+ {
+ bit = 128;
+ scanptr ++;
+ }
+ }
+ }
+ else if (bits == 2)
+ {
+ TIFFReadScanline(tif, scanline, x, 0);
+ for (ycount = img->ysize, scanptr = scanline,
+ p = in + 3 * ystart, bit = 0xc0;
+ ycount > 0;
+ ycount --, p += ydir)
+ {
+ pixel = *scanptr & 0xc0;
+ while (pixel > 3)
+ pixel >>= 2;
+
+ p[0] = redcmap[pixel];
+ p[1] = greencmap[pixel];
+ p[2] = bluecmap[pixel];
+
+ if (bit > 3)
+ bit >>= 2;
+ else
+ {
+ bit = 0xc0;
+ scanptr ++;
+ }
+ }
+ }
+ else if (bits == 4)
+ {
+ TIFFReadScanline(tif, scanline, x, 0);
+ for (ycount = img->ysize, scanptr = scanline,
+ p = in + 3 * ystart, bit = 0xf0;
+ ycount > 0;
+ ycount --, p += ydir)
+ {
+ if (bit == 0xf0)
+ {
+ pixel = (*scanptr & 0xf0) >> 4;
+ p[0] = redcmap[pixel];
+ p[1] = greencmap[pixel];
+ p[2] = bluecmap[pixel];
+ bit = 0x0f;
+ }
+ else
+ {
+ pixel = *scanptr++ & 0x0f;
+ p[0] = redcmap[pixel];
+ p[1] = greencmap[pixel];
+ p[2] = bluecmap[pixel];
+ bit = 0xf0;
+ }
+ }
+ }
+ else
+ {
+ TIFFReadScanline(tif, scanline, x, 0);
+
+ for (ycount = img->ysize, p = in + 3 * ystart, scanptr = scanline;
+ ycount > 0;
+ ycount --, p += ydir)
+ {
+ p[0] = redcmap[*scanptr];
+ p[1] = greencmap[*scanptr];
+ p[2] = bluecmap[*scanptr++];
+ }
+ }
+
+ if (img->colorspace == IMAGE_RGB)
+ {
+ if (lut)
+ ImageLut(in, img->ysize * 3, lut);
+
+ ImagePutCol(img, x, 0, img->ysize, in);
+ }
+ else
+ {
+ switch (img->colorspace)
+ {
+ case IMAGE_WHITE :
+ ImageRGBToWhite(in, out, img->ysize);
+ break;
+ case IMAGE_BLACK :
+ ImageRGBToBlack(in, out, img->ysize);
+ break;
+ case IMAGE_CMY :
+ ImageRGBToCMY(in, out, img->ysize);
+ break;
+ case IMAGE_CMYK :
+ ImageRGBToCMYK(in, out, img->ysize);
+ break;
+ }
+
+ if (lut)
+ ImageLut(out, img->ysize * bpp, lut);
+
+ ImagePutCol(img, x, 0, img->ysize, out);
+ }
+ }
+ }
+ break;
+
+ case PHOTOMETRIC_RGB :
+ if (orientation < ORIENTATION_LEFTTOP)
+ {
+ /*
+ * Row major order...
+ */
+
+ for (y = ystart, ycount = img->ysize;
+ ycount > 0;
+ ycount --, y += ydir)
+ {
+ if (bits == 1)
+ {
+ TIFFReadScanline(tif, scanline, y, 0);
+ for (xcount = img->xsize, scanptr = scanline, p = in + xstart * 3, bit = 0xf0;
+ xcount > 0;
+ xcount --, p += pstep)
+ {
+ if (*scanptr & bit & 0x88)
+ p[0] = 255;
+ else
+ p[0] = 0;
+
+ if (*scanptr & bit & 0x44)
+ p[1] = 255;
+ else
+ p[1] = 0;
+
+ if (*scanptr & bit & 0x22)
+ p[2] = 255;
+ else
+ p[2] = 0;
+
+ if (bit == 0xf0)
+ bit = 0x0f;
+ else
+ {
+ bit = 0xf0;
+ scanptr ++;
+ }
+ }
+ }
+ else if (bits == 2)
+ {
+ TIFFReadScanline(tif, scanline, y, 0);
+ for (xcount = img->xsize, scanptr = scanline, p = in + xstart * 3;
+ xcount > 0;
+ xcount --, p += pstep, scanptr ++)
+ {
+ pixel = *scanptr >> 2;
+ p[0] = 255 * (pixel & 3) / 3;
+ pixel >>= 2;
+ p[1] = 255 * (pixel & 3) / 3;
+ pixel >>= 2;
+ p[2] = 255 * (pixel & 3) / 3;
+ }
+ }
+ else if (bits == 4)
+ {
+ TIFFReadScanline(tif, scanline, y, 0);
+ for (xcount = img->xsize, scanptr = scanline, p = in + xstart * 3;
+ xcount > 0;
+ xcount -= 2, p += 2 * pstep, scanptr += 3)
+ {
+ pixel = scanptr[0];
+ p[1] = 255 * (pixel & 15) / 15;
+ pixel >>= 4;
+ p[0] = 255 * (pixel & 15) / 15;
+ pixel = scanptr[1];
+ p[2] = 255 * ((pixel >> 4) & 15) / 15;
+
+ if (xcount > 1)
+ {
+ p[pstep + 0] = 255 * (pixel & 15) / 15;
+ pixel = scanptr[2];
+ p[pstep + 2] = 255 * (pixel & 15) / 15;
+ pixel >>= 4;
+ p[pstep + 1] = 255 * (pixel & 15) / 15;
+ }
+ }
+ }
+ else if (xdir < 0 || alpha)
+ {
+ TIFFReadScanline(tif, scanline, y, 0);
+
+ if (alpha)
+ {
+ for (xcount = img->xsize, p = in + xstart * 3, scanptr = scanline;
+ xcount > 0;
+ xcount --, p += pstep, scanptr += 4)
+ {
+ p[0] = (scanptr[0] * scanptr[3] + 255 * (255 - scanptr[3])) / 255;
+ p[1] = (scanptr[1] * scanptr[3] + 255 * (255 - scanptr[3])) / 255;
+ p[2] = (scanptr[2] * scanptr[3] + 255 * (255 - scanptr[3])) / 255;
+ }
+ }
+ else
+ {
+ for (xcount = img->xsize, p = in + xstart * 3, scanptr = scanline;
+ xcount > 0;
+ xcount --, p += pstep, scanptr += 3)
+ {
+ p[0] = scanptr[0];
+ p[1] = scanptr[1];
+ p[2] = scanptr[2];
+ }
+ }
+ }
+ else
+ TIFFReadScanline(tif, in, y, 0);
+
+ if ((saturation != 100 || hue != 0) && bpp > 1)
+ ImageRGBAdjust(in, img->xsize, saturation, hue);
+
+ if (img->colorspace == IMAGE_RGB)
+ {
+ if (lut)
+ ImageLut(in, img->xsize * 3, lut);
+
+ ImagePutRow(img, 0, y, img->xsize, in);
+ }
+ else
+ {
+ switch (img->colorspace)
+ {
+ case IMAGE_WHITE :
+ ImageRGBToWhite(in, out, img->xsize);
+ break;
+ case IMAGE_BLACK :
+ ImageRGBToBlack(in, out, img->xsize);
+ break;
+ case IMAGE_CMY :
+ ImageRGBToCMY(in, out, img->xsize);
+ break;
+ case IMAGE_CMYK :
+ ImageRGBToCMYK(in, out, img->xsize);
+ break;
+ }
+
+ if (lut)
+ ImageLut(out, img->xsize * bpp, lut);
+
+ ImagePutRow(img, 0, y, img->xsize, out);
+ }
+ }
+ }
+ else
+ {
+ /*
+ * Column major order...
+ */
+
+ for (x = xstart, xcount = img->xsize;
+ xcount > 0;
+ xcount --, x += xdir)
+ {
+ if (bits == 1)
+ {
+ TIFFReadScanline(tif, scanline, x, 0);
+ for (ycount = img->ysize, scanptr = scanline, p = in + ystart * 3, bit = 0xf0;
+ ycount > 0;
+ ycount --, p += pstep)
+ {
+ if (*scanptr & bit & 0x88)
+ p[0] = one;
+ else
+ p[0] = zero;
+
+ if (*scanptr & bit & 0x44)
+ p[1] = one;
+ else
+ p[1] = zero;
+
+ if (*scanptr & bit & 0x22)
+ p[2] = one;
+ else
+ p[2] = zero;
+
+ if (bit == 0xf0)
+ bit = 0x0f;
+ else
+ {
+ bit = 0xf0;
+ scanptr ++;
+ }
+ }
+ }
+ else if (bits == 2)
+ {
+ TIFFReadScanline(tif, scanline, x, 0);
+ for (ycount = img->ysize, scanptr = scanline, p = in + ystart * 3;
+ ycount > 0;
+ ycount --, p += pstep, scanptr ++)
+ {
+ pixel = *scanptr >> 2;
+ p[0] = 255 * (pixel & 3) / 3;
+ pixel >>= 2;
+ p[1] = 255 * (pixel & 3) / 3;
+ pixel >>= 2;
+ p[2] = 255 * (pixel & 3) / 3;
+ }
+ }
+ else if (bits == 4)
+ {
+ TIFFReadScanline(tif, scanline, x, 0);
+ for (ycount = img->ysize, scanptr = scanline, p = in + ystart * 3;
+ ycount > 0;
+ ycount -= 2, p += 2 * pstep, scanptr += 3)
+ {
+ pixel = scanptr[0];
+ p[1] = 255 * (pixel & 15) / 15;
+ pixel >>= 4;
+ p[0] = 255 * (pixel & 15) / 15;
+ pixel = scanptr[1];
+ p[2] = 255 * ((pixel >> 4) & 15) / 15;
+
+ if (ycount > 1)
+ {
+ p[pstep + 0] = 255 * (pixel & 15) / 15;
+ pixel = scanptr[2];
+ p[pstep + 2] = 255 * (pixel & 15) / 15;
+ pixel >>= 4;
+ p[pstep + 1] = 255 * (pixel & 15) / 15;
+ }
+ }
+ }
+ else if (ydir < 0 || alpha)
+ {
+ TIFFReadScanline(tif, scanline, x, 0);
+
+ if (alpha)
+ {
+ for (ycount = img->ysize, p = in + ystart * 3, scanptr = scanline;
+ ycount > 0;
+ ycount --, p += pstep, scanptr += 4)
+ {
+ p[0] = (scanptr[0] * scanptr[3] + 255 * (255 - scanptr[3])) / 255;
+ p[1] = (scanptr[1] * scanptr[3] + 255 * (255 - scanptr[3])) / 255;
+ p[2] = (scanptr[2] * scanptr[3] + 255 * (255 - scanptr[3])) / 255;
+ }
+ }
+ else
+ {
+ for (ycount = img->ysize, p = in + ystart * 3, scanptr = scanline;
+ ycount > 0;
+ ycount --, p += pstep, scanptr += 3)
+ {
+ p[0] = scanptr[0];
+ p[1] = scanptr[1];
+ p[2] = scanptr[2];
+ }
+ }
+ }
+ else
+ TIFFReadScanline(tif, in, x, 0);
+
+ if ((saturation != 100 || hue != 0) && bpp > 1)
+ ImageRGBAdjust(in, img->ysize, saturation, hue);
+
+ if (img->colorspace == IMAGE_RGB)
+ {
+ if (lut)
+ ImageLut(in, img->ysize * 3, lut);
+
+ ImagePutCol(img, x, 0, img->ysize, in);
+ }
+ else
+ {
+ switch (img->colorspace)
+ {
+ case IMAGE_WHITE :
+ ImageRGBToWhite(in, out, img->ysize);
+ break;
+ case IMAGE_BLACK :
+ ImageRGBToBlack(in, out, img->ysize);
+ break;
+ case IMAGE_CMY :
+ ImageRGBToCMY(in, out, img->ysize);
+ break;
+ case IMAGE_CMYK :
+ ImageRGBToCMYK(in, out, img->ysize);
+ break;
+ }
+
+ if (lut)
+ ImageLut(out, img->ysize * bpp, lut);
+
+ ImagePutCol(img, x, 0, img->ysize, out);
+ }
+ }
+ }
+ break;
+
+ case PHOTOMETRIC_SEPARATED :
+ TIFFGetField(tif, TIFFTAG_INKSET, &inkset);
+
+ if (inkset == INKSET_CMYK)
+ {
+ if (orientation < ORIENTATION_LEFTTOP)
+ {
+ /*
+ * Row major order...
+ */
+
+ for (y = ystart, ycount = img->ysize;
+ ycount > 0;
+ ycount --, y += ydir)
+ {
+ if (bits == 1)
+ {
+ TIFFReadScanline(tif, scanline, y, 0);
+ for (xcount = img->xsize, scanptr = scanline, p = in + xstart * 3, bit = 0xf0;
+ xcount > 0;
+ xcount --, p += pstep)
+ {
+ if (*scanptr & bit & 0x11)
+ {
+ p[0] = 0;
+ p[1] = 0;
+ p[2] = 0;
+ }
+ else
+ {
+ if (*scanptr & bit & 0x88)
+ p[0] = 0;
+ else
+ p[0] = 255;
+
+ if (*scanptr & bit & 0x44)
+ p[1] = 0;
+ else
+ p[1] = 255;
+
+ if (*scanptr & bit & 0x22)
+ p[2] = 0;
+ else
+ p[2] = 255;
+ }
+
+ if (bit == 0xf0)
+ bit = 0x0f;
+ else
+ {
+ bit = 0xf0;
+ scanptr ++;
+ }
+ }
+ }
+ else if (bits == 2)
+ {
+ TIFFReadScanline(tif, scanline, y, 0);
+ for (xcount = img->xsize, scanptr = scanline, p = in + xstart * 3;
+ xcount > 0;
+ xcount --, p += pstep, scanptr ++)
+ {
+ pixel = *scanptr;
+ k = 255 * (pixel & 3) / 3;
+ if (k == 255)
+ {
+ p[0] = 0;
+ p[1] = 0;
+ p[2] = 0;
+ }
+ else
+ {
+ pixel >>= 2;
+ b = 255 - 255 * (pixel & 3) / 3 - k;
+ if (b < 0)
+ p[2] = 0;
+ else if (b < 256)
+ p[2] = b;
+ else
+ p[2] = 255;
+
+ pixel >>= 2;
+ g = 255 - 255 * (pixel & 3) / 3 - k;
+ if (g < 0)
+ p[1] = 0;
+ else if (g < 256)
+ p[1] = g;
+ else
+ p[1] = 255;
+
+ pixel >>= 2;
+ r = 255 - 255 * (pixel & 3) / 3 - k;
+ if (r < 0)
+ p[0] = 0;
+ else if (r < 256)
+ p[0] = r;
+ else
+ p[0] = 255;
+ }
+ }
+ }
+ else if (bits == 4)
+ {
+ TIFFReadScanline(tif, scanline, y, 0);
+ for (xcount = img->xsize, scanptr = scanline, p = in + xstart * 3;
+ xcount > 0;
+ xcount --, p += pstep, scanptr += 2)
+ {
+ pixel = scanptr[1];
+ k = 255 * (pixel & 15) / 15;
+ if (k == 255)
+ {
+ p[0] = 0;
+ p[1] = 0;
+ p[2] = 0;
+ }
+ else
+ {
+ pixel >>= 4;
+ b = 255 - 255 * (pixel & 15) / 15 - k;
+ if (b < 0)
+ p[2] = 0;
+ else if (b < 256)
+ p[2] = b;
+ else
+ p[2] = 255;
+
+ pixel = scanptr[0];
+ g = 255 - 255 * (pixel & 15) / 15 - k;
+ if (g < 0)
+ p[1] = 0;
+ else if (g < 256)
+ p[1] = g;
+ else
+ p[1] = 255;
+
+ pixel >>= 4;
+ r = 255 - 255 * (pixel & 15) / 15 - k;
+ if (r < 0)
+ p[0] = 0;
+ else if (r < 256)
+ p[0] = r;
+ else
+ p[0] = 255;
+ }
+ }
+ }
+ else
+ {
+ TIFFReadScanline(tif, scanline, y, 0);
+
+ for (xcount = img->xsize, p = in + xstart * 3, scanptr = scanline;
+ xcount > 0;
+ xcount --, p += pstep, scanptr += 4)
+ {
+ k = scanptr[3];
+ if (k == 255)
+ {
+ p[0] = 0;
+ p[1] = 0;
+ p[2] = 0;
+ }
+ else
+ {
+ r = 255 - scanptr[0] - k;
+ if (r < 0)
+ p[0] = 0;
+ else if (r < 256)
+ p[0] = r;
+ else
+ p[0] = 255;
+
+ g = 255 - scanptr[1] - k;
+ if (g < 0)
+ p[1] = 0;
+ else if (g < 256)
+ p[1] = g;
+ else
+ p[1] = 255;
+
+ b = 255 - scanptr[2] - k;
+ if (b < 0)
+ p[2] = 0;
+ else if (b < 256)
+ p[2] = b;
+ else
+ p[2] = 255;
+ }
+ }
+ }
+
+ if ((saturation != 100 || hue != 0) && bpp > 1)
+ ImageRGBAdjust(in, img->xsize, saturation, hue);
+
+ if (img->colorspace == IMAGE_RGB)
+ {
+ if (lut)
+ ImageLut(in, img->xsize * 3, lut);
+
+ ImagePutRow(img, 0, y, img->xsize, in);
+ }
+ else
+ {
+ switch (img->colorspace)
+ {
+ case IMAGE_WHITE :
+ ImageRGBToWhite(in, out, img->xsize);
+ break;
+ case IMAGE_BLACK :
+ ImageRGBToBlack(in, out, img->xsize);
+ break;
+ case IMAGE_CMY :
+ ImageRGBToCMY(in, out, img->xsize);
+ break;
+ case IMAGE_CMYK :
+ ImageRGBToCMYK(in, out, img->xsize);
+ break;
+ }
+
+ if (lut)
+ ImageLut(out, img->xsize * 3, lut);
+
+ ImagePutRow(img, 0, y, img->xsize, out);
+ }
+ }
+ }
+ else
+ {
+ /*
+ * Column major order...
+ */
+
+ for (x = xstart, xcount = img->xsize;
+ xcount > 0;
+ xcount --, x += xdir)
+ {
+ if (bits == 1)
+ {
+ TIFFReadScanline(tif, scanline, x, 0);
+ for (ycount = img->ysize, scanptr = scanline, p = in + xstart * 3, bit = 0xf0;
+ ycount > 0;
+ ycount --, p += pstep)
+ {
+ if (*scanptr & bit & 0x11)
+ {
+ p[0] = 0;
+ p[1] = 0;
+ p[2] = 0;
+ }
+ else
+ {
+ if (*scanptr & bit & 0x88)
+ p[0] = 0;
+ else
+ p[0] = 255;
+
+ if (*scanptr & bit & 0x44)
+ p[1] = 0;
+ else
+ p[1] = 255;
+
+ if (*scanptr & bit & 0x22)
+ p[2] = 0;
+ else
+ p[2] = 255;
+ }
+
+ if (bit == 0xf0)
+ bit = 0x0f;
+ else
+ {
+ bit = 0xf0;
+ scanptr ++;
+ }
+ }
+ }
+ else if (bits == 2)
+ {
+ TIFFReadScanline(tif, scanline, x, 0);
+ for (ycount = img->ysize, scanptr = scanline, p = in + xstart * 3;
+ ycount > 0;
+ ycount --, p += pstep, scanptr ++)
+ {
+ pixel = *scanptr;
+ k = 255 * (pixel & 3) / 3;
+ if (k == 255)
+ {
+ p[0] = 0;
+ p[1] = 0;
+ p[2] = 0;
+ }
+ else
+ {
+ pixel >>= 2;
+ b = 255 - 255 * (pixel & 3) / 3 - k;
+ if (b < 0)
+ p[2] = 0;
+ else if (b < 256)
+ p[2] = b;
+ else
+ p[2] = 255;
+
+ pixel >>= 2;
+ g = 255 - 255 * (pixel & 3) / 3 - k;
+ if (g < 0)
+ p[1] = 0;
+ else if (g < 256)
+ p[1] = g;
+ else
+ p[1] = 255;
+
+ pixel >>= 2;
+ r = 255 - 255 * (pixel & 3) / 3 - k;
+ if (r < 0)
+ p[0] = 0;
+ else if (r < 256)
+ p[0] = r;
+ else
+ p[0] = 255;
+ }
+ }
+ }
+ else if (bits == 4)
+ {
+ TIFFReadScanline(tif, scanline, x, 0);
+ for (ycount = img->ysize, scanptr = scanline, p = in + xstart * 3;
+ ycount > 0;
+ ycount --, p += pstep, scanptr += 2)
+ {
+ pixel = scanptr[1];
+ k = 255 * (pixel & 15) / 15;
+ if (k == 255)
+ {
+ p[0] = 0;
+ p[1] = 0;
+ p[2] = 0;
+ }
+ else
+ {
+ pixel >>= 4;
+ b = 255 - 255 * (pixel & 15) / 15 - k;
+ if (b < 0)
+ p[2] = 0;
+ else if (b < 256)
+ p[2] = b;
+ else
+ p[2] = 255;
+
+ pixel = scanptr[0];
+ g = 255 - 255 * (pixel & 15) / 15 - k;
+ if (g < 0)
+ p[1] = 0;
+ else if (g < 256)
+ p[1] = g;
+ else
+ p[1] = 255;
+
+ pixel >>= 4;
+ r = 255 - 255 * (pixel & 15) / 15 - k;
+ if (r < 0)
+ p[0] = 0;
+ else if (r < 256)
+ p[0] = r;
+ else
+ p[0] = 255;
+ }
+ }
+ }
+ else
+ {
+ TIFFReadScanline(tif, scanline, x, 0);
+
+ for (ycount = img->ysize, p = in + xstart * 3, scanptr = scanline;
+ ycount > 0;
+ ycount --, p += pstep, scanptr += 4)
+ {
+ k = scanptr[3];
+ if (k == 255)
+ {
+ p[0] = 0;
+ p[1] = 0;
+ p[2] = 0;
+ }
+ else
+ {
+ r = 255 - scanptr[0] - k;
+ if (r < 0)
+ p[0] = 0;
+ else if (r < 256)
+ p[0] = r;
+ else
+ p[0] = 255;
+
+ g = 255 - scanptr[1] - k;
+ if (g < 0)
+ p[1] = 0;
+ else if (g < 256)
+ p[1] = g;
+ else
+ p[1] = 255;
+
+ b = 255 - scanptr[2] - k;
+ if (b < 0)
+ p[2] = 0;
+ else if (b < 256)
+ p[2] = b;
+ else
+ p[2] = 255;
+ }
+ }
+ }
+
+ if ((saturation != 100 || hue != 0) && bpp > 1)
+ ImageRGBAdjust(in, img->ysize, saturation, hue);
+
+ if (img->colorspace == IMAGE_RGB)
+ {
+ if (lut)
+ ImageLut(in, img->ysize * 3, lut);
+
+ ImagePutCol(img, x, 0, img->ysize, in);
+ }
+ else
+ {
+ switch (img->colorspace)
+ {
+ case IMAGE_WHITE :
+ ImageRGBToWhite(in, out, img->ysize);
+ break;
+ case IMAGE_BLACK :
+ ImageRGBToBlack(in, out, img->ysize);
+ break;
+ case IMAGE_CMY :
+ ImageRGBToCMY(in, out, img->ysize);
+ break;
+ case IMAGE_CMYK :
+ ImageRGBToCMYK(in, out, img->ysize);
+ break;
+ }
+
+ if (lut)
+ ImageLut(out, img->ysize * bpp, lut);
+
+ ImagePutCol(img, x, 0, img->ysize, out);
+ }
+ }
+ }
+
+ break;
+ }
+
+ default :
+ _TIFFfree(scanline);
+ free(in);
+ free(out);
+
+ TIFFClose(tif);
+ return (-1);
+ }
+
+ /*
+ * Free temporary buffers, close the TIFF file, and return.
+ */
+
+ _TIFFfree(scanline);
+ free(in);
+ free(out);
+
+ TIFFClose(tif);
+ return (0);
+}
+
+
+#endif /* HAVE_LIBTIFF */
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/filter/image-zoom.c b/filter/image-zoom.c
new file mode 100644
index 000000000..455dd5226
--- /dev/null
+++ b/filter/image-zoom.c
@@ -0,0 +1,310 @@
+/*
+ * "$Id$"
+ *
+ * Image zoom routines for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1993-1999 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * Contents:
+ *
+ * ImageZoomAlloc() - Allocate a pixel zoom record...
+ * ImageZoomFill() - Fill a zoom record with image data utilizing bilinear
+ * interpolation.
+ * ImageZoomQFill() - Fill a zoom record quickly using nearest-neighbor
+ * sampling.
+ * ImageZoomFree() - Free a zoom record...
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "image.h"
+
+
+/*
+ * 'ZoomAlloc()' - Allocate a pixel zoom record...
+ */
+
+izoom_t *
+ImageZoomAlloc(image_t *img, /* I - Image to zoom */
+ int x0, /* I - Upper-lefthand corner */
+ int y0, /* I - ... */
+ int x1, /* I - Lower-righthand corner */
+ int y1, /* I - ... */
+ int xsize, /* I - Final width of image */
+ int ysize, /* I - Final height of image */
+ int rotated) /* I - Non-zero if image is rotated 90 degs */
+{
+ izoom_t *z; /* New zoom record */
+
+
+ if ((z = (izoom_t *)calloc(1, sizeof(izoom_t))) == NULL)
+ return (NULL);
+
+ z->img = img;
+ z->row = 0;
+ z->depth = ImageGetDepth(img);
+ z->rotated = rotated;
+
+ if (rotated)
+ {
+ z->xorig = x1;
+ z->yorig = y0;
+ z->width = y1 - y0 + 1;
+ z->height = x1 - x0 + 1;
+ z->xsize = xsize;
+ z->ysize = ysize;
+ z->xmod = z->width % z->xsize;
+ z->xstep = z->width / z->xsize;
+ z->xincr = 1;
+ z->ymod = z->height % z->ysize;
+ z->ystep = z->height / z->ysize;
+ z->yincr = 1;
+ z->instep = z->xstep * z->depth;
+ z->inincr = z->xincr * z->depth;
+
+ if (z->width < img->ysize)
+ z->xmax = z->width;
+ else
+ z->xmax = z->width - 1;
+
+ if (z->height < img->xsize)
+ z->ymax = z->height;
+ else
+ z->ymax = z->height - 1;
+ }
+ else
+ {
+ z->xorig = x0;
+ z->yorig = y0;
+ z->width = x1 - x0 + 1;
+ z->height = y1 - y0 + 1;
+ z->xsize = xsize;
+ z->ysize = ysize;
+ z->xmod = z->width % z->xsize;
+ z->xstep = z->width / z->xsize;
+ z->xincr = 1;
+ z->ymod = z->height % z->ysize;
+ z->ystep = z->height / z->ysize;
+ z->yincr = 1;
+ z->instep = z->xstep * z->depth;
+ z->inincr = z->xincr * z->depth;
+
+ if (z->width < img->xsize)
+ z->xmax = z->width;
+ else
+ z->xmax = z->width - 1;
+
+ if (z->height < img->ysize)
+ z->ymax = z->height;
+ else
+ z->ymax = z->height - 1;
+ }
+
+ if ((z->rows[0] = (ib_t *)malloc(z->xsize * z->depth)) == NULL)
+ {
+ free(z);
+ return (NULL);
+ }
+
+ if ((z->rows[1] = (ib_t *)malloc(z->xsize * z->depth)) == NULL)
+ {
+ free(z->rows[0]);
+ free(z);
+ return (NULL);
+ }
+
+ if ((z->in = (ib_t *)malloc(z->width * z->depth)) == NULL)
+ {
+ free(z->rows[0]);
+ free(z->rows[1]);
+ free(z);
+ return (NULL);
+ }
+
+ return (z);
+}
+
+
+/*
+ * 'ImageZoomFill()' - Fill a zoom record with image data utilizing bilinear
+ * interpolation.
+ */
+
+void
+ImageZoomFill(izoom_t *z, /* I - Zoom record to fill */
+ int iy) /* I - Zoom image row */
+{
+ ib_t *r, /* Row pointer */
+ *inptr; /* Pixel pointer */
+ int xerr0, /* X error counter */
+ xerr1; /* ... */
+ int ix,
+ x,
+ count,
+ z_depth,
+ z_xstep,
+ z_xincr,
+ z_instep,
+ z_inincr,
+ z_xmax,
+ z_xmod,
+ z_xsize;
+
+
+ if (iy > z->ymax)
+ iy = z->ymax;
+
+ z->row ^= 1;
+
+ z_depth = z->depth;
+ z_xsize = z->xsize;
+ z_xmax = z->xmax;
+ z_xmod = z->xmod;
+ z_xstep = z->xstep;
+ z_xincr = z->xincr;
+ z_instep = z->instep;
+ z_inincr = z->inincr;
+
+ if (z->rotated)
+ ImageGetCol(z->img, z->xorig - iy, z->yorig, z->width, z->in);
+ else
+ ImageGetRow(z->img, z->xorig, z->yorig + iy, z->width, z->in);
+
+ if (z_inincr < 0)
+ inptr = z->in + (z->width - 1) * z_depth;
+ else
+ inptr = z->in;
+
+ for (x = z_xsize, xerr0 = z_xsize, xerr1 = 0, ix = 0, r = z->rows[z->row];
+ x > 0;
+ x --)
+ {
+ if (ix < z_xmax)
+ {
+ for (count = 0; count < z_depth; count ++)
+ *r++ = (inptr[count] * xerr0 + inptr[z_depth + count] * xerr1) / z_xsize;
+ }
+ else
+ {
+ for (count = 0; count < z_depth; count ++)
+ *r++ = inptr[count];
+ }
+
+ ix += z_xstep;
+ inptr += z_instep;
+ xerr0 -= z_xmod;
+ xerr1 += z_xmod;
+
+ if (xerr0 <= 0)
+ {
+ xerr0 += z_xsize;
+ xerr1 -= z_xsize;
+ ix += z_xincr;
+ inptr += z_inincr;
+ }
+ }
+}
+
+
+/*
+ * 'ImageZoomQFill()' - Fill a zoom record quickly using nearest-neighbor sampling.
+ */
+
+void
+ImageZoomQFill(izoom_t *z, /* I - Zoom record to fill */
+ int iy) /* I - Zoom image row */
+{
+ ib_t *r, /* Row pointer */
+ *inptr; /* Pixel pointer */
+ int xerr0; /* X error counter */
+ int ix,
+ x,
+ count,
+ z_depth,
+ z_xstep,
+ z_xincr,
+ z_instep,
+ z_inincr,
+ z_xmod,
+ z_xsize;
+
+
+ if (iy > z->ymax)
+ iy = z->ymax;
+
+ z->row ^= 1;
+
+ z_depth = z->depth;
+ z_xsize = z->xsize;
+ z_xmod = z->xmod;
+ z_xstep = z->xstep;
+ z_xincr = z->xincr;
+ z_instep = z->instep;
+ z_inincr = z->inincr;
+
+ if (z->rotated)
+ ImageGetCol(z->img, z->xorig - iy, z->yorig, z->width, z->in);
+ else
+ ImageGetRow(z->img, z->xorig, z->yorig + iy, z->width, z->in);
+
+ if (z_inincr < 0)
+ inptr = z->in + (z->width - 1) * z_depth;
+ else
+ inptr = z->in;
+
+ for (x = z_xsize, xerr0 = z_xsize, ix = 0, r = z->rows[z->row];
+ x > 0;
+ x --)
+ {
+ for (count = 0; count < z_depth; count ++)
+ *r++ = inptr[count];
+
+ ix += z_xstep;
+ inptr += z_instep;
+ xerr0 -= z_xmod;
+
+ if (xerr0 <= 0)
+ {
+ xerr0 += z_xsize;
+ ix += z_xincr;
+ inptr += z_inincr;
+ }
+ }
+}
+
+
+/*
+ * 'ImageZoomFree()' - Free a zoom record...
+ */
+
+void
+ImageZoomFree(izoom_t *z) /* I - Zoom record to free */
+{
+ free(z->rows[0]);
+ free(z->rows[1]);
+ free(z->in);
+ free(z);
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/filter/image.c b/filter/image.c
new file mode 100644
index 000000000..af9d4cd0a
--- /dev/null
+++ b/filter/image.c
@@ -0,0 +1,743 @@
+/*
+ * "$Id$"
+ *
+ * Base image support for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1993-1999 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * Contents:
+ *
+ * ImageOpen() - Open an image file and read it into memory.
+ * ImageClose() - Close an image file.
+ * ImageSetMaxTiles() - Set the maximum number of tiles to cache.
+ * ImageSetProfile() - Set the device color profile.
+ * ImageGetCol() - Get a column of pixels from an image.
+ * ImageGetRow() - Get a row of pixels from an image.
+ * ImagePutCol() - Put a column of pixels to an image.
+ * ImagePutRow() - Put a row of pixels to an image.
+ * get_tile() - Get a cached tile.
+ * flush_tile() - Flush the least-recently-used tile in the cache.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "image.h"
+#include <unistd.h>
+#include <ctype.h>
+#include <math.h>
+
+
+/*
+ * Globals...
+ */
+
+int ImageHaveProfile = 0; /* Do we have a color profile? */
+int ImageDensity[256]; /* Ink/marker density LUT */
+int ImageMatrix[3][3][256]; /* Color transform matrix LUT */
+
+
+/*
+ * Local functions...
+ */
+
+static ib_t *get_tile(image_t *img, int x, int y);
+static void flush_tile(image_t *img);
+
+
+/*
+ * 'ImageOpen()' - Open an image file and read it into memory.
+ */
+
+image_t * /* O - New image */
+ImageOpen(char *filename, /* I - Filename of image */
+ int primary, /* I - Primary colorspace needed */
+ int secondary, /* I - Secondary colorspace if primary no good */
+ int saturation, /* I - Color saturation level */
+ int hue, /* I - Color hue adjustment */
+ ib_t *lut) /* I - RGB gamma/brightness LUT */
+{
+ FILE *fp; /* File pointer */
+ unsigned char header[16], /* First 16 bytes of file */
+ header2[16]; /* Bytes 2048-2064 (PhotoCD) */
+ image_t *img; /* New image buffer */
+ int status; /* Status of load... */
+
+
+ /*
+ * Range check...
+ */
+
+ if (filename == NULL)
+ return (NULL);
+
+ /*
+ * Figure out the file type...
+ */
+
+ if ((fp = fopen(filename, "r")) == NULL)
+ return (NULL);
+
+ if (fread(header, 1, sizeof(header), fp) == 0)
+ {
+ fclose(fp);
+ return (NULL);
+ }
+
+ fseek(fp, 2048, SEEK_SET);
+ memset(header2, 0, sizeof(header2));
+ fread(header2, 1, sizeof(header2), fp);
+ fseek(fp, 0, SEEK_SET);
+
+ /*
+ * Allocate memory...
+ */
+
+ img = calloc(sizeof(image_t), 1);
+
+ if (img == NULL)
+ {
+ fclose(fp);
+ return (NULL);
+ }
+
+ /*
+ * Load the image as appropriate...
+ */
+
+ img->max_ics = TILE_MINIMUM;
+ img->xppi = 128;
+ img->yppi = 128;
+
+ if (memcmp(header, "GIF87a", 6) == 0 ||
+ memcmp(header, "GIF89a", 6) == 0)
+ status = ImageReadGIF(img, fp, primary, secondary, saturation, hue, lut);
+ else if (header[0] == 0x01 && header[1] == 0xda)
+ status = ImageReadSGI(img, fp, primary, secondary, saturation, hue, lut);
+ else if (header[0] == 0x59 && header[1] == 0xa6 &&
+ header[2] == 0x6a && header[3] == 0x95)
+ status = ImageReadSunRaster(img, fp, primary, secondary, saturation, hue, lut);
+ else if (header[0] == 'P' && header[1] >= '1' && header[1] <= '6')
+ status = ImageReadPNM(img, fp, primary, secondary, saturation, hue, lut);
+ else if (memcmp(header2, "PCD_IPI", 7) == 0)
+ status = ImageReadPhotoCD(img, fp, primary, secondary, saturation, hue, lut);
+#if defined(HAVE_LIBPNG) && defined(HAVE_LIBZ)
+ else if (memcmp(header, "\211PNG", 4) == 0)
+ status = ImageReadPNG(img, fp, primary, secondary, saturation, hue, lut);
+#endif /* HAVE_LIBPNG && HAVE_LIBZ */
+#ifdef HAVE_LIBJPEG
+ else if (memcmp(header + 6, "JFIF", 4) == 0)
+ status = ImageReadJPEG(img, fp, primary, secondary, saturation, hue, lut);
+#endif /* HAVE_LIBJPEG */
+#ifdef HAVE_LIBTIFF
+ else if (memcmp(header, "MM", 2) == 0 ||
+ memcmp(header, "II", 2) == 0)
+ status = ImageReadTIFF(img, fp, primary, secondary, saturation, hue, lut);
+#endif /* HAVE_LIBTIFF */
+ else
+ {
+ fclose(fp);
+ status = -1;
+ }
+
+ if (status)
+ {
+ free(img);
+ return (NULL);
+ }
+ else
+ return (img);
+}
+
+
+/*
+ * 'ImageClose()' - Close an image file.
+ */
+
+void
+ImageClose(image_t *img) /* I - Image to close */
+{
+ ic_t *current, /* Current cached tile */
+ *next; /* Next cached tile */
+
+
+ /*
+ * Free the image cache...
+ */
+
+ for (current = img->first; current != NULL; current = next)
+ {
+ next = current->next;
+ free(current);
+ }
+
+ /*
+ * Wipe the tile cache file (if any)...
+ */
+
+ if (img->cachefile != NULL)
+ {
+ fclose(img->cachefile);
+ unlink(img->cachename);
+ }
+
+ /*
+ * Free the rest of memory...
+ */
+
+ if (img->tiles != NULL)
+ {
+ free(img->tiles[0]);
+ free(img->tiles);
+ }
+
+ free(img);
+}
+
+
+/*
+ * 'ImageSetMaxTiles()' - Set the maximum number of tiles to cache.
+ *
+ * If the "max_tiles" argument is 0 then the maximum number of tiles is
+ * computed from the image size.
+ */
+
+void
+ImageSetMaxTiles(image_t *img, /* I - Image to set */
+ int max_tiles) /* I - Number of tiles to cache */
+{
+ int cache_size, /* Size of tile cache in bytes */
+ min_tiles, /* Minimum number of tiles to cache */
+ max_size; /* Maximum cache size in bytes */
+ char *cache_env, /* Cache size environment variable */
+ cache_units[255]; /* Cache size units */
+
+
+ min_tiles = max(TILE_MINIMUM,
+ 1 + max((img->xsize + TILE_SIZE - 1) / TILE_SIZE,
+ (img->ysize + TILE_SIZE - 1) / TILE_SIZE));
+
+ if (max_tiles == 0)
+ max_tiles = ((img->xsize + TILE_SIZE - 1) / TILE_SIZE) *
+ ((img->ysize + TILE_SIZE - 1) / TILE_SIZE);
+
+ cache_size = max_tiles * TILE_SIZE * TILE_SIZE * ImageGetDepth(img);
+
+ if ((cache_env = getenv("RIP_MAX_CACHE")) != NULL)
+ {
+ switch (sscanf(cache_env, "%d%s", &max_size, cache_units))
+ {
+ case 0 :
+ max_size = 32 * 1024 * 1024;
+ break;
+ case 1 :
+ max_size *= 4 * TILE_SIZE * TILE_SIZE;
+ break;
+ case 2 :
+ if (tolower(cache_units[0]) == 'g')
+ max_size *= 1024 * 1024 * 1024;
+ else if (tolower(cache_units[0]) == 'm')
+ max_size *= 1024 * 1024;
+ else if (tolower(cache_units[0]) == 'k')
+ max_size *= 1024;
+ else if (tolower(cache_units[0]) == 't')
+ max_size *= 4 * TILE_SIZE * TILE_SIZE;
+ break;
+ }
+ }
+ else
+ max_size = 32 * 1024 * 1024;
+
+ if (cache_size > max_size)
+ max_tiles = max_size / TILE_SIZE / TILE_SIZE / ImageGetDepth(img);
+
+ if (max_tiles < min_tiles)
+ max_tiles = min_tiles;
+
+ img->max_ics = max_tiles;
+
+#ifdef DEBUG
+ fprintf(stderr, "ImageSetMaxTiles: max_ics=%d...\n", img->max_ics);
+#endif /* DEBUG */
+}
+
+
+/*
+ * 'ImageSetProfile()' - Set the device color profile.
+ */
+
+void
+ImageSetProfile(float d, /* I - Ink/marker density */
+ float g, /* I - Ink/marker gamma */
+ float matrix[3][3]) /* I - Color transform matrix */
+{
+ int i, j, k; /* Looping vars */
+
+
+ ImageHaveProfile = 1;
+
+ for (i = 0; i < 3; i ++)
+ for (j = 0; j < 3; j ++)
+ for (k = 0; k < 256; k ++)
+ ImageMatrix[i][j][k] = (int)(k * matrix[i][j] + 0.5);
+
+ for (k = 0; k < 256; k ++)
+ ImageDensity[k] = 255.0 * d * pow((float)k / 255.0, g) + 0.5;
+}
+
+
+/*
+ * 'ImageGetCol()' - Get a column of pixels from an image.
+ */
+
+int
+ImageGetCol(image_t *img,
+ int x,
+ int y,
+ int height,
+ ib_t *pixels)
+{
+ int bpp,
+ twidth,
+ count;
+ ib_t *ib;
+
+
+ if (img == NULL || x < 0 || x >= img->xsize || y >= img->ysize)
+ return (-1);
+
+ if (y < 0)
+ {
+ height += y;
+ y = 0;
+ }
+
+ if ((y + height) > img->ysize)
+ height = img->ysize - y;
+
+ if (height < 1)
+ return (-1);
+
+ bpp = ImageGetDepth(img);
+ twidth = bpp * (TILE_SIZE - 1);
+
+ while (height > 0)
+ {
+ ib = get_tile(img, x, y);
+
+ if (ib == NULL)
+ return (-1);
+
+ count = TILE_SIZE - (y & (TILE_SIZE - 1));
+ if (count > height)
+ count = height;
+
+ y += count;
+ height -= count;
+
+ for (; count > 0; count --, ib += twidth)
+ switch (bpp)
+ {
+ case 4 :
+ *pixels++ = *ib++;
+ case 3 :
+ *pixels++ = *ib++;
+ *pixels++ = *ib++;
+ case 1 :
+ *pixels++ = *ib++;
+ break;
+ }
+ }
+
+ return (0);
+}
+
+
+/*
+ * 'ImageGetRow()' - Get a row of pixels from an image.
+ */
+
+int
+ImageGetRow(image_t *img,
+ int x,
+ int y,
+ int width,
+ ib_t *pixels)
+{
+ int bpp,
+ count;
+ ib_t *ib;
+
+
+ if (img == NULL || y < 0 || y >= img->ysize || x >= img->xsize)
+ return (-1);
+
+ if (x < 0)
+ {
+ width += x;
+ x = 0;
+ }
+
+ if ((x + width) > img->xsize)
+ width = img->xsize - x;
+
+ if (width < 1)
+ return (-1);
+
+ bpp = img->colorspace < 0 ? -img->colorspace : img->colorspace;
+
+ while (width > 0)
+ {
+ ib = get_tile(img, x, y);
+
+ if (ib == NULL)
+ return (-1);
+
+ count = TILE_SIZE - (x & (TILE_SIZE - 1));
+ if (count > width)
+ count = width;
+ memcpy(pixels, ib, count * bpp);
+ pixels += count * bpp;
+ x += count;
+ width -= count;
+ }
+
+ return (0);
+}
+
+
+/*
+ * 'ImagePutCol()' - Put a column of pixels to an image.
+ */
+
+int
+ImagePutCol(image_t *img,
+ int x,
+ int y,
+ int height,
+ ib_t *pixels)
+{
+ int bpp,
+ twidth,
+ count;
+ int tilex,
+ tiley;
+ ib_t *ib;
+
+
+ if (img == NULL || x < 0 || x >= img->xsize || y >= img->ysize)
+ return (-1);
+
+ if (y < 0)
+ {
+ height += y;
+ y = 0;
+ }
+
+ if ((y + height) > img->ysize)
+ height = img->ysize - y;
+
+ if (height < 1)
+ return (-1);
+
+ bpp = ImageGetDepth(img);
+ twidth = bpp * (TILE_SIZE - 1);
+ tilex = x / TILE_SIZE;
+ tiley = y / TILE_SIZE;
+
+ while (height > 0)
+ {
+ ib = get_tile(img, x, y);
+
+ if (ib == NULL)
+ return (-1);
+
+ img->tiles[tiley][tilex].dirty = 1;
+ tiley ++;
+
+ count = TILE_SIZE - (y & (TILE_SIZE - 1));
+ if (count > height)
+ count = height;
+
+ y += count;
+ height -= count;
+
+ for (; count > 0; count --, ib += twidth)
+ switch (bpp)
+ {
+ case 4 :
+ *ib++ = *pixels++;
+ case 3 :
+ *ib++ = *pixels++;
+ *ib++ = *pixels++;
+ case 1 :
+ *ib++ = *pixels++;
+ break;
+ }
+ }
+
+ return (0);
+}
+
+
+/*
+ * 'ImagePutRow()' - Put a row of pixels to an image.
+ */
+
+int
+ImagePutRow(image_t *img,
+ int x,
+ int y,
+ int width,
+ ib_t *pixels)
+{
+ int bpp,
+ count;
+ int tilex,
+ tiley;
+ ib_t *ib;
+
+
+ if (img == NULL || y < 0 || y >= img->ysize || x >= img->xsize)
+ return (-1);
+
+ if (x < 0)
+ {
+ width += x;
+ x = 0;
+ }
+
+ if ((x + width) > img->xsize)
+ width = img->xsize - x;
+
+ if (width < 1)
+ return (-1);
+
+ bpp = img->colorspace < 0 ? -img->colorspace : img->colorspace;
+ tilex = x / TILE_SIZE;
+ tiley = y / TILE_SIZE;
+
+ while (width > 0)
+ {
+ ib = get_tile(img, x, y);
+
+ if (ib == NULL)
+ return (-1);
+
+ img->tiles[tiley][tilex].dirty = 1;
+
+ count = TILE_SIZE - (x & (TILE_SIZE - 1));
+ if (count > width)
+ count = width;
+ memcpy(ib, pixels, count * bpp);
+ pixels += count * bpp;
+ x += count;
+ width -= count;
+ tilex ++;
+ }
+
+ return (0);
+}
+
+
+/*
+ * 'get_tile()' - Get a cached tile.
+ */
+
+static ib_t *
+get_tile(image_t *img,
+ int x,
+ int y)
+{
+ int bpp,
+ tilex,
+ tiley,
+ xtiles,
+ ytiles;
+ ic_t *ic;
+ itile_t *tile;
+
+
+ if (img->tiles == NULL)
+ {
+ xtiles = (img->xsize + TILE_SIZE - 1) / TILE_SIZE;
+ ytiles = (img->ysize + TILE_SIZE - 1) / TILE_SIZE;
+
+#ifdef DEBUG
+ fprintf(stderr, "get_tile: Creating tile array (%dx%d)\n", xtiles, ytiles);
+#endif /* DEBUG */
+
+ img->tiles = calloc(sizeof(itile_t *), ytiles);
+ tile = calloc(sizeof(itile_t), xtiles * ytiles);
+
+ for (tiley = 0; tiley < ytiles; tiley ++)
+ {
+ img->tiles[tiley] = tile;
+ for (tilex = xtiles; tilex > 0; tilex --, tile ++)
+ tile->pos = -1;
+ }
+ }
+
+ bpp = ImageGetDepth(img);
+ tilex = x / TILE_SIZE;
+ tiley = y / TILE_SIZE;
+ x &= (TILE_SIZE - 1);
+ y &= (TILE_SIZE - 1);
+
+ tile = img->tiles[tiley] + tilex;
+
+ if ((ic = tile->ic) == NULL)
+ {
+ if (img->num_ics < img->max_ics)
+ {
+#ifdef DEBUG
+ fputs("get_tile: Allocating new cache tile...\n", stderr);
+#endif /* DEBUG */
+
+ ic = calloc(sizeof(ic_t) + bpp * TILE_SIZE * TILE_SIZE, 1);
+ ic->pixels = ((ib_t *)ic) + sizeof(ic_t);
+
+ img->num_ics ++;
+ }
+ else
+ {
+#ifdef DEBUG
+ fputs("get_tile: Flushing old cache tile...\n", stderr);
+#endif /* DEBUG */
+
+ flush_tile(img);
+ ic = img->first;
+ }
+
+ ic->tile = tile;
+ tile->ic = ic;
+
+ if (tile->pos >= 0)
+ {
+#ifdef DEBUG
+ fprintf(stderr, "get_tile: loading cache tile from file position %d...\n",
+ tile->pos);
+#endif /* DEBUG */
+
+ if (ftell(img->cachefile) != tile->pos)
+ if (fseek(img->cachefile, tile->pos, SEEK_SET))
+ perror("get_tile:");
+
+ fread(ic->pixels, bpp, TILE_SIZE * TILE_SIZE, img->cachefile);
+ }
+ else
+ {
+#ifdef DEBUG
+ fputs("get_tile: Clearing cache tile...\n", stderr);
+#endif /* DEBUG */
+
+ memset(ic->pixels, 0, bpp * TILE_SIZE * TILE_SIZE);
+ }
+ }
+
+ if (ic == img->first)
+ img->first = ic->next;
+ else if (img->first == NULL)
+ img->first = ic;
+
+ if (ic != img->last)
+ {
+ if (img->last != NULL)
+ img->last->next = ic;
+
+ ic->prev = img->last;
+ img->last = ic;
+ }
+
+ ic->next = NULL;
+
+ return (ic->pixels + bpp * (y * TILE_SIZE + x));
+}
+
+
+/*
+ * 'flush_tile()' - Flush the least-recently-used tile in the cache.
+ */
+
+static void
+flush_tile(image_t *img)
+{
+ int bpp;
+ itile_t *tile;
+
+
+
+#ifdef DEBUG
+ fprintf(stderr, "flush_tile(%08x)...\n", img);
+#endif /* DEBUG */
+
+ bpp = ImageGetDepth(img);
+ tile = img->first->tile;
+
+ if (!tile->dirty)
+ {
+ tile->ic = NULL;
+ return;
+ }
+
+ if (img->cachefile == NULL)
+ {
+ cupsTempFile(img->cachename, sizeof(img->cachename));
+
+#ifdef DEBUG
+ fprintf(stderr, "flush_tile: Creating cache file %s...\n", img->cachename);
+#endif /* DEBUG */
+
+ if ((img->cachefile = fopen(img->cachename, "wb+")) == NULL)
+ {
+ fprintf(stderr, "flush_tile: Unable to create swap file - %s\n",
+ strerror(errno));
+ return;
+ }
+ }
+
+ if (tile->pos >= 0)
+ {
+ if (ftell(img->cachefile) != tile->pos)
+ if (fseek(img->cachefile, tile->pos, SEEK_SET))
+ perror("flush_tile:");
+ }
+ else
+ {
+ if (fseek(img->cachefile, 0, SEEK_END))
+ perror("flush_tile:");
+
+ tile->pos = ftell(img->cachefile);
+ }
+
+#ifdef DEBUG
+ fprintf(stderr, "flush_tile: Wrote tile cache at position %d...\n",
+ tile->pos);
+#endif /* DEBUG */
+
+ fwrite(tile->ic->pixels, bpp, TILE_SIZE * TILE_SIZE, img->cachefile);
+ tile->ic = NULL;
+ tile->dirty = 0;
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/filter/image.h b/filter/image.h
new file mode 100644
index 000000000..eb1192dae
--- /dev/null
+++ b/filter/image.h
@@ -0,0 +1,223 @@
+/*
+ * "$Id$"
+ *
+ * Image library definitions for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1993-1999 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ */
+
+#ifndef _IMAGE_H_
+# define _IMAGE_H_
+
+/*
+ * Include necessary headers...
+ */
+
+# include <stdio.h>
+# include <stdlib.h>
+# include <string.h>
+# include <errno.h>
+# include <config.h>
+
+
+/*
+ * Colorspaces...
+ */
+
+# define IMAGE_CMYK -4 /* Cyan, magenta, yellow, and black */
+# define IMAGE_CMY -3 /* Cyan, magenta, and yellow */
+# define IMAGE_BLACK -1 /* Black */
+# define IMAGE_WHITE 1 /* White (luminance) */
+# define IMAGE_RGB 3 /* Red, green, and blue */
+
+/*
+ * Tile definitions...
+ */
+
+# define TILE_SIZE 256 /* 256x256 pixel tiles */
+# define TILE_MINIMUM 10 /* Minimum number of tiles */
+
+/*
+ * min/max/abs macros...
+ */
+
+#ifndef max
+# define max(a,b) ((a) > (b) ? (a) : (b))
+#endif /* !max */
+#ifndef min
+# define min(a,b) ((a) < (b) ? (a) : (b))
+#endif /* !min */
+#ifndef abs
+# define abs(a) ((a) < 0 ? -(a) : (a))
+#endif /* !abs */
+
+
+/*
+ * Image byte type...
+ */
+
+typedef unsigned char ib_t;
+
+/*
+ * Tile cache structure...
+ */
+
+typedef struct ic_str
+{
+ struct ic_str *prev, /* Previous tile in cache */
+ *next; /* Next tile in cache */
+ void *tile; /* Tile this is attached to */
+ ib_t *pixels; /* Pixel data */
+} ic_t;
+
+/*
+ * Tile structure...
+ */
+
+typedef struct
+{
+ int dirty; /* True if tile is dirty */
+ long pos; /* Position of tile on disk (-1 if not written) */
+ ic_t *ic; /* Pixel data */
+} itile_t;
+
+/*
+ * Image structure...
+ */
+
+typedef struct
+{
+ int colorspace; /* Colorspace of image */
+ unsigned xsize, /* Width of image in pixels */
+ ysize, /* Height of image in pixels */
+ xppi, /* X resolution in pixels-per-inch */
+ yppi, /* Y resolution in pixels-per-inch */
+ num_ics, /* Number of cached tiles */
+ max_ics; /* Maximum number of cached tiles */
+ itile_t **tiles; /* Tiles in image */
+ ic_t *first, /* First cached tile in image */
+ *last; /* Last cached tile in image */
+ FILE *cachefile; /* Tile cache file */
+ char cachename[256]; /* Tile cache filename */
+} image_t;
+
+/*
+ * Image row zooming structure...
+ */
+
+typedef struct
+{
+ image_t *img; /* Image to zoom */
+ unsigned xorig,
+ yorig,
+ width, /* Width of input area */
+ height, /* Height of input area */
+ depth, /* Number of bytes per pixel */
+ rotated, /* Non-zero if image needs to be rotated */
+ xsize, /* Width of output image */
+ ysize, /* Height of output image */
+ xmax, /* Maximum input image X position */
+ ymax, /* Maximum input image Y position */
+ xmod, /* Threshold for Bresenheim rounding */
+ ymod; /* ... */
+ int xstep, /* Amount to step for each pixel along X */
+ xincr,
+ instep, /* Amount to step pixel pointer along X */
+ inincr,
+ ystep, /* Amount to step for each pixel along Y */
+ yincr,
+ row; /* Current row */
+ ib_t *rows[2], /* Horizontally scaled pixel data */
+ *in; /* Unscaled input pixel data */
+} izoom_t;
+
+
+/*
+ * Basic image functions...
+ */
+
+extern image_t *ImageOpen(char *filename, int primary, int secondary,
+ int saturation, int hue, ib_t *lut);
+extern void ImageClose(image_t *img);
+extern void ImageSetMaxTiles(image_t *img, int max_tiles);
+extern void ImageSetProfile(float d, float g, float matrix[3][3]);
+
+#define ImageGetDepth(img) ((img)->colorspace < 0 ? -(img)->colorspace : (img)->colorspace)
+extern int ImageGetCol(image_t *img, int x, int y, int height, ib_t *pixels);
+extern int ImageGetRow(image_t *img, int x, int y, int width, ib_t *pixels);
+extern int ImagePutCol(image_t *img, int x, int y, int height, ib_t *pixels);
+extern int ImagePutRow(image_t *img, int x, int y, int width, ib_t *pixels);
+
+/*
+ * File formats...
+ */
+
+extern int ImageReadGIF(image_t *img, FILE *fp, int primary, int secondary,
+ int saturation, int hue, ib_t *lut);
+extern int ImageReadJPEG(image_t *img, FILE *fp, int primary, int secondary,
+ int saturation, int hue, ib_t *lut);
+extern int ImageReadPNG(image_t *img, FILE *fp, int primary, int secondary,
+ int saturation, int hue, ib_t *lut);
+extern int ImageReadPNM(image_t *img, FILE *fp, int primary, int secondary,
+ int saturation, int hue, ib_t *lut);
+extern int ImageReadPhotoCD(image_t *img, FILE *fp, int primary,
+ int secondary, int saturation, int hue, ib_t *lut);
+extern int ImageReadSGI(image_t *img, FILE *fp, int primary, int secondary,
+ int saturation, int hue, ib_t *lut);
+extern int ImageReadSunRaster(image_t *img, FILE *fp, int primary,
+ int secondary, int saturation, int hue, ib_t *lut);
+extern int ImageReadTIFF(image_t *img, FILE *fp, int primary, int secondary,
+ int saturation, int hue, ib_t *lut);
+
+/*
+ * Colorspace conversions...
+ */
+
+extern void ImageWhiteToWhite(ib_t *in, ib_t *out, int count);
+extern void ImageWhiteToRGB(ib_t *in, ib_t *out, int count);
+extern void ImageWhiteToBlack(ib_t *in, ib_t *out, int count);
+extern void ImageWhiteToCMY(ib_t *in, ib_t *out, int count);
+extern void ImageWhiteToCMYK(ib_t *in, ib_t *out, int count);
+
+extern void ImageRGBToWhite(ib_t *in, ib_t *out, int count);
+extern void ImageRGBToRGB(ib_t *in, ib_t *out, int count);
+extern void ImageRGBToBlack(ib_t *in, ib_t *out, int count);
+extern void ImageRGBToCMY(ib_t *in, ib_t *out, int count);
+extern void ImageRGBToCMYK(ib_t *in, ib_t *out, int count);
+
+extern void ImageRGBAdjust(ib_t *pixels, int count, int saturation, int hue);
+
+extern void ImageLut(ib_t *pixels, int count, ib_t *lut);
+
+/*
+ * Image scaling operations...
+ */
+
+extern izoom_t *ImageZoomAlloc(image_t *img, int x0, int y0, int x1, int y1,
+ int xsize, int ysize, int rotated);
+extern void ImageZoomFill(izoom_t *z, int iy);
+extern void ImageZoomQFill(izoom_t *z, int iy);
+extern void ImageZoomFree(izoom_t *z);
+
+
+#endif /* !_IMAGE_H_ */
+
+/*
+ * End of "$Id$".
+ */
diff --git a/filter/imagetops.c b/filter/imagetops.c
new file mode 100644
index 000000000..e6c28eca0
--- /dev/null
+++ b/filter/imagetops.c
@@ -0,0 +1,494 @@
+/*
+ * "$Id$"
+ *
+ * Image file to PostScript filter for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1993-1999 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * Contents:
+ *
+ * main() - Main entry...
+ * ps_hex() - Print binary data as a series of hexadecimal numbers.
+ * ps_ascii85() - Print binary data as a series of base-85 numbers.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "common.h"
+#include "image.h"
+#include <math.h>
+
+
+/*
+ * Globals...
+ */
+
+int Flip = 0, /* Flip/mirror pages */
+ Collate = 0, /* Collate copies? */
+ Copies = 1; /* Number of copies */
+
+
+/*
+ * Local functions...
+ */
+
+static void ps_hex(ib_t *, int);
+static void ps_ascii85(ib_t *, int, int);
+
+
+/*
+ * 'main()' - Main entry...
+ */
+
+int /* O - Exit status */
+main(int argc, /* I - Number of command-line arguments */
+ char *argv[]) /* I - Command-line arguments */
+{
+ image_t *img; /* Image to print */
+ float xprint, /* Printable area */
+ yprint,
+ xinches, /* Total size in inches */
+ yinches;
+ float xsize, /* Total size in points */
+ ysize;
+ int xpages, /* # x pages */
+ ypages, /* # y pages */
+ xpage, /* Current x page */
+ ypage, /* Current y page */
+ page; /* Current page number */
+ int x0, y0, /* Corners of the page in image coords */
+ x1, y1;
+ ib_t *row; /* Current row */
+ int y; /* Current Y coordinate in image */
+ int colorspace; /* Output colorspace */
+ int out_offset, /* Offset into output buffer */
+ out_length; /* Length of output buffer */
+ ppd_file_t *ppd; /* PPD file */
+ int num_options; /* Number of print options */
+ cups_option_t *options; /* Print options */
+ const char *val; /* Option value */
+ int slowcollate; /* Collate copies the slow way */
+ float g; /* Gamma correction value */
+ float b; /* Brightness factor */
+ float zoom; /* Zoom facter */
+ int ppi; /* Pixels-per-inch */
+ int hue, sat; /* Hue and saturation adjustment */
+ int realcopies; /* Real copies being printed */
+
+
+ if (argc != 7)
+ {
+ fputs("ERROR: imagetops job-id user title copies options file\n", stderr);
+ return (1);
+ }
+
+ /*
+ * Process command-line options and write the prolog...
+ */
+
+ zoom = 0.0;
+ ppi = 0;
+ hue = 0;
+ sat = 100;
+ g = 1.0;
+ b = 1.0;
+
+ Copies = atoi(argv[4]);
+
+ options = NULL;
+ num_options = cupsParseOptions(argv[5], 0, &options);
+
+ ppd = SetCommonOptions(num_options, options, 1);
+
+ ppdMarkDefaults(ppd);
+ cupsMarkOptions(ppd, num_options, options);
+
+ if ((val = cupsGetOption("multiple-document-handling", num_options, options)) != NULL)
+ {
+ /*
+ * This IPP attribute is unnecessarily complicated...
+ *
+ * single-document, separate-documents-collated-copies, and
+ * single-document-new-sheet all require collated copies.
+ *
+ * separate-documents-collated-copies allows for uncollated copies.
+ */
+
+ Collate = strcmp(val, "separate-documents-collated-copies") != 0;
+ }
+
+ if ((val = cupsGetOption("Collate", num_options, options)) != NULL &&
+ strcmp(val, "True") == 0)
+ Collate = 1;
+
+ if ((val = cupsGetOption("gamma", num_options, options)) != NULL)
+ g = atoi(val) * 0.001f;
+
+ if ((val = cupsGetOption("brightness", num_options, options)) != NULL)
+ b = atoi(val) * 0.01f;
+
+ if ((val = cupsGetOption("scaling", num_options, options)) != NULL)
+ zoom = atoi(val) * 0.01;
+
+ if ((val = cupsGetOption("ppi", num_options, options)) != NULL)
+ ppi = atoi(val);
+
+ if ((val = cupsGetOption("saturation", num_options, options)) != NULL)
+ sat = atoi(val);
+
+ if ((val = cupsGetOption("hue", num_options, options)) != NULL)
+ hue = atoi(val);
+
+ /*
+ * Open the input image to print...
+ */
+
+ colorspace = ColorDevice ? IMAGE_RGB : IMAGE_WHITE;
+
+ if ((img = ImageOpen(argv[6], colorspace, IMAGE_WHITE, sat, hue, NULL)) == NULL)
+ {
+ fputs("ERROR: Unable to open image file for printing!\n", stderr);
+ ppdClose(ppd);
+ return (1);
+ }
+
+ colorspace = img->colorspace;
+
+ /*
+ * Scale as necessary...
+ */
+
+ xprint = (PageRight - PageLeft) / 72.0;
+ yprint = (PageTop - PageBottom) / 72.0;
+
+ if (zoom == 0.0 && ppi == 0)
+ ppi = img->xppi;
+
+ if (ppi > 0)
+ {
+ /*
+ * Scale the image as neccesary to match the desired pixels-per-inch.
+ */
+
+ xinches = (float)img->xsize / (float)ppi;
+ yinches = (float)img->ysize / (float)ppi;
+ }
+ else
+ {
+ /*
+ * Scale percentage of page size...
+ */
+
+ xsize = xprint * zoom;
+ ysize = xsize * img->ysize / img->xsize;
+
+ if (ysize > (yprint * zoom))
+ {
+ ysize = yprint * zoom;
+ xsize = ysize * img->xsize / img->ysize;
+ }
+
+ xinches = xsize;
+ yinches = ysize;
+ }
+
+ xpages = ceil(xinches / xprint);
+ ypages = ceil(yinches / yprint);
+
+ /*
+ * See if we need to collate, and if so how we need to do it...
+ */
+
+ if (xpages == 1 && ypages == 1)
+ Collate = 0;
+
+ slowcollate = Collate && ppdFindOption(ppd, "Collate") == NULL;
+
+ /*
+ * Write any "exit server" options that have been selected...
+ */
+
+ ppdEmit(ppd, stdout, PPD_ORDER_EXIT);
+
+ /*
+ * Write any JCL commands that are needed to print PostScript code...
+ */
+
+ if (ppd != NULL && ppd->jcl_begin && ppd->jcl_ps)
+ {
+ fputs(ppd->jcl_begin, stdout);
+ ppdEmit(ppd, stdout, PPD_ORDER_JCL);
+ fputs(ppd->jcl_ps, stdout);
+ }
+
+ /*
+ * Start sending the document with any commands needed...
+ */
+
+ puts("%!");
+
+ if (ppd != NULL && ppd->patches != NULL)
+ puts(ppd->patches);
+
+ ppdEmit(ppd, stdout, PPD_ORDER_DOCUMENT);
+ ppdEmit(ppd, stdout, PPD_ORDER_ANY);
+ ppdEmit(ppd, stdout, PPD_ORDER_PROLOG);
+
+ if (g != 1.0 || b != 1.0)
+ printf("{ neg 1 add dup 0 lt { pop 1 } { %.3f exp neg 1 add } "
+ "ifelse %.3f mul } bind settransfer\n", g, b);
+
+ if (Copies > 1 && !slowcollate)
+ {
+ printf("/#copies %d def\n", Copies);
+ realcopies = Copies;
+ Copies = 1;
+ }
+ else
+ realcopies = 1;
+
+ /*
+ * Output the pages...
+ */
+
+ xprint = xinches / xpages;
+ yprint = yinches / ypages;
+ row = malloc(img->xsize * abs(colorspace) + 3);
+
+ for (page = 1; Copies > 0; Copies --)
+ for (xpage = 0; xpage < xpages; xpage ++)
+ for (ypage = 0; ypage < ypages; ypage ++, page ++)
+ {
+ fprintf(stderr, "PAGE: %d %d\n", page, realcopies);
+ fprintf(stderr, "INFO: Printing page %d...\n", page);
+
+ ppdEmit(ppd, stdout, PPD_ORDER_PAGE);
+
+ puts("gsave");
+
+ if (Flip)
+ printf("%.0f 0 translate -1 1 scale\n", PageWidth);
+
+ switch (Orientation)
+ {
+ case 1 : /* Landscape */
+ printf("%.0f 0 translate 90 rotate\n", PageLength);
+ break;
+ case 2 : /* Reverse Portrait */
+ printf("%.0f %.0f translate 180 rotate\n", PageWidth, PageLength);
+ break;
+ case 3 : /* Reverse Landscape */
+ printf("0 %.0f translate -90 rotate\n", PageWidth);
+ break;
+ }
+
+ x0 = img->xsize * xpage / xpages;
+ x1 = img->xsize * (xpage + 1) / xpages - 1;
+ y0 = img->ysize * ypage / ypages;
+ y1 = img->ysize * (ypage + 1) / ypages - 1;
+
+ printf("%.1f %.1f translate\n", PageLeft, PageBottom + 72.0 * yprint);
+ printf("%.3f %.3f scale\n\n",
+ xprint * 72.0 / (x1 - x0 + 1),
+ yprint * 72.0 / (y1 - y0 + 1));
+
+ if (LanguageLevel == 1)
+ {
+ printf("/picture %d string def\n", (x1 - x0 + 1) * abs(colorspace));
+ printf("%d %d 8[1 0 0 -1 0 1]", (x1 - x0 + 1), (y1 - y0 + 1));
+
+ if (colorspace == IMAGE_WHITE)
+ puts("{currentfile picture readhexstring pop} image");
+ else
+ puts("{currentfile picture readhexstring pop} false 3 colorimage");
+
+ for (y = y0; y <= y1; y ++)
+ {
+ ImageGetRow(img, x0, y, x1 - x0 + 1, row);
+ ps_hex(row, (x1 - x0 + 1) * abs(colorspace));
+ }
+ }
+ else
+ {
+ if (colorspace == IMAGE_WHITE)
+ puts("/DeviceGray setcolorspace");
+ else
+ puts("/DeviceRGB setcolorspace");
+
+ printf("<<"
+ "/ImageType 1"
+ "/Width %d"
+ "/Height %d"
+ "/BitsPerComponent 8",
+ x1 - x0 + 1, y1 - y0 + 1);
+
+ if (colorspace == IMAGE_WHITE)
+ fputs("/Decode[0 1]", stdout);
+ else
+ fputs("/Decode[0 1 0 1 0 1]", stdout);
+
+ fputs("/DataSource currentfile /ASCII85Decode filter", stdout);
+
+ if (((x1 - x0 + 1) / xprint) < 100.0)
+ fputs("/Interpolate true", stdout);
+
+ puts("/ImageMatrix[1 0 0 -1 0 1]>>image");
+
+ for (y = y0, out_offset = 0; y <= y1; y ++)
+ {
+ ImageGetRow(img, x0, y, x1 - x0 + 1, row + out_offset);
+
+ out_length = (x1 - x0 + 1) * abs(colorspace) + out_offset;
+ out_offset = out_length & 3;
+
+ ps_ascii85(row, out_length, y == y1);
+
+ if (out_offset > 0)
+ memcpy(row, row + out_length - out_offset, out_offset);
+ }
+ }
+
+ puts("grestore");
+ puts("showpage");
+ }
+
+ /*
+ * End the job with the appropriate JCL command or CTRL-D otherwise.
+ */
+
+ if (ppd != NULL && ppd->jcl_end)
+ fputs(ppd->jcl_end, stdout);
+ else
+ putchar(0x04);
+
+ /*
+ * Close files...
+ */
+
+ ImageClose(img);
+ ppdClose(ppd);
+
+ return (0);
+}
+
+
+/*
+ * 'ps_hex()' - Print binary data as a series of hexadecimal numbers.
+ */
+
+static void
+ps_hex(ib_t *data, /* I - Data to print */
+ int length) /* I - Number of bytes to print */
+{
+ int col;
+ static char *hex = "0123456789ABCDEF";
+
+
+ col = 0;
+
+ while (length > 0)
+ {
+ /*
+ * Put the hex chars out to the file; note that we don't use printf()
+ * for speed reasons...
+ */
+
+ putchar(hex[*data >> 4]);
+ putchar(hex[*data & 15]);
+
+ data ++;
+ length --;
+
+ col = (col + 1) & 31;
+ if (col == 0 && length > 0)
+ putchar('\n');
+ }
+
+ putchar('\n');
+}
+
+
+/*
+ * 'ps_ascii85()' - Print binary data as a series of base-85 numbers.
+ */
+
+static void
+ps_ascii85(ib_t *data, /* I - Data to print */
+ int length, /* I - Number of bytes to print */
+ int last_line) /* I - Last line of raster data? */
+{
+ int i; /* Looping var */
+ unsigned b; /* Binary data word */
+ unsigned char c[5]; /* ASCII85 encoded chars */
+
+
+ while (length > 3)
+ {
+ b = (((((data[0] << 8) | data[1]) << 8) | data[2]) << 8) | data[3];
+
+ if (b == 0)
+ putchar('z');
+ else
+ {
+ c[4] = (b % 85) + '!';
+ b /= 85;
+ c[3] = (b % 85) + '!';
+ b /= 85;
+ c[2] = (b % 85) + '!';
+ b /= 85;
+ c[1] = (b % 85) + '!';
+ b /= 85;
+ c[0] = b + '!';
+
+ fwrite(c, 5, 1, stdout);
+ }
+
+ data += 4;
+ length -= 4;
+ }
+
+ if (last_line)
+ {
+ if (length > 0)
+ {
+ for (b = 0, i = length; i > 0; b = (b << 8) | data[0], data ++, i --);
+
+ c[4] = (b % 85) + '!';
+ b /= 85;
+ c[3] = (b % 85) + '!';
+ b /= 85;
+ c[2] = (b % 85) + '!';
+ b /= 85;
+ c[1] = (b % 85) + '!';
+ b /= 85;
+ c[0] = b + '!';
+
+ fwrite(c, length + 1, 1, stdout);
+ }
+
+ puts("~>");
+ }
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/filter/imagetoraster.c b/filter/imagetoraster.c
new file mode 100644
index 000000000..47aa6c505
--- /dev/null
+++ b/filter/imagetoraster.c
@@ -0,0 +1,3839 @@
+/*
+ * "$Id$"
+ *
+ * Image file to raster filter for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1993-1999 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * Contents:
+ *
+ * main() - Main entry...
+ * format_CMY() - Convert image data to CMY.
+ * format_CMYK() - Convert image data to CMYK.
+ * format_K() - Convert image data to black.
+ * format_KCMY() - Convert image data to KCMY.
+ * format_KCMYcm() - Convert image data to KCMYcm.
+ * format_RGBA() - Convert image data to RGBA.
+ * format_W() - Convert image data to luminance.
+ * format_YMC() - Convert image data to YMC.
+ * format_YMCK() - Convert image data to YMCK.
+ * make_lut() - Make a lookup table given gamma and brightness values.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "common.h"
+#include "image.h"
+#include <cups/raster.h>
+#include <math.h>
+
+
+/*
+ * Globals...
+ */
+
+int Flip = 0, /* Flip/mirror pages */
+ Collate = 0, /* Collate copies? */
+ Copies = 1; /* Number of copies */
+int Floyd16x16[16][16] = /* Traditional Floyd ordered dither */
+ {
+ { 0, 128, 32, 160, 8, 136, 40, 168,
+ 2, 130, 34, 162, 10, 138, 42, 170 },
+ { 192, 64, 224, 96, 200, 72, 232, 104,
+ 194, 66, 226, 98, 202, 74, 234, 106 },
+ { 48, 176, 16, 144, 56, 184, 24, 152,
+ 50, 178, 18, 146, 58, 186, 26, 154 },
+ { 240, 112, 208, 80, 248, 120, 216, 88,
+ 242, 114, 210, 82, 250, 122, 218, 90 },
+ { 12, 140, 44, 172, 4, 132, 36, 164,
+ 14, 142, 46, 174, 6, 134, 38, 166 },
+ { 204, 76, 236, 108, 196, 68, 228, 100,
+ 206, 78, 238, 110, 198, 70, 230, 102 },
+ { 60, 188, 28, 156, 52, 180, 20, 148,
+ 62, 190, 30, 158, 54, 182, 22, 150 },
+ { 252, 124, 220, 92, 244, 116, 212, 84,
+ 254, 126, 222, 94, 246, 118, 214, 86 },
+ { 3, 131, 35, 163, 11, 139, 43, 171,
+ 1, 129, 33, 161, 9, 137, 41, 169 },
+ { 195, 67, 227, 99, 203, 75, 235, 107,
+ 193, 65, 225, 97, 201, 73, 233, 105 },
+ { 51, 179, 19, 147, 59, 187, 27, 155,
+ 49, 177, 17, 145, 57, 185, 25, 153 },
+ { 243, 115, 211, 83, 251, 123, 219, 91,
+ 241, 113, 209, 81, 249, 121, 217, 89 },
+ { 15, 143, 47, 175, 7, 135, 39, 167,
+ 13, 141, 45, 173, 5, 133, 37, 165 },
+ { 207, 79, 239, 111, 199, 71, 231, 103,
+ 205, 77, 237, 109, 197, 69, 229, 101 },
+ { 63, 191, 31, 159, 55, 183, 23, 151,
+ 61, 189, 29, 157, 53, 181, 21, 149 },
+ { 254, 127, 223, 95, 247, 119, 215, 87,
+ 253, 125, 221, 93, 245, 117, 213, 85 }
+ };
+int Floyd8x8[8][8] =
+ {
+ { 0, 32, 8, 40, 2, 34, 10, 42 },
+ { 48, 16, 56, 24, 50, 18, 58, 26 },
+ { 12, 44, 4, 36, 14, 46, 6, 38 },
+ { 60, 28, 52, 20, 62, 30, 54, 22 },
+ { 3, 35, 11, 43, 1, 33, 9, 41 },
+ { 51, 19, 59, 27, 49, 17, 57, 25 },
+ { 15, 47, 7, 39, 13, 45, 5, 37 },
+ { 63, 31, 55, 23, 61, 29, 53, 21 }
+ };
+int Floyd4x4[4][4] =
+ {
+ { 0, 8, 2, 10 },
+ { 12, 4, 14, 6 },
+ { 3, 11, 1, 9 },
+ { 15, 7, 13, 5 }
+ };
+
+ib_t OnPixels[256], /* On-pixel LUT */
+ OffPixels[256]; /* Off-pixel LUT */
+int Planes[] = /* Number of planes for each colorspace */
+ { 1, 3, 4, 1, 3, 3, 4, 4, 4, 6, 4, 4, 1, 1, 1 };
+
+
+/*
+ * Local functions...
+ */
+
+static void exec_choice(cups_page_header_t *header, ppd_choice_t *choice);
+static void format_CMY(cups_page_header_t *header, unsigned char *row, int y, int z, int xsize, int ysize, int yerr0, int yerr1, ib_t *r0, ib_t *r1);
+static void format_CMYK(cups_page_header_t *header, unsigned char *row, int y, int z, int xsize, int ysize, int yerr0, int yerr1, ib_t *r0, ib_t *r1);
+static void format_K(cups_page_header_t *header, unsigned char *row, int y, int z, int xsize, int ysize, int yerr0, int yerr1, ib_t *r0, ib_t *r1);
+static void format_KCMYcm(cups_page_header_t *header, unsigned char *row, int y, int z, int xsize, int ysize, int yerr0, int yerr1, ib_t *r0, ib_t *r1);
+static void format_KCMY(cups_page_header_t *header, unsigned char *row, int y, int z, int xsize, int ysize, int yerr0, int yerr1, ib_t *r0, ib_t *r1);
+#define format_RGB format_CMY
+static void format_RGBA(cups_page_header_t *header, unsigned char *row, int y, int z, int xsize, int ysize, int yerr0, int yerr1, ib_t *r0, ib_t *r1);
+static void format_W(cups_page_header_t *header, unsigned char *row, int y, int z, int xsize, int ysize, int yerr0, int yerr1, ib_t *r0, ib_t *r1);
+static void format_YMC(cups_page_header_t *header, unsigned char *row, int y, int z, int xsize, int ysize, int yerr0, int yerr1, ib_t *r0, ib_t *r1);
+static void format_YMCK(cups_page_header_t *header, unsigned char *row, int y, int z, int xsize, int ysize, int yerr0, int yerr1, ib_t *r0, ib_t *r1);
+static void make_lut(ib_t *, int, float, float);
+
+
+/*
+ * 'main()' - Main entry...
+ */
+
+int /* O - Exit status */
+main(int argc, /* I - Number of command-line arguments */
+ char *argv[]) /* I - Command-line arguments */
+{
+ int i; /* Looping var */
+ image_t *img; /* Image to print */
+ float xprint, /* Printable area */
+ yprint,
+ xinches, /* Total size in inches */
+ yinches;
+ float xsize, /* Total size in points */
+ ysize;
+ int xpages, /* # x pages */
+ ypages, /* # y pages */
+ xpage, /* Current x page */
+ ypage, /* Current y page */
+ xtemp, /* Bitmap width in pixels */
+ ytemp, /* Bitmap height in pixels */
+ page; /* Current page number */
+ int x0, y0, /* Corners of the page in image coords */
+ x1, y1;
+ ppd_file_t *ppd; /* PPD file */
+ ppd_choice_t *choice; /* PPD option choice */
+ char *resolution, /* Output resolution */
+ *media_type; /* Media type */
+ ppd_profile_t *profile; /* Color profile */
+ cups_raster_t *ras; /* Raster stream */
+ cups_page_header_t header; /* Page header */
+ int num_options; /* Number of print options */
+ cups_option_t *options; /* Print options */
+ const char *val; /* Option value */
+ int slowcollate, /* Collate copies the slow way */
+ slowcopies; /* Make copies the "slow" way? */
+ float g; /* Gamma correction value */
+ float b; /* Brightness factor */
+ float zoom; /* Zoom facter */
+ int ppi; /* Pixels-per-inch */
+ int hue, sat; /* Hue and saturation adjustment */
+ izoom_t *z; /* ImageZoom buffer */
+ int primary, /* Primary image colorspace */
+ secondary; /* Secondary image colorspace */
+ ib_t *row, /* Current row */
+ *r0, /* Top row */
+ *r1; /* Bottom row */
+ int y, /* Current Y coordinate on page */
+ iy, /* Current Y coordinate in image */
+ last_iy, /* Previous Y coordinate in image */
+ yerr0, /* Top Y error value */
+ yerr1, /* Bottom Y error value */
+ blank; /* Blank value */
+ ib_t lut[256]; /* Gamma/brightness LUT */
+ int plane, /* Current color plane */
+ num_planes; /* Number of color planes */
+
+
+ if (argc != 7)
+ {
+ fputs("ERROR: imagetoraster job-id user title copies options file\n", stderr);
+ return (1);
+ }
+
+ fprintf(stderr, "INFO: %s %s %s %s %s %s %s\n", argv[0], argv[1], argv[2],
+ argv[3], argv[4], argv[5], argv[6]);
+
+ /*
+ * Process command-line options and write the prolog...
+ */
+
+ zoom = 0.0;
+ ppi = 0;
+ hue = 0;
+ sat = 100;
+ g = 1.0;
+ b = 1.0;
+
+ Copies = atoi(argv[4]);
+
+ options = NULL;
+ num_options = cupsParseOptions(argv[5], 0, &options);
+
+ ppd = SetCommonOptions(num_options, options, 0);
+
+ ppdMarkDefaults(ppd);
+ cupsMarkOptions(ppd, num_options, options);
+
+ if ((val = cupsGetOption("multiple-document-handling", num_options, options)) != NULL)
+ {
+ /*
+ * This IPP attribute is unnecessarily complicated...
+ *
+ * single-document, separate-documents-collated-copies, and
+ * single-document-new-sheet all require collated copies.
+ *
+ * separate-documents-collated-copies allows for uncollated copies.
+ */
+
+ Collate = strcmp(val, "separate-documents-collated-copies") != 0;
+ }
+
+ if ((val = cupsGetOption("Collate", num_options, options)) != NULL &&
+ strcmp(val, "True") == 0)
+ Collate = 1;
+
+ if ((val = cupsGetOption("gamma", num_options, options)) != NULL)
+ g = atoi(val) * 0.001f;
+
+ if ((val = cupsGetOption("brightness", num_options, options)) != NULL)
+ b = atoi(val) * 0.01f;
+
+ if ((val = cupsGetOption("scaling", num_options, options)) != NULL)
+ zoom = atoi(val) * 0.01;
+
+ if ((val = cupsGetOption("ppi", num_options, options)) != NULL)
+ ppi = atoi(val);
+
+ if ((val = cupsGetOption("saturation", num_options, options)) != NULL)
+ sat = atoi(val);
+
+ if ((val = cupsGetOption("hue", num_options, options)) != NULL)
+ hue = atoi(val);
+
+ /*
+ * Set the needed options in the page header...
+ */
+
+ memset(&header, 0, sizeof(header));
+ header.HWResolution[0] = 100;
+ header.HWResolution[1] = 100;
+ header.cupsBitsPerColor = 1;
+ header.cupsColorOrder = CUPS_ORDER_CHUNKED;
+ header.cupsColorSpace = CUPS_CSPACE_RGB;
+
+ if ((choice = ppdFindMarkedChoice(ppd, "ColorModel")) != NULL)
+ exec_choice(&header, choice);
+
+ if ((choice = ppdFindMarkedChoice(ppd, "InputSlot")) != NULL)
+ exec_choice(&header, choice);
+
+ if ((choice = ppdFindMarkedChoice(ppd, "MediaType")) != NULL)
+ {
+ exec_choice(&header, choice);
+
+ media_type = choice->choice;
+ }
+ else
+ media_type = "";
+
+ if ((choice = ppdFindMarkedChoice(ppd, "Resolution")) != NULL)
+ {
+ exec_choice(&header, choice);
+
+ resolution = choice->choice;
+ }
+ else
+ resolution = "";
+
+ /*
+ * Choose the appropriate colorspace and color profile...
+ */
+
+ switch (header.cupsColorSpace)
+ {
+ case CUPS_CSPACE_W :
+ primary = IMAGE_WHITE;
+ secondary = IMAGE_WHITE;
+ header.cupsBitsPerPixel = header.cupsBitsPerColor;
+ break;
+
+ case CUPS_CSPACE_RGB :
+ case CUPS_CSPACE_RGBA :
+ primary = IMAGE_RGB;
+ secondary = IMAGE_RGB;
+
+ if (header.cupsColorOrder == CUPS_ORDER_CHUNKED)
+ {
+ if (header.cupsBitsPerColor >= 8)
+ header.cupsBitsPerPixel = header.cupsBitsPerColor * 3;
+ else
+ header.cupsBitsPerPixel = header.cupsBitsPerColor * 4;
+ }
+ else
+ header.cupsBitsPerPixel = header.cupsBitsPerColor;
+ break;
+
+ case CUPS_CSPACE_K :
+ case CUPS_CSPACE_WHITE :
+ case CUPS_CSPACE_GOLD :
+ case CUPS_CSPACE_SILVER :
+ primary = IMAGE_BLACK;
+ secondary = IMAGE_BLACK;
+ header.cupsBitsPerPixel = header.cupsBitsPerColor;
+ break;
+
+ default :
+ primary = IMAGE_CMYK;
+ secondary = IMAGE_CMYK;
+
+ if (header.cupsColorOrder == CUPS_ORDER_CHUNKED)
+ header.cupsBitsPerPixel = header.cupsBitsPerColor * 4;
+ else
+ header.cupsBitsPerPixel = header.cupsBitsPerColor;
+ break;
+
+ case CUPS_CSPACE_CMY :
+ case CUPS_CSPACE_YMC :
+ primary = IMAGE_CMY;
+ secondary = IMAGE_CMY;
+
+ if (header.cupsColorOrder == CUPS_ORDER_CHUNKED)
+ {
+ if (header.cupsBitsPerColor >= 8)
+ header.cupsBitsPerPixel = 24;
+ else
+ header.cupsBitsPerPixel = header.cupsBitsPerColor * 4;
+ }
+ else
+ header.cupsBitsPerPixel = header.cupsBitsPerColor;
+ break;
+
+ case CUPS_CSPACE_KCMYcm :
+ if (header.cupsBitsPerPixel == 1)
+ {
+ primary = IMAGE_CMY;
+ secondary = IMAGE_CMY;
+
+ if (header.cupsColorOrder == CUPS_ORDER_CHUNKED)
+ header.cupsBitsPerPixel = 8;
+ else
+ header.cupsBitsPerPixel = 1;
+ }
+ else
+ {
+ primary = IMAGE_CMYK;
+ secondary = IMAGE_CMYK;
+
+ if (header.cupsColorOrder == CUPS_ORDER_CHUNKED)
+ header.cupsBitsPerPixel = header.cupsBitsPerColor * 4;
+ else
+ header.cupsBitsPerPixel = header.cupsBitsPerColor;
+ }
+ break;
+ }
+
+ /*
+ * Find a color profile matching the current options...
+ */
+
+ if (ppd != NULL)
+ {
+ for (i = 0, profile = ppd->profiles; i < ppd->num_profiles; i ++, profile ++)
+ if ((strcmp(profile->resolution, resolution) == 0 ||
+ profile->resolution[0] == '-') &&
+ (strcmp(profile->media_type, media_type) == 0 ||
+ profile->media_type[0] == '-'))
+ break;
+
+ /*
+ * If we found a color profile, use it!
+ */
+
+ if (i < ppd->num_profiles)
+ ImageSetProfile(profile->density, profile->gamma, profile->matrix);
+ }
+
+ /*
+ * Create a gamma/brightness LUT...
+ */
+
+ make_lut(lut, primary, g, b);
+
+ /*
+ * Open the input image to print...
+ */
+
+ fputs("INFO: Loading image file...\n", stderr);
+
+ if ((img = ImageOpen(argv[6], primary, secondary, sat, hue, lut)) == NULL)
+ {
+ fputs("ERROR: Unable to open image file for printing!\n", stderr);
+ ppdClose(ppd);
+ return (1);
+ }
+
+ /*
+ * Scale as necessary...
+ */
+
+ if (Orientation & 1)
+ {
+ xprint = (PageTop - PageBottom) / 72.0;
+ yprint = (PageRight - PageLeft) / 72.0;
+ }
+ else
+ {
+ xprint = (PageRight - PageLeft) / 72.0;
+ yprint = (PageTop - PageBottom) / 72.0;
+ }
+
+ if (zoom == 0.0 && ppi == 0)
+ ppi = img->xppi;
+
+ if (ppi > 0)
+ {
+ /*
+ * Scale the image as neccesary to match the desired pixels-per-inch.
+ */
+
+ xinches = (float)img->xsize / (float)ppi;
+ yinches = (float)img->ysize / (float)ppi;
+ }
+ else
+ {
+ /*
+ * Scale percentage of page size...
+ */
+
+ xsize = xprint * zoom;
+ ysize = xsize * img->ysize / img->xsize;
+
+ if (ysize > (yprint * zoom))
+ {
+ ysize = yprint * zoom;
+ xsize = ysize * img->xsize / img->ysize;
+ }
+
+ xinches = xsize;
+ yinches = ysize;
+ }
+
+ xpages = ceil(xinches / xprint);
+ ypages = ceil(yinches / yprint);
+
+ /*
+ * Compute the bitmap size...
+ */
+
+ xprint = xinches / xpages;
+ yprint = yinches / ypages;
+
+ if ((val = cupsGetOption("Page", num_options, options)) != NULL &&
+ strncmp(val, "Custom.", 7) == 0)
+ {
+ if (Orientation & 1)
+ {
+ header.cupsWidth = yprint * header.HWResolution[0];
+ header.cupsHeight = xprint * header.HWResolution[1];
+ header.PageSize[0] = yprint * 72.0;
+ header.PageSize[1] = xprint * 72.0;
+ }
+ else
+ {
+ header.cupsWidth = xprint * header.HWResolution[0];
+ header.cupsHeight = yprint * header.HWResolution[1];
+ header.PageSize[0] = xprint * 72.0;
+ header.PageSize[1] = yprint * 72.0;
+ }
+ }
+ else
+ {
+ header.cupsWidth = (PageRight - PageLeft) * header.HWResolution[0] / 72.0;
+ header.cupsHeight = (PageTop - PageBottom) * header.HWResolution[1] / 72.0;
+ header.PageSize[0] = PageWidth;
+ header.PageSize[1] = PageLength;
+ }
+
+ switch (header.cupsColorOrder)
+ {
+ case CUPS_ORDER_CHUNKED :
+ header.cupsBytesPerLine = (header.cupsBitsPerPixel *
+ header.cupsWidth + 7) / 8;
+ num_planes = 1;
+ break;
+
+ case CUPS_ORDER_BANDED :
+ if (header.cupsColorSpace == CUPS_CSPACE_KCMYcm &&
+ header.cupsBitsPerColor > 1)
+ header.cupsBytesPerLine = (header.cupsBitsPerPixel *
+ header.cupsWidth + 7) / 8 * 4;
+ else
+ header.cupsBytesPerLine = (header.cupsBitsPerPixel *
+ header.cupsWidth + 7) / 8 *
+ Planes[header.cupsColorSpace];
+ num_planes = 1;
+ break;
+
+ case CUPS_ORDER_PLANAR :
+ header.cupsBytesPerLine = (header.cupsBitsPerPixel *
+ header.cupsWidth + 7) / 8;
+ num_planes = Planes[header.cupsColorSpace];
+ break;
+ }
+
+ /*
+ * See if we need to collate, and if so how we need to do it...
+ */
+
+ if (xpages == 1 && ypages == 1)
+ Collate = 0;
+
+ slowcollate = Collate && ppdFindOption(ppd, "Collate") == NULL;
+ if (ppd != NULL)
+ slowcopies = ppd->manual_copies;
+ else
+ slowcopies = 1;
+
+ if (Copies > 1 && !slowcollate && !slowcopies)
+ {
+ header.Collate = (cups_bool_t)Collate;
+ header.NumCopies = Copies;
+
+ Copies = 1;
+ }
+ else
+ header.NumCopies = 1;
+
+ /*
+ * Create the dithering lookup tables...
+ */
+
+ OnPixels[0] = 0x00;
+ OnPixels[255] = 0xff;
+ OffPixels[0] = 0x00;
+ OffPixels[255] = 0xff;
+
+ switch (header.cupsBitsPerColor)
+ {
+ case 2 :
+ for (i = 1; i < 255; i ++)
+ {
+ OnPixels[i] = 0x55 * (i / 85 + 1);
+ OffPixels[i] = 0x55 * (i / 64);
+ }
+ break;
+ case 4 :
+ for (i = 1; i < 255; i ++)
+ {
+ OnPixels[i] = 17 * (i / 17 + 1);
+ OffPixels[i] = 17 * (i / 16);
+ }
+
+ OnPixels[255] = OffPixels[255] = 0xff;
+ break;
+ }
+
+ /*
+ * Output the pages...
+ */
+
+ fprintf(stderr, "DEBUG: cupsWidth = %d\n", header.cupsWidth);
+ fprintf(stderr, "DEBUG: cupsHeight = %d\n", header.cupsHeight);
+ fprintf(stderr, "DEBUG: cupsBitsPerColor = %d\n", header.cupsBitsPerColor);
+ fprintf(stderr, "DEBUG: cupsBitsPerPixel = %d\n", header.cupsBitsPerPixel);
+ fprintf(stderr, "DEBUG: cupsBytesPerLine = %d\n", header.cupsBytesPerLine);
+ fprintf(stderr, "DEBUG: cupsColorOrder = %d\n", header.cupsColorOrder);
+ fprintf(stderr, "DEBUG: cupsColorSpace = %d\n", header.cupsColorSpace);
+ fprintf(stderr, "DEBUG: img->colorspace = %d\n", img->colorspace);
+
+ row = malloc(header.cupsBytesPerLine);
+ ras = cupsRasterOpen(1, CUPS_RASTER_WRITE);
+ blank = img->colorspace < 0 ? 0 : ~0;
+
+ for (i = 0, page = 1; i < Copies; i ++)
+ for (xpage = 0; xpage < xpages; xpage ++)
+ for (ypage = 0; ypage < ypages; ypage ++, page ++)
+ {
+ fprintf(stderr, "INFO: Formatting page %d...\n", page);
+
+ if (Orientation & 1)
+ {
+ x0 = img->xsize * ypage / ypages;
+ x1 = img->xsize * (ypage + 1) / ypages - 1;
+ y0 = img->ysize * xpage / xpages;
+ y1 = img->ysize * (xpage + 1) / xpages - 1;
+
+ xtemp = header.HWResolution[0] * yprint;
+ ytemp = header.HWResolution[1] * xprint;
+ }
+ else
+ {
+ x0 = img->xsize * xpage / xpages;
+ x1 = img->xsize * (xpage + 1) / xpages - 1;
+ y0 = img->ysize * ypage / ypages;
+ y1 = img->ysize * (ypage + 1) / ypages - 1;
+
+ xtemp = header.HWResolution[0] * xprint;
+ ytemp = header.HWResolution[1] * yprint;
+ }
+
+ cupsRasterWriteHeader(ras, &header);
+
+ for (plane = 0; plane < num_planes; plane ++)
+ {
+ /*
+ * Initialize the image "zoom" engine...
+ */
+
+ z = ImageZoomAlloc(img, x0, y0, x1, y1, xtemp, ytemp, Orientation & 1);
+
+ /*
+ * Write leading blank space as needed...
+ */
+
+ if (header.cupsHeight > z->ysize && Orientation < 2)
+ {
+ memset(row, blank, header.cupsBytesPerLine);
+
+ for (y = header.cupsHeight - z->ysize; y > 0; y --)
+ {
+ if (cupsRasterWritePixels(ras, row, header.cupsBytesPerLine) <
+ header.cupsBytesPerLine)
+ {
+ fputs("ERROR: Unable to write raster data to driver!\n", stderr);
+ ImageClose(img);
+ exit(1);
+ }
+ }
+ }
+
+ /*
+ * Then write image data...
+ */
+
+ for (y = z->ysize, yerr0 = 0, yerr1 = z->ysize, iy = 0, last_iy = -2;
+ y > 0;
+ y --)
+ {
+ if (iy != last_iy)
+ {
+ if (header.cupsBitsPerColor >= 8)
+ {
+ /*
+ * Do bilinear interpolation for 8+ bpp images...
+ */
+
+ if ((iy - last_iy) > 1)
+ ImageZoomFill(z, iy);
+
+ ImageZoomFill(z, iy + z->yincr);
+ }
+ else
+ {
+ /*
+ * Just do nearest-neighbor sampling for < 8 bpp images...
+ */
+
+ ImageZoomQFill(z, iy);
+ }
+
+ last_iy = iy;
+ }
+
+ /*
+ * Format this line of raster data for the printer...
+ */
+
+ memset(row, blank, header.cupsBytesPerLine);
+
+ r0 = z->rows[z->row];
+ r1 = z->rows[1 - z->row];
+
+ switch (header.cupsColorSpace)
+ {
+ case CUPS_CSPACE_W :
+ format_W(&header, row, y, plane, z->xsize, z->ysize,
+ yerr0, yerr1, r0, r1);
+ break;
+ case CUPS_CSPACE_RGB :
+ format_RGB(&header, row, y, plane, z->xsize, z->ysize,
+ yerr0, yerr1, r0, r1);
+ break;
+ case CUPS_CSPACE_RGBA :
+ format_RGBA(&header, row, y, plane, z->xsize, z->ysize,
+ yerr0, yerr1, r0, r1);
+ break;
+ case CUPS_CSPACE_K :
+ case CUPS_CSPACE_WHITE :
+ case CUPS_CSPACE_GOLD :
+ case CUPS_CSPACE_SILVER :
+ format_K(&header, row, y, plane, z->xsize, z->ysize,
+ yerr0, yerr1, r0, r1);
+ break;
+ case CUPS_CSPACE_CMY :
+ format_CMY(&header, row, y, plane, z->xsize, z->ysize,
+ yerr0, yerr1, r0, r1);
+ break;
+ case CUPS_CSPACE_YMC :
+ format_YMC(&header, row, y, plane, z->xsize, z->ysize,
+ yerr0, yerr1, r0, r1);
+ break;
+ case CUPS_CSPACE_CMYK :
+ format_CMYK(&header, row, y, plane, z->xsize, z->ysize,
+ yerr0, yerr1, r0, r1);
+ break;
+ case CUPS_CSPACE_YMCK :
+ case CUPS_CSPACE_GMCK :
+ case CUPS_CSPACE_GMCS :
+ format_YMCK(&header, row, y, plane, z->xsize, z->ysize,
+ yerr0, yerr1, r0, r1);
+ break;
+ case CUPS_CSPACE_KCMY :
+ format_KCMY(&header, row, y, plane, z->xsize, z->ysize,
+ yerr0, yerr1, r0, r1);
+ break;
+ case CUPS_CSPACE_KCMYcm :
+ format_KCMYcm(&header, row, y, plane, z->xsize, z->ysize,
+ yerr0, yerr1, r0, r1);
+ break;
+ }
+
+ /*
+ * Write the raster data to the driver...
+ */
+
+ if (cupsRasterWritePixels(ras, row, header.cupsBytesPerLine) <
+ header.cupsBytesPerLine)
+ {
+ fputs("ERROR: Unable to write raster data to driver!\n", stderr);
+ ImageClose(img);
+ exit(1);
+ }
+
+ /*
+ * Compute the next scanline in the image...
+ */
+
+ iy += z->ystep;
+ yerr0 += z->ymod;
+ yerr1 -= z->ymod;
+ if (yerr1 <= 0)
+ {
+ yerr0 -= z->ysize;
+ yerr1 += z->ysize;
+ iy += z->yincr;
+ }
+ }
+
+ /*
+ * Write trailing blank space as needed...
+ */
+
+ if (header.cupsHeight > z->ysize && Orientation >= 2)
+ {
+ memset(row, blank, header.cupsBytesPerLine);
+
+ for (y = header.cupsHeight - z->ysize; y > 0; y --)
+ {
+ if (cupsRasterWritePixels(ras, row, header.cupsBytesPerLine) <
+ header.cupsBytesPerLine)
+ {
+ fputs("ERROR: Unable to write raster data to driver!\n", stderr);
+ ImageClose(img);
+ exit(1);
+ }
+ }
+ }
+
+ /*
+ * Free memory used for the "zoom" engine...
+ */
+
+ ImageZoomFree(z);
+ }
+ }
+
+ /*
+ * Close files...
+ */
+
+ free(row);
+ cupsRasterClose(ras);
+ ImageClose(img);
+ ppdClose(ppd);
+
+ return (0);
+}
+
+
+/*
+ * 'exec_choice()' - Execute PostScript setpagedevice commands as appropriate.
+ */
+
+static void
+exec_choice(cups_page_header_t *header, /* I - Page header */
+ ppd_choice_t *choice) /* I - Option choice to execute */
+{
+ char *code, /* Pointer into code string */
+ *ptr, /* Pointer into name/value string */
+ name[255], /* Name of pagedevice entry */
+ value[1024]; /* Value of pagedevice entry */
+
+
+ for (code = choice->code; *code != '\0';)
+ {
+ /*
+ * Search for the start of a dictionary name...
+ */
+
+ while (*code != '/' && *code != '\0')
+ code ++;
+
+ if (*code == '\0')
+ break;
+
+ /*
+ * Get the name...
+ */
+
+ code ++;
+ for (ptr = name; isalnum(*code) && (ptr - name) < (sizeof(name) - 1);)
+ *ptr++ = *code++;
+ *ptr = '\0';
+
+ /*
+ * The parse the value as needed...
+ */
+
+ while (isspace(*code))
+ code ++;
+
+ if (*code == '\0')
+ break;
+
+ if (*code == '[')
+ {
+ /*
+ * Read array of values...
+ */
+
+ code ++;
+ for (ptr = value;
+ *code != ']' && *code != '\0' &&
+ (ptr - value) < (sizeof(value) - 1);)
+ *ptr++ = *code++;
+ *ptr = '\0';
+ }
+ else if (*code == '(')
+ {
+ /*
+ * Read string value...
+ */
+
+ code ++;
+ for (ptr = value;
+ *code != ')' && *code != '\0' &&
+ (ptr - value) < (sizeof(value) - 1);)
+ if (*code == '\\')
+ {
+ code ++;
+ if (isdigit(*code))
+ *ptr++ = (char)strtol(code, &code, 8);
+ else
+ *ptr++ = *code++;
+ }
+ else
+ *ptr++ = *code++;
+
+ *ptr = '\0';
+ }
+ else if (isdigit(*code) || *code == '-')
+ {
+ /*
+ * Read single number...
+ */
+
+ for (ptr = value;
+ (isdigit(*code) || *code == '-') &&
+ (ptr - value) < (sizeof(value) - 1);)
+ *ptr++ = *code++;
+ *ptr = '\0';
+ }
+ else
+ continue;
+
+ /*
+ * Assign the value as needed...
+ */
+
+ if (strcmp(name, "cupsMediaType") == 0)
+ header->cupsMediaType = atoi(value);
+ else if (strcmp(name, "cupsBitsPerColor") == 0)
+ header->cupsBitsPerColor = atoi(value);
+ else if (strcmp(name, "cupsColorOrder") == 0)
+ header->cupsColorOrder = (cups_order_t)atoi(value);
+ else if (strcmp(name, "cupsColorSpace") == 0)
+ header->cupsColorSpace = (cups_cspace_t)atoi(value);
+ else if (strcmp(name, "cupsCompression") == 0)
+ header->cupsCompression = atoi(value);
+ else if (strcmp(name, "cupsRowCount") == 0)
+ header->cupsRowCount = atoi(value);
+ else if (strcmp(name, "cupsRowFeed") == 0)
+ header->cupsRowFeed = atoi(value);
+ else if (strcmp(name, "cupsRowStep") == 0)
+ header->cupsRowStep = atoi(value);
+ else if (strcmp(name, "HWResolution") == 0)
+ sscanf(value, "%d%d", header->HWResolution + 0, header->HWResolution + 1);
+ else if (strcmp(name, "cupsMediaPosition") == 0)
+ header->MediaPosition = atoi(value);
+ else if (strcmp(name, "MediaType") == 0)
+ strcpy(header->MediaType, value);
+ else if (strcmp(name, "OutputType") == 0)
+ strcpy(header->OutputType, value);
+ }
+}
+
+
+/*
+ * 'format_CMY()' - Convert image data to CMY.
+ */
+
+static void
+format_CMY(cups_page_header_t *header, /* I - Page header */
+ unsigned char *row, /* IO - Bitmap data for device */
+ int y, /* I - Current row */
+ int z, /* I - Current plane */
+ int xsize, /* I - Width of image data */
+ int ysize, /* I - Height of image data */
+ int yerr0, /* I - Top Y error */
+ int yerr1, /* I - Bottom Y error */
+ ib_t *r0, /* I - Primary image data */
+ ib_t *r1) /* I - Image data for interpolation */
+{
+ ib_t *ptr, /* Pointer into row */
+ *cptr, /* Pointer into cyan */
+ *mptr, /* Pointer into magenta */
+ *yptr, /* Pointer into yellow */
+ bitmask; /* Current mask for pixel */
+ int bitoffset; /* Current offset in line */
+ int bandwidth; /* Width of a color band */
+ int x, /* Current X coordinate on page */
+ *dither; /* Pointer into dither array */
+
+
+ if (Orientation == 1 || Orientation == 2)
+ bitoffset = header->cupsBitsPerPixel * (header->cupsWidth - xsize);
+ else
+ bitoffset = 0;
+
+ ptr = row + bitoffset / 8;
+ bandwidth = header->cupsBytesPerLine / 4;
+
+ switch (header->cupsColorOrder)
+ {
+ case CUPS_ORDER_CHUNKED :
+ switch (header->cupsBitsPerColor)
+ {
+ case 1 :
+ bitmask = 64 >> (bitoffset & 7);
+ dither = Floyd16x16[y & 15];
+
+ for (x = xsize ; x > 0; x --)
+ {
+ if (*r0++ > dither[x & 15])
+ *ptr ^= bitmask;
+ bitmask >>= 1;
+
+ if (*r0++ > dither[x & 15])
+ *ptr ^= bitmask;
+ bitmask >>= 1;
+
+ if (*r0++ > dither[x & 15])
+ *ptr ^= bitmask;
+
+ if (bitmask > 1)
+ bitmask >>= 2;
+ else
+ {
+ bitmask = 64;
+ ptr ++;
+ }
+ }
+ break;
+
+ case 2 :
+ dither = Floyd8x8[y & 7];
+
+ for (x = xsize ; x > 0; x --, r0 += 3)
+ {
+ if ((r0[0] & 63) > dither[x & 7])
+ *ptr ^= (0x30 & OnPixels[r0[0]]);
+ else
+ *ptr ^= (0x30 & OffPixels[r0[0]]);
+
+ if ((r0[1] & 63) > dither[x & 7])
+ *ptr ^= (0x0c & OnPixels[r0[1]]);
+ else
+ *ptr ^= (0x0c & OffPixels[r0[1]]);
+
+ if ((r0[2] & 63) > dither[x & 7])
+ *ptr++ ^= (0x03 & OnPixels[r0[2]]);
+ else
+ *ptr++ ^= (0x03 & OffPixels[r0[2]]);
+ }
+ break;
+
+ case 4 :
+ dither = Floyd4x4[y & 3];
+
+ for (x = xsize ; x > 0; x --, r0 += 3)
+ {
+ if ((r0[0] & 15) > dither[x & 3])
+ *ptr++ ^= (0x0f & OnPixels[r0[0]]);
+ else
+ *ptr++ ^= (0x0f & OffPixels[r0[0]]);
+
+ if ((r0[1] & 15) > dither[x & 3])
+ *ptr ^= (0xf0 & OnPixels[r0[1]]);
+ else
+ *ptr ^= (0xf0 & OffPixels[r0[1]]);
+
+ if ((r0[2] & 15) > dither[x & 3])
+ *ptr++ ^= (0x0f & OnPixels[r0[2]]);
+ else
+ *ptr++ ^= (0x0f & OffPixels[r0[2]]);
+ }
+ break;
+
+ case 8 :
+ for (x = xsize * 3; x > 0; x --, r0 ++, r1 ++)
+ if (*r0 == *r1)
+ *ptr++ = *r0;
+ else
+ *ptr++ = (*r0 * yerr0 + *r1 * yerr1) / ysize;
+ break;
+ }
+ break;
+
+ case CUPS_ORDER_BANDED :
+ cptr = ptr;
+ mptr = ptr + bandwidth;
+ yptr = ptr + 2 * bandwidth;
+
+ switch (header->cupsBitsPerColor)
+ {
+ case 1 :
+ bitmask = 0x80 >> (bitoffset & 7);
+ dither = Floyd16x16[y & 15];
+
+ for (x = xsize; x > 0; x --)
+ {
+ if (*r0++ > dither[x & 15])
+ *cptr ^= bitmask;
+ if (*r0++ > dither[x & 15])
+ *mptr ^= bitmask;
+ if (*r0++ > dither[x & 15])
+ *yptr ^= bitmask;
+
+ if (bitmask > 1)
+ bitmask >>= 1;
+ else
+ {
+ bitmask = 0x80;
+ cptr ++;
+ mptr ++;
+ yptr ++;
+ }
+ }
+ break;
+
+ case 2 :
+ bitmask = 0xc0 >> (bitoffset & 7);
+ dither = Floyd8x8[y & 7];
+
+ for (x = xsize; x > 0; x --)
+ {
+ if ((*r0 & 63) > dither[x & 7])
+ *cptr ^= (bitmask & OnPixels[*r0++]);
+ else
+ *cptr ^= (bitmask & OffPixels[*r0++]);
+
+ if ((*r0 & 63) > dither[x & 7])
+ *mptr ^= (bitmask & OnPixels[*r0++]);
+ else
+ *mptr ^= (bitmask & OffPixels[*r0++]);
+
+ if ((*r0 & 63) > dither[x & 7])
+ *yptr ^= (bitmask & OnPixels[*r0++]);
+ else
+ *yptr ^= (bitmask & OffPixels[*r0++]);
+
+ if (bitmask > 3)
+ bitmask >>= 2;
+ else
+ {
+ bitmask = 0xc0;
+
+ cptr ++;
+ mptr ++;
+ yptr ++;
+ }
+ }
+ break;
+
+ case 4 :
+ bitmask = 0xf0 >> (bitoffset & 7);
+ dither = Floyd4x4[y & 3];
+
+ for (x = xsize; x > 0; x --)
+ {
+ if ((*r0 & 15) > dither[x & 3])
+ *cptr ^= (bitmask & OnPixels[*r0++]);
+ else
+ *cptr ^= (bitmask & OffPixels[*r0++]);
+
+ if ((*r0 & 15) > dither[x & 3])
+ *mptr ^= (bitmask & OnPixels[*r0++]);
+ else
+ *mptr ^= (bitmask & OffPixels[*r0++]);
+
+ if ((*r0 & 15) > dither[x & 3])
+ *yptr ^= (bitmask & OnPixels[*r0++]);
+ else
+ *yptr ^= (bitmask & OffPixels[*r0++]);
+
+ if (bitmask == 0xf0)
+ bitmask = 0x0f;
+ else
+ {
+ bitmask = 0xf0;
+
+ cptr ++;
+ mptr ++;
+ yptr ++;
+ }
+ }
+ break;
+
+ case 8 :
+ for (x = xsize; x > 0; x --, r0 += 3, r1 += 3)
+ {
+ if (r0[0] == r1[0])
+ *cptr++ = r0[0];
+ else
+ *cptr++ = (r0[0] * yerr0 + r1[0] * yerr1) / ysize;
+
+ if (r0[1] == r1[1])
+ *mptr++ = r0[1];
+ else
+ *mptr++ = (r0[1] * yerr0 + r1[1] * yerr1) / ysize;
+
+ if (r0[2] == r1[2])
+ *yptr++ = r0[2];
+ else
+ *yptr++ = (r0[2] * yerr0 + r1[2] * yerr1) / ysize;
+ }
+ break;
+ }
+ break;
+
+ case CUPS_ORDER_PLANAR :
+ switch (header->cupsBitsPerColor)
+ {
+ case 1 :
+ bitmask = 0x80 >> (bitoffset & 7);
+ dither = Floyd16x16[y & 15];
+
+ switch (z)
+ {
+ case 0 :
+ for (x = xsize; x > 0; x --, r0 += 3)
+ {
+ if (r0[0] > dither[x & 15])
+ *ptr ^= bitmask;
+
+ if (bitmask > 1)
+ bitmask >>= 1;
+ else
+ {
+ bitmask = 0x80;
+ ptr ++;
+ }
+ }
+ break;
+
+ case 1 :
+ for (x = xsize; x > 0; x --, r0 += 3)
+ {
+ if (r0[1] > dither[x & 15])
+ *ptr ^= bitmask;
+
+ if (bitmask > 1)
+ bitmask >>= 1;
+ else
+ {
+ bitmask = 0x80;
+ ptr ++;
+ }
+ }
+ break;
+
+ case 2 :
+ for (x = xsize; x > 0; x --, r0 += 3)
+ {
+ if (r0[2] > dither[x & 15])
+ *ptr ^= bitmask;
+
+ if (bitmask > 1)
+ bitmask >>= 1;
+ else
+ {
+ bitmask = 0x80;
+ ptr ++;
+ }
+ }
+ break;
+ }
+ break;
+
+ case 2 :
+ bitmask = 0xc0 >> (bitoffset & 7);
+ dither = Floyd8x8[y & 7];
+ r0 += z;
+
+ for (x = xsize; x > 0; x --, r0 += 3)
+ {
+ if ((*r0 & 63) > dither[x & 7])
+ *ptr ^= (bitmask & OnPixels[*r0]);
+ else
+ *ptr ^= (bitmask & OffPixels[*r0]);
+
+ if (bitmask > 3)
+ bitmask >>= 2;
+ else
+ {
+ bitmask = 0xc0;
+
+ ptr ++;
+ }
+ }
+ break;
+
+ case 4 :
+ bitmask = 0xf0 >> (bitoffset & 7);
+ dither = Floyd4x4[y & 3];
+ r0 += z;
+
+ for (x = xsize; x > 0; x --, r0 += 3)
+ {
+ if ((*r0 & 15) > dither[x & 3])
+ *ptr ^= (bitmask & OnPixels[*r0]);
+ else
+ *ptr ^= (bitmask & OffPixels[*r0]);
+
+ if (bitmask == 0xf0)
+ bitmask = 0x0f;
+ else
+ {
+ bitmask = 0xf0;
+
+ ptr ++;
+ }
+ }
+ break;
+
+ case 8 :
+ r0 += z;
+ r1 += z;
+
+ for (x = xsize; x > 0; x --, r0 += 3, r1 += 3)
+ {
+ if (*r0 == *r1)
+ *ptr++ = *r0;
+ else
+ *ptr++ = (*r0 * yerr0 + *r1 * yerr1) / ysize;
+ }
+ break;
+ }
+ break;
+ }
+}
+
+
+/*
+ * 'format_CMYK()' - Convert image data to CMYK.
+ */
+
+static void
+format_CMYK(cups_page_header_t *header, /* I - Page header */
+ unsigned char *row, /* IO - Bitmap data for device */
+ int y, /* I - Current row */
+ int z, /* I - Current plane */
+ int xsize, /* I - Width of image data */
+ int ysize, /* I - Height of image data */
+ int yerr0, /* I - Top Y error */
+ int yerr1, /* I - Bottom Y error */
+ ib_t *r0, /* I - Primary image data */
+ ib_t *r1) /* I - Image data for interpolation */
+{
+ ib_t *ptr, /* Pointer into row */
+ *cptr, /* Pointer into cyan */
+ *mptr, /* Pointer into magenta */
+ *yptr, /* Pointer into yellow */
+ *kptr, /* Pointer into black */
+ bitmask; /* Current mask for pixel */
+ int bitoffset; /* Current offset in line */
+ int bandwidth; /* Width of a color band */
+ int x, /* Current X coordinate on page */
+ *dither; /* Pointer into dither array */
+
+
+ if (Orientation == 1 || Orientation == 2)
+ bitoffset = header->cupsBitsPerPixel * (header->cupsWidth - xsize);
+ else
+ bitoffset = 0;
+
+ ptr = row + bitoffset / 8;
+ bandwidth = header->cupsBytesPerLine / 4;
+
+ switch (header->cupsColorOrder)
+ {
+ case CUPS_ORDER_CHUNKED :
+ switch (header->cupsBitsPerColor)
+ {
+ case 1 :
+ bitmask = 128 >> (bitoffset & 7);
+ dither = Floyd16x16[y & 15];
+
+ for (x = xsize ; x > 0; x --)
+ {
+ if (*r0++ > dither[x & 15])
+ *ptr ^= bitmask;
+ bitmask >>= 1;
+
+ if (*r0++ > dither[x & 15])
+ *ptr ^= bitmask;
+ bitmask >>= 1;
+
+ if (*r0++ > dither[x & 15])
+ *ptr ^= bitmask;
+ bitmask >>= 1;
+
+ if (*r0++ > dither[x & 15])
+ *ptr ^= bitmask;
+
+ if (bitmask > 1)
+ bitmask >>= 1;
+ else
+ {
+ bitmask = 128;
+ ptr ++;
+ }
+ }
+ break;
+
+ case 2 :
+ dither = Floyd8x8[y & 7];
+
+ for (x = xsize ; x > 0; x --, r0 += 4)
+ {
+ if ((r0[0] & 63) > dither[x & 7])
+ *ptr ^= (0xc0 & OnPixels[r0[0]]);
+ else
+ *ptr ^= (0xc0 & OffPixels[r0[0]]);
+
+ if ((r0[1] & 63) > dither[x & 7])
+ *ptr ^= (0x30 & OnPixels[r0[1]]);
+ else
+ *ptr ^= (0x30 & OffPixels[r0[1]]);
+
+ if ((r0[2] & 63) > dither[x & 7])
+ *ptr ^= (0x0c & OnPixels[r0[2]]);
+ else
+ *ptr ^= (0x0c & OffPixels[r0[2]]);
+
+ if ((r0[3] & 63) > dither[x & 7])
+ *ptr++ ^= (0x03 & OnPixels[r0[3]]);
+ else
+ *ptr++ ^= (0x03 & OffPixels[r0[3]]);
+ }
+ break;
+
+ case 4 :
+ dither = Floyd4x4[y & 3];
+
+ for (x = xsize ; x > 0; x --, r0 += 4)
+ {
+ if ((r0[0] & 15) > dither[x & 3])
+ *ptr ^= (0xf0 & OnPixels[r0[0]]);
+ else
+ *ptr ^= (0xf0 & OffPixels[r0[0]]);
+
+ if ((r0[1] & 15) > dither[x & 3])
+ *ptr++ ^= (0x0f & OnPixels[r0[1]]);
+ else
+ *ptr++ ^= (0x0f & OffPixels[r0[1]]);
+
+ if ((r0[2] & 15) > dither[x & 3])
+ *ptr ^= (0xf0 & OnPixels[r0[2]]);
+ else
+ *ptr ^= (0xf0 & OffPixels[r0[2]]);
+
+ if ((r0[3] & 15) > dither[x & 3])
+ *ptr++ ^= (0x0f & OnPixels[r0[3]]);
+ else
+ *ptr++ ^= (0x0f & OffPixels[r0[3]]);
+ }
+ break;
+
+ case 8 :
+ for (x = xsize * 4; x > 0; x --, r0 ++, r1 ++)
+ if (*r0 == *r1)
+ *ptr++ = *r0;
+ else
+ *ptr++ = (*r0 * yerr0 + *r1 * yerr1) / ysize;
+ break;
+ }
+ break;
+
+ case CUPS_ORDER_BANDED :
+ cptr = ptr;
+ mptr = ptr + bandwidth;
+ yptr = ptr + 2 * bandwidth;
+ kptr = ptr + 3 * bandwidth;
+
+ switch (header->cupsBitsPerColor)
+ {
+ case 1 :
+ bitmask = 0x80 >> (bitoffset & 7);
+ dither = Floyd16x16[y & 15];
+
+ for (x = xsize; x > 0; x --)
+ {
+ if (*r0++ > dither[x & 15])
+ *cptr ^= bitmask;
+ if (*r0++ > dither[x & 15])
+ *mptr ^= bitmask;
+ if (*r0++ > dither[x & 15])
+ *yptr ^= bitmask;
+ if (*r0++ > dither[x & 15])
+ *kptr ^= bitmask;
+
+ if (bitmask > 1)
+ bitmask >>= 1;
+ else
+ {
+ bitmask = 0x80;
+ cptr ++;
+ mptr ++;
+ yptr ++;
+ kptr ++;
+ }
+ }
+ break;
+
+ case 2 :
+ bitmask = 0xc0 >> (bitoffset & 7);
+ dither = Floyd8x8[y & 7];
+
+ for (x = xsize; x > 0; x --)
+ {
+ if ((*r0 & 63) > dither[x & 7])
+ *cptr ^= (bitmask & OnPixels[*r0++]);
+ else
+ *cptr ^= (bitmask & OffPixels[*r0++]);
+
+ if ((*r0 & 63) > dither[x & 7])
+ *mptr ^= (bitmask & OnPixels[*r0++]);
+ else
+ *mptr ^= (bitmask & OffPixels[*r0++]);
+
+ if ((*r0 & 63) > dither[x & 7])
+ *yptr ^= (bitmask & OnPixels[*r0++]);
+ else
+ *yptr ^= (bitmask & OffPixels[*r0++]);
+
+ if ((*r0 & 63) > dither[x & 7])
+ *kptr ^= (bitmask & OnPixels[*r0++]);
+ else
+ *kptr ^= (bitmask & OffPixels[*r0++]);
+
+ if (bitmask > 3)
+ bitmask >>= 2;
+ else
+ {
+ bitmask = 0xc0;
+
+ cptr ++;
+ mptr ++;
+ yptr ++;
+ kptr ++;
+ }
+ }
+ break;
+
+ case 4 :
+ bitmask = 0xf0 >> (bitoffset & 7);
+ dither = Floyd4x4[y & 3];
+
+ for (x = xsize; x > 0; x --)
+ {
+ if ((*r0 & 15) > dither[x & 3])
+ *cptr ^= (bitmask & OnPixels[*r0++]);
+ else
+ *cptr ^= (bitmask & OffPixels[*r0++]);
+
+ if ((*r0 & 15) > dither[x & 3])
+ *mptr ^= (bitmask & OnPixels[*r0++]);
+ else
+ *mptr ^= (bitmask & OffPixels[*r0++]);
+
+ if ((*r0 & 15) > dither[x & 3])
+ *yptr ^= (bitmask & OnPixels[*r0++]);
+ else
+ *yptr ^= (bitmask & OffPixels[*r0++]);
+
+ if ((*r0 & 15) > dither[x & 3])
+ *kptr ^= (bitmask & OnPixels[*r0++]);
+ else
+ *kptr ^= (bitmask & OffPixels[*r0++]);
+
+ if (bitmask == 0xf0)
+ bitmask = 0x0f;
+ else
+ {
+ bitmask = 0xf0;
+
+ cptr ++;
+ mptr ++;
+ yptr ++;
+ kptr ++;
+ }
+ }
+ break;
+
+ case 8 :
+ for (x = xsize; x > 0; x --, r0 += 4, r1 += 4)
+ {
+ if (r0[0] == r1[0])
+ *cptr++ = r0[0];
+ else
+ *cptr++ = (r0[0] * yerr0 + r1[0] * yerr1) / ysize;
+
+ if (r0[1] == r1[1])
+ *mptr++ = r0[1];
+ else
+ *mptr++ = (r0[1] * yerr0 + r1[1] * yerr1) / ysize;
+
+ if (r0[2] == r1[2])
+ *yptr++ = r0[2];
+ else
+ *yptr++ = (r0[2] * yerr0 + r1[2] * yerr1) / ysize;
+
+ if (r0[3] == r1[3])
+ *kptr++ = r0[3];
+ else
+ *kptr++ = (r0[3] * yerr0 + r1[3] * yerr1) / ysize;
+ }
+ break;
+ }
+ break;
+
+ case CUPS_ORDER_PLANAR :
+ switch (header->cupsBitsPerColor)
+ {
+ case 1 :
+ bitmask = 0x80 >> (bitoffset & 7);
+ dither = Floyd16x16[y & 15];
+ r0 += z;
+
+ for (x = xsize; x > 0; x --, r0 += 4)
+ {
+ if (*r0 > dither[x & 15])
+ *ptr ^= bitmask;
+
+ if (bitmask > 1)
+ bitmask >>= 1;
+ else
+ {
+ bitmask = 0x80;
+ ptr ++;
+ }
+ }
+ break;
+
+ case 2 :
+ bitmask = 0xc0 >> (bitoffset & 7);
+ dither = Floyd8x8[y & 7];
+ r0 += z;
+
+ for (x = xsize; x > 0; x --, r0 += 4)
+ {
+ if ((*r0 & 63) > dither[x & 7])
+ *ptr ^= (bitmask & OnPixels[*r0]);
+ else
+ *ptr ^= (bitmask & OffPixels[*r0]);
+
+ if (bitmask > 3)
+ bitmask >>= 2;
+ else
+ {
+ bitmask = 0xc0;
+
+ ptr ++;
+ }
+ }
+ break;
+
+ case 4 :
+ bitmask = 0xf0 >> (bitoffset & 7);
+ dither = Floyd4x4[y & 3];
+ r0 += z;
+
+ for (x = xsize; x > 0; x --, r0 += 4)
+ {
+ if ((*r0 & 15) > dither[x & 3])
+ *ptr ^= (bitmask & OnPixels[*r0]);
+ else
+ *ptr ^= (bitmask & OffPixels[*r0]);
+
+ if (bitmask == 0xf0)
+ bitmask = 0x0f;
+ else
+ {
+ bitmask = 0xf0;
+
+ ptr ++;
+ }
+ }
+ break;
+
+ case 8 :
+ r0 += z;
+ r1 += z;
+
+ for (x = xsize; x > 0; x --, r0 += 4, r1 += 4)
+ {
+ if (*r0 == *r1)
+ *ptr++ = *r0;
+ else
+ *ptr++ = (*r0 * yerr0 + *r1 * yerr1) / ysize;
+ }
+ break;
+ }
+ break;
+ }
+}
+
+
+/*
+ * 'format_K()' - Convert image data to black.
+ */
+
+static void
+format_K(cups_page_header_t *header, /* I - Page header */
+ unsigned char *row, /* IO - Bitmap data for device */
+ int y, /* I - Current row */
+ int z, /* I - Current plane */
+ int xsize, /* I - Width of image data */
+ int ysize, /* I - Height of image data */
+ int yerr0, /* I - Top Y error */
+ int yerr1, /* I - Bottom Y error */
+ ib_t *r0, /* I - Primary image data */
+ ib_t *r1) /* I - Image data for interpolation */
+{
+ ib_t *ptr, /* Pointer into row */
+ bitmask; /* Current mask for pixel */
+ int bitoffset; /* Current offset in line */
+ int x, /* Current X coordinate on page */
+ *dither; /* Pointer into dither array */
+
+
+ if (Orientation == 1 || Orientation == 2)
+ bitoffset = header->cupsBitsPerPixel * (header->cupsWidth - xsize);
+ else
+ bitoffset = 0;
+
+ ptr = row + bitoffset / 8;
+
+ switch (header->cupsBitsPerColor)
+ {
+ case 1 :
+ bitmask = 0x80 >> (bitoffset & 7);
+ dither = Floyd16x16[y & 15];
+
+ for (x = xsize; x > 0; x --)
+ {
+ if (*r0++ > dither[x & 15])
+ *ptr ^= bitmask;
+
+ if (bitmask > 1)
+ bitmask >>= 1;
+ else
+ {
+ bitmask = 0x80;
+ ptr ++;
+ }
+ }
+ break;
+
+ case 2 :
+ bitmask = 0xc0 >> (bitoffset & 7);
+ dither = Floyd8x8[y & 7];
+
+ for (x = xsize; x > 0; x --)
+ {
+ if ((*r0 & 63) > dither[x & 7])
+ *ptr ^= (bitmask & OnPixels[*r0++]);
+ else
+ *ptr ^= (bitmask & OffPixels[*r0++]);
+
+ if (bitmask > 3)
+ bitmask >>= 2;
+ else
+ {
+ bitmask = 0xc0;
+
+ ptr ++;
+ }
+ }
+ break;
+
+ case 4 :
+ bitmask = 0xf0 >> (bitoffset & 7);
+ dither = Floyd4x4[y & 3];
+
+ for (x = xsize; x > 0; x --)
+ {
+ if ((*r0 & 15) > dither[x & 3])
+ *ptr ^= (bitmask & OnPixels[*r0++]);
+ else
+ *ptr ^= (bitmask & OffPixels[*r0++]);
+
+ if (bitmask == 0xf0)
+ bitmask = 0x0f;
+ else
+ {
+ bitmask = 0xf0;
+
+ ptr ++;
+ }
+ }
+ break;
+
+ case 8 :
+ for (x = xsize; x > 0; x --, r0 ++, r1 ++)
+ {
+ if (*r0 == *r1)
+ *ptr++ = *r0;
+ else
+ *ptr++ = (*r0 * yerr0 + *r1 * yerr1) / ysize;
+ }
+ break;
+ }
+}
+
+
+/*
+ * 'format_KCMY()' - Convert image data to KCMY.
+ */
+
+static void
+format_KCMY(cups_page_header_t *header, /* I - Page header */
+ unsigned char *row, /* IO - Bitmap data for device */
+ int y, /* I - Current row */
+ int z, /* I - Current plane */
+ int xsize, /* I - Width of image data */
+ int ysize, /* I - Height of image data */
+ int yerr0, /* I - Top Y error */
+ int yerr1, /* I - Bottom Y error */
+ ib_t *r0, /* I - Primary image data */
+ ib_t *r1) /* I - Image data for interpolation */
+{
+ ib_t *ptr, /* Pointer into row */
+ *cptr, /* Pointer into cyan */
+ *mptr, /* Pointer into magenta */
+ *yptr, /* Pointer into yellow */
+ *kptr, /* Pointer into black */
+ bitmask; /* Current mask for pixel */
+ int bitoffset; /* Current offset in line */
+ int bandwidth; /* Width of a color band */
+ int x, /* Current X coordinate on page */
+ *dither; /* Pointer into dither array */
+
+
+ if (Orientation == 1 || Orientation == 2)
+ bitoffset = header->cupsBitsPerPixel * (header->cupsWidth - xsize);
+ else
+ bitoffset = 0;
+
+ ptr = row + bitoffset / 8;
+ bandwidth = header->cupsBytesPerLine / 4;
+
+ switch (header->cupsColorOrder)
+ {
+ case CUPS_ORDER_CHUNKED :
+ switch (header->cupsBitsPerColor)
+ {
+ case 1 :
+ bitmask = 128 >> (bitoffset & 7);
+ dither = Floyd16x16[y & 15];
+
+ for (x = xsize ; x > 0; x --, r0 += 4)
+ {
+ if (r0[3] > dither[x & 15])
+ *ptr ^= bitmask;
+ bitmask >>= 1;
+
+ if (r0[0] > dither[x & 15])
+ *ptr ^= bitmask;
+ bitmask >>= 1;
+
+ if (r0[1] > dither[x & 15])
+ *ptr ^= bitmask;
+ bitmask >>= 1;
+
+ if (r0[2] > dither[x & 15])
+ *ptr ^= bitmask;
+
+ if (bitmask > 1)
+ bitmask >>= 1;
+ else
+ {
+ bitmask = 128;
+ ptr ++;
+ }
+ }
+ break;
+
+ case 2 :
+ dither = Floyd8x8[y & 7];
+
+ for (x = xsize ; x > 0; x --, r0 += 4)
+ {
+ if ((r0[3] & 63) > dither[x & 7])
+ *ptr ^= (0xc0 & OnPixels[r0[3]]);
+ else
+ *ptr ^= (0xc0 & OffPixels[r0[3]]);
+
+ if ((r0[0] & 63) > dither[x & 7])
+ *ptr ^= (0x30 & OnPixels[r0[0]]);
+ else
+ *ptr ^= (0x30 & OffPixels[r0[0]]);
+
+ if ((r0[1] & 63) > dither[x & 7])
+ *ptr ^= (0x0c & OnPixels[r0[1]]);
+ else
+ *ptr ^= (0x0c & OffPixels[r0[1]]);
+
+ if ((r0[2] & 63) > dither[x & 7])
+ *ptr++ ^= (0x03 & OnPixels[r0[2]]);
+ else
+ *ptr++ ^= (0x03 & OffPixels[r0[2]]);
+ }
+ break;
+
+ case 4 :
+ dither = Floyd4x4[y & 3];
+
+ for (x = xsize ; x > 0; x --, r0 += 4)
+ {
+ if ((r0[3] & 15) > dither[x & 3])
+ *ptr ^= (0xf0 & OnPixels[r0[3]]);
+ else
+ *ptr ^= (0xf0 & OffPixels[r0[3]]);
+
+ if ((r0[0] & 15) > dither[x & 3])
+ *ptr++ ^= (0x0f & OnPixels[r0[0]]);
+ else
+ *ptr++ ^= (0x0f & OffPixels[r0[0]]);
+
+ if ((r0[1] & 15) > dither[x & 3])
+ *ptr ^= (0xf0 & OnPixels[r0[1]]);
+ else
+ *ptr ^= (0xf0 & OffPixels[r0[1]]);
+
+ if ((r0[2] & 15) > dither[x & 3])
+ *ptr++ ^= (0x0f & OnPixels[r0[2]]);
+ else
+ *ptr++ ^= (0x0f & OffPixels[r0[2]]);
+ }
+ break;
+
+ case 8 :
+ for (x = xsize; x > 0; x --, r0 += 4, r1 += 4)
+ {
+ if (r0[3] == r1[3])
+ *ptr++ = r0[3];
+ else
+ *ptr++ = (r0[3] * yerr0 + r1[3] * yerr1) / ysize;
+
+ if (r0[0] == r1[0])
+ *ptr++ = r0[0];
+ else
+ *ptr++ = (r0[0] * yerr0 + r1[0] * yerr1) / ysize;
+
+ if (r0[1] == r1[1])
+ *ptr++ = r0[1];
+ else
+ *ptr++ = (r0[1] * yerr0 + r1[1] * yerr1) / ysize;
+
+ if (r0[2] == r1[2])
+ *ptr++ = r0[2];
+ else
+ *ptr++ = (r0[2] * yerr0 + r1[2] * yerr1) / ysize;
+ }
+ break;
+ }
+ break;
+
+ case CUPS_ORDER_BANDED :
+ kptr = ptr;
+ cptr = ptr + bandwidth;
+ mptr = ptr + 2 * bandwidth;
+ yptr = ptr + 3 * bandwidth;
+
+ switch (header->cupsBitsPerColor)
+ {
+ case 1 :
+ bitmask = 0x80 >> (bitoffset & 7);
+ dither = Floyd16x16[y & 15];
+
+ for (x = xsize; x > 0; x --)
+ {
+ if (*r0++ > dither[x & 15])
+ *cptr ^= bitmask;
+ if (*r0++ > dither[x & 15])
+ *mptr ^= bitmask;
+ if (*r0++ > dither[x & 15])
+ *yptr ^= bitmask;
+ if (*r0++ > dither[x & 15])
+ *kptr ^= bitmask;
+
+ if (bitmask > 1)
+ bitmask >>= 1;
+ else
+ {
+ bitmask = 0x80;
+ cptr ++;
+ mptr ++;
+ yptr ++;
+ kptr ++;
+ }
+ }
+ break;
+
+ case 2 :
+ bitmask = 0xc0 >> (bitoffset & 7);
+ dither = Floyd8x8[y & 7];
+
+ for (x = xsize; x > 0; x --)
+ {
+ if ((*r0 & 63) > dither[x & 7])
+ *cptr ^= (bitmask & OnPixels[*r0++]);
+ else
+ *cptr ^= (bitmask & OffPixels[*r0++]);
+
+ if ((*r0 & 63) > dither[x & 7])
+ *mptr ^= (bitmask & OnPixels[*r0++]);
+ else
+ *mptr ^= (bitmask & OffPixels[*r0++]);
+
+ if ((*r0 & 63) > dither[x & 7])
+ *yptr ^= (bitmask & OnPixels[*r0++]);
+ else
+ *yptr ^= (bitmask & OffPixels[*r0++]);
+
+ if ((*r0 & 63) > dither[x & 7])
+ *kptr ^= (bitmask & OnPixels[*r0++]);
+ else
+ *kptr ^= (bitmask & OffPixels[*r0++]);
+
+ if (bitmask > 3)
+ bitmask >>= 2;
+ else
+ {
+ bitmask = 0xc0;
+
+ cptr ++;
+ mptr ++;
+ yptr ++;
+ kptr ++;
+ }
+ }
+ break;
+
+ case 4 :
+ bitmask = 0xf0 >> (bitoffset & 7);
+ dither = Floyd4x4[y & 3];
+
+ for (x = xsize; x > 0; x --)
+ {
+ if ((*r0 & 15) > dither[x & 3])
+ *cptr ^= (bitmask & OnPixels[*r0++]);
+ else
+ *cptr ^= (bitmask & OffPixels[*r0++]);
+
+ if ((*r0 & 15) > dither[x & 3])
+ *mptr ^= (bitmask & OnPixels[*r0++]);
+ else
+ *mptr ^= (bitmask & OffPixels[*r0++]);
+
+ if ((*r0 & 15) > dither[x & 3])
+ *yptr ^= (bitmask & OnPixels[*r0++]);
+ else
+ *yptr ^= (bitmask & OffPixels[*r0++]);
+
+ if ((*r0 & 15) > dither[x & 3])
+ *kptr ^= (bitmask & OnPixels[*r0++]);
+ else
+ *kptr ^= (bitmask & OffPixels[*r0++]);
+
+ if (bitmask == 0xf0)
+ bitmask = 0x0f;
+ else
+ {
+ bitmask = 0xf0;
+
+ cptr ++;
+ mptr ++;
+ yptr ++;
+ kptr ++;
+ }
+ }
+ break;
+
+ case 8 :
+ for (x = xsize; x > 0; x --, r0 += 4, r1 += 4)
+ {
+ if (r0[0] == r1[0])
+ *cptr++ = r0[0];
+ else
+ *cptr++ = (r0[0] * yerr0 + r1[0] * yerr1) / ysize;
+
+ if (r0[1] == r1[1])
+ *mptr++ = r0[1];
+ else
+ *mptr++ = (r0[1] * yerr0 + r1[1] * yerr1) / ysize;
+
+ if (r0[2] == r1[2])
+ *yptr++ = r0[2];
+ else
+ *yptr++ = (r0[2] * yerr0 + r1[2] * yerr1) / ysize;
+
+ if (r0[3] == r1[3])
+ *kptr++ = r0[3];
+ else
+ *kptr++ = (r0[3] * yerr0 + r1[3] * yerr1) / ysize;
+ }
+ break;
+ }
+ break;
+
+ case CUPS_ORDER_PLANAR :
+ switch (header->cupsBitsPerColor)
+ {
+ case 1 :
+ bitmask = 0x80 >> (bitoffset & 7);
+ dither = Floyd16x16[y & 15];
+ if (z == 0)
+ r0 += 3;
+ else
+ r0 += z - 1;
+
+ for (x = xsize; x > 0; x --, r0 += 4)
+ {
+ if (*r0 > dither[x & 15])
+ *ptr ^= bitmask;
+
+ if (bitmask > 1)
+ bitmask >>= 1;
+ else
+ {
+ bitmask = 0x80;
+ ptr ++;
+ }
+ }
+ break;
+
+ case 2 :
+ bitmask = 0xc0 >> (bitoffset & 7);
+ dither = Floyd8x8[y & 7];
+ if (z == 0)
+ r0 += 3;
+ else
+ r0 += z - 1;
+
+ for (x = xsize; x > 0; x --, r0 += 4)
+ {
+ if ((*r0 & 63) > dither[x & 7])
+ *ptr ^= (bitmask & OnPixels[*r0]);
+ else
+ *ptr ^= (bitmask & OffPixels[*r0]);
+
+ if (bitmask > 3)
+ bitmask >>= 2;
+ else
+ {
+ bitmask = 0xc0;
+
+ ptr ++;
+ }
+ }
+ break;
+
+ case 4 :
+ bitmask = 0xf0 >> (bitoffset & 7);
+ dither = Floyd4x4[y & 3];
+ if (z == 0)
+ r0 += 3;
+ else
+ r0 += z - 1;
+
+ for (x = xsize; x > 0; x --, r0 += 4)
+ {
+ if ((*r0 & 15) > dither[x & 3])
+ *ptr ^= (bitmask & OnPixels[*r0]);
+ else
+ *ptr ^= (bitmask & OffPixels[*r0]);
+
+ if (bitmask == 0xf0)
+ bitmask = 0x0f;
+ else
+ {
+ bitmask = 0xf0;
+
+ ptr ++;
+ }
+ }
+ break;
+
+ case 8 :
+ if (z == 0)
+ {
+ r0 += 3;
+ r1 += 3;
+ }
+ else
+ {
+ r0 += z - 1;
+ r1 += z - 1;
+ }
+
+ for (x = xsize; x > 0; x --, r0 += 4, r1 += 4)
+ {
+ if (*r0 == *r1)
+ *ptr++ = *r0;
+ else
+ *ptr++ = (*r0 * yerr0 + *r1 * yerr1) / ysize;
+ }
+ break;
+ }
+ break;
+ }
+}
+
+
+/*
+ * 'format_KCMYcm()' - Convert image data to KCMYcm.
+ */
+
+static void
+format_KCMYcm(cups_page_header_t *header,/* I - Page header */
+ unsigned char *row, /* IO - Bitmap data for device */
+ int y, /* I - Current row */
+ int z, /* I - Current plane */
+ int xsize, /* I - Width of image data */
+ int ysize, /* I - Height of image data */
+ int yerr0, /* I - Top Y error */
+ int yerr1, /* I - Bottom Y error */
+ ib_t *r0, /* I - Primary image data */
+ ib_t *r1) /* I - Image data for interpolation */
+{
+ int pc, pm, py, pk; /* Cyan, magenta, yellow, and black values */
+ ib_t *ptr, /* Pointer into row */
+ *cptr, /* Pointer into cyan */
+ *mptr, /* Pointer into magenta */
+ *yptr, /* Pointer into yellow */
+ *kptr, /* Pointer into black */
+ *lcptr, /* Pointer into light cyan */
+ *lmptr, /* Pointer into light magenta */
+ bitmask; /* Current mask for pixel */
+ int bitoffset; /* Current offset in line */
+ int bandwidth; /* Width of a color band */
+ int x, /* Current X coordinate on page */
+ *dither; /* Pointer into dither array */
+
+
+ if (Orientation == 1 || Orientation == 2)
+ bitoffset = header->cupsBitsPerPixel * (header->cupsWidth - xsize);
+ else
+ bitoffset = 0;
+
+ ptr = row + bitoffset / 8;
+ if (header->cupsBitsPerColor == 1)
+ bandwidth = header->cupsBytesPerLine / 6;
+ else
+ bandwidth = header->cupsBytesPerLine / 4;
+
+ switch (header->cupsColorOrder)
+ {
+ case CUPS_ORDER_CHUNKED :
+ switch (header->cupsBitsPerColor)
+ {
+ case 1 :
+ dither = Floyd16x16[y & 15];
+
+ for (x = xsize ; x > 0; x --)
+ {
+ pc = *r0++ > dither[x & 15];
+ pm = *r0++ > dither[x & 15];
+ py = *r0++ > dither[x & 15];
+ pk = *r0++ > dither[x & 15];
+
+ if (pk)
+ *ptr++ ^= 32; /* Black */
+ else if (pc && pm)
+ *ptr++ ^= 17; /* Blue (cyan + light magenta) */
+ else if (pc && py)
+ *ptr++ ^= 6; /* Green (light cyan + yellow) */
+ else if (pm && py)
+ *ptr++ ^= 12; /* Red (magenta + yellow) */
+ else if (pc)
+ *ptr++ ^= 16;
+ else if (pm)
+ *ptr++ ^= 8;
+ else if (py)
+ *ptr++ ^= 4;
+ }
+ break;
+
+ case 8 :
+ for (x = xsize; x > 0; x --, r0 += 4, r1 += 4)
+ {
+ if (r0[3] == r1[3])
+ *ptr++ = r0[3];
+ else
+ *ptr++ = (r0[3] * yerr0 + r1[3] * yerr1) / ysize;
+
+ if (r0[0] == r1[0])
+ *ptr++ = r0[0];
+ else
+ *ptr++ = (r0[0] * yerr0 + r1[0] * yerr1) / ysize;
+
+ if (r0[1] == r1[1])
+ *ptr++ = r0[1];
+ else
+ *ptr++ = (r0[1] * yerr0 + r1[1] * yerr1) / ysize;
+
+ if (r0[2] == r1[2])
+ *ptr++ = r0[2];
+ else
+ *ptr++ = (r0[2] * yerr0 + r1[2] * yerr1) / ysize;
+ }
+ break;
+ }
+ break;
+
+ case CUPS_ORDER_BANDED :
+ kptr = ptr;
+ cptr = ptr + bandwidth;
+ mptr = ptr + 2 * bandwidth;
+ yptr = ptr + 3 * bandwidth;
+ lcptr = ptr + 4 * bandwidth;
+ lmptr = ptr + 5 * bandwidth;
+
+ switch (header->cupsBitsPerColor)
+ {
+ case 1 :
+ bitmask = 0x80 >> (bitoffset & 7);
+ dither = Floyd16x16[y & 15];
+
+ for (x = xsize; x > 0; x --)
+ {
+ pc = *r0++ > dither[x & 15];
+ pm = *r0++ > dither[x & 15];
+ py = *r0++ > dither[x & 15];
+ pk = *r0++ > dither[x & 15];
+
+ if (pk)
+ *kptr ^= bitmask; /* Black */
+ else if (pc && pm)
+ {
+ *cptr ^= bitmask; /* Blue (cyan + light magenta) */
+ *lmptr ^= bitmask;
+ }
+ else if (pc && py)
+ {
+ *lcptr ^= bitmask; /* Green (light cyan + yellow) */
+ *yptr ^= bitmask;
+ }
+ else if (pm && py)
+ {
+ *mptr ^= bitmask; /* Red (magenta + yellow) */
+ *yptr ^= bitmask;
+ }
+ else if (pc)
+ *cptr ^= bitmask;
+ else if (pm)
+ *mptr ^= bitmask;
+ else if (py)
+ *yptr ^= bitmask;
+
+ if (bitmask > 1)
+ bitmask >>= 1;
+ else
+ {
+ bitmask = 0x80;
+ cptr ++;
+ mptr ++;
+ yptr ++;
+ kptr ++;
+ lcptr ++;
+ lmptr ++;
+ }
+ }
+ break;
+
+ case 8 :
+ for (x = xsize; x > 0; x --, r0 += 4, r1 += 4)
+ {
+ if (r0[0] == r1[0])
+ *cptr++ = r0[0];
+ else
+ *cptr++ = (r0[0] * yerr0 + r1[0] * yerr1) / ysize;
+
+ if (r0[1] == r1[1])
+ *mptr++ = r0[1];
+ else
+ *mptr++ = (r0[1] * yerr0 + r1[1] * yerr1) / ysize;
+
+ if (r0[2] == r1[2])
+ *yptr++ = r0[2];
+ else
+ *yptr++ = (r0[2] * yerr0 + r1[2] * yerr1) / ysize;
+
+ if (r0[3] == r1[3])
+ *kptr++ = r0[3];
+ else
+ *kptr++ = (r0[3] * yerr0 + r1[3] * yerr1) / ysize;
+ }
+ break;
+ }
+ break;
+
+ case CUPS_ORDER_PLANAR :
+ switch (header->cupsBitsPerColor)
+ {
+ case 1 :
+ bitmask = 0x80 >> (bitoffset & 7);
+ dither = Floyd16x16[y & 15];
+
+ switch (z)
+ {
+ case 0 :
+ for (x = xsize; x > 0; x --, r0 += 4)
+ {
+ if (r0[3] > dither[x & 15])
+ *ptr ^= bitmask;
+
+ if (bitmask > 1)
+ bitmask >>= 1;
+ else
+ {
+ bitmask = 0x80;
+ ptr ++;
+ }
+ }
+ break;
+
+ case 1 :
+ for (x = xsize; x > 0; x --, r0 += 4)
+ {
+ if (r0[0] > dither[x & 15] &&
+ r0[2] < dither[x & 15])
+ *ptr ^= bitmask;
+
+ if (bitmask > 1)
+ bitmask >>= 1;
+ else
+ {
+ bitmask = 0x80;
+ ptr ++;
+ }
+ }
+ break;
+
+ case 2 :
+ for (x = xsize; x > 0; x --, r0 += 4)
+ {
+ if (r0[1] > dither[x & 15] &&
+ (r0[0] < dither[x & 15] ||
+ r0[2] > dither[x & 15]))
+ *ptr ^= bitmask;
+
+ if (bitmask > 1)
+ bitmask >>= 1;
+ else
+ {
+ bitmask = 0x80;
+ ptr ++;
+ }
+ }
+ break;
+
+ case 3 :
+ for (x = xsize; x > 0; x --, r0 += 4)
+ {
+ if (r0[2] > dither[x & 15] &&
+ (r0[0] < dither[x & 15] ||
+ r0[1] < dither[x & 15]))
+ *ptr ^= bitmask;
+
+ if (bitmask > 1)
+ bitmask >>= 1;
+ else
+ {
+ bitmask = 0x80;
+ ptr ++;
+ }
+ }
+ break;
+
+ case 4 :
+ for (x = xsize; x > 0; x --, r0 += 4)
+ {
+ if (r0[0] > dither[x & 15] &&
+ r0[2] > dither[x & 15])
+ *ptr ^= bitmask;
+
+ if (bitmask > 1)
+ bitmask >>= 1;
+ else
+ {
+ bitmask = 0x80;
+ ptr ++;
+ }
+ }
+ break;
+
+ case 5 :
+ for (x = xsize; x > 0; x --, r0 += 4)
+ {
+ if (r0[0] > dither[x & 15] &&
+ r0[1] > dither[x & 15] &&
+ r0[2] < dither[x & 15])
+ *ptr ^= bitmask;
+
+ if (bitmask > 1)
+ bitmask >>= 1;
+ else
+ {
+ bitmask = 0x80;
+ ptr ++;
+ }
+ }
+ break;
+ }
+ break;
+
+ case 8 :
+ if (z == 0)
+ {
+ r0 += 3;
+ r1 += 3;
+ }
+ else
+ {
+ r0 += z - 1;
+ r1 += z - 1;
+ }
+
+ for (x = xsize; x > 0; x --, r0 += 4, r1 += 4)
+ {
+ if (*r0 == *r1)
+ *ptr++ = *r0;
+ else
+ *ptr++ = (*r0 * yerr0 + *r1 * yerr1) / ysize;
+ }
+ break;
+ }
+ break;
+ }
+}
+
+
+/*
+ * 'format_RGBA()' - Convert image data to RGBA.
+ */
+
+static void
+format_RGBA(cups_page_header_t *header, /* I - Page header */
+ unsigned char *row, /* IO - Bitmap data for device */
+ int y, /* I - Current row */
+ int z, /* I - Current plane */
+ int xsize, /* I - Width of image data */
+ int ysize, /* I - Height of image data */
+ int yerr0, /* I - Top Y error */
+ int yerr1, /* I - Bottom Y error */
+ ib_t *r0, /* I - Primary image data */
+ ib_t *r1) /* I - Image data for interpolation */
+{
+ ib_t *ptr, /* Pointer into row */
+ *cptr, /* Pointer into cyan */
+ *mptr, /* Pointer into magenta */
+ *yptr, /* Pointer into yellow */
+ bitmask; /* Current mask for pixel */
+ int bitoffset; /* Current offset in line */
+ int bandwidth; /* Width of a color band */
+ int x, /* Current X coordinate on page */
+ *dither; /* Pointer into dither array */
+
+
+ if (Orientation == 1 || Orientation == 2)
+ bitoffset = header->cupsBitsPerPixel * (header->cupsWidth - xsize);
+ else
+ bitoffset = 0;
+
+ ptr = row + bitoffset / 8;
+ bandwidth = header->cupsBytesPerLine / 4;
+
+ switch (header->cupsColorOrder)
+ {
+ case CUPS_ORDER_CHUNKED :
+ switch (header->cupsBitsPerColor)
+ {
+ case 1 :
+ bitmask = 128 >> (bitoffset & 7);
+ dither = Floyd16x16[y & 15];
+
+ for (x = xsize ; x > 0; x --)
+ {
+ if (*r0++ > dither[x & 15])
+ *ptr ^= bitmask;
+ bitmask >>= 1;
+
+ if (*r0++ > dither[x & 15])
+ *ptr ^= bitmask;
+ bitmask >>= 1;
+
+ if (*r0++ > dither[x & 15])
+ *ptr ^= bitmask;
+
+ if (bitmask > 2)
+ {
+ *ptr ^= 16;
+ bitmask >>= 2;
+ }
+ else
+ {
+ bitmask = 128;
+ *ptr++ ^= 1;
+ }
+ }
+ break;
+
+ case 2 :
+ dither = Floyd8x8[y & 7];
+
+ for (x = xsize ; x > 0; x --, r0 += 3)
+ {
+ if ((r0[0] & 63) > dither[x & 7])
+ *ptr ^= (0xc0 & OnPixels[r0[0]]);
+ else
+ *ptr ^= (0xc0 & OffPixels[r0[0]]);
+
+ if ((r0[1] & 63) > dither[x & 7])
+ *ptr ^= (0x30 & OnPixels[r0[1]]);
+ else
+ *ptr ^= (0x30 & OffPixels[r0[1]]);
+
+ if ((r0[2] & 63) > dither[x & 7])
+ *ptr ^= (0x0c & OnPixels[r0[2]]);
+ else
+ *ptr ^= (0x0c & OffPixels[r0[2]]);
+
+ *ptr++ ^= 0x03;
+ }
+ break;
+
+ case 4 :
+ dither = Floyd4x4[y & 3];
+
+ for (x = xsize ; x > 0; x --, r0 += 3)
+ {
+ if ((r0[0] & 15) > dither[x & 3])
+ *ptr ^= (0xf0 & OnPixels[r0[0]]);
+ else
+ *ptr ^= (0xf0 & OffPixels[r0[0]]);
+
+ if ((r0[1] & 15) > dither[x & 3])
+ *ptr++ ^= (0x0f & OnPixels[r0[1]]);
+ else
+ *ptr++ ^= (0x0f & OffPixels[r0[1]]);
+
+ if ((r0[2] & 15) > dither[x & 3])
+ *ptr ^= (0xf0 & OnPixels[r0[2]]);
+ else
+ *ptr ^= (0xf0 & OffPixels[r0[2]]);
+
+ *ptr++ ^= 0x0f;
+ }
+ break;
+
+ case 8 :
+ for (x = xsize; x > 0; x --, r0 += 3, r1 += 3)
+ {
+ if (r0[0] == r1[0])
+ *ptr++ = r0[0];
+ else
+ *ptr++ = (r0[0] * yerr0 + r1[0] * yerr1) / ysize;
+
+ if (r0[1] == r1[1])
+ *ptr++ = r0[1];
+ else
+ *ptr++ = (r0[1] * yerr0 + r1[1] * yerr1) / ysize;
+
+ if (r0[2] == r1[2])
+ *ptr++ = r0[2];
+ else
+ *ptr++ = (r0[2] * yerr0 + r1[2] * yerr1) / ysize;
+
+ *ptr++ = 255;
+ }
+ break;
+ }
+ break;
+
+ case CUPS_ORDER_BANDED :
+ cptr = ptr;
+ mptr = ptr + bandwidth;
+ yptr = ptr + 2 * bandwidth;
+
+ memset(ptr + 3 * bandwidth, 255, bandwidth);
+
+ switch (header->cupsBitsPerColor)
+ {
+ case 1 :
+ bitmask = 0x80 >> (bitoffset & 7);
+ dither = Floyd16x16[y & 15];
+
+ for (x = xsize; x > 0; x --)
+ {
+ if (*r0++ > dither[x & 15])
+ *cptr ^= bitmask;
+ if (*r0++ > dither[x & 15])
+ *mptr ^= bitmask;
+ if (*r0++ > dither[x & 15])
+ *yptr ^= bitmask;
+
+ if (bitmask > 1)
+ bitmask >>= 1;
+ else
+ {
+ bitmask = 0x80;
+ cptr ++;
+ mptr ++;
+ yptr ++;
+ }
+ }
+ break;
+
+ case 2 :
+ bitmask = 0xc0 >> (bitoffset & 7);
+ dither = Floyd8x8[y & 7];
+
+ for (x = xsize; x > 0; x --)
+ {
+ if ((*r0 & 63) > dither[x & 7])
+ *cptr ^= (bitmask & OnPixels[*r0++]);
+ else
+ *cptr ^= (bitmask & OffPixels[*r0++]);
+
+ if ((*r0 & 63) > dither[x & 7])
+ *mptr ^= (bitmask & OnPixels[*r0++]);
+ else
+ *mptr ^= (bitmask & OffPixels[*r0++]);
+
+ if ((*r0 & 63) > dither[x & 7])
+ *yptr ^= (bitmask & OnPixels[*r0++]);
+ else
+ *yptr ^= (bitmask & OffPixels[*r0++]);
+
+ if (bitmask > 3)
+ bitmask >>= 2;
+ else
+ {
+ bitmask = 0xc0;
+
+ cptr ++;
+ mptr ++;
+ yptr ++;
+ }
+ }
+ break;
+
+ case 4 :
+ bitmask = 0xf0 >> (bitoffset & 7);
+ dither = Floyd4x4[y & 3];
+
+ for (x = xsize; x > 0; x --)
+ {
+ if ((*r0 & 15) > dither[x & 3])
+ *cptr ^= (bitmask & OnPixels[*r0++]);
+ else
+ *cptr ^= (bitmask & OffPixels[*r0++]);
+
+ if ((*r0 & 15) > dither[x & 3])
+ *mptr ^= (bitmask & OnPixels[*r0++]);
+ else
+ *mptr ^= (bitmask & OffPixels[*r0++]);
+
+ if ((*r0 & 15) > dither[x & 3])
+ *yptr ^= (bitmask & OnPixels[*r0++]);
+ else
+ *yptr ^= (bitmask & OffPixels[*r0++]);
+
+ if (bitmask == 0xf0)
+ bitmask = 0x0f;
+ else
+ {
+ bitmask = 0xf0;
+
+ cptr ++;
+ mptr ++;
+ yptr ++;
+ }
+ }
+ break;
+
+ case 8 :
+ for (x = xsize; x > 0; x --, r0 += 3, r1 += 3)
+ {
+ if (r0[0] == r1[0])
+ *cptr++ = r0[0];
+ else
+ *cptr++ = (r0[0] * yerr0 + r1[0] * yerr1) / ysize;
+
+ if (r0[1] == r1[1])
+ *mptr++ = r0[1];
+ else
+ *mptr++ = (r0[1] * yerr0 + r1[1] * yerr1) / ysize;
+
+ if (r0[2] == r1[2])
+ *yptr++ = r0[2];
+ else
+ *yptr++ = (r0[2] * yerr0 + r1[2] * yerr1) / ysize;
+ }
+ break;
+ }
+ break;
+
+ case CUPS_ORDER_PLANAR :
+ if (z == 3)
+ {
+ memset(row, 255, header->cupsBytesPerLine);
+ break;
+ }
+
+ switch (header->cupsBitsPerColor)
+ {
+ case 1 :
+ bitmask = 0x80 >> (bitoffset & 7);
+ dither = Floyd16x16[y & 15];
+
+ switch (z)
+ {
+ case 0 :
+ for (x = xsize; x > 0; x --, r0 += 3)
+ {
+ if (r0[0] > dither[x & 15])
+ *ptr ^= bitmask;
+
+ if (bitmask > 1)
+ bitmask >>= 1;
+ else
+ {
+ bitmask = 0x80;
+ ptr ++;
+ }
+ }
+ break;
+
+ case 1 :
+ for (x = xsize; x > 0; x --, r0 += 3)
+ {
+ if (r0[1] > dither[x & 15])
+ *ptr ^= bitmask;
+
+ if (bitmask > 1)
+ bitmask >>= 1;
+ else
+ {
+ bitmask = 0x80;
+ ptr ++;
+ }
+ }
+ break;
+
+ case 2 :
+ for (x = xsize; x > 0; x --, r0 += 3)
+ {
+ if (r0[2] > dither[x & 15])
+ *ptr ^= bitmask;
+
+ if (bitmask > 1)
+ bitmask >>= 1;
+ else
+ {
+ bitmask = 0x80;
+ ptr ++;
+ }
+ }
+ break;
+ }
+ break;
+
+ case 2 :
+ bitmask = 0xc0 >> (bitoffset & 7);
+ dither = Floyd8x8[y & 7];
+ r0 += z;
+
+ for (x = xsize; x > 0; x --, r0 += 3)
+ {
+ if ((*r0 & 63) > dither[x & 7])
+ *ptr ^= (bitmask & OnPixels[*r0]);
+ else
+ *ptr ^= (bitmask & OffPixels[*r0]);
+
+ if (bitmask > 3)
+ bitmask >>= 2;
+ else
+ {
+ bitmask = 0xc0;
+
+ ptr ++;
+ }
+ }
+ break;
+
+ case 4 :
+ bitmask = 0xf0 >> (bitoffset & 7);
+ dither = Floyd4x4[y & 3];
+ r0 += z;
+
+ for (x = xsize; x > 0; x --, r0 += 3)
+ {
+ if ((*r0 & 15) > dither[x & 3])
+ *ptr ^= (bitmask & OnPixels[*r0]);
+ else
+ *ptr ^= (bitmask & OffPixels[*r0]);
+
+ if (bitmask == 0xf0)
+ bitmask = 0x0f;
+ else
+ {
+ bitmask = 0xf0;
+
+ ptr ++;
+ }
+ }
+ break;
+
+ case 8 :
+ r0 += z;
+ r1 += z;
+
+ for (x = xsize; x > 0; x --, r0 += 3, r1 += 3)
+ {
+ if (*r0 == *r1)
+ *ptr++ = *r0;
+ else
+ *ptr++ = (*r0 * yerr0 + *r1 * yerr1) / ysize;
+ }
+ break;
+ }
+ break;
+ }
+}
+
+
+/*
+ * 'format_W()' - Convert image data to luminance.
+ */
+
+static void
+format_W(cups_page_header_t *header, /* I - Page header */
+ unsigned char *row, /* IO - Bitmap data for device */
+ int y, /* I - Current row */
+ int z, /* I - Current plane */
+ int xsize, /* I - Width of image data */
+ int ysize, /* I - Height of image data */
+ int yerr0, /* I - Top Y error */
+ int yerr1, /* I - Bottom Y error */
+ ib_t *r0, /* I - Primary image data */
+ ib_t *r1) /* I - Image data for interpolation */
+{
+ ib_t *ptr, /* Pointer into row */
+ bitmask; /* Current mask for pixel */
+ int bitoffset; /* Current offset in line */
+ int x, /* Current X coordinate on page */
+ *dither; /* Pointer into dither array */
+
+
+ if (Orientation == 1 || Orientation == 2)
+ bitoffset = header->cupsBitsPerPixel * (header->cupsWidth - xsize);
+ else
+ bitoffset = 0;
+
+ ptr = row + bitoffset / 8;
+
+ switch (header->cupsBitsPerColor)
+ {
+ case 1 :
+ bitmask = 0x80 >> (bitoffset & 7);
+ dither = Floyd16x16[y & 15];
+
+ for (x = xsize; x > 0; x --)
+ {
+ if (*r0++ > dither[x & 15])
+ *ptr ^= bitmask;
+
+ if (bitmask > 1)
+ bitmask >>= 1;
+ else
+ {
+ bitmask = 0x80;
+ ptr ++;
+ }
+ }
+ break;
+
+ case 2 :
+ bitmask = 0xc0 >> (bitoffset & 7);
+ dither = Floyd8x8[y & 7];
+
+ for (x = xsize; x > 0; x --)
+ {
+ if ((*r0 & 63) > dither[x & 7])
+ *ptr ^= (bitmask & OnPixels[*r0++]);
+ else
+ *ptr ^= (bitmask & OffPixels[*r0++]);
+
+ if (bitmask > 3)
+ bitmask >>= 2;
+ else
+ {
+ bitmask = 0xc0;
+
+ ptr ++;
+ }
+ }
+ break;
+
+ case 4 :
+ bitmask = 0xf0 >> (bitoffset & 7);
+ dither = Floyd4x4[y & 3];
+
+ for (x = xsize; x > 0; x --)
+ {
+ if ((*r0 & 15) > dither[x & 3])
+ *ptr ^= (bitmask & OnPixels[*r0++]);
+ else
+ *ptr ^= (bitmask & OffPixels[*r0++]);
+
+ if (bitmask == 0xf0)
+ bitmask = 0x0f;
+ else
+ {
+ bitmask = 0xf0;
+
+ ptr ++;
+ }
+ }
+ break;
+
+ case 8 :
+ for (x = xsize; x > 0; x --, r0 ++, r1 ++)
+ {
+ if (*r0 == *r1)
+ *ptr++ = *r0;
+ else
+ *ptr++ = (*r0 * yerr0 + *r1 * yerr1) / ysize;
+ }
+ break;
+ }
+}
+
+
+/*
+ * 'format_YMC()' - Convert image data to YMC.
+ */
+
+static void
+format_YMC(cups_page_header_t *header, /* I - Page header */
+ unsigned char *row, /* IO - Bitmap data for device */
+ int y, /* I - Current row */
+ int z, /* I - Current plane */
+ int xsize, /* I - Width of image data */
+ int ysize, /* I - Height of image data */
+ int yerr0, /* I - Top Y error */
+ int yerr1, /* I - Bottom Y error */
+ ib_t *r0, /* I - Primary image data */
+ ib_t *r1) /* I - Image data for interpolation */
+{
+ ib_t *ptr, /* Pointer into row */
+ *cptr, /* Pointer into cyan */
+ *mptr, /* Pointer into magenta */
+ *yptr, /* Pointer into yellow */
+ bitmask; /* Current mask for pixel */
+ int bitoffset; /* Current offset in line */
+ int bandwidth; /* Width of a color band */
+ int x, /* Current X coordinate on page */
+ *dither; /* Pointer into dither array */
+
+
+ if (Orientation == 1 || Orientation == 2)
+ bitoffset = header->cupsBitsPerPixel * (header->cupsWidth - xsize);
+ else
+ bitoffset = 0;
+
+ ptr = row + bitoffset / 8;
+ bandwidth = header->cupsBytesPerLine / 4;
+
+ switch (header->cupsColorOrder)
+ {
+ case CUPS_ORDER_CHUNKED :
+ switch (header->cupsBitsPerColor)
+ {
+ case 1 :
+ bitmask = 64 >> (bitoffset & 7);
+ dither = Floyd16x16[y & 15];
+
+ for (x = xsize ; x > 0; x --, r0 += 3)
+ {
+ if (r0[2] > dither[x & 15])
+ *ptr ^= bitmask;
+ bitmask >>= 1;
+
+ if (r0[1] > dither[x & 15])
+ *ptr ^= bitmask;
+ bitmask >>= 1;
+
+ if (r0[0] > dither[x & 15])
+ *ptr ^= bitmask;
+
+ if (bitmask > 1)
+ bitmask >>= 2;
+ else
+ {
+ bitmask = 64;
+ ptr ++;
+ }
+ }
+ break;
+
+ case 2 :
+ dither = Floyd8x8[y & 7];
+
+ for (x = xsize ; x > 0; x --, r0 += 3)
+ {
+ if ((r0[2] & 63) > dither[x & 7])
+ *ptr ^= (0x30 & OnPixels[r0[2]]);
+ else
+ *ptr ^= (0x30 & OffPixels[r0[2]]);
+
+ if ((r0[1] & 63) > dither[x & 7])
+ *ptr ^= (0x0c & OnPixels[r0[1]]);
+ else
+ *ptr ^= (0x0c & OffPixels[r0[1]]);
+
+ if ((r0[0] & 63) > dither[x & 7])
+ *ptr++ ^= (0x03 & OnPixels[r0[0]]);
+ else
+ *ptr++ ^= (0x03 & OffPixels[r0[0]]);
+ }
+ break;
+
+ case 4 :
+ dither = Floyd4x4[y & 3];
+
+ for (x = xsize ; x > 0; x --, r0 += 3)
+ {
+ if ((r0[2] & 15) > dither[x & 3])
+ *ptr++ ^= (0x0f & OnPixels[r0[2]]);
+ else
+ *ptr++ ^= (0x0f & OffPixels[r0[2]]);
+
+ if ((r0[1] & 15) > dither[x & 3])
+ *ptr ^= (0xf0 & OnPixels[r0[1]]);
+ else
+ *ptr ^= (0xf0 & OffPixels[r0[1]]);
+
+ if ((r0[0] & 15) > dither[x & 3])
+ *ptr++ ^= (0x0f & OnPixels[r0[0]]);
+ else
+ *ptr++ ^= (0x0f & OffPixels[r0[0]]);
+ }
+ break;
+
+ case 8 :
+ for (x = xsize; x > 0; x --, r0 += 3, r1 += 3)
+ {
+ if (r0[2] == r1[2])
+ *ptr++ = r0[2];
+ else
+ *ptr++ = (r0[2] * yerr0 + r1[2] * yerr1) / ysize;
+
+ if (r0[1] == r1[1])
+ *ptr++ = r0[1];
+ else
+ *ptr++ = (r0[1] * yerr0 + r1[1] * yerr1) / ysize;
+
+ if (r0[0] == r1[0])
+ *ptr++ = r0[0];
+ else
+ *ptr++ = (r0[0] * yerr0 + r1[0] * yerr1) / ysize;
+ }
+ break;
+ }
+ break;
+
+ case CUPS_ORDER_BANDED :
+ yptr = ptr;
+ mptr = ptr + bandwidth;
+ cptr = ptr + 2 * bandwidth;
+
+ switch (header->cupsBitsPerColor)
+ {
+ case 1 :
+ bitmask = 0x80 >> (bitoffset & 7);
+ dither = Floyd16x16[y & 15];
+
+ for (x = xsize; x > 0; x --)
+ {
+ if (*r0++ > dither[x & 15])
+ *cptr ^= bitmask;
+ if (*r0++ > dither[x & 15])
+ *mptr ^= bitmask;
+ if (*r0++ > dither[x & 15])
+ *yptr ^= bitmask;
+
+ if (bitmask > 1)
+ bitmask >>= 1;
+ else
+ {
+ bitmask = 0x80;
+ cptr ++;
+ mptr ++;
+ yptr ++;
+ }
+ }
+ break;
+
+ case 2 :
+ bitmask = 0xc0 >> (bitoffset & 7);
+ dither = Floyd8x8[y & 7];
+
+ for (x = xsize; x > 0; x --)
+ {
+ if ((*r0 & 63) > dither[x & 7])
+ *cptr ^= (bitmask & OnPixels[*r0++]);
+ else
+ *cptr ^= (bitmask & OffPixels[*r0++]);
+
+ if ((*r0 & 63) > dither[x & 7])
+ *mptr ^= (bitmask & OnPixels[*r0++]);
+ else
+ *mptr ^= (bitmask & OffPixels[*r0++]);
+
+ if ((*r0 & 63) > dither[x & 7])
+ *yptr ^= (bitmask & OnPixels[*r0++]);
+ else
+ *yptr ^= (bitmask & OffPixels[*r0++]);
+
+ if (bitmask > 3)
+ bitmask >>= 2;
+ else
+ {
+ bitmask = 0xc0;
+
+ cptr ++;
+ mptr ++;
+ yptr ++;
+ }
+ }
+ break;
+
+ case 4 :
+ bitmask = 0xf0 >> (bitoffset & 7);
+ dither = Floyd4x4[y & 3];
+
+ for (x = xsize; x > 0; x --)
+ {
+ if ((*r0 & 15) > dither[x & 3])
+ *cptr ^= (bitmask & OnPixels[*r0++]);
+ else
+ *cptr ^= (bitmask & OffPixels[*r0++]);
+
+ if ((*r0 & 15) > dither[x & 3])
+ *mptr ^= (bitmask & OnPixels[*r0++]);
+ else
+ *mptr ^= (bitmask & OffPixels[*r0++]);
+
+ if ((*r0 & 15) > dither[x & 3])
+ *yptr ^= (bitmask & OnPixels[*r0++]);
+ else
+ *yptr ^= (bitmask & OffPixels[*r0++]);
+
+ if (bitmask == 0xf0)
+ bitmask = 0x0f;
+ else
+ {
+ bitmask = 0xf0;
+
+ cptr ++;
+ mptr ++;
+ yptr ++;
+ }
+ }
+ break;
+
+ case 8 :
+ for (x = xsize; x > 0; x --, r0 += 3, r1 += 3)
+ {
+ if (r0[0] == r1[0])
+ *cptr++ = r0[0];
+ else
+ *cptr++ = (r0[0] * yerr0 + r1[0] * yerr1) / ysize;
+
+ if (r0[1] == r1[1])
+ *mptr++ = r0[1];
+ else
+ *mptr++ = (r0[1] * yerr0 + r1[1] * yerr1) / ysize;
+
+ if (r0[2] == r1[2])
+ *yptr++ = r0[2];
+ else
+ *yptr++ = (r0[2] * yerr0 + r1[2] * yerr1) / ysize;
+ }
+ break;
+ }
+ break;
+
+ case CUPS_ORDER_PLANAR :
+ switch (header->cupsBitsPerColor)
+ {
+ case 1 :
+ bitmask = 0x80 >> (bitoffset & 7);
+ dither = Floyd16x16[y & 15];
+
+ switch (z)
+ {
+ case 2 :
+ for (x = xsize; x > 0; x --, r0 += 3)
+ {
+ if (r0[0] > dither[x & 15])
+ *ptr ^= bitmask;
+
+ if (bitmask > 1)
+ bitmask >>= 1;
+ else
+ {
+ bitmask = 0x80;
+ ptr ++;
+ }
+ }
+ break;
+
+ case 1 :
+ for (x = xsize; x > 0; x --, r0 += 3)
+ {
+ if (r0[1] > dither[x & 15])
+ *ptr ^= bitmask;
+
+ if (bitmask > 1)
+ bitmask >>= 1;
+ else
+ {
+ bitmask = 0x80;
+ ptr ++;
+ }
+ }
+ break;
+
+ case 0 :
+ for (x = xsize; x > 0; x --, r0 += 3)
+ {
+ if (r0[2] > dither[x & 15])
+ *ptr ^= bitmask;
+
+ if (bitmask > 1)
+ bitmask >>= 1;
+ else
+ {
+ bitmask = 0x80;
+ ptr ++;
+ }
+ }
+ break;
+ }
+ break;
+
+ case 2 :
+ bitmask = 0xc0 >> (bitoffset & 7);
+ dither = Floyd8x8[y & 7];
+ z = 2 - z;
+ r0 += z;
+
+ for (x = xsize; x > 0; x --, r0 += 3)
+ {
+ if ((*r0 & 63) > dither[x & 7])
+ *ptr ^= (bitmask & OnPixels[*r0]);
+ else
+ *ptr ^= (bitmask & OffPixels[*r0]);
+
+ if (bitmask > 3)
+ bitmask >>= 2;
+ else
+ {
+ bitmask = 0xc0;
+
+ ptr ++;
+ }
+ }
+ break;
+
+ case 4 :
+ bitmask = 0xf0 >> (bitoffset & 7);
+ dither = Floyd4x4[y & 3];
+ z = 2 - z;
+ r0 += z;
+
+ for (x = xsize; x > 0; x --, r0 += 3)
+ {
+ if ((*r0 & 15) > dither[x & 3])
+ *ptr ^= (bitmask & OnPixels[*r0]);
+ else
+ *ptr ^= (bitmask & OffPixels[*r0]);
+
+ if (bitmask == 0xf0)
+ bitmask = 0x0f;
+ else
+ {
+ bitmask = 0xf0;
+
+ ptr ++;
+ }
+ }
+ break;
+
+ case 8 :
+ z = 2 - z;
+ r0 += z;
+ r1 += z;
+
+ for (x = xsize; x > 0; x --, r0 += 3, r1 += 3)
+ {
+ if (*r0 == *r1)
+ *ptr++ = *r0;
+ else
+ *ptr++ = (*r0 * yerr0 + *r1 * yerr1) / ysize;
+ }
+ break;
+ }
+ break;
+ }
+}
+
+
+/*
+ * 'format_YMCK()' - Convert image data to YMCK.
+ */
+
+static void
+format_YMCK(cups_page_header_t *header, /* I - Page header */
+ unsigned char *row, /* IO - Bitmap data for device */
+ int y, /* I - Current row */
+ int z, /* I - Current plane */
+ int xsize, /* I - Width of image data */
+ int ysize, /* I - Height of image data */
+ int yerr0, /* I - Top Y error */
+ int yerr1, /* I - Bottom Y error */
+ ib_t *r0, /* I - Primary image data */
+ ib_t *r1) /* I - Image data for interpolation */
+{
+ ib_t *ptr, /* Pointer into row */
+ *cptr, /* Pointer into cyan */
+ *mptr, /* Pointer into magenta */
+ *yptr, /* Pointer into yellow */
+ *kptr, /* Pointer into black */
+ bitmask; /* Current mask for pixel */
+ int bitoffset; /* Current offset in line */
+ int bandwidth; /* Width of a color band */
+ int x, /* Current X coordinate on page */
+ *dither; /* Pointer into dither array */
+
+
+ if (Orientation == 1 || Orientation == 2)
+ bitoffset = header->cupsBitsPerPixel * (header->cupsWidth - xsize);
+ else
+ bitoffset = 0;
+
+ ptr = row + bitoffset / 8;
+ bandwidth = header->cupsBytesPerLine / 4;
+
+ switch (header->cupsColorOrder)
+ {
+ case CUPS_ORDER_CHUNKED :
+ switch (header->cupsBitsPerColor)
+ {
+ case 1 :
+ bitmask = 128 >> (bitoffset & 7);
+ dither = Floyd16x16[y & 15];
+
+ for (x = xsize ; x > 0; x --, r0 += 4)
+ {
+ if (r0[2] > dither[x & 15])
+ *ptr ^= bitmask;
+ bitmask >>= 1;
+
+ if (r0[1] > dither[x & 15])
+ *ptr ^= bitmask;
+ bitmask >>= 1;
+
+ if (r0[0] > dither[x & 15])
+ *ptr ^= bitmask;
+ bitmask >>= 1;
+
+ if (r0[3] > dither[x & 15])
+ *ptr ^= bitmask;
+
+ if (bitmask > 1)
+ bitmask >>= 1;
+ else
+ {
+ bitmask = 128;
+
+ ptr ++;
+ }
+ }
+ break;
+
+ case 2 :
+ dither = Floyd8x8[y & 7];
+
+ for (x = xsize ; x > 0; x --, r0 += 4)
+ {
+ if ((r0[2] & 63) > dither[x & 7])
+ *ptr ^= (0xc0 & OnPixels[r0[2]]);
+ else
+ *ptr ^= (0xc0 & OffPixels[r0[2]]);
+
+ if ((r0[1] & 63) > dither[x & 7])
+ *ptr ^= (0x30 & OnPixels[r0[1]]);
+ else
+ *ptr ^= (0x30 & OffPixels[r0[1]]);
+
+ if ((r0[0] & 63) > dither[x & 7])
+ *ptr ^= (0x0c & OnPixels[r0[0]]);
+ else
+ *ptr ^= (0x0c & OffPixels[r0[0]]);
+
+ if ((r0[3] & 63) > dither[x & 7])
+ *ptr++ ^= (0x03 & OnPixels[r0[3]]);
+ else
+ *ptr++ ^= (0x03 & OffPixels[r0[3]]);
+ }
+ break;
+
+ case 4 :
+ dither = Floyd4x4[y & 3];
+
+ for (x = xsize ; x > 0; x --, r0 += 4)
+ {
+ if ((r0[2] & 15) > dither[x & 3])
+ *ptr ^= (0xf0 & OnPixels[r0[2]]);
+ else
+ *ptr ^= (0xf0 & OffPixels[r0[2]]);
+
+ if ((r0[1] & 15) > dither[x & 3])
+ *ptr++ ^= (0x0f & OnPixels[r0[1]]);
+ else
+ *ptr++ ^= (0x0f & OffPixels[r0[1]]);
+
+ if ((r0[0] & 15) > dither[x & 3])
+ *ptr ^= (0xf0 & OnPixels[r0[0]]);
+ else
+ *ptr ^= (0xf0 & OffPixels[r0[0]]);
+
+ if ((r0[3] & 15) > dither[x & 3])
+ *ptr++ ^= (0x0f & OnPixels[r0[3]]);
+ else
+ *ptr++ ^= (0x0f & OffPixels[r0[3]]);
+ }
+ break;
+
+ case 8 :
+ for (x = xsize; x > 0; x --, r0 += 4, r1 += 4)
+ {
+ if (r0[2] == r1[2])
+ *ptr++ = r0[2];
+ else
+ *ptr++ = (r0[2] * yerr0 + r1[2] * yerr1) / ysize;
+
+ if (r0[1] == r1[1])
+ *ptr++ = r0[1];
+ else
+ *ptr++ = (r0[1] * yerr0 + r1[1] * yerr1) / ysize;
+
+ if (r0[0] == r1[0])
+ *ptr++ = r0[0];
+ else
+ *ptr++ = (r0[0] * yerr0 + r1[0] * yerr1) / ysize;
+
+ if (r0[3] == r1[3])
+ *ptr++ = r0[3];
+ else
+ *ptr++ = (r0[3] * yerr0 + r1[3] * yerr1) / ysize;
+ }
+ break;
+ }
+ break;
+
+ case CUPS_ORDER_BANDED :
+ yptr = ptr;
+ mptr = ptr + bandwidth;
+ cptr = ptr + 2 * bandwidth;
+ kptr = ptr + 3 * bandwidth;
+
+ switch (header->cupsBitsPerColor)
+ {
+ case 1 :
+ bitmask = 0x80 >> (bitoffset & 7);
+ dither = Floyd16x16[y & 15];
+
+ for (x = xsize; x > 0; x --)
+ {
+ if (*r0++ > dither[x & 15])
+ *cptr ^= bitmask;
+ if (*r0++ > dither[x & 15])
+ *mptr ^= bitmask;
+ if (*r0++ > dither[x & 15])
+ *yptr ^= bitmask;
+ if (*r0++ > dither[x & 15])
+ *kptr ^= bitmask;
+
+ if (bitmask > 1)
+ bitmask >>= 1;
+ else
+ {
+ bitmask = 0x80;
+
+ cptr ++;
+ mptr ++;
+ yptr ++;
+ kptr ++;
+ }
+ }
+ break;
+
+ case 2 :
+ bitmask = 0xc0 >> (bitoffset & 7);
+ dither = Floyd8x8[y & 7];
+
+ for (x = xsize; x > 0; x --)
+ {
+ if ((*r0 & 63) > dither[x & 7])
+ *cptr ^= (bitmask & OnPixels[*r0++]);
+ else
+ *cptr ^= (bitmask & OffPixels[*r0++]);
+
+ if ((*r0 & 63) > dither[x & 7])
+ *mptr ^= (bitmask & OnPixels[*r0++]);
+ else
+ *mptr ^= (bitmask & OffPixels[*r0++]);
+
+ if ((*r0 & 63) > dither[x & 7])
+ *yptr ^= (bitmask & OnPixels[*r0++]);
+ else
+ *yptr ^= (bitmask & OffPixels[*r0++]);
+
+ if ((*r0 & 63) > dither[x & 7])
+ *kptr ^= (bitmask & OnPixels[*r0++]);
+ else
+ *kptr ^= (bitmask & OffPixels[*r0++]);
+
+ if (bitmask > 3)
+ bitmask >>= 2;
+ else
+ {
+ bitmask = 0xc0;
+
+ cptr ++;
+ mptr ++;
+ yptr ++;
+ kptr ++;
+ }
+ }
+ break;
+
+ case 4 :
+ bitmask = 0xf0 >> (bitoffset & 7);
+ dither = Floyd4x4[y & 3];
+
+ for (x = xsize; x > 0; x --)
+ {
+ if ((*r0 & 15) > dither[x & 3])
+ *cptr ^= (bitmask & OnPixels[*r0++]);
+ else
+ *cptr ^= (bitmask & OffPixels[*r0++]);
+
+ if ((*r0 & 15) > dither[x & 3])
+ *mptr ^= (bitmask & OnPixels[*r0++]);
+ else
+ *mptr ^= (bitmask & OffPixels[*r0++]);
+
+ if ((*r0 & 15) > dither[x & 3])
+ *yptr ^= (bitmask & OnPixels[*r0++]);
+ else
+ *yptr ^= (bitmask & OffPixels[*r0++]);
+
+ if ((*r0 & 15) > dither[x & 3])
+ *kptr ^= (bitmask & OnPixels[*r0++]);
+ else
+ *kptr ^= (bitmask & OffPixels[*r0++]);
+
+ if (bitmask == 0xf0)
+ bitmask = 0x0f;
+ else
+ {
+ bitmask = 0xf0;
+
+ cptr ++;
+ mptr ++;
+ yptr ++;
+ kptr ++;
+ }
+ }
+ break;
+
+ case 8 :
+ for (x = xsize; x > 0; x --, r0 += 4, r1 += 4)
+ {
+ if (r0[0] == r1[0])
+ *cptr++ = r0[0];
+ else
+ *cptr++ = (r0[0] * yerr0 + r1[0] * yerr1) / ysize;
+
+ if (r0[1] == r1[1])
+ *mptr++ = r0[1];
+ else
+ *mptr++ = (r0[1] * yerr0 + r1[1] * yerr1) / ysize;
+
+ if (r0[2] == r1[2])
+ *yptr++ = r0[2];
+ else
+ *yptr++ = (r0[2] * yerr0 + r1[2] * yerr1) / ysize;
+
+ if (r0[3] == r1[3])
+ *kptr++ = r0[3];
+ else
+ *kptr++ = (r0[3] * yerr0 + r1[3] * yerr1) / ysize;
+ }
+ break;
+ }
+ break;
+
+ case CUPS_ORDER_PLANAR :
+ switch (header->cupsBitsPerColor)
+ {
+ case 1 :
+ bitmask = 0x80 >> (bitoffset & 7);
+ dither = Floyd16x16[y & 15];
+
+ if (z < 3)
+ r0 += 2 - z;
+ else
+ r0 += z;
+
+ for (x = xsize; x > 0; x --, r0 += 4)
+ {
+ if (*r0 > dither[x & 15])
+ *ptr ^= bitmask;
+
+ if (bitmask > 1)
+ bitmask >>= 1;
+ else
+ {
+ bitmask = 0x80;
+ ptr ++;
+ }
+ }
+ break;
+
+ case 2 :
+ bitmask = 0xc0 >> (bitoffset & 7);
+ dither = Floyd8x8[y & 7];
+ if (z == 3)
+ r0 += 3;
+ else
+ r0 += 2 - z;
+
+ for (x = xsize; x > 0; x --, r0 += 4)
+ {
+ if ((*r0 & 63) > dither[x & 7])
+ *ptr ^= (bitmask & OnPixels[*r0]);
+ else
+ *ptr ^= (bitmask & OffPixels[*r0]);
+
+ if (bitmask > 3)
+ bitmask >>= 2;
+ else
+ {
+ bitmask = 0xc0;
+
+ ptr ++;
+ }
+ }
+ break;
+
+ case 4 :
+ bitmask = 0xf0 >> (bitoffset & 7);
+ dither = Floyd4x4[y & 3];
+ if (z == 3)
+ r0 += 3;
+ else
+ r0 += 2 - z;
+
+ for (x = xsize; x > 0; x --, r0 += 4)
+ {
+ if ((*r0 & 15) > dither[x & 3])
+ *ptr ^= (bitmask & OnPixels[*r0]);
+ else
+ *ptr ^= (bitmask & OffPixels[*r0]);
+
+ if (bitmask == 0xf0)
+ bitmask = 0x0f;
+ else
+ {
+ bitmask = 0xf0;
+
+ ptr ++;
+ }
+ }
+ break;
+
+ case 8 :
+ if (z == 3)
+ {
+ r0 += 3;
+ r1 += 3;
+ }
+ else
+ {
+ r0 += 2 - z;
+ r1 += 2 - z;
+ }
+
+ for (x = xsize; x > 0; x --, r0 += 4, r1 += 4)
+ {
+ if (*r0 == *r1)
+ *ptr++ = *r0;
+ else
+ *ptr++ = (*r0 * yerr0 + *r1 * yerr1) / ysize;
+ }
+ break;
+ }
+ break;
+ }
+}
+
+
+/*
+ * 'make_lut()' - Make a lookup table given gamma and brightness values.
+ */
+
+static void
+make_lut(ib_t *lut, /* I - Lookup table */
+ int colorspace, /* I - Colorspace */
+ float g, /* I - Image gamma */
+ float b) /* I - Image brightness */
+{
+ int i; /* Looping var */
+ int v; /* Current value */
+
+
+ g = 1.0 / g;
+ b = 1.0 / b;
+
+ for (i = 0; i < 256; i ++)
+ {
+ if (colorspace < 0)
+ v = 255.0 * b * (1.0 - pow(1.0 - (float)i / 255.0, g)) + 0.5;
+ else
+ v = 255.0 * (1.0 - b * (1.0 - pow((float)i / 255.0, g))) + 0.5;
+
+ if (v < 0)
+ *lut++ = 0;
+ else if (v > 255)
+ *lut++ = 255;
+ else
+ *lut++ = v;
+ }
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/filter/pstops.c b/filter/pstops.c
new file mode 100644
index 000000000..f93d76f26
--- /dev/null
+++ b/filter/pstops.c
@@ -0,0 +1,804 @@
+/*
+ * "$Id$"
+ *
+ * PostScript filter for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1993-1999 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * Contents:
+ *
+ * main() - Main entry...
+ * check_range() - Check to see if the current page is selected for
+ * copy_bytes() - Copy bytes from the input file to stdout...
+ * end_nup() - End processing for N-up printing...
+ * psgets() - Get a line from a file.
+ * start_nup() - Start processing for N-up printing...
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "common.h"
+
+
+/*
+ * Constants...
+ */
+
+#define MAX_PAGES 10000
+
+
+/*
+ * Globals...
+ */
+
+int NumPages = 0; /* Number of pages in file */
+long Pages[MAX_PAGES]; /* Offsets to each page */
+char PageLabels[MAX_PAGES][64];
+ /* Page labels */
+const char *PageRanges = NULL; /* Range of pages selected */
+const char *PageSet = NULL; /* All, Even, Odd pages */
+int Order = 0, /* 0 = normal, 1 = reverse pages */
+ Flip = 0, /* Flip/mirror pages */
+ NUp = 1, /* Number of pages on each sheet (1, 2, 4) */
+ Collate = 0, /* Collate copies? */
+ Copies = 1; /* Number of copies */
+
+
+/*
+ * Local functions...
+ */
+
+static int check_range(int page);
+static void copy_bytes(FILE *fp, size_t length);
+static void end_nup(int number);
+static char *psgets(char *buf, size_t len, FILE *fp);
+static void start_nup(int number);
+
+
+/*
+ * 'main()' - Main entry...
+ */
+
+int /* O - Exit status */
+main(int argc, /* I - Number of command-line arguments */
+ char *argv[]) /* I - Command-line arguments */
+{
+ FILE *fp; /* Print file */
+ ppd_file_t *ppd; /* PPD file */
+ int num_options; /* Number of print options */
+ cups_option_t *options; /* Print options */
+ const char *val; /* Option value */
+ char tempfile[255]; /* Temporary file name */
+ FILE *temp; /* Temporary file */
+ int number; /* Page number */
+ int slowcollate; /* 1 if we need to collate manually */
+ int sloworder; /* 1 if we need to order manually */
+ char line[8192]; /* Line buffer */
+ float g; /* Gamma correction value */
+ float b; /* Brightness factor */
+ int level; /* Nesting level for embedded files */
+ int nbytes, /* Number of bytes read */
+ tbytes; /* Total bytes to read for binary data */
+ int page; /* Current page sequence number */
+
+
+ if (argc < 6 || argc > 7)
+ {
+ fputs("ERROR: pstops job-id user title copies options [file]\n", stderr);
+ return (1);
+ }
+
+ /*
+ * If we have 7 arguments, print the file named on the command-line.
+ * Otherwise, send stdin instead...
+ */
+
+ if (argc == 6)
+ fp = stdin;
+ else
+ {
+ /*
+ * Try to open the print file...
+ */
+
+ if ((fp = fopen(argv[6], "rb")) == NULL)
+ {
+ perror("ERROR: unable to open print file - ");
+ return (1);
+ }
+ }
+
+ /*
+ * Process command-line options and write the prolog...
+ */
+
+ g = 1.0;
+ b = 1.0;
+
+ Copies = atoi(argv[4]);
+
+ ppd = ppdOpenFile(getenv("PPD"));
+
+ options = NULL;
+ num_options = cupsParseOptions(argv[5], 0, &options);
+
+ ppd = SetCommonOptions(num_options, options, 1);
+
+ ppdMarkDefaults(ppd);
+ cupsMarkOptions(ppd, num_options, options);
+
+ if ((val = cupsGetOption("page-ranges", num_options, options)) != NULL)
+ PageRanges = val;
+
+ if ((val = cupsGetOption("page-set", num_options, options)) != NULL)
+ PageSet = val;
+
+ if ((val = cupsGetOption("multiple-document-handling", num_options, options)) != NULL)
+ {
+ /*
+ * This IPP attribute is unnecessarily complicated...
+ *
+ * single-document, separate-documents-collated-copies, and
+ * single-document-new-sheet all require collated copies.
+ *
+ * separate-documents-collated-copies allows for uncollated copies.
+ */
+
+ Collate = strcmp(val, "separate-documents-collated-copies") != 0;
+ }
+
+ if ((val = cupsGetOption("Collate", num_options, options)) != NULL &&
+ strcmp(val, "True") == 0)
+ Collate = 1;
+
+ if ((val = cupsGetOption("OutputOrder", num_options, options)) != NULL &&
+ strcmp(val, "Reverse") == 0)
+ Order = 1;
+
+ if ((val = cupsGetOption("number-up", num_options, options)) != NULL)
+ NUp = atoi(val);
+
+ if ((val = cupsGetOption("gamma", num_options, options)) != NULL)
+ g = atoi(val) * 0.001f;
+
+ if ((val = cupsGetOption("brightness", num_options, options)) != NULL)
+ b = atoi(val) * 0.01f;
+
+ /*
+ * See if we have to filter the fast or slow way...
+ */
+
+ if (ppdFindOption(ppd, "Collate") == NULL && Collate && Copies > 1)
+ slowcollate = 1;
+ else
+ slowcollate = 0;
+
+ if (ppdFindOption(ppd, "OutputOrder") == NULL && Order)
+ sloworder = 1;
+ else
+ sloworder = 0;
+
+ /*
+ * If we need to filter slowly, then create a temporary file for page data...
+ *
+ * If the temp file can't be created, then we'll ignore the collating/output
+ * order options...
+ */
+
+ if (sloworder || slowcollate)
+ {
+ temp = fopen(cupsTempFile(tempfile, sizeof(tempfile)), "wb+");
+
+ if (temp == NULL)
+ slowcollate = sloworder = 0;
+ }
+
+ /*
+ * Write any "exit server" options that have been selected...
+ */
+
+ ppdEmit(ppd, stdout, PPD_ORDER_EXIT);
+
+ /*
+ * Write any JCL commands that are needed to print PostScript code...
+ */
+
+ if (ppd != NULL && ppd->jcl_begin && ppd->jcl_ps)
+ {
+ fputs(ppd->jcl_begin, stdout);
+ ppdEmit(ppd, stdout, PPD_ORDER_JCL);
+ fputs(ppd->jcl_ps, stdout);
+ }
+
+ /*
+ * Read the first line to see if we have DSC comments...
+ */
+
+ if (psgets(line, sizeof(line), fp) == NULL)
+ {
+ fputs("ERROR: Empty print file!\n", stderr);
+ ppdClose(ppd);
+ return (1);
+ }
+
+ /*
+ * Start sending the document with any commands needed...
+ */
+
+ puts(line);
+
+ if (ppd != NULL && ppd->patches != NULL)
+ puts(ppd->patches);
+
+ ppdEmit(ppd, stdout, PPD_ORDER_DOCUMENT);
+ ppdEmit(ppd, stdout, PPD_ORDER_ANY);
+ ppdEmit(ppd, stdout, PPD_ORDER_PROLOG);
+
+ if (NUp > 1)
+ puts("userdict begin\n"
+ "/ESPshowpage /showpage load def\n"
+ "/showpage { } def\n"
+ "end");
+
+ if (g != 1.0 || b != 1.0)
+ printf("{ neg 1 add dup 0 lt { pop 1 } { %.3f exp neg 1 add } "
+ "ifelse %.3f mul } bind settransfer\n", g, b);
+
+ if (Copies > 1 && (!Collate || !slowcollate))
+ printf("/#copies %d def\n", Copies);
+
+ if (strncmp(line, "%!PS-Adobe-", 11) == 0)
+ {
+ /*
+ * OK, we have DSC comments; read until we find a %%Page comment...
+ */
+
+ level = 0;
+
+ while (psgets(line, sizeof(line), fp) != NULL)
+ if (strncmp(line, "%%BeginDocument:", 16) == 0 ||
+ strncmp(line, "%%BeginDocument ", 16) == 0) /* Adobe Acrobat BUG */
+ level ++;
+ else if (strcmp(line, "%%EndDocument") == 0 && level > 0)
+ level --;
+ else if (strncmp(line, "%%Page:", 7) == 0 && level == 0)
+ break;
+ else if (strncmp(line, "%%BeginBinary:", 14) == 0 ||
+ (strncmp(line, "%%BeginData:", 12) == 0 &&
+ strstr(line, "Binary") != NULL))
+ {
+ /*
+ * Copy binary data...
+ */
+
+ tbytes = atoi(strchr(line, ':') + 1);
+ while (tbytes > 0)
+ {
+ nbytes = fread(line, 1, sizeof(line), fp);
+ fwrite(line, 1, nbytes, stdout);
+ tbytes -= nbytes;
+ }
+ }
+ else
+ puts(line);
+
+ /*
+ * Then read all of the pages, filtering as needed...
+ */
+
+ for (page = 1;;)
+ {
+ if (strncmp(line, "%%BeginDocument:", 16) == 0 ||
+ strncmp(line, "%%BeginDocument ", 16) == 0) /* Adobe Acrobat BUG */
+ level ++;
+ else if (strcmp(line, "%%EndDocument") == 0 && level > 0)
+ level --;
+ else if (strncmp(line, "%%Page:", 7) == 0 && level == 0)
+ {
+ if (sscanf(line, "%*s%*s%d", &number) == 1)
+ {
+ if (!check_range(number))
+ {
+ while (psgets(line, sizeof(line), fp) != NULL)
+ if (strncmp(line, "%%BeginDocument:", 16) == 0 ||
+ strncmp(line, "%%BeginDocument ", 16) == 0) /* Adobe Acrobat BUG */
+ level ++;
+ else if (strcmp(line, "%%EndDocument") == 0 && level > 0)
+ level --;
+ else if (strncmp(line, "%%Page:", 7) == 0 && level == 0)
+ break;
+
+ continue;
+ }
+
+ if (!sloworder && NumPages > 0)
+ end_nup(NumPages - 1);
+
+ if (slowcollate || sloworder)
+ Pages[NumPages] = ftell(temp);
+
+ if (!sloworder)
+ {
+ if ((NumPages % NUp) == 0)
+ {
+ if (ppd == NULL || ppd->num_filters == 0)
+ fprintf(stderr, "PAGE: %d %d\n", page, Copies);
+
+ printf("%%%%Page: %d %d\n", page, page);
+ page ++;
+ ppdEmit(ppd, stdout, PPD_ORDER_PAGE);
+ }
+
+ start_nup(NumPages);
+ }
+
+ NumPages ++;
+ }
+ }
+ else if (strncmp(line, "%%BeginBinary:", 14) == 0 ||
+ (strncmp(line, "%%BeginData:", 12) == 0 &&
+ strstr(line, "Binary") != NULL))
+ {
+ /*
+ * Copy binary data...
+ */
+
+ tbytes = atoi(strchr(line, ':') + 1);
+ while (tbytes > 0)
+ {
+ nbytes = fread(line, 1, sizeof(line), fp);
+
+ if (!sloworder)
+ fwrite(line, 1, nbytes, stdout);
+
+ if (slowcollate || sloworder)
+ fwrite(line, 1, nbytes, stdout);
+
+ tbytes -= nbytes;
+ }
+ }
+ else
+ {
+ if (!sloworder)
+ puts(line);
+
+ if (slowcollate || sloworder)
+ {
+ fputs(line, temp);
+ putc('\n', temp);
+ }
+ }
+
+ if (psgets(line, sizeof(line), fp) == NULL)
+ break;
+ }
+
+ if (!sloworder)
+ end_nup((NumPages + NUp - 1) & (NUp - 1));
+
+ if (slowcollate || sloworder)
+ {
+ Pages[NumPages] = ftell(temp);
+ page = 1;
+
+ if (!sloworder)
+ {
+ while (Copies > 0)
+ {
+ rewind(temp);
+
+ for (number = 0; number < NumPages; number ++)
+ {
+ if ((number % NUp) == 0)
+ {
+ if (ppd == NULL || ppd->num_filters == 0)
+ fprintf(stderr, "PAGE: %d 1\n", page);
+
+ printf("%%%%Page: %d %d\n", page, page);
+ page ++;
+ ppdEmit(ppd, stdout, PPD_ORDER_PAGE);
+ }
+
+ start_nup(number);
+ copy_bytes(temp, Pages[number + 1] - Pages[number]);
+ end_nup(number);
+ }
+
+ Copies --;
+ }
+ }
+ else
+ {
+ do
+ {
+ for (number = NumPages - 1; number >= 0; number --)
+ {
+ if ((number % NUp) == 0)
+ {
+ if (ppd == NULL || ppd->num_filters == 0)
+ fprintf(stderr, "PAGE: %d %d\n", page,
+ slowcollate ? 1 : Copies);
+
+ printf("%%%%Page: %d %d\n", page, page);
+ page ++;
+ ppdEmit(ppd, stdout, PPD_ORDER_PAGE);
+ }
+
+ start_nup(NumPages - 1 - number);
+ fseek(temp, Pages[number], SEEK_SET);
+ copy_bytes(temp, Pages[number + 1] - Pages[number]);
+ end_nup(NumPages - 1 - number);
+ }
+
+ Copies --;
+ }
+ while (Copies > 0 || !slowcollate);
+ }
+ }
+ }
+ else
+ {
+ /*
+ * No DSC comments - write any page commands and then the rest of the file...
+ */
+
+ if (ppd == NULL || ppd->num_filters == 0)
+ fprintf(stderr, "PAGE: 1 %d\n", slowcollate ? 1 : Copies);
+
+ ppdEmit(ppd, stdout, PPD_ORDER_PAGE);
+
+ while (psgets(line, sizeof(line), fp) != NULL)
+ {
+ puts(line);
+
+ if (slowcollate)
+ {
+ fputs(line, temp);
+ putc('\n', temp);
+ }
+ }
+
+ if (slowcollate)
+ {
+ while (Copies > 1)
+ {
+ if (ppd == NULL || ppd->num_filters == 0)
+ fputs("PAGE: 1 1\n", stderr);
+
+ ppdEmit(ppd, stdout, PPD_ORDER_PAGE);
+ rewind(temp);
+ copy_bytes(temp, 0);
+ }
+ }
+ }
+
+ /*
+ * End the job with the appropriate JCL command or CTRL-D otherwise.
+ */
+
+ if (ppd != NULL && ppd->jcl_end)
+ fputs(ppd->jcl_end, stdout);
+ else
+ putchar(0x04);
+
+ /*
+ * Close files and remove the temporary file if needed...
+ */
+
+ if (slowcollate || sloworder)
+ {
+ fclose(temp);
+ unlink(tempfile);
+ }
+
+ ppdClose(ppd);
+
+ if (fp != stdin)
+ fclose(fp);
+
+ return (0);
+}
+
+
+/*
+ * 'check_range()' - Check to see if the current page is selected for
+ * printing.
+ */
+
+static int /* O - 1 if selected, 0 otherwise */
+check_range(int page) /* I - Page number */
+{
+ const char *range; /* Pointer into range string */
+ int lower, upper; /* Lower and upper page numbers */
+
+
+ if (PageSet != NULL)
+ {
+ /*
+ * See if we only print even or odd pages...
+ */
+
+ if (strcmp(PageSet, "even") == 0 && (page & 1))
+ return (0);
+ if (strcmp(PageSet, "odd") == 0 && !(page & 1))
+ return (0);
+ }
+
+ if (PageRanges == NULL)
+ return (1); /* No range, print all pages... */
+
+ for (range = PageRanges; *range != '\0';)
+ {
+ if (*range == '-')
+ {
+ lower = 1;
+ range ++;
+ upper = strtol(range, (char **)&range, 10);
+ }
+ else
+ {
+ lower = strtol(range, (char **)&range, 10);
+
+ if (*range == '-')
+ {
+ range ++;
+ if (!isdigit(*range))
+ upper = 65535;
+ else
+ upper = strtol(range, (char **)&range, 10);
+ }
+ else
+ upper = lower;
+ }
+
+ if (page >= lower && page <= upper)
+ return (1);
+
+ if (*range == ',')
+ range ++;
+ else
+ break;
+ }
+
+ return (0);
+}
+
+
+/*
+ * 'copy_bytes()' - Copy bytes from the input file to stdout...
+ */
+
+static void
+copy_bytes(FILE *fp, /* I - File to read from */
+ size_t length) /* I - Length of page data */
+{
+ char buffer[8192]; /* Data buffer */
+ size_t nbytes, /* Number of bytes read */
+ nleft; /* Number of bytes left/remaining */
+
+
+ nleft = length;
+
+ while (nleft > 0 || length == 0)
+ {
+ if ((nbytes = fread(buffer, 1, sizeof(buffer), fp)) < 1)
+ return;
+
+ nleft -= nbytes;
+
+ fwrite(buffer, 1, nbytes, stdout);
+ }
+}
+
+
+/*
+ * 'end_nup()' - End processing for N-up printing...
+ */
+
+static void
+end_nup(int number) /* I - Page number */
+{
+ if (Flip || Orientation || NUp > 1)
+ puts("grestoreall");
+
+ switch (NUp)
+ {
+ case 2 :
+ if ((number & 1) == 1)
+ puts("ESPshowpage");
+ break;
+
+ case 4 :
+ if ((number & 3) == 3)
+ puts("ESPshowpage");
+ break;
+ }
+}
+
+
+/*
+ * 'psgets()' - Get a line from a file.
+ *
+ * Note:
+ *
+ * This function differs from the gets() function in that it
+ * handles any combination of CR, LF, or CR LF to end input
+ * lines.
+ */
+
+static char * /* O - String or NULL if EOF */
+psgets(char *buf, /* I - Buffer to read into */
+ size_t len, /* I - Length of buffer */
+ FILE *fp) /* I - File to read from */
+{
+ char *bufptr; /* Pointer into buffer */
+ int ch; /* Character from file */
+
+
+ len --;
+ bufptr = buf;
+
+ while ((bufptr - buf) < len)
+ {
+ if ((ch = getc(fp)) == EOF)
+ break;
+
+ if (ch == 0x0d)
+ {
+ /*
+ * Got a CR; see if there is a LF as well...
+ */
+
+ ch = getc(fp);
+ if (ch != EOF && ch != 0x0a)
+ ungetc(ch, fp); /* Nope, save it for later... */
+
+ break;
+ }
+ else if (ch == 0x0a)
+ break;
+ else
+ *bufptr++ = ch;
+ }
+
+ /*
+ * Nul-terminate the string and return it (or NULL for EOF).
+ */
+
+ *bufptr = '\0';
+
+ if (ch == EOF && bufptr == buf)
+ return (NULL);
+ else
+ return (buf);
+}
+
+
+/*
+ * 'start_nup()' - Start processing for N-up printing...
+ */
+
+static void
+start_nup(int number) /* I - Page number */
+{
+ int x, y; /* Relative position of subpage */
+ float w, l, /* Width and length of subpage */
+ tx, ty; /* Translation values for subpage */
+
+
+ if (Flip || Orientation || NUp > 1)
+ puts("gsave");
+
+ if (Flip)
+ printf("%.0f 0 translate -1 1 scale\n", PageWidth);
+
+ switch (Orientation)
+ {
+ case 1 : /* Landscape */
+ printf("%.0f 0 translate 90 rotate\n", PageLength);
+ break;
+ case 2 : /* Reverse Portrait */
+ printf("%.0f %.0f translate 180 rotate\n", PageWidth, PageLength);
+ break;
+ case 3 : /* Reverse Landscape */
+ printf("0 %.0f translate -90 rotate\n", PageWidth);
+ break;
+ }
+
+ switch (NUp)
+ {
+ case 2 :
+ x = number & 1;
+
+ if (Orientation & 1)
+ {
+ x = 1 - x;
+ w = PageLength;
+ l = w * PageLength / PageWidth;
+
+ if (l > (PageWidth * 0.5))
+ {
+ l = PageWidth * 0.5;
+ w = l * PageWidth / PageLength;
+ }
+
+ tx = PageWidth * 0.5 - l;
+ ty = (PageLength - w) * 0.5;
+ }
+ else
+ {
+ l = PageWidth;
+ w = l * PageWidth / PageLength;
+
+ if (w > (PageLength * 0.5))
+ {
+ w = PageLength * 0.5;
+ l = w * PageLength / PageWidth;
+ }
+
+ tx = PageLength * 0.5 - w;
+ ty = (PageWidth - l) * 0.5;
+ }
+
+ if (Orientation & 1)
+ {
+ printf("0 %.0f translate -90 rotate\n", PageLength);
+ printf("%.0f %.0f translate %.3f %.3f scale\n",
+ ty, tx + l * x, w / PageWidth, l / PageLength);
+ }
+ else
+ {
+ printf("%.0f 0 translate 90 rotate\n", PageWidth);
+ printf("%.0f %.0f translate %.3f %.3f scale\n",
+ tx + w * x, ty, w / PageWidth, l / PageLength);
+ }
+
+ printf("newpath\n"
+ "0 0 moveto\n"
+ "%.0f 0 lineto\n"
+ "%.0f %.0f lineto\n"
+ "0 %.0f lineto\n"
+ "closepath clip newpath\n",
+ PageWidth, PageWidth, PageLength, PageLength);
+ break;
+
+ case 4 :
+ x = number & 1;
+ y = 1 - ((number & 2) != 0);
+ w = PageWidth * 0.5;
+ l = PageLength * 0.5;
+
+ printf("%.0f %.0f translate 0.5 0.5 scale\n", x * w, y * l);
+ printf("newpath\n"
+ "0 0 moveto\n"
+ "%.0f 0 lineto\n"
+ "%.0f %.0f lineto\n"
+ "0 %.0f lineto\n"
+ "closepath clip newpath\n",
+ PageWidth, PageWidth, PageLength, PageLength);
+ break;
+ }
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/filter/rastertohp.c b/filter/rastertohp.c
new file mode 100644
index 000000000..85de33ed0
--- /dev/null
+++ b/filter/rastertohp.c
@@ -0,0 +1,493 @@
+/*
+ * "$Id$"
+ *
+ * Hewlett-Packard Page Control Language and Raster Transfer Language
+ * filter for ESP Print.
+ *
+ * Copyright 1993-1999 by Easy Software Products
+ *
+ * These coded instructions, statements, and computer programs contain
+ * unpublished proprietary information of Easy Software Products, and
+ * are protected by Federal copyright law. They may not be disclosed
+ * to third parties or copied or duplicated in any form, in whole or
+ * in part, without the prior written consent of Easy Software Products.
+ *
+ * Contents:
+ *
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include <cups/cups.h>
+#include <cups/raster.h>
+#include <cups/string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+
+/*
+ * Globals...
+ */
+
+unsigned char *Planes[4], /* Output buffers */
+ *CompBuffer; /* Compression buffer */
+int NumPlanes, /* Number of color planes */
+ Feed; /* Number of lines to skip */
+
+
+/*
+ * Prototypes...
+ */
+
+void Setup(void);
+void StartPage(cups_page_header_t *header);
+void EndPage(cups_page_header_t *header);
+void Shutdown(void);
+
+void CompressData(unsigned char *line, int length, int plane, int type);
+void OutputLine(cups_page_header_t *header);
+
+
+/*
+ * 'Setup()' - Prepare the printer for printing.
+ */
+
+void
+Setup(void)
+{
+ /*
+ * Send a PCL reset sequence.
+ */
+
+ putchar(0x1b);
+ putchar('E');
+}
+
+
+/*
+ * 'StartPage()' - Start a page of graphics.
+ */
+
+void
+StartPage(cups_page_header_t *header) /* I - Page header */
+{
+ int plane; /* Looping var */
+
+
+ /*
+ * Set the media type, position, and size...
+ */
+
+ printf("\033&l6D\033&k12H"); /* Set 6 LPI, 10 CPI */
+ printf("\033&l%.2fP", /* Set page length */
+ header->PageSize[1] / 12.0);
+ printf("\033&l%dX", header->NumCopies); /* Set number copies */
+ if (header->MediaPosition)
+ printf("\033&l%dH", header->MediaPosition); /* Set media position */
+ if (header->cupsMediaType)
+ printf("\033&l%dM", /* Set media type */
+ header->cupsMediaType);
+
+ /*
+ * Set graphics mode...
+ */
+
+ if (header->cupsColorSpace == CUPS_CSPACE_KCMY)
+ {
+ NumPlanes = 4;
+ printf("\033*r-4U"); /* Set KCMY graphics */
+ }
+ else
+ NumPlanes = 1;
+
+ printf("\033*t%dR", header->HWResolution[0]); /* Set resolution */
+ printf("\033*r%dS", header->cupsWidth); /* Set width */
+ printf("\033*r%dT", header->cupsHeight); /* Set height */
+ printf("\033&a0H\033&a0V"); /* Set top-of-page */
+ printf("\033*r1A"); /* Start graphics */
+
+ if (header->cupsCompression)
+ printf("\033*b%dM", /* Set compression */
+ header->cupsCompression);
+
+ Feed = 0; /* No blank lines yet */
+
+ /*
+ * Allocate memory for a line of graphics...
+ */
+
+ Planes[0] = malloc(header->cupsBytesPerLine);
+ for (plane = 1; plane < NumPlanes; plane ++)
+ Planes[plane] = Planes[0] + plane * header->cupsBytesPerLine / NumPlanes;
+
+ if (header->cupsCompression)
+ CompBuffer = malloc(header->cupsBytesPerLine * 2);
+}
+
+
+/*
+ * 'EndPage()' - Finish a page of graphics.
+ */
+
+void
+EndPage(cups_page_header_t *header) /* I - Page header */
+{
+ /*
+ * Eject the current page...
+ */
+
+ if (NumPlanes > 1)
+ {
+ printf("\033*rC"); /* End color GFX */
+ printf("\033&l0H"); /* Eject current page */
+ }
+ else
+ {
+ printf("\033*r0B"); /* End GFX */
+ printf("\014"); /* Eject currnet page */
+ }
+
+ /*
+ * Free memory...
+ */
+
+ free(Planes[0]);
+
+ if (header->cupsCompression)
+ free(CompBuffer);
+}
+
+
+/*
+ * 'Shutdown()' - Shutdown the printer.
+ */
+
+void
+Shutdown(void)
+{
+ /*
+ * Send a PCL reset sequence.
+ */
+
+ putchar(0x1b);
+ putchar('E');
+}
+
+
+/*
+ * 'CompressData()' - Compress a line of graphics.
+ */
+
+void
+CompressData(unsigned char *line, /* I - Data to compress */
+ int length, /* I - Number of bytes */
+ int plane, /* I - Color plane */
+ int type) /* I - Type of compression */
+{
+ unsigned char *line_ptr, /* Current byte pointer */
+ *line_end, /* End-of-line byte pointer */
+ *comp_ptr, /* Pointer into compression buffer */
+ *start; /* Start of compression sequence */
+ int count; /* Count of bytes for output */
+
+
+ switch (type)
+ {
+ case 0 :
+ /*
+ * Do no compression...
+ */
+
+ line_ptr = line;
+ line_end = line + length;
+ break;
+
+ case 1 :
+ /*
+ * Do run-length encoding...
+ */
+
+ line_end = line + length;
+ for (line_ptr = line, comp_ptr = CompBuffer;
+ line_ptr < line_end;
+ comp_ptr += 2, line_ptr += count)
+ {
+ for (count = 1;
+ (line_ptr + count) < line_end &&
+ line_ptr[0] == line_ptr[count] &&
+ count < 256;
+ count ++);
+
+ comp_ptr[0] = count - 1;
+ comp_ptr[1] = line_ptr[0];
+ }
+
+ line_ptr = CompBuffer;
+ line_end = comp_ptr;
+ break;
+
+ case 2 :
+ /*
+ * Do TIFF pack-bits encoding...
+ */
+
+ line_ptr = line;
+ line_end = line + length;
+ comp_ptr = CompBuffer;
+
+ while (line_ptr < line_end)
+ {
+ if ((line_ptr + 1) >= line_end)
+ {
+ /*
+ * Single byte on the end...
+ */
+
+ *comp_ptr++ = 0x00;
+ *comp_ptr++ = *line_ptr++;
+ }
+ else if (line_ptr[0] == line_ptr[1])
+ {
+ /*
+ * Repeated sequence...
+ */
+
+ line_ptr ++;
+ count = 2;
+
+ while (line_ptr < (line_end - 1) &&
+ line_ptr[0] == line_ptr[1] &&
+ count < 127)
+ {
+ line_ptr ++;
+ count ++;
+ }
+
+ *comp_ptr++ = 257 - count;
+ *comp_ptr++ = *line_ptr++;
+ }
+ else
+ {
+ /*
+ * Non-repeated sequence...
+ */
+
+ start = line_ptr;
+ line_ptr ++;
+ count = 1;
+
+ while (line_ptr < (line_end - 1) &&
+ line_ptr[0] != line_ptr[1] &&
+ count < 127)
+ {
+ line_ptr ++;
+ count ++;
+ }
+
+ *comp_ptr++ = count - 1;
+
+ memcpy(comp_ptr, start, count);
+ comp_ptr += count;
+ }
+ }
+
+ line_ptr = CompBuffer;
+ line_end = comp_ptr;
+ break;
+ }
+
+ /*
+ * Set the length of the data and write a raster plane...
+ */
+
+ printf("\033*b%d%c", line_end - line_ptr, plane);
+ fwrite(line_ptr, line_end - line_ptr, 1, stdout);
+}
+
+
+/*
+ * 'OutputLine()' - Output a line of graphics.
+ */
+
+void
+OutputLine(cups_page_header_t *header) /* I - Page header */
+{
+ int plane; /* Current plane */
+
+
+ /*
+ * Output whitespace as needed...
+ */
+
+ if (Feed > 0)
+ {
+ printf("\033*b%dY", Feed);
+ Feed = 0;
+ }
+
+ /*
+ * Write bitmap data as needed...
+ */
+
+ for (plane = 0; plane < NumPlanes; plane ++)
+ CompressData(Planes[plane], header->cupsBytesPerLine / NumPlanes,
+ plane < (NumPlanes - 1) ? 'V' : 'W',
+ header->cupsCompression);
+}
+
+
+/*
+ * 'main()' - Main entry and processing of driver.
+ */
+
+int /* O - Exit status */
+main(int argc, /* I - Number of command-line arguments */
+ char *argv[]) /* I - Command-line arguments */
+{
+ int fd; /* File descriptor */
+ cups_raster_t *ras; /* Raster stream for printing */
+ cups_page_header_t header; /* Page header from file */
+ int page; /* Current page */
+ int y; /* Current line */
+
+
+ /*
+ * Check for valid arguments...
+ */
+
+ if (argc < 6 || argc > 7)
+ {
+ /*
+ * We don't have the correct number of arguments; write an error message
+ * and then sleep for 1 second to give the scheduler a chance to read
+ * the message.
+ */
+
+ fputs("ERROR: rastertopcl job-id user title copies options [file]\n", stderr);
+ sleep(1);
+ return (1);
+ }
+
+ /*
+ * Open the page stream...
+ */
+
+ if (argc == 7)
+ {
+ if ((fd = open(argv[6], O_RDONLY)) == -1)
+ {
+ perror("ERROR: Unable to open raster file - ");
+ sleep(1);
+ return (1);
+ }
+ }
+ else
+ fd = 0;
+
+ ras = cupsRasterOpen(fd, CUPS_RASTER_READ);
+
+ /*
+ * Initialize the print device...
+ */
+
+ Setup();
+
+ /*
+ * Process pages as needed...
+ */
+
+ page = 0;
+
+ while (cupsRasterReadHeader(ras, &header))
+ {
+ /*
+ * Write a status message with the page number and number of copies.
+ */
+
+ page ++;
+
+ fprintf(stderr, "PAGE: %d %d\n", page, header.NumCopies);
+
+ /*
+ * Start the page...
+ */
+
+ StartPage(&header);
+
+ /*
+ * Loop for each line on the page...
+ */
+
+ for (y = 0; y < header.cupsHeight; y ++)
+ {
+ /*
+ * Let the user know how far we have progressed...
+ */
+
+ if ((y & 127) == 0)
+ fprintf(stderr, "INFO: Printing page %d, %d%% complete...\n", page,
+ 100 * y / header.cupsHeight);
+
+ /*
+ * Read a line of graphics...
+ */
+
+ if (cupsRasterReadPixels(ras, Planes[0], header.cupsBytesPerLine) < 1)
+ break;
+
+ /*
+ * See if the line is blank; if not, write it to the printer...
+ */
+
+ if (Planes[0][0] ||
+ memcmp(Planes[0], Planes[0] + 1, header.cupsBytesPerLine - 1))
+ OutputLine(&header);
+ else
+ Feed ++;
+ }
+
+ /*
+ * Eject the page...
+ */
+
+ EndPage(&header);
+ }
+
+ /*
+ * Shutdown the printer...
+ */
+
+ Shutdown();
+
+ /*
+ * Close the raster stream...
+ */
+
+ cupsRasterClose(ras);
+ if (fd != 0)
+ close(fd);
+
+ /*
+ * If no pages were printed, send an error message...
+ */
+
+ if (page == 0)
+ fputs("ERROR: No pages found!\n", stderr);
+ else
+ fputs("INFO: Ready to print.\n", stderr);
+
+ /*
+ * Sleep for 1 second and return...
+ */
+
+ sleep(1);
+ return (page == 0);
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/filter/textcommon.c b/filter/textcommon.c
new file mode 100644
index 000000000..2558905a4
--- /dev/null
+++ b/filter/textcommon.c
@@ -0,0 +1,745 @@
+/*
+ * "$Id$"
+ *
+ * Common text filter routines for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-1999 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * Contents:
+ *
+ * TextMain() - Standard main entry for text filters.
+ * compare_keywords() - Compare two C/C++ keywords.
+ * getutf8() - Get a UTF-8 encoded wide character...
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "textcommon.h"
+
+
+/*
+ * Globals...
+ */
+
+int WrapLines = 1, /* Wrap text in lines */
+ SizeLines = 60, /* Number of lines on a page */
+ SizeColumns = 80, /* Number of columns on a line */
+ PageColumns = 1, /* Number of columns on a page */
+ ColumnGutter = 0, /* Number of characters between text columns */
+ ColumnWidth = 80, /* Width of each column */
+ PrettyPrint = 0, /* Do pretty code formatting */
+ Copies = 1; /* Number of copies */
+lchar_t **Page = NULL; /* Page characters */
+int NumPages = 0; /* Number of pages in document */
+int CharsPerInch = 10; /* Number of character columns per inch */
+int LinesPerInch = 6; /* Number of lines per inch */
+int UTF8 = 0; /* Use UTF-8 encoding? */
+char *Keywords[] = /* List of known keywords... */
+ {
+ "and",
+ "and_eq",
+ "asm",
+ "auto",
+ "bitand",
+ "bitor",
+ "bool",
+ "break",
+ "case",
+ "catch",
+ "char",
+ "class",
+ "compl",
+ "const",
+ "continue",
+ "default",
+ "delete",
+ "do",
+ "double",
+ "else",
+ "enum",
+ "explicit",
+ "extern",
+ "false",
+ "float",
+ "for",
+ "friend",
+ "goto",
+ "if",
+ "inline",
+ "int",
+ "long",
+ "mutable",
+ "namespace",
+ "new",
+ "not",
+ "not_eq",
+ "operator",
+ "or",
+ "or_eq",
+ "private",
+ "protected",
+ "public",
+ "register",
+ "return",
+ "short",
+ "signed",
+ "sizeof",
+ "static",
+ "struct",
+ "switch",
+ "template",
+ "this",
+ "throw",
+ "true",
+ "try",
+ "typedef",
+ "typename",
+ "union",
+ "unsigned",
+ "virtual",
+ "void",
+ "volatile",
+ "while",
+ "xor",
+ "xor_eq"
+ };
+
+
+/*
+ * Local functions...
+ */
+
+static int compare_keywords(const void *, const void *);
+static int getutf8(FILE *fp);
+
+
+/*
+ * 'TextMain()' - Standard main entry for text filters.
+ */
+
+int /* O - Exit status */
+TextMain(char *name, /* I - Name of filter */
+ int argc, /* I - Number of command-line arguments */
+ char *argv[]) /* I - Command-line arguments */
+{
+ FILE *fp; /* Print file */
+ ppd_file_t *ppd; /* PPD file */
+ int i, /* Looping var */
+ ch, /* Current char from file */
+ lastch, /* Previous char from file */
+ attr, /* Current attribute */
+ line, /* Current line */
+ column, /* Current column */
+ page_column; /* Current page column */
+ int num_options; /* Number of print options */
+ cups_option_t *options; /* Print options */
+ const char *val; /* Option value */
+ char keyword[64], /* Keyword string */
+ *keyptr; /* Pointer into string */
+ int keycol; /* Column where keyword starts */
+ int ccomment; /* Inside a C-style comment? */
+ int cstring; /* Inside a C string */
+
+
+ if (argc < 6 || argc > 7)
+ {
+ fprintf(stderr, "ERROR: %s job-id user title copies options [file]\n",
+ name);
+ return (1);
+ }
+
+ /*
+ * If we have 7 arguments, print the file named on the command-line.
+ * Otherwise, send stdin instead...
+ */
+
+ if (argc == 6)
+ fp = stdin;
+ else
+ {
+ /*
+ * Try to open the print file...
+ */
+
+ if ((fp = fopen(argv[6], "rb")) == NULL)
+ {
+ perror("ERROR: unable to open print file - ");
+ return (1);
+ }
+ }
+
+ /*
+ * Process command-line options and write the prolog...
+ */
+
+ options = NULL;
+ num_options = cupsParseOptions(argv[5], 0, &options);
+
+ if ((val = cupsGetOption("prettyprint", num_options, options)) != NULL)
+ {
+ PrettyPrint = 1;
+ PageLeft = 72.0f;
+ PageRight = PageWidth - 36.0f;
+ PageBottom = PageBottom > 36.0f ? PageBottom : 36.0f;
+ PageTop = PageLength - 36.0f;
+ CharsPerInch = 12;
+ LinesPerInch = 8;
+ }
+
+ if ((ppd = SetCommonOptions(num_options, options, 1)) != NULL)
+ ppdClose(ppd);
+
+ WrapLines = cupsGetOption("nowrap", num_options, options) == NULL;
+
+ if ((val = cupsGetOption("columns", num_options, options)) != NULL)
+ PageColumns = atoi(val);
+
+ if ((val = cupsGetOption("cpi", num_options, options)) != NULL)
+ CharsPerInch = atoi(val);
+
+ if ((val = cupsGetOption("lpi", num_options, options)) != NULL)
+ LinesPerInch = atoi(val);
+
+ if ((val = cupsGetOption("prettyprint", num_options, options)) != NULL)
+ PageTop -= 216.0f / LinesPerInch;
+
+ Copies = atoi(argv[4]);
+
+ WriteProlog(argv[3], argv[2], ppd);
+
+ /*
+ * Read text from the specified source and print it...
+ */
+
+ column = 0;
+ line = 0;
+ page_column = 0;
+ attr = 0;
+ keyptr = keyword;
+ keycol = 0;
+ ccomment = 0;
+ cstring = 0;
+
+ while ((ch = getutf8(fp)) >= 0)
+ {
+ /*
+ * Control codes:
+ *
+ * BS Backspace (0x08)
+ * HT Horizontal tab; next 8th column (0x09)
+ * LF Line feed; forward full line (0x0a)
+ * VT Vertical tab; reverse full line (0x0b)
+ * FF Form feed (0x0c)
+ * CR Carriage return (0x0d)
+ * ESC 7 Reverse full line (0x1b 0x37)
+ * ESC 8 Reverse half line (0x1b 0x38)
+ * ESC 9 Forward half line (0x1b 0x39)
+ */
+
+ switch (ch)
+ {
+ case 0x08 : /* BS - backspace for boldface & underline */
+ if (column > 0)
+ column --;
+
+ keyptr = keyword;
+ keycol = column;
+ break;
+
+ case 0x09 : /* HT - tab to next 8th column */
+ if (PrettyPrint && keyptr > keyword)
+ {
+ *keyptr = '\0';
+ keyptr = keyword;
+
+ if (bsearch(&keyptr, Keywords, sizeof(Keywords) / sizeof(Keywords[0]),
+ sizeof(Keywords[0]), compare_keywords))
+ {
+ /*
+ * Put keywords in boldface...
+ */
+
+ i = page_column * (ColumnWidth + ColumnGutter);
+
+ while (keycol < column)
+ {
+ Page[line][keycol + i].attr |= ATTR_BOLD;
+ keycol ++;
+ }
+ }
+ }
+
+ column = (column + 8) & ~7;
+
+ if (column >= ColumnWidth && WrapLines)
+ { /* Wrap text to margins */
+ line ++;
+ column = 0;
+
+ if (line >= SizeLines)
+ {
+ page_column ++;
+ line = 0;
+
+ if (page_column >= PageColumns)
+ {
+ WritePage();
+ page_column = 0;
+ }
+ }
+ }
+
+ keycol = column;
+ break;
+
+ case 0x0a : /* LF - output current line */
+ if (PrettyPrint && keyptr > keyword)
+ {
+ *keyptr = '\0';
+ keyptr = keyword;
+
+ if (bsearch(&keyptr, Keywords, sizeof(Keywords) / sizeof(Keywords[0]),
+ sizeof(Keywords[0]), compare_keywords))
+ {
+ /*
+ * Put keywords in boldface...
+ */
+
+ i = page_column * (ColumnWidth + ColumnGutter);
+
+ while (keycol < column)
+ {
+ Page[line][keycol + i].attr |= ATTR_BOLD;
+ keycol ++;
+ }
+ }
+ }
+
+ line ++;
+ column = 0;
+ keycol = 0;
+
+ if (!ccomment && !cstring)
+ attr &= ~(ATTR_ITALIC | ATTR_BOLD | ATTR_RED | ATTR_GREEN | ATTR_BLUE);
+
+ if (line >= SizeLines)
+ {
+ page_column ++;
+ line = 0;
+
+ if (page_column >= PageColumns)
+ {
+ WritePage();
+ page_column = 0;
+ }
+ }
+ break;
+
+ case 0x0b : /* VT - move up 1 line */
+ if (line > 0)
+ line --;
+
+ keyptr = keyword;
+ keycol = column;
+
+ if (!ccomment && !cstring)
+ attr &= ~(ATTR_ITALIC | ATTR_BOLD | ATTR_RED | ATTR_GREEN | ATTR_BLUE);
+ break;
+
+ case 0x0c : /* FF - eject current page... */
+ if (PrettyPrint && keyptr > keyword)
+ {
+ *keyptr = '\0';
+ keyptr = keyword;
+
+ if (bsearch(&keyptr, Keywords, sizeof(Keywords) / sizeof(Keywords[0]),
+ sizeof(Keywords[0]), compare_keywords))
+ {
+ /*
+ * Put keywords in boldface...
+ */
+
+ i = page_column * (ColumnWidth + ColumnGutter);
+
+ while (keycol < column)
+ {
+ Page[line][keycol + i].attr |= ATTR_BOLD;
+ keycol ++;
+ }
+ }
+ }
+
+ page_column ++;
+ column = 0;
+ keycol = 0;
+ line = 0;
+
+ if (!ccomment && !cstring)
+ attr &= ~(ATTR_ITALIC | ATTR_BOLD | ATTR_RED | ATTR_GREEN | ATTR_BLUE);
+
+ if (page_column >= PageColumns)
+ {
+ WritePage();
+ page_column = 0;
+ }
+ break;
+
+ case 0x0d : /* CR */
+ column = 0;
+ break;
+
+ case 0x1b : /* Escape sequence */
+ ch = getutf8(fp);
+ if (ch == '7')
+ {
+ /*
+ * ESC 7 Reverse full line (0x1b 0x37)
+ */
+
+ if (line > 0)
+ line --;
+ }
+ else if (ch == '8')
+ {
+ /*
+ * ESC 8 Reverse half line (0x1b 0x38)
+ */
+
+ if ((attr & ATTR_RAISED) && line > 0)
+ {
+ attr &= ~ATTR_RAISED;
+ line --;
+ }
+ else if (attr & ATTR_LOWERED)
+ attr &= ~ATTR_LOWERED;
+ else
+ attr |= ATTR_RAISED;
+ }
+ else if (ch == '9')
+ {
+ /*
+ * ESC 9 Forward half line (0x1b 0x39)
+ */
+
+ if ((attr & ATTR_LOWERED) && line < (SizeLines - 1))
+ {
+ attr &= ~ATTR_LOWERED;
+ line ++;
+ }
+ else if (attr & ATTR_RAISED)
+ attr &= ~ATTR_RAISED;
+ else
+ attr |= ATTR_LOWERED;
+ }
+ break;
+
+ default : /* All others... */
+ if (ch < ' ')
+ break; /* Ignore other control chars */
+
+ if (PrettyPrint)
+ {
+ /*
+ * Do highlighting of C/C++ keywords, preprocessor commands,
+ * and comments...
+ */
+
+ if ((ch == ' ' || ch == '\t') && (attr & ATTR_BOLD))
+ {
+ /*
+ * Stop bolding preprocessor command...
+ */
+
+ attr &= ~ATTR_BOLD;
+ }
+ else if (!(isalnum(ch) || ch == '_') && keyptr > keyword)
+ {
+ /*
+ * Look for a keyword...
+ */
+
+ *keyptr = '\0';
+ keyptr = keyword;
+
+ if (!(attr & ATTR_ITALIC) &&
+ bsearch(&keyptr, Keywords, sizeof(Keywords) / sizeof(Keywords[0]),
+ sizeof(Keywords[0]), compare_keywords))
+ {
+ /*
+ * Put keywords in boldface...
+ */
+
+ i = page_column * (ColumnWidth + ColumnGutter);
+
+ while (keycol < column)
+ {
+ Page[line][keycol + i].attr |= ATTR_BOLD;
+ keycol ++;
+ }
+ }
+ }
+ else if ((isalnum(ch) || ch == '_') && !ccomment && !cstring)
+ {
+ /*
+ * Add characters to the current keyword (if they'll fit).
+ */
+
+ if (keyptr == keyword)
+ keycol = column;
+
+ if (keyptr < (keyword + sizeof(keyword) - 1))
+ *keyptr++ = ch;
+ }
+ else if (ch == '\"' && lastch != '\\' && !ccomment && !cstring)
+ {
+ /*
+ * Start a C string constant...
+ */
+
+ cstring = -1;
+ attr |= ATTR_BLUE;
+ }
+ else if (ch == '*' && lastch == '/' && !cstring)
+ {
+ /*
+ * Start a C-style comment...
+ */
+
+ ccomment = 1;
+ attr |= ATTR_ITALIC | ATTR_GREEN;
+ }
+ else if (ch == '/' && lastch == '/' && !cstring)
+ {
+ /*
+ * Start a C++-style comment...
+ */
+
+ attr |= ATTR_ITALIC | ATTR_GREEN;
+ }
+ else if (ch == '#' && column == 0 && !ccomment && !cstring)
+ {
+ /*
+ * Start a preprocessor command...
+ */
+
+ attr |= ATTR_BOLD | ATTR_RED;
+ }
+ }
+
+ if (column >= ColumnWidth && WrapLines)
+ { /* Wrap text to margins */
+ column = 0;
+ line ++;
+
+ if (line >= SizeLines)
+ {
+ page_column ++;
+ line = 0;
+
+ if (page_column >= PageColumns)
+ {
+ WritePage();
+ page_column = 0;
+ }
+ }
+ }
+
+ /*
+ * Add text to the current column & line...
+ */
+
+ if (column < ColumnWidth)
+ {
+ i = column + page_column * (ColumnWidth + ColumnGutter);
+
+ if (PrettyPrint)
+ Page[line][i].attr = attr;
+ else if (ch == Page[line][i].ch)
+ Page[line][i].attr |= ATTR_BOLD;
+ else if (Page[line][i].ch == '_')
+ Page[line][i].attr |= ATTR_UNDERLINE;
+ else
+ Page[line][i].attr = attr;
+
+ Page[line][i].ch = ch;
+ }
+
+ if (PrettyPrint)
+ {
+ if ((ch == '{' || ch == '}') && !ccomment && !cstring &&
+ column < ColumnWidth)
+ {
+ /*
+ * Highlight curley braces...
+ */
+
+ Page[line][i].attr |= ATTR_BOLD;
+ }
+ else if ((ch == '/' || ch == '*') && lastch == '/' &&
+ column < ColumnWidth)
+ {
+ /*
+ * Highlight first comment character...
+ */
+
+ Page[line][i - 1].attr = attr;
+ }
+ else if (ch == '\"' && lastch != '\\' && !ccomment && cstring > 0)
+ {
+ /*
+ * End a C string constant...
+ */
+
+ cstring = 0;
+ attr &= ~ATTR_BLUE;
+ }
+ else if (ch == '/' && lastch == '*' && ccomment)
+ {
+ /*
+ * End a C-style comment...
+ */
+
+ ccomment = 0;
+ attr &= ~(ATTR_ITALIC | ATTR_GREEN);
+ }
+
+ if (cstring < 0)
+ cstring = 1;
+ }
+
+ column ++;
+ break;
+ }
+
+ /*
+ * Save this character for the next cycle.
+ */
+
+ lastch = ch;
+ }
+
+ /*
+ * Write any remaining page data...
+ */
+
+ if (line > 0 || page_column > 0 || column > 0)
+ WritePage();
+
+ /*
+ * Write the epilog and return...
+ */
+
+ WriteEpilogue();
+
+ return (0);
+}
+
+
+/*
+ * 'compare_keywords()' - Compare two C/C++ keywords.
+ */
+
+static int /* O - Result of strcmp */
+compare_keywords(const void *k1, /* I - First keyword */
+ const void *k2) /* I - Second keyword */
+{
+ return (strcmp(*((const char **)k1), *((const char **)k2)));
+}
+
+
+/*
+ * 'getutf8()' - Get a UTF-8 encoded wide character...
+ */
+
+static int /* O - Character or -1 on error */
+getutf8(FILE *fp) /* I - File to read from */
+{
+ int ch; /* Current character value */
+ int next; /* Next character from file */
+
+
+ /*
+ * Read the first character and process things accordingly...
+ *
+ * UTF-8 maps 16-bit characters to:
+ *
+ * 0 to 127 = 0xxxxxxx
+ * 128 to 2047 = 110xxxxx 10yyyyyy (xxxxxyyyyyy)
+ * 2048 to 65535 = 1110xxxx 10yyyyyy 10zzzzzz (xxxxyyyyyyzzzzzz)
+ *
+ * We also accept:
+ *
+ * 128 to 191 = 10xxxxxx
+ *
+ * since this range of values is otherwise undefined unless you are
+ * in the middle of a multi-byte character...
+ *
+ * This code currently does not support anything beyond 16-bit
+ * characters, in part because PostScript doesn't support more than
+ * 16-bit characters...
+ */
+
+ if ((ch = getc(fp)) == EOF)
+ return (EOF);
+
+ if (ch < 0xc0 || !UTF8) /* One byte character? */
+ return (ch);
+ else if ((ch & 0xe0) == 0xc0)
+ {
+ /*
+ * Two byte character...
+ */
+
+ if ((next = getc(fp)) == EOF)
+ return (EOF);
+ else
+ return (((ch & 0x1f) << 6) | (next & 0x3f));
+ }
+ else if ((ch & 0xf0) == 0xe0)
+ {
+ /*
+ * Three byte character...
+ */
+
+ if ((next = getc(fp)) == EOF)
+ return (EOF);
+
+ ch = ((ch & 0x0f) << 6) | (next & 0x3f);
+
+ if ((next = getc(fp)) == EOF)
+ return (EOF);
+ else
+ return ((ch << 6) | (next & 0x3f));
+ }
+ else
+ {
+ /*
+ * More than three bytes... We don't support that...
+ */
+
+ return (EOF);
+ }
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/filter/textcommon.h b/filter/textcommon.h
new file mode 100644
index 000000000..cffd4db73
--- /dev/null
+++ b/filter/textcommon.h
@@ -0,0 +1,89 @@
+/*
+ * "$Id$"
+ *
+ * Common text filter definitions for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-1999 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "common.h"
+
+
+/*
+ * Constants...
+ */
+
+#define ATTR_BOLD 0x01
+#define ATTR_UNDERLINE 0x02
+#define ATTR_RAISED 0x04
+#define ATTR_LOWERED 0x08
+#define ATTR_ITALIC 0x10
+#define ATTR_RED 0x20
+#define ATTR_GREEN 0x40
+#define ATTR_BLUE 0x80
+
+
+/*
+ * Structures...
+ */
+
+typedef struct /**** Character/attribute structure... ****/
+{
+ unsigned short ch, /* Character */
+ attr; /* Any attributes */
+} lchar_t;
+
+
+/*
+ * Globals...
+ */
+
+extern int WrapLines, /* Wrap text in lines */
+ SizeLines, /* Number of lines on a page */
+ SizeColumns, /* Number of columns on a line */
+ PageColumns, /* Number of columns on a page */
+ ColumnGutter, /* Number of characters between text columns */
+ ColumnWidth, /* Width of each column */
+ PrettyPrint, /* Do pretty code formatting */
+ Copies; /* Number of copies to produce */
+extern lchar_t **Page; /* Page characters */
+extern int NumPages; /* Number of pages in document */
+extern int CharsPerInch, /* Number of character columns per inch */
+ LinesPerInch, /* Number of lines per inch */
+ UTF8; /* Use UTF-8 encoding? */
+extern char *Keywords[]; /* List of known keywords... */
+
+
+/*
+ * Required functions...
+ */
+
+extern int TextMain(char *name, int argc, char *argv[]);
+extern void WriteEpilogue(void);
+extern void WritePage(void);
+extern void WriteProlog(char *title, char *user, ppd_file_t *ppd);
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/filter/texttops.c b/filter/texttops.c
new file mode 100644
index 000000000..b8757bf9c
--- /dev/null
+++ b/filter/texttops.c
@@ -0,0 +1,568 @@
+/*
+ * "$Id$"
+ *
+ * Text to PostScript filter for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1993-1999 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * Contents:
+ *
+ * main() - Main entry for text to PostScript filter.
+ * WriteEpilogue() - Write the PostScript file epilogue.
+ * WritePage() - Write a page of text.
+ * WriteProlog() - Write the PostScript file prolog with options.
+ * write_line() - Write a row of text.
+ * write_string() - Write a string of text.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "textcommon.h"
+
+
+/*
+ * Globals...
+ */
+
+char *Glyphs[65536]; /* PostScript glyphs for Unicode */
+
+/*
+ * Local functions...
+ */
+
+static void write_line(int row, lchar_t *line);
+static void write_string(int col, int row, int len, lchar_t *s);
+
+
+/*
+ * 'main()' - Main entry for text to PostScript filter.
+ */
+
+int /* O - Exit status */
+main(int argc, /* I - Number of command-line arguments */
+ char *argv[]) /* I - Command-line arguments */
+{
+ return (TextMain("texttops", argc, argv));
+}
+
+
+/*
+ * 'WriteEpilogue()' - Write the PostScript file epilogue.
+ */
+
+void
+WriteEpilogue(void)
+{
+ puts("%%BeginTrailer");
+ printf("%%%%Pages: %d\n", NumPages);
+ puts("%%EOF");
+
+ free(Page[0]);
+ free(Page);
+}
+
+
+/*
+ * 'WritePage()' - Write a page of text.
+ */
+
+void
+WritePage(void)
+{
+ int line; /* Current line */
+
+
+ NumPages ++;
+ printf("%%%%Page: %d %d\n", NumPages, NumPages);
+
+ puts("gsave");
+
+ if (PrettyPrint)
+ printf("%d H\n", NumPages);
+
+ for (line = 0; line < SizeLines; line ++)
+ write_line(line, Page[line]);
+
+ puts("grestore");
+ puts("showpage");
+
+ memset(Page[0], 0, sizeof(lchar_t) * SizeColumns * SizeLines);
+}
+
+
+/*
+ * 'WriteProlog()' - Write the PostScript file prolog with options.
+ */
+
+void
+WriteProlog(char *title, /* I - Title of job */
+ char *user, /* I - Username */
+ ppd_file_t *ppd) /* I - PPD file info */
+{
+ int line; /* Current output line */
+ char *charset; /* Character set string */
+ char filename[1024]; /* Glyph filenames */
+ FILE *fp; /* Glyph files */
+ int ch, unicode; /* Character values */
+ char glyph[64]; /* Glyph name */
+ int chars[256]; /* Character encoding array */
+ time_t curtime; /* Current time */
+ struct tm *curtm; /* Current date */
+ char curdate[255]; /* Current date (text format) */
+
+
+ curtime = time(NULL);
+ curtm = localtime(&curtime);
+ strftime(curdate, sizeof(curdate), "%c", curtm);
+
+ puts("%!PS-Adobe-3.0");
+ printf("%%%%BoundingBox: %.0f %.0f %.0f %.0f\n", PageLeft, PageBottom,
+ PageRight, PageTop);
+ if (Orientation & 1)
+ puts("%%Orientation: Landscape");
+ puts("%%Creator: texttops/" CUPS_SVERSION);
+ printf("%%%%CreationDate: %s\n", curdate);
+ printf("%%%%Title: %s\n", title);
+ printf("%%%%For: %s\n", user);
+ if (PrettyPrint)
+ puts("%%DocumentNeededResources: font Courier Courier-Bold Courier-Oblique");
+ else
+ puts("%%DocumentNeededResources: font Courier Courier-Bold");
+ puts("%%DocumentSuppliedResources: procset texttops 1.0 0");
+ puts("%%Pages: (atend)");
+
+ puts("%%EndComments");
+
+ SizeColumns = (PageRight - PageLeft) / 72.0 * CharsPerInch;
+ SizeLines = (PageTop - PageBottom) / 72.0 * LinesPerInch;
+
+ Page = calloc(sizeof(lchar_t *), SizeLines);
+ Page[0] = calloc(sizeof(lchar_t), SizeColumns * SizeLines);
+ for (line = 1; line < SizeLines; line ++)
+ Page[line] = Page[0] + line * SizeColumns;
+
+ if (PageColumns > 1)
+ {
+ ColumnGutter = CharsPerInch / 2;
+ ColumnWidth = (SizeColumns - ColumnGutter * (PageColumns - 1)) /
+ PageColumns;
+ }
+ else
+ ColumnWidth = SizeColumns;
+
+ /*
+ * Get the output character set; if it is undefined or "us-ascii", do
+ * nothing because we can use the default encoding...
+ */
+
+ puts("%%BeginProlog");
+ puts("%%BeginResource: procset texttops 1.0 0");
+
+ charset = getenv("CHARSET");
+ if (charset != NULL && strcmp(charset, "us-ascii") != 0)
+ {
+ /*
+ * Load the PostScript glyph names and the corresponding character
+ * set definition...
+ */
+
+ memset(Glyphs, 0, sizeof(Glyphs));
+
+ if ((fp = fopen(CUPS_DATADIR "/data/psglyphs", "r")) != NULL)
+ {
+ while (fscanf(fp, "%x%s", &unicode, glyph) == 2)
+ Glyphs[unicode] = strdup(glyph);
+
+ fclose(fp);
+ }
+
+ if (strncmp(charset, "iso-", 4) == 0)
+ {
+ memset(chars, 0, sizeof(chars));
+
+ sprintf(filename, CUPS_DATADIR "/%s", charset + 4);
+
+ if ((fp = fopen(filename, "r")) != NULL)
+ {
+ while (fscanf(fp, "%x%x", &ch, &unicode) == 2)
+ chars[ch] = unicode;
+
+ fclose(fp);
+ }
+ }
+ else
+ {
+ /*
+ * UTF-8 encoding - just pass the first 256 characters for now...
+ */
+
+ UTF8 = 1;
+
+ for (unicode = 0; unicode < 256; unicode ++)
+ chars[unicode] = unicode;
+ }
+
+ /*
+ * Write the encoding array...
+ */
+
+ printf("%% %s encoding\n", charset);
+ puts("/textEncoding [");
+
+ for (ch = 0; ch < 256; ch ++)
+ {
+ if (Glyphs[chars[ch]])
+ printf("/%s", Glyphs[chars[ch]]);
+ else
+ printf("/.notdef");
+
+ if ((ch & 7) == 7)
+ putchar('\n');
+ }
+
+ puts("] def");
+
+ puts("% Reencode fonts");
+ puts("/Courier findfont");
+ puts("dup length dict begin\n"
+ " { 1 index /FID ne { def } { pop pop } ifelse } forall\n"
+ " /Encoding textEncoding def\n"
+ " currentdict\n"
+ "end");
+ puts("/Courier exch definefont pop");
+
+ puts("/Courier-Bold findfont");
+ puts("dup length dict begin\n"
+ " { 1 index /FID ne { def } { pop pop } ifelse } forall\n"
+ " /Encoding textEncoding def\n"
+ " currentdict\n"
+ "end");
+ puts("/Courier-Bold exch definefont pop");
+
+ if (PrettyPrint)
+ {
+ puts("/Courier-Oblique findfont");
+ puts("dup length dict begin\n"
+ " { 1 index /FID ne { def } { pop pop } ifelse } forall\n"
+ " /Encoding textEncoding def\n"
+ " currentdict\n"
+ "end");
+ puts("/Courier-Oblique exch definefont pop");
+ }
+ }
+
+ puts("% Define fonts");
+
+ printf("/FN /Courier findfont [%.1f 0 0 %.1f 0 0] makefont def\n",
+ 120.0 / CharsPerInch, 68.0 / LinesPerInch);
+ printf("/FB /Courier-Bold findfont [%.1f 0 0 %.1f 0 0] makefont def\n",
+ 120.0 / CharsPerInch, 68.0 / LinesPerInch);
+ if (PrettyPrint)
+ printf("/FI /Courier-Oblique findfont [%.1f 0 0 %.1f 0 0] makefont def\n",
+ 120.0 / CharsPerInch, 68.0 / LinesPerInch);
+
+ puts("% Common procedures");
+
+ puts("/N { FN setfont moveto } bind def");
+ puts("/B { FB setfont moveto } bind def");
+ puts("/U { gsave 0 rlineto stroke grestore } bind def");
+
+ if (PrettyPrint)
+ {
+ if (ColorDevice)
+ {
+ puts("/S { 0.0 setgray show } bind def");
+ puts("/r { 0.5 0.0 0.0 setrgbcolor show } bind def");
+ puts("/g { 0.0 0.5 0.0 setrgbcolor show } bind def");
+ puts("/b { 0.0 0.0 0.5 setrgbcolor show } bind def");
+ }
+ else
+ {
+ puts("/S { 0.0 setgray show } bind def");
+ puts("/r { 0.2 setgray show } bind def");
+ puts("/g { 0.2 setgray show } bind def");
+ puts("/b { 0.2 setgray show } bind def");
+ }
+
+ puts("/I { FI setfont moveto } bind def");
+
+ puts("/P 20 string def");
+ printf("/T(");
+
+ while (*title != '\0')
+ {
+ if (*title == '(' || *title == ')' || *title == '\\')
+ putchar('\\');
+
+ putchar(*title++);
+ }
+
+ puts(")def");
+
+ puts("/H {");
+ puts("gsave");
+ puts("\t0.9 setgray");
+
+ if (Duplex)
+ {
+ puts("\tdup 2 mod 0 eq {");
+ printf("\t\t%.1f %.1f translate } {\n",
+ PageWidth - PageRight, PageTop + 72.0f / LinesPerInch);
+ printf("\t\t%.1f %.1f translate } ifelse\n",
+ PageLeft, PageTop + 72.0f / LinesPerInch);
+ }
+ else
+ printf("\t%.1f %.1f translate\n",
+ PageLeft, PageTop + 72.0f / LinesPerInch);
+
+ printf("\t0 0 %.1f %.1f rectfill\n", PageRight - PageLeft,
+ 144.0f / LinesPerInch);
+
+ puts("\tFB setfont");
+ puts("\t0 setgray");
+
+ if (Duplex)
+ {
+ puts("\tdup 2 mod 0 eq {");
+ printf("\t\tT stringwidth pop neg %.1f add %.1f } {\n",
+ PageRight - PageLeft - 36.0f / LinesPerInch,
+ (0.5f + 0.157f) * 72.0f / LinesPerInch);
+ printf("\t\t%.1f %.1f } ifelse\n", 36.0f / LinesPerInch,
+ (0.5f + 0.157f) * 72.0f / LinesPerInch);
+ }
+ else
+ printf("\t%.1f %.1f\n", 36.0f / LinesPerInch,
+ (0.5f + 0.157f) * 72.0f / LinesPerInch);
+
+ puts("\tmoveto T show");
+
+ printf("\t(%s)\n", curdate);
+ printf("\tdup stringwidth pop neg 2 div %.1f add %.1f\n",
+ (PageRight - PageLeft) * 0.5,
+ (0.5f + 0.157f) * 72.0f / LinesPerInch);
+ puts("\tmoveto show");
+
+ if (Duplex)
+ {
+ puts("\tdup P cvs exch 2 mod 0 eq {");
+ printf("\t\t%.1f %.1f } {\n", 36.0f / LinesPerInch,
+ (0.5f + 0.157f) * 72.0f / LinesPerInch);
+ printf("\t\tdup stringwidth pop neg %.1f add %.1f } ifelse\n",
+ PageRight - PageLeft - 36.0f / LinesPerInch,
+ (0.5f + 0.157f) * 72.0f / LinesPerInch);
+ }
+ else
+ printf("\tP cvs dup stringwidth pop neg %.1f add %.1f\n",
+ PageRight - PageLeft - 36.0f / LinesPerInch,
+ (0.5f + 0.157f) * 72.0f / LinesPerInch);
+
+ puts("\tmoveto show");
+ puts("\tgrestore");
+ puts("} bind def");
+ }
+ else
+ puts("/S { show } bind def");
+
+ puts("%%EndResource");
+
+ puts("%%EndProlog");
+}
+
+
+/*
+ * 'write_line()' - Write a row of text.
+ */
+
+static void
+write_line(int row, /* I - Row number (0 to N) */
+ lchar_t *line) /* I - Line to print */
+{
+ int col; /* Current column */
+ int attr; /* Current attribute */
+ lchar_t *start; /* First character in sequence */
+
+
+ for (col = 0, start = line; col < SizeColumns;)
+ {
+ while (col < SizeColumns && (line->ch == ' ' || line->ch == 0))
+ {
+ col ++;
+ line ++;
+ }
+
+ if (col >= SizeColumns)
+ break;
+
+ attr = line->attr;
+ start = line;
+
+ while (col < SizeColumns && line->ch != 0 && attr == line->attr)
+ {
+ col ++;
+ line ++;
+ }
+
+ write_string(col - (line - start), row, line - start, start);
+ }
+}
+
+
+/*
+ * 'write_string()' - Write a string of text.
+ */
+
+static void
+write_string(int col, /* I - Start column */
+ int row, /* I - Row */
+ int len, /* I - Number of characters */
+ lchar_t *s) /* I - String to print */
+{
+ int i; /* Looping var */
+ float x, y; /* Position of text */
+ unsigned attr; /* Character attributes */
+
+
+ /*
+ * Position the text and set the font...
+ */
+
+ if (Duplex && (NumPages & 1) == 0)
+ {
+ x = PageWidth - PageRight;
+ y = PageTop;
+ }
+ else
+ {
+ x = PageLeft;
+ y = PageTop;
+ }
+
+ x += (float)col * 72.0f / (float)CharsPerInch;
+ y -= (float)(row + 0.843) * 72.0f / (float)LinesPerInch;
+
+ attr = s->attr;
+
+ if (attr & ATTR_RAISED)
+ y += 36.0 / (float)LinesPerInch;
+ else if (attr & ATTR_LOWERED)
+ y -= 36.0 / (float)LinesPerInch;
+
+ if (x == (int)x)
+ printf("%.0f ", x);
+ else
+ printf("%.1f ", x);
+
+ if (y == (int)y)
+ printf("%.0f ", y);
+ else
+ printf("%.1f ", y);
+
+ if (attr & ATTR_BOLD)
+ putchar('B');
+ else if (attr & ATTR_ITALIC)
+ putchar('I');
+ else
+ putchar('N');
+
+ if (attr & ATTR_UNDERLINE)
+ printf(" %.1f U", (float)len * 72.0 / (float)CharsPerInch);
+
+ /*
+ * See if the string contains 16-bit characters...
+ */
+
+ for (i = 0; i < len; i ++)
+ if (s[i].ch > 255)
+ break;
+
+ if (i < len)
+ {
+ /*
+ * Write a hex Unicode string...
+ */
+
+ fputs("<feff", stdout);
+
+ while (len > 0)
+ {
+ printf("%04x", s->ch);
+ len --;
+ s ++;
+ }
+
+ putchar('>');
+ }
+ else
+ {
+ /*
+ * Write a quoted string...
+ */
+
+ putchar('(');
+
+ while (len > 0)
+ {
+ if (s->ch > 126)
+ {
+ /*
+ * Quote 8-bit characters...
+ */
+
+ printf("\\%03o", s->ch);
+ }
+ else
+ {
+ /*
+ * Quote the parenthesis and backslash as needed...
+ */
+
+ if (s->ch == '(' || s->ch == ')' || s->ch == '\\')
+ putchar('\\');
+
+ putchar(s->ch);
+ }
+
+ len --;
+ s ++;
+ }
+
+ putchar(')');
+ }
+
+ if (PrettyPrint)
+ {
+ if (attr & ATTR_RED)
+ puts("r");
+ else if (attr & ATTR_GREEN)
+ puts("g");
+ else if (attr & ATTR_BLUE)
+ puts("b");
+ else
+ puts("S");
+ }
+ else
+ puts("S");
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/fonts/AvantGarde-Book b/fonts/AvantGarde-Book
new file mode 100644
index 000000000..ff3b81847
--- /dev/null
+++ b/fonts/AvantGarde-Book
Binary files differ
diff --git a/fonts/AvantGarde-BookOblique b/fonts/AvantGarde-BookOblique
new file mode 100644
index 000000000..12af8ae8d
--- /dev/null
+++ b/fonts/AvantGarde-BookOblique
Binary files differ
diff --git a/fonts/AvantGarde-Demi b/fonts/AvantGarde-Demi
new file mode 100644
index 000000000..e0df0c709
--- /dev/null
+++ b/fonts/AvantGarde-Demi
Binary files differ
diff --git a/fonts/AvantGarde-DemiOblique b/fonts/AvantGarde-DemiOblique
new file mode 100644
index 000000000..de6c3491e
--- /dev/null
+++ b/fonts/AvantGarde-DemiOblique
Binary files differ
diff --git a/fonts/Bookman-Demi b/fonts/Bookman-Demi
new file mode 100644
index 000000000..da92e614c
--- /dev/null
+++ b/fonts/Bookman-Demi
Binary files differ
diff --git a/fonts/Bookman-DemiItalic b/fonts/Bookman-DemiItalic
new file mode 100644
index 000000000..59072a526
--- /dev/null
+++ b/fonts/Bookman-DemiItalic
Binary files differ
diff --git a/fonts/Bookman-Light b/fonts/Bookman-Light
new file mode 100644
index 000000000..4416dbf31
--- /dev/null
+++ b/fonts/Bookman-Light
Binary files differ
diff --git a/fonts/Bookman-LightItalic b/fonts/Bookman-LightItalic
new file mode 100644
index 000000000..b8522c63c
--- /dev/null
+++ b/fonts/Bookman-LightItalic
Binary files differ
diff --git a/fonts/Courier b/fonts/Courier
new file mode 100644
index 000000000..54fe52724
--- /dev/null
+++ b/fonts/Courier
Binary files differ
diff --git a/fonts/Courier-Bold b/fonts/Courier-Bold
new file mode 100644
index 000000000..7b31de35f
--- /dev/null
+++ b/fonts/Courier-Bold
Binary files differ
diff --git a/fonts/Courier-BoldOblique b/fonts/Courier-BoldOblique
new file mode 100644
index 000000000..45b11a12f
--- /dev/null
+++ b/fonts/Courier-BoldOblique
Binary files differ
diff --git a/fonts/Courier-Oblique b/fonts/Courier-Oblique
new file mode 100644
index 000000000..bbd2f0534
--- /dev/null
+++ b/fonts/Courier-Oblique
Binary files differ
diff --git a/fonts/Helvetica b/fonts/Helvetica
new file mode 100644
index 000000000..5f20a5aad
--- /dev/null
+++ b/fonts/Helvetica
Binary files differ
diff --git a/fonts/Helvetica-Bold b/fonts/Helvetica-Bold
new file mode 100644
index 000000000..85ef201db
--- /dev/null
+++ b/fonts/Helvetica-Bold
Binary files differ
diff --git a/fonts/Helvetica-BoldOblique b/fonts/Helvetica-BoldOblique
new file mode 100644
index 000000000..48c34c1ca
--- /dev/null
+++ b/fonts/Helvetica-BoldOblique
Binary files differ
diff --git a/fonts/Helvetica-Narrow b/fonts/Helvetica-Narrow
new file mode 100644
index 000000000..e81691b52
--- /dev/null
+++ b/fonts/Helvetica-Narrow
Binary files differ
diff --git a/fonts/Helvetica-Narrow-Bold b/fonts/Helvetica-Narrow-Bold
new file mode 100644
index 000000000..9ff539416
--- /dev/null
+++ b/fonts/Helvetica-Narrow-Bold
Binary files differ
diff --git a/fonts/Helvetica-Narrow-BoldOblique b/fonts/Helvetica-Narrow-BoldOblique
new file mode 100644
index 000000000..9ca742cd6
--- /dev/null
+++ b/fonts/Helvetica-Narrow-BoldOblique
Binary files differ
diff --git a/fonts/Helvetica-Narrow-Oblique b/fonts/Helvetica-Narrow-Oblique
new file mode 100644
index 000000000..04045ae3f
--- /dev/null
+++ b/fonts/Helvetica-Narrow-Oblique
Binary files differ
diff --git a/fonts/Helvetica-Oblique b/fonts/Helvetica-Oblique
new file mode 100644
index 000000000..0653f80bf
--- /dev/null
+++ b/fonts/Helvetica-Oblique
Binary files differ
diff --git a/fonts/Makefile b/fonts/Makefile
new file mode 100644
index 000000000..f287800b1
--- /dev/null
+++ b/fonts/Makefile
@@ -0,0 +1,68 @@
+#
+# "$Id$"
+#
+# Fonts makefile for the Common UNIX Printing System (CUPS).
+#
+# Copyright 1993-1999 by Easy Software Products.
+#
+# These coded instructions, statements, and computer programs are the
+# property of Easy Software Products and are protected by Federal
+# copyright law. Distribution and use rights are outlined in the file
+# "LICENSE.txt" which should have been included with this file. If this
+# file is missing or damaged please contact Easy Software Products
+# at:
+#
+# Attn: CUPS Licensing Information
+# Easy Software Products
+# 44141 Airport View Drive, Suite 204
+# Hollywood, Maryland 20636-3111 USA
+#
+# Voice: (301) 373-9603
+# EMail: cups-info@cups.org
+# WWW: http://www.cups.org
+#
+
+include ../Makedefs
+
+#
+# Font files...
+#
+
+FONTS = AvantGarde-Book AvantGarde-BookOblique AvantGarde-Demi \
+ AvantGarde-DemiOblique Bookman-Demi Bookman-DemiItalic \
+ Bookman-Light Bookman-LightItalic Courier Courier-Bold \
+ Courier-BoldOblique Courier-Oblique Helvetica \
+ Helvetica-Bold Helvetica-BoldOblique Helvetica-Narrow \
+ Helvetica-Narrow-Bold Helvetica-Narrow-BoldOblique \
+ Helvetica-Narrow-Oblique Helvetica-Oblique \
+ NewCenturySchlbk-Bold NewCenturySchlbk-BoldItalic \
+ NewCenturySchlbk-Italic NewCenturySchlbk-Roman \
+ Palatino-Bold Palatino-BoldItalic Palatino-Italic \
+ Palatino-Roman Symbol Times-Bold Times-BoldItalic \
+ Times-Italic Times-Roman Utopia-Bold Utopia-BoldItalic \
+ Utopia-Italic Utopia-Regular ZapfChancery-MediumItalic \
+ ZapfDingbats
+
+#
+# Make everything...
+#
+
+all:
+
+#
+# Clean all config and object files...
+#
+
+clean:
+
+#
+# Install files...
+#
+
+install:
+ -$(MKDIR) $(DATADIR)/fonts
+ $(CP) $(FONTS) $(DATADIR)/fonts
+
+#
+# End of "$Id$".
+#
diff --git a/fonts/NewCenturySchlbk-Bold b/fonts/NewCenturySchlbk-Bold
new file mode 100644
index 000000000..41876dcd2
--- /dev/null
+++ b/fonts/NewCenturySchlbk-Bold
Binary files differ
diff --git a/fonts/NewCenturySchlbk-BoldItalic b/fonts/NewCenturySchlbk-BoldItalic
new file mode 100644
index 000000000..8ed9fdf52
--- /dev/null
+++ b/fonts/NewCenturySchlbk-BoldItalic
Binary files differ
diff --git a/fonts/NewCenturySchlbk-Italic b/fonts/NewCenturySchlbk-Italic
new file mode 100644
index 000000000..d21342585
--- /dev/null
+++ b/fonts/NewCenturySchlbk-Italic
Binary files differ
diff --git a/fonts/NewCenturySchlbk-Roman b/fonts/NewCenturySchlbk-Roman
new file mode 100644
index 000000000..46df4a4d9
--- /dev/null
+++ b/fonts/NewCenturySchlbk-Roman
Binary files differ
diff --git a/fonts/Palatino-Bold b/fonts/Palatino-Bold
new file mode 100644
index 000000000..8f8ef3649
--- /dev/null
+++ b/fonts/Palatino-Bold
Binary files differ
diff --git a/fonts/Palatino-BoldItalic b/fonts/Palatino-BoldItalic
new file mode 100644
index 000000000..da1128f14
--- /dev/null
+++ b/fonts/Palatino-BoldItalic
Binary files differ
diff --git a/fonts/Palatino-Italic b/fonts/Palatino-Italic
new file mode 100644
index 000000000..c8d447a64
--- /dev/null
+++ b/fonts/Palatino-Italic
Binary files differ
diff --git a/fonts/Palatino-Roman b/fonts/Palatino-Roman
new file mode 100644
index 000000000..ba252bdcb
--- /dev/null
+++ b/fonts/Palatino-Roman
Binary files differ
diff --git a/fonts/Symbol b/fonts/Symbol
new file mode 100644
index 000000000..e96e6e045
--- /dev/null
+++ b/fonts/Symbol
Binary files differ
diff --git a/fonts/Times-Bold b/fonts/Times-Bold
new file mode 100644
index 000000000..b03ed85d4
--- /dev/null
+++ b/fonts/Times-Bold
Binary files differ
diff --git a/fonts/Times-BoldItalic b/fonts/Times-BoldItalic
new file mode 100644
index 000000000..7fcda1807
--- /dev/null
+++ b/fonts/Times-BoldItalic
Binary files differ
diff --git a/fonts/Times-Italic b/fonts/Times-Italic
new file mode 100644
index 000000000..b4b018a8d
--- /dev/null
+++ b/fonts/Times-Italic
Binary files differ
diff --git a/fonts/Times-Roman b/fonts/Times-Roman
new file mode 100644
index 000000000..67172c8d9
--- /dev/null
+++ b/fonts/Times-Roman
Binary files differ
diff --git a/fonts/Utopia-Bold b/fonts/Utopia-Bold
new file mode 100644
index 000000000..06b91085a
--- /dev/null
+++ b/fonts/Utopia-Bold
Binary files differ
diff --git a/fonts/Utopia-BoldItalic b/fonts/Utopia-BoldItalic
new file mode 100644
index 000000000..c36689694
--- /dev/null
+++ b/fonts/Utopia-BoldItalic
Binary files differ
diff --git a/fonts/Utopia-Italic b/fonts/Utopia-Italic
new file mode 100644
index 000000000..e33f16af9
--- /dev/null
+++ b/fonts/Utopia-Italic
Binary files differ
diff --git a/fonts/Utopia-Regular b/fonts/Utopia-Regular
new file mode 100644
index 000000000..1772a3a0b
--- /dev/null
+++ b/fonts/Utopia-Regular
Binary files differ
diff --git a/fonts/ZapfChancery-MediumItalic b/fonts/ZapfChancery-MediumItalic
new file mode 100644
index 000000000..a8a97002d
--- /dev/null
+++ b/fonts/ZapfChancery-MediumItalic
Binary files differ
diff --git a/fonts/ZapfDingbats b/fonts/ZapfDingbats
new file mode 100644
index 000000000..eaf7105c8
--- /dev/null
+++ b/fonts/ZapfDingbats
Binary files differ
diff --git a/locale/C/cups_C b/locale/C/cups_C
new file mode 100644
index 000000000..dc69f651c
--- /dev/null
+++ b/locale/C/cups_C
@@ -0,0 +1,123 @@
+us-ascii
+OK
+Cancel
+Help
+Quit
+Close
+Yes
+No
+On
+Off
+Save
+Discard
+Default
+Options
+More Info
+Black
+Color
+Cyan
+Magenta
+Yellow
+Copyright 1993-1999 by Easy Software Products, All Rights Reserved.
+General
+Printer
+Image Options
+HP-GL/2 Options
+Extra
+Document
+Other
+Print Pages:
+Entire Document
+Page Range:
+Reverse Order:
+Page Format:
+ 1-Up
+ 2-Up
+ 4-Up
+Image Scaling:
+Use Natural Image Size
+Zoom by Percent
+Zoom by PPI
+Mirror Image:
+Color Saturation:
+Color Hue:
+Fit to Page:
+Shading:
+Pen Width:
+Gamma Correction:
+Brightness:
+Add
+Delete
+Modify
+Printer URI
+Printer Name
+Printer Location
+Printer Info
+Printer Make and Model
+Device URI
+Formatting Page
+Printing Page
+Initializing Printer
+Printer State
+Accepting Jobs
+Not Accepting Jobs
+Print Jobs
+Class
+Local
+Remote
+Duplexing
+Stapling
+Fast Copies
+Collated Copies
+Hole Punching
+Covering
+Binding
+Sorting
+Small (up to 9.5x14in)
+Medium (9.5x14in to 13x19in)
+Large (13x19in and larger)
+Custom Size
+Idle
+Processing
+Stopped
+All
+Odd
+Even Pages
+Darker Lighter
+Media Size
+Media Type
+Media Source
+Orientation:
+Portrait
+Landscape
+Job State
+Job Name
+User Name
+Priority
+Copies
+File Size
+Pending
+Output Mode
+Resolution
+400 Your browser sent a request that this server could not understand.
+This server could not verify that you are authorized to access the resource.
+You must pay to access this server.
+You don't have permission to access the resource on this server.
+The requested resource was not found on this server.
+The requested method is not allowed with the resource.
+An appropriate representation for the resource was not found on this server.
+You don't have permission to use this server as a proxy host.
+The request has taken too long to complete and has been aborted.
+The requested resource has more than one value.
+The requested resource is gone and has not been replaced.
+The requested method requires a valid Content-Length.
+The precondition on the request evaluated to false.
+The request is too large for this server to process.
+The request URI is too large for this server to process.
+The request format is not understood by this server.
+500 The server has detected an unrecoverable error and cannot process your request.
+The requested method is not implemented by this server.
+The proxy server received an invalid response from an upstream server.
+The requested resource is currently unavailable on this server.
+The proxy server has taken too long to respond to this server.
+This server does not support the HTTP version required by your browser.
diff --git a/locale/Makefile b/locale/Makefile
new file mode 100644
index 000000000..06ab0ab1f
--- /dev/null
+++ b/locale/Makefile
@@ -0,0 +1,73 @@
+#
+# "$Id$"
+#
+# Locale file makefile for the Common UNIX Printing System (CUPS).
+#
+# Copyright 1993-1999 by Easy Software Products.
+#
+# These coded instructions, statements, and computer programs are the
+# property of Easy Software Products and are protected by Federal
+# copyright law. Distribution and use rights are outlined in the file
+# "LICENSE.txt" which should have been included with this file. If this
+# file is missing or damaged please contact Easy Software Products
+# at:
+#
+# Attn: CUPS Licensing Information
+# Easy Software Products
+# 44141 Airport View Drive, Suite 204
+# Hollywood, Maryland 20636-3111 USA
+#
+# Voice: (301) 373-9603
+# EMail: cups-info@cups.org
+# WWW: http://www.cups.org
+#
+
+include ../Makedefs
+
+#
+# Locales...
+#
+
+LOCALES = C de en es fr it
+
+#
+# Make everything...
+#
+
+all: translate
+
+#
+# Clean all config and object files...
+#
+
+clean:
+
+#
+# Install files...
+#
+
+install:
+ -$(MKDIR) $(LIBDIR)/locale
+ for dir in $(LOCALES) ; do \
+ if test ! -d $(LIBDIR)/locale/$$dir ; then \
+ $(MKDIR) $(LIBDIR)/locale/$$dir ; \
+ fi ; \
+ $(CP) cups_$$dir $(LIBDIR)/locale/$$dir ; \
+ done
+
+#
+# translate - a simple utility to use Bablefish to translate the POSIX message
+# file to one of several languages.
+#
+# translate outfile language
+#
+
+translate: translate.o ../cups/libcups.a
+ echo Linking $<...
+ $(CC) $(LDFLAGS) -o translate translate.o $(LIBS)
+
+translate.o: ../cups/http.h
+
+#
+# End of "$Id$".
+#
diff --git a/locale/de/cups_de b/locale/de/cups_de
new file mode 100644
index 000000000..7a387cec7
--- /dev/null
+++ b/locale/de/cups_de
@@ -0,0 +1,123 @@
+iso-8859-1
+OK
+Löschen
+Hilfe
+Beendet
+Nah
+Ja
+Nein
+Auf
+Weg von
+Außer
+Ausschuß
+Rückstellung
+Optionen
+Mehr INFO
+Schwarzes
+Farbe
+Cyan-blau
+Magenta
+Gelb
+Copyright 1993-1999 durch Easy Software Products, alle Rechte vorbehalten.
+General
+Drucker
+BildOptionen
+HP-GL/2 Optionen
+Extrakosten
+Dokument
+Anderes
+DruckSeiten:
+Gesamtes Dokument
+Seitenbereich:
+RückOrdnung:
+Seite Format:
+ 1-Up
+ 2-Up
+ 4-Up
+BildScaling:
+Natürliche BildGröße Des Gebrauches
+Zoom durch Percent
+Zoom durch PPI
+SpiegelBild:
+Farbe Sättigung:
+Farbe Farbe:
+Passen Sie, um zu paginieren:
+Schattierend:
+FederBreite:
+Gamma Korrektur:
+Helligkeit:
+Fügen Sie hinzu
+Löschung
+ändern Sie
+DruckerURI
+DruckerName
+DruckerStandort
+Drucker-cInfo
+Drucker bilden und formen
+EinheitURI
+Formatierung Seite
+Seite Druckend
+InitialisierenDrucker
+DruckerZustand
+Jobs Annehmend
+Jobs Nicht, Annehmend
+Druckjobs
+Kategorie
+Lokal
+Entfernte Station
+Duplexing
+Heftend
+Schnelle Plattenkopierprogramme
+Gemischte Exemplare
+Bohrung Lochen
+Bedeckung
+Binden
+Sortierend
+Klein (bis 9.5x1în)
+Medium (9.5x1în bis 13x19in)
+Groß (13x19in und größeres)
+Kundenspezifische Größe
+Leerlauf
+Verarbeitend
+Gestoppt
+Alles
+Ungerade
+Gleichmäßige Seiten
+Dunkleres Heller
+MediaGröße
+MediaArt
+MediaQuelle
+Lagebestimmung:
+Portrait
+Landschaft
+Job State
+Job Name
+User Name
+Priority
+Copies
+File Size
+Pending
+Output Mode
+Resolution
+400 Ihre Datenbanksuchroutine sendete einen Antrag, den dieser Server nicht verstehen könnte.
+Dieser Server könnte nicht überprüfen, daß Sie autorisiert sind, das Hilfsmittel zuzugreifen.
+Sie müssen zahlen, diesen Server zuzugreifen.
+Sie haben nicht Erlaubnis, das Hilfsmittel auf diesem Server zuzugreifen.
+Das erbetene Hilfsmittel wurde nicht auf diesem Server gefunden.
+Die erbetene Methode wird nicht mit dem Hilfsmittel erlaubt.
+Eine passende Darstellung für das Hilfsmittel wurde nicht auf diesem Server gefunden.
+Sie haben nicht Erlaubnis, diesen Server als Proxyhauptrechner zu benutzen.
+Der Antrag hat zu lang genommen, um durchzuführen und ist abgebrochen worden.
+Das erbetene Hilfsmittel hat mehr als einen Wert.
+Das erbetene Hilfsmittel wird gegangen und ist nicht ersetzt worden.
+Die erbetene Methode benötigt ein gültiges Content-Length.
+Die Vorbedingung auf dem Antrag wertete zu falschem aus.
+Der Antrag ist zu groß, damit dieser Server verarbeitet.
+Der AntragcUri ist zu groß, damit dieser Server verarbeitet.
+Das Antragformat wird nicht durch diesen Server verstanden.
+500 Der Server hat einen unrecoverable Fehler ermittelt und nicht Ihren Antrag verarbeiten kann.
+Die erbetene Methode wird nicht durch diesen Server eingeführt.
+Das proxy server empfing eine unzulässige Antwort von einem aufwärts gerichteten Server.
+Das erbetene Hilfsmittel ist aktuell auf diesem Server nicht erreichbar.
+Das proxy server hat zu lang genommen, um auf diesen Server zu reagieren.
+Dieser Server unterstützt nicht die HTTP-Version, die durch Ihre Datenbanksuchroutine angefordert wird.
diff --git a/locale/en/cups_en b/locale/en/cups_en
new file mode 100644
index 000000000..de5472dbb
--- /dev/null
+++ b/locale/en/cups_en
@@ -0,0 +1,123 @@
+iso-8859-1
+OK
+Cancel
+Help
+Quit
+Close
+Yes
+No
+On
+Off
+Save
+Discard
+Default
+Options
+More Info
+Black
+Colour
+Cyan
+Magenta
+Yellow
+Copyright 1993-1999 by Easy Software Products, All Rights Reserved.
+General
+Printer
+Image Options
+HP-GL/2 Options
+Extra
+Document
+Other
+Print Pages:
+Entire Document
+Page Range:
+Reverse Order:
+Page Format:
+ 1-Up
+ 2-Up
+ 4-Up
+Image Scaling:
+Use Natural Image Size
+Zoom by Percent
+Zoom by PPI
+Mirror Image:
+Colour Saturation:
+Colour Hue:
+Fit to Page:
+Shading:
+Pen Width:
+Gamma Correction:
+Brightness:
+Add
+Delete
+Modify
+Printer URI
+Printer Name
+Printer Location
+Printer Info
+Printer Make and Model
+Device URI
+Formatting Page
+Printing Page
+Initializing Printer
+Printer State
+Accepting Jobs
+Not Accepting Jobs
+Print Jobs
+Class
+Local
+Remote
+Duplexing
+Stapling
+Fast Copies
+Collated Copies
+Hole Punching
+Covering
+Binding
+Sorting
+Small (up to 9.5x14in)
+Medium (9.5x14in to 13x19in)
+Large (13x19in and larger)
+Custom Size
+Idle
+Processing
+Stopped
+All
+Odd
+Even Pages
+Darker Lighter
+Media Size
+Media Type
+Media Source
+Orientation:
+Portrait
+Landscape
+Job State
+Job Name
+User Name
+Priority
+Copies
+File Size
+Pending
+Output Mode
+Resolution
+400 Your browser sent a request that this server could not understand.
+This server could not verify that you are authorized to access the resource.
+You must pay to access this server.
+You don't have permission to access the resource on this server.
+The requested resource was not found on this server.
+The requested method is not allowed with the resource.
+An appropriate representation for the resource was not found on this server.
+You don't have permission to use this server as a proxy host.
+The request has taken too long to complete and has been aborted.
+The requested resource has more than one value.
+The requested resource is gone and has not been replaced.
+The requested method requires a valid Content-Length.
+The precondition on the request evaluated to false.
+The request is too large for this server to process.
+The request URI is too large for this server to process.
+The request format is not understood by this server.
+500 The server has detected an unrecoverable error and cannot process your request.
+The requested method is not implemented by this server.
+The proxy server received an invalid response from an upstream server.
+The requested resource is currently unavailable on this server.
+The proxy server has taken too long to respond to this server.
+This server does not support the HTTP version required by your browser.
diff --git a/locale/es/cups_es b/locale/es/cups_es
new file mode 100644
index 000000000..0971aa552
--- /dev/null
+++ b/locale/es/cups_es
@@ -0,0 +1,123 @@
+iso-8859-1
+OK
+Cancelación
+Ayuda
+Salido
+Cercano
+Sí
+No
+En
+De
+Excepto
+Descarte
+Valor por defecto
+Opciones
+Más Info
+Negro
+Color
+Ciánico
+Magenta
+Amarillo
+El copyright 1993-1999 por Easy Software Products, todos endereza reservado.
+General
+Impresora
+Opciones De la Imagen
+Opciones De HP-GL/2
+Suplemento
+Documento
+Otro
+Paginaciones De la Impresión:
+Entero Documento
+Rango De Paginación:
+Orden Reversa:
+Formato De la Paginación:
+ 1-Up
+ 2-Up
+ 4-Up
+Escalamiento De la Imagen:
+Talla Natural De la Imagen Del Uso
+Zoom de Percent
+Zoom de PPI
+Imagen Del Espejo:
+Saturación Del Color:
+Tonalidad Del Color:
+Quepa para paginar:
+Sombreando:
+Anchura De la Pluma:
+Corrección Gamma:
+Brillo:
+Agregue
+Cancelación
+Modifiqúese
+URI De la Impresora
+Nombre De la Impresora
+Localización De la Impresora
+Impresora Info
+La impresora hace y modela
+URI Del Dispositivo
+Paginación Del Formato
+Imprimiendo La Paginación
+De Incialización Impresora
+Estado De la Impresora
+Validando Trabajos
+No validando Trabajos
+Trabajos De Impresión
+Clase
+Local
+Telecontrol
+Duplexing
+Sujetando con grapa
+Rápidas Copias
+Clasificadas Copias
+Perforación Del Agujero
+Cubierta
+Atando
+Clasificando
+Pequeño (los hasta 9.5x14in)
+Media (los 9.5x14in a el 13x19in)
+Grande (el 13x19in y más grande)
+De encargo Talla
+Marcha lenta
+Procesando
+Parado
+Todo
+Impar
+Uniformes Paginaciones
+Más Oscuro Más Brillante
+Talla De Media
+Tipo De Media
+Fuente De los Media
+Orientación:
+Retrato
+Paisaje
+Job State
+Job Name
+User Name
+Priority
+Copies
+File Size
+Pending
+Output Mode
+Resolution
+400 Su browser envió una petición que este servidor no podría entender.
+Este servidor no podría verificar que le autoricen a tener acceso al recurso.
+Usted debe pagar tener acceso a este servidor.
+Usted no tiene permiso de tener acceso al recurso en este servidor.
+El recurso solicitado no fue encontrado en este servidor.
+El método solicitado no se permite con el recurso.
+Una representación apropiada para el recurso no fue encontrada en este servidor.
+Usted no tiene permiso de utilizar este servidor como ordenador principal del poder.
+La petición ha durado demasiado para terminar y se ha abortado.
+El recurso solicitado tiene más de un valor.
+Se va y no se ha substituido el recurso solicitado.
+El método solicitado requiere un Content-Length válido.
+La condición previa en la petición evaluó a falso.
+La petición es demasiado grande para que este servidor procese.
+El URI de la petición es demasiado grande para que este servidor procese.
+El formato de la petición no es entendido por este servidor.
+500 El servidor ha detectado un error irrecuperable y no puede procesar su petición.
+El método solicitado no es puesto en ejecución por este servidor.
+El proxy server recibió una respuesta inválida de un servidor por aguas arriba.
+El recurso solicitado es actualmente inasequible en este servidor.
+El proxy server ha durado demasiado para responder a este servidor.
+Este servidor no utiliza la versión del HTTP requerida por su browser.
diff --git a/locale/fr/cups_fr b/locale/fr/cups_fr
new file mode 100644
index 000000000..fa5c05924
--- /dev/null
+++ b/locale/fr/cups_fr
@@ -0,0 +1,123 @@
+iso-8859-1
+OK
+Annulation
+Aide
+Quitté
+Étroit
+Oui
+Non
+Sur
+Outre de
+Économiser
+Écart
+Défaut
+Options
+Plus D'Information
+Noir
+Couleur
+Cyan
+Magenta
+Jaune
+Copyright 1993-1999 par Easy Software Products, tous droits réservés.
+Général
+Imprimante
+Options D'Image
+Options D'HP-GL/2
+Frais supplémentaires
+Document
+Autre
+Pages D'Impression:
+Entier Document
+Chaîne De Page
+Commande D'Inversion:
+Format De Page:
+ 1-Up
+ 2-Up
+ 4-Up
+Graduation D'Image:
+Taille Normale D'Image D'Utilisation
+Zoom par Percent
+Zoom par PPI
+Image De Miroir:
+Saturation De Couleur:
+Tonalité De Couleur:
+Adaptez pour paginer:
+Ombrageant:
+Largeur De Crayon lecteur:
+Gamma Correction:
+Éclat:
+Ajoutez
+Effacement
+Modifiez
+URI D'Imprimante
+Nom D'Imprimante
+Emplacement D'Imprimante
+Information D'Imprimante
+L'imprimante font et modèlent
+URI De Dispositif
+Page De Formatage
+Imprimant La Page
+D' Initialisation Imprimante
+État D'Imprimante
+Recevant Les Travaux
+Ne recevant pas Les Travaux
+Tirages
+Classe
+Local
+Périphérique
+Duplexage
+Agrafant
+Rapides Copies
+Assemblées Copies
+Poinçon De Trou
+Bâche
+Liant
+Triant
+Petit (jusqu'à 9.5x1în)
+Support (9.5x1în à 13x19in)
+Grand (13x19in et plus grand)
+Faite sur commande Taille
+Ralenti
+Traitant
+Arrêté
+Tout
+Impair
+Même Pages
+Plus foncé Plus Lumineux
+Taille De Medias
+Type De Supports
+Source De Medias
+Orientation:
+Verticale
+Horizontal
+Job State
+Job Name
+User Name
+Priority
+Copies
+File Size
+Pending
+Output Mode
+Resolution
+400 Votre browser a envoyé une demande que ce serveur ne pourrait pas comprendre.
+Ce serveur ne pourrait pas vérifier que vous êtes autorisés à accéder à la ressource.
+Vous devez payer pour accéder à ce serveur.
+Vous n'avez pas la permission d'accéder à la ressource sur ce serveur.
+La ressource demandée n'a pas été trouvée sur ce serveur.
+On ne permet pas la méthode demandée avec la ressource.
+Une représentation appropriée pour la ressource n'a pas été trouvée sur ce serveur.
+Vous n'avez pas la permission d'utiliser ce serveur comme centre serveur de procuration.
+La demande a pris trop longtemps pour se terminer et a été interrompue.
+La ressource demandée a plus d'une valeur.
+La ressource demandée est allée et n'a pas été substituée.
+La méthode demandée exige un Content-Length valide.
+La condition préalable sur la demande a évalué à faux.
+La demande est trop grande pour que ce serveur traite.
+L'cUri de demande est trop grand pour que ce serveur traite.
+Le format de demande n'est pas compris par ce serveur.
+500 Le serveur a détecté une erreur irrémédiable et ne peut pas traiter votre demande.
+La méthode demandée n'est pas appliquée par ce serveur.
+Le proxy server a reçu une réponse incorrecte d'un serveur ascendant.
+La ressource demandée est actuel indisponible sur ce serveur.
+Le proxy server a pris trop longtemps pour répondre à ce serveur.
+Ce serveur ne supporte pas la version de HTTP exigée par votre browser.
diff --git a/locale/it/cups_it b/locale/it/cups_it
new file mode 100644
index 000000000..228c6a835
--- /dev/null
+++ b/locale/it/cups_it
@@ -0,0 +1,123 @@
+iso-8859-1
+GIUSTO
+Annullamento
+Aiuto
+Rinunciato
+Vicino
+Sì
+No
+Su
+Fuori di
+Risparmi
+Scarto
+Difetto
+Opzioni
+Più Info
+Nero
+Colore
+Ciano
+Fucsina
+Colore giallo
+Copyright 1993-1999 da di Easy Software Products, tutti radrizza riservato.
+Generalità
+Stampante
+Opzioni Di Immagine
+Opzioni Di HP-GL/2
+Supplemento
+Documento
+Altro
+Pagine Della Stampa:
+Intero Documento
+Gamma Di Pagina:
+Ordine D'inversione:
+Formato Della Pagina:
+ 1-Up
+ 2-Up
+ 4-Up
+Scaling Di Immagine:
+Formato Naturale Di Immagine Di Uso
+Zoom da Percent
+Zoom da PPI
+Immagine Dello Specchio:
+Saturazione Di Colore:
+Tonalità Di Colore:
+Adattare per paginare:
+Proteggendo:
+Larghezza Della Penna:
+Correzione Gamma:
+Luminosità:
+Aggiungere
+Cancellazione
+Modificare
+URI Della Stampante
+Nome Della Stampante
+Posizione Della Stampante
+Stampante Info
+Stampante fa e modella
+URI Del Dispositivo
+Pagina Di Formattazione
+Stampando Pagina
+D' Inizializzazione Stampante
+Condizione Della Stampante
+Accettando I Lavori
+Non accettando I Lavori
+Lavori Di Stampa
+Codice categoria
+Locale
+Periferico
+Utilizzazione per due usi
+Cucendo con punti metallici
+Veloci Copie
+Fascicolate Copie
+Perforazione Del Foro
+Covering
+Legandosi
+Ordinando
+Piccolo (fino a 9.5x1în)
+Media (9.5x1în - 13x19in)
+Grande (13x19in e più grande)
+Su ordinazione Formato
+Idle
+Elaborando
+Arrestato
+Tutto
+Dispari
+Anche Pagine
+Più Scuro Più Luminoso
+Formato Di Media
+Tipo Di Media
+Sorgente Di Media
+Orientamento:
+Portrait
+Paesaggio
+Job State
+Job Name
+User Name
+Priority
+Copies
+File Size
+Pending
+Output Mode
+Resolution
+400 Il vostro browser ha trasmesso una richiesta che questo server non potrebbe capire.
+Questo server non potrebbe verificare che siete autorizzati ad accedere alla risorsa.
+Dovete pagare accedere a questo server.
+Non avete permesso accedere alla risorsa su questo server.
+La risorsa chiesta non è stata trovata su questo server.
+Il metodo chiesto non è permesso con la risorsa.
+Una rappresentazione adatta per la risorsa non è stata trovata su questo server.
+Non avete permesso utilizzare questo server come calcolatore centrale di procura.
+La richiesta ha preso troppo lungamente per completare ed è stata abbandonata.
+La risorsa chiesta ha più di un valore.
+La risorsa chiesta è andata e non è stata sostituita.
+Il metodo chiesto richiede un Content-Length valido.
+Il presupposto sulla richiesta ha valutato a falso.
+La richiesta è troppo grande affinchè questo server elabori.
+Il URI di richiesta è troppo grande affinchè questo server elabori.
+Il formato di richiesta non è capito da questo server.
+500 Il server ha rilevato un errore unrecoverable e non può elaborare la vostra richiesta.
+Il metodo chiesto non è effettuato da questo server.
+Il proxy server ha ricevuto una risposta non valida da un server verso l'alto.
+La risorsa chiesta è attualmente non disponibile su questo server.
+Il proxy server ha preso troppo lungamente per rispondere a questo server.
+Questo server non sostiene la versione del HTTP richiesta dal vostro browser.
diff --git a/locale/locale.txt b/locale/locale.txt
new file mode 100644
index 000000000..f9abe72d6
--- /dev/null
+++ b/locale/locale.txt
@@ -0,0 +1,32 @@
+This directory contains the message strings used by CUPS for various
+languages. Each subdirectory corresponds to a different locale, and
+the cups_xx and cups_xx_YY files contain the messages for the locales
+named "xx" or "xx_YY".
+
+Each message file starts with a character set identifier, which can be
+one of the following:
+
+ us-ascii
+ iso-8859-1
+ iso-8859-2
+ iso-8859-3
+ iso-8859-4
+ iso-8859-5
+ iso-8859-6
+ iso-8859-7
+ iso-8859-8
+ iso-8859-9
+ utf-8
+
+After that, all non-blank lines are treated as messages, with any
+leading whitespace removed. If a line starts with a number then the
+message index is updated to the number. Otherwise, the next message
+number is used.
+
+The message indices are defined in the include file <cups/language.h>.
+The HTTP status messages use the status codes defined in <cups/http.h>.
+
+If you would like to contribute a new message file for your locale, or
+have corrections to the current ones, please send them to:
+
+ cups-support@cups.org
diff --git a/locale/translate.c b/locale/translate.c
new file mode 100644
index 000000000..e56690be6
--- /dev/null
+++ b/locale/translate.c
@@ -0,0 +1,259 @@
+/*
+ * "$Id$"
+ *
+ * HTTP-based translation program for the Common UNIX Printing System (CUPS).
+ *
+ * This program uses AltaVista's "babelfish" page to translate the POSIX
+ * message file (C/cups_C) to several different languages. The translation
+ * isn't perfect, but it's a good start (better than working from scratch.)
+ *
+ * Copyright 1997-1999 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44145 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * Contents:
+ *
+ * main() - Main entry.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include <cups/http.h>
+
+
+/*
+ * 'main()' - Main entry.
+ */
+
+int /* O - Exit status */
+main(int argc, /* I - Number of command-line arguments */
+ char *argv[]) /* I - Command-line arguments */
+{
+ http_t *http; /* HTTP connection */
+ http_status_t status; /* Status of GET command */
+ char line[1024], /* Line from file */
+ *lineptr, /* Pointer into line */
+ buffer[2048], /* Input/output buffer */
+ *bufptr, /* Pointer into buffer */
+ length[16]; /* Content length */
+ int bytes; /* Number of bytes read */
+ FILE *in, /* Input file */
+ *out; /* Output file */
+
+
+ if (argc != 3)
+ {
+ fputs("Usage: translate outfile language\n", stderr);
+ return (1);
+ }
+
+ if ((in = fopen("C/cups_C", "r")) == NULL)
+ {
+ perror("translate: Unable to open input file");
+ return (1);
+ }
+
+ if ((out = fopen(argv[1], "w")) == NULL)
+ {
+ perror("translate: Unable to create output file");
+ fclose(in);
+ return (1);
+ }
+
+ /*
+ * Do character set...
+ */
+
+ fgets(line, sizeof(line), in);
+ fputs("iso-8859-1\n", out); /* Right now that's all that Babelfish does */
+
+ /*
+ * Then strings...
+ */
+
+ while (fgets(line, sizeof(line), in) != NULL)
+ {
+ /*
+ * Strip trailing newline if necessary...
+ */
+
+ lineptr = line + strlen(line) - 1;
+ if (*lineptr == '\n')
+ *lineptr = '\0';
+
+ /*
+ * Skip leading numbers and whitespace...
+ */
+
+ lineptr = line;
+ while (isdigit(*lineptr))
+ putc(*lineptr++, out);
+
+ while (isspace(*lineptr))
+ putc(*lineptr++, out);
+
+ if (*lineptr == '\0')
+ {
+ putc('\n', out);
+ continue;
+ }
+
+ /*
+ * Encode the line into the buffer...
+ */
+
+ sprintf(buffer, "doit=done&lp=en_%s&urltext=[", argv[2]);
+ bufptr = buffer + strlen(buffer);
+
+ while (*lineptr)
+ {
+ if (*lineptr == ' ')
+ *bufptr++ = '+';
+ else if (*lineptr < ' ' || *lineptr == '%')
+ {
+ sprintf(bufptr, "%%%02X", *lineptr & 255);
+ bufptr += 3;
+ }
+ else
+ *bufptr++ = *lineptr;
+
+ lineptr ++;
+ }
+
+ *bufptr++ = '&';
+ *bufptr = '\0';
+
+ sprintf(length, "%d", bufptr - buffer);
+
+ /*
+ * Send the request...
+ */
+
+ if ((http = httpConnect("dns.easysw.com", 80)) == NULL)
+ {
+ perror("translate: Unable to contact proxy server");
+ fclose(in);
+ fclose(out);
+ return (1);
+ }
+
+ lineptr = line;
+ while (isdigit(*lineptr))
+ lineptr ++;
+ while (isspace(*lineptr))
+ lineptr ++;
+
+ printf("%s = ", lineptr);
+ fflush(stdout);
+
+ http->version = HTTP_1_0;
+ httpClearFields(http);
+ httpSetField(http, HTTP_FIELD_CONTENT_TYPE,
+ "application/x-www-form-urlencoded");
+ httpSetField(http, HTTP_FIELD_CONTENT_LENGTH, length);
+ if (httpPost(http, "http://babelfish.altavista.digital.com/cgi-bin/translate?"))
+ httpPost(http, "http://babelfish.altavista.digital.com/cgi-bin/translate?");
+
+ httpWrite(http, buffer, bufptr - buffer);
+
+ while ((status = httpUpdate(http)) == HTTP_CONTINUE);
+
+ if (status == HTTP_OK)
+ {
+ int sawparen = 0;
+ int skipws = 1;
+ int sawbracket = 0;
+
+ while ((bytes = httpRead(http, buffer, sizeof(buffer))) > 0)
+ {
+ buffer[bytes] = '\0';
+
+ for (bufptr = buffer; *bufptr; bufptr ++)
+ {
+ if (*bufptr == '>')
+ sawbracket = 0;
+ else if (*bufptr == '<')
+ {
+ sawbracket = 1;
+ if (sawparen)
+ break;
+ }
+ else if (*bufptr == '[' && !sawbracket)
+ sawparen = 1;
+ else if (sawparen)
+ {
+ if (skipws)
+ {
+ if (!isspace(*bufptr))
+ {
+ skipws = 0;
+ *bufptr = toupper(*bufptr);
+ }
+ }
+
+ if (!skipws)
+ {
+ if (*bufptr == '\n')
+ {
+ putc(' ', out);
+ putchar(' ');
+ }
+ else
+ {
+ putc(*bufptr, out);
+ putchar(*bufptr);
+ }
+ }
+ }
+ }
+
+ if (sawparen && sawbracket)
+ break;
+ }
+
+ httpFlush(http);
+ putc('\n', out);
+ putchar('\n');
+ }
+ else
+ {
+ printf("HTTP error %d\n", status);
+
+ fprintf(out, "%s\n", lineptr);
+ httpFlush(http);
+ }
+
+ httpClose(http);
+ }
+
+ fclose(in);
+ fclose(out);
+
+ return (0);
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/man/Makefile b/man/Makefile
new file mode 100644
index 000000000..55c2fbe9f
--- /dev/null
+++ b/man/Makefile
@@ -0,0 +1,78 @@
+#
+# "$Id$"
+#
+# Man page makefile for the Common UNIX Printing System (CUPS).
+#
+# Copyright 1993-1999 by Easy Software Products.
+#
+# These coded instructions, statements, and computer programs are the
+# property of Easy Software Products and are protected by Federal
+# copyright law. Distribution and use rights are outlined in the file
+# "LICENSE.txt" which should have been included with this file. If this
+# file is missing or damaged please contact Easy Software Products
+# at:
+#
+# Attn: CUPS Licensing Information
+# Easy Software Products
+# 44141 Airport View Drive, Suite 204
+# Hollywood, Maryland 20636-3111 USA
+#
+# Voice: (301) 373-9603
+# EMail: cups-info@cups.org
+# WWW: http://www.cups.org
+#
+
+include ../Makedefs
+
+#
+# Man pages...
+#
+
+MAN1 = backend.1 filter.1 lprm.1 lpr.1 lpstat.1 lp.1
+MAN5 = classes.conf.5 cupsd.conf.5 mime.convs.5 mime.types.5 \
+ printers.conf.5
+MAN8 = accept.8 cupsd.8 enable.8 lpadmin.8 lpc.8
+
+CAT1 = $(MAN1:.1=.$(CAT))
+CAT5 = $(MAN5:.5=.$(CAT))
+CAT8 = $(MAN8:.8=.$(CAT))
+
+#
+# Make everything...
+#
+
+all: $(CAT1) $(CAT5) $(CAT8)
+
+#
+# Clean all config and object files...
+#
+
+clean:
+
+#
+# Install files...
+#
+
+install:
+ -$(MKDIR) $(MANDIR)/man1
+ $(CP) $(MAN1) $(MANDIR)/man1
+ $(LN) lp.1 $(MANDIR)/man1/cancel.1
+ -$(MKDIR) $(MANDIR)/man5
+ $(CP) $(MAN5) $(MANDIR)/man5
+ -$(MKDIR) $(MANDIR)/man8
+ $(CP) $(MAN8) $(MANDIR)/man8
+ $(LN) accept.8 $(MANDIR)/man8/reject.8
+ $(LN) enable.8 $(MANDIR)/man8/disable.8
+ -$(MKDIR) $(MANDIR)/cat1
+ $(CP) $(CAT1) $(MANDIR)/cat1
+ $(LN) lp.$(CAT) $(MANDIR)/cat1/cancel.$(CAT)
+ -$(MKDIR) $(MANDIR)/cat5
+ $(CP) $(CAT5) $(MANDIR)/cat5
+ -$(MKDIR) $(MANDIR)/cat8
+ $(CP) $(CAT8) $(MANDIR)/cat8
+ $(LN) accept.$(CAT) $(MANDIR)/cat8/reject.$(CAT)
+ $(LN) enable.$(CAT) $(MANDIR)/cat8/disable.$(CAT)
+
+#
+# End of "$Id$".
+#
diff --git a/man/accept.8 b/man/accept.8
new file mode 100644
index 000000000..fa379027f
--- /dev/null
+++ b/man/accept.8
@@ -0,0 +1,57 @@
+.\"
+.\" "$Id: accept.8 380 1999-06-10 16:15:04Z mike $"
+.\"
+.\" accept/reject man page for the Common UNIX Printing System (CUPS).
+.\"
+.\" Copyright 1997-1999 by Easy Software Products.
+.\"
+.\" These coded instructions, statements, and computer programs are the
+.\" property of Easy Software Products and are protected by Federal
+.\" copyright law. Distribution and use rights are outlined in the file
+.\" "LICENSE.txt" which should have been included with this file. If this
+.\" file is missing or damaged please contact Easy Software Products
+.\" at:
+.\"
+.\" Attn: CUPS Licensing Information
+.\" Easy Software Products
+.\" 44141 Airport View Drive, Suite 204
+.\" Hollywood, Maryland 20636-3111 USA
+.\"
+.\" Voice: (301) 373-9603
+.\" EMail: cups-info@cups.org
+.\" WWW: http://www.cups.org
+.\"
+.TH accept 8 "Common UNIX Printing System" "14 May 1999" "Easy Software Products"
+.SH NAME
+accept/reject \- accept/reject jobs sent to a destination
+.SH SYNOPSIS
+.B accept
+destination(s)
+.br
+.B reject
+[ -h
+.I server
+] [ -r [
+.I reason
+] ]
+destination(s)
+.SH DESCRIPTION
+\fBaccept\fR instructs the printing system to accept print jobs to the
+specified destinations.
+.LP
+\fBreject\fR instructs the printing system to reject print jobs to the
+specified destinations. The \fI-r\fR option sets the reason for rejecting
+print jobs. If not specified the reason defaults to "Reason Unknown".
+.SH COMPATIBILITY
+The CUPS versions of \fBaccept\fR and \fBreject\fR may ask the user for an
+access password depending on the printing system configuration. This differs
+from the System V versions which require the root user to execute these
+commands.
+.SH SEE ALSO
+cancel(1), disable(8), enable(8), lp(1), lpadmin(8), lpstat(1),
+CUPS Software Administrator's Manual
+.SH COPYRIGHT
+Copyright 1993-1999 by Easy Software Products, All Rights Reserved.
+.\"
+.\" End of "$Id: accept.8 380 1999-06-10 16:15:04Z mike $".
+.\"
diff --git a/man/accept.z b/man/accept.z
new file mode 100644
index 000000000..6ec1f4f6c
--- /dev/null
+++ b/man/accept.z
Binary files differ
diff --git a/man/backend.1 b/man/backend.1
new file mode 100644
index 000000000..6b89cef89
--- /dev/null
+++ b/man/backend.1
@@ -0,0 +1,88 @@
+.\"
+.\" "$Id: backend.1 327 1999-05-14 17:03:06Z mike $"
+.\"
+.\" backend man page for the Common UNIX Printing System (CUPS).
+.\"
+.\" Copyright 1997-1999 by Easy Software Products.
+.\"
+.\" These coded instructions, statements, and computer programs are the
+.\" property of Easy Software Products and are protected by Federal
+.\" copyright law. Distribution and use rights are outlined in the file
+.\" "LICENSE.txt" which should have been included with this file. If this
+.\" file is missing or damaged please contact Easy Software Products
+.\" at:
+.\"
+.\" Attn: CUPS Licensing Information
+.\" Easy Software Products
+.\" 44141 Airport View Drive, Suite 204
+.\" Hollywood, Maryland 20636-3111 USA
+.\"
+.\" Voice: (301) 373-9603
+.\" EMail: cups-info@cups.org
+.\" WWW: http://www.cups.org
+.\"
+.TH backend 1 "Common UNIX Printing System" "14 May 1999" "Easy Software Products"
+.SH NAME
+backend \- backend transmission interfaces
+.SH SYNOPSIS
+.B backend
+job user title num-copies options
+.I [ filename ]
+.SH DESCRIPTION
+The CUPS backend interface provides a standard method for sending document
+files to different physical interfaces.
+.LP
+Backends must be capable of reading from a filename on the command-line
+or from the standard input, copying stdin to a temporary if required by
+the physical interface.
+.LP
+The command name (argv[0]) is set to the device URI of the destination printer.
+.SH ENVIRONMENT VARIABLES
+The following environment variables are defined by the CUPS server when
+executing the backend:
+.TP 5
+CHARSET
+.br
+The default text character set (typically us-ascii or iso-8859-1).
+.TP 5
+LANG
+.br
+The default language locale (typically C or en).
+.TP 5
+PATH
+.br
+The standard execution path for external programs that may be run by the backend.
+.TP 5
+PPD
+.br
+The full pathname of the PostScript Printer Description (PPD) file for
+this printer.
+.TP 5
+RIP_CACHE
+.br
+The recommended amount of memory to use for Raster Image Processors (RIPs).
+.TP 5
+SERVER_ROOT
+.br
+The root directory of the server.
+.TP 5
+SOFTWARE
+.br
+The name and version number of the server (typically CUPS/1.0).
+.TP 5
+TZ
+.br
+The timezone of the server (typically GMT).
+.TP 5
+USER
+.br
+The user executing the backend (typically root).
+.SH SEE ALSO
+cupsd(8), filter(1)
+CUPS Software Administrator's Manual,
+CUPS Interface Design Description
+.SH COPYRIGHT
+Copyright 1993-1999 by Easy Software Products, All Rights Reserved.
+.\"
+.\" End of "$Id: backend.1 327 1999-05-14 17:03:06Z mike $".
+.\"
diff --git a/man/backend.z b/man/backend.z
new file mode 100644
index 000000000..2baa1b475
--- /dev/null
+++ b/man/backend.z
Binary files differ
diff --git a/man/classes.conf.5 b/man/classes.conf.5
new file mode 100644
index 000000000..5fb42a7b9
--- /dev/null
+++ b/man/classes.conf.5
@@ -0,0 +1,36 @@
+.\"
+.\" "$Id: classes.conf.5 327 1999-05-14 17:03:06Z mike $"
+.\"
+.\" classes.conf man page for the Common UNIX Printing System (CUPS).
+.\"
+.\" Copyright 1997-1999 by Easy Software Products.
+.\"
+.\" These coded instructions, statements, and computer programs are the
+.\" property of Easy Software Products and are protected by Federal
+.\" copyright law. Distribution and use rights are outlined in the file
+.\" "LICENSE.txt" which should have been included with this file. If this
+.\" file is missing or damaged please contact Easy Software Products
+.\" at:
+.\"
+.\" Attn: CUPS Licensing Information
+.\" Easy Software Products
+.\" 44141 Airport View Drive, Suite 204
+.\" Hollywood, Maryland 20636-3111 USA
+.\"
+.\" Voice: (301) 373-9603
+.\" EMail: cups-info@cups.org
+.\" WWW: http://www.cups.org
+.\"
+.TH classes.conf 5 "Common UNIX Printing System" "14 May 1999" "Easy Software Products"
+.SH NAME
+classes.conf \- class configuration file for cups
+.SH DESCRIPTION
+.SH SEE ALSO
+cupsd(8), cupsd.conf(5), mime.convs(5), mime.types(5), printers.conf(5),
+CUPS Software Administrator's Manual,
+CUPS Interface Design Description
+.SH COPYRIGHT
+Copyright 1993-1999 by Easy Software Products, All Rights Reserved.
+.\"
+.\" End of "$Id: classes.conf.5 327 1999-05-14 17:03:06Z mike $".
+.\"
diff --git a/man/classes.conf.z b/man/classes.conf.z
new file mode 100644
index 000000000..1d3b5b98f
--- /dev/null
+++ b/man/classes.conf.z
Binary files differ
diff --git a/man/cupsd.8 b/man/cupsd.8
new file mode 100644
index 000000000..3e81631af
--- /dev/null
+++ b/man/cupsd.8
@@ -0,0 +1,47 @@
+.\"
+.\" "$Id: cupsd.8 327 1999-05-14 17:03:06Z mike $"
+.\"
+.\" cupsd man page for the Common UNIX Printing System (CUPS).
+.\"
+.\" Copyright 1997-1999 by Easy Software Products.
+.\"
+.\" These coded instructions, statements, and computer programs are the
+.\" property of Easy Software Products and are protected by Federal
+.\" copyright law. Distribution and use rights are outlined in the file
+.\" "LICENSE.txt" which should have been included with this file. If this
+.\" file is missing or damaged please contact Easy Software Products
+.\" at:
+.\"
+.\" Attn: CUPS Licensing Information
+.\" Easy Software Products
+.\" 44141 Airport View Drive, Suite 204
+.\" Hollywood, Maryland 20636-3111 USA
+.\"
+.\" Voice: (301) 373-9603
+.\" EMail: cups-info@cups.org
+.\" WWW: http://www.cups.org
+.\"
+.TH cupsd 8 "Common UNIX Printing System" "14 May 1999" "Easy Software Products"
+.SH NAME
+cupsd \- common unix printing system daemon
+.SH SYNOPSIS
+.B cups
+.I [ \-c config-file ]
+.SH DESCRIPTION
+\fBcupsd\fR is the scheduler for the Common UNIX Printing System. It implements
+a printing system based upon the Internet Printing Protocol, version 1.0. If
+no options are specified on the command-line then the default configuration file
+(usually \fI/var/cups/conf/cupsf.conf\fR) will be used.
+.SH COMPATIBILITY
+\fBcupsd\fR implements all of the required IPP/1.0 attributes and operations.
+It also implements optional operation set 1 and several CUPS-specific
+administation operations.
+.SH SEE ALSO
+classes.conf(5), cupsd.conf(5), mime.convs(5), mime.types(5), printers.conf(5),
+CUPS Software Administrator's Manual,
+CUPS Interface Design Description
+.SH COPYRIGHT
+Copyright 1993-1999 by Easy Software Products, All Rights Reserved.
+.\"
+.\" End of "$Id: cupsd.8 327 1999-05-14 17:03:06Z mike $".
+.\"
diff --git a/man/cupsd.conf.5 b/man/cupsd.conf.5
new file mode 100644
index 000000000..33d5781c8
--- /dev/null
+++ b/man/cupsd.conf.5
@@ -0,0 +1,36 @@
+.\"
+.\" "$Id: cupsd.conf.5 327 1999-05-14 17:03:06Z mike $"
+.\"
+.\" cupsd.conf man page for the Common UNIX Printing System (CUPS).
+.\"
+.\" Copyright 1997-1999 by Easy Software Products.
+.\"
+.\" These coded instructions, statements, and computer programs are the
+.\" property of Easy Software Products and are protected by Federal
+.\" copyright law. Distribution and use rights are outlined in the file
+.\" "LICENSE.txt" which should have been included with this file. If this
+.\" file is missing or damaged please contact Easy Software Products
+.\" at:
+.\"
+.\" Attn: CUPS Licensing Information
+.\" Easy Software Products
+.\" 44141 Airport View Drive, Suite 204
+.\" Hollywood, Maryland 20636-3111 USA
+.\"
+.\" Voice: (301) 373-9603
+.\" EMail: cups-info@cups.org
+.\" WWW: http://www.cups.org
+.\"
+.TH cupsd.conf 5 "Common UNIX Printing System" "14 May 1999" "Easy Software Products"
+.SH NAME
+cupsd.conf \- server configuration file for cups
+.SH DESCRIPTION
+.SH SEE ALSO
+classes.conf(5), cupsd(8), mime.convs(5), mime.types(5), printers.conf(5),
+CUPS Software Administrator's Manual,
+CUPS Interface Design Description
+.SH COPYRIGHT
+Copyright 1993-1999 by Easy Software Products, All Rights Reserved.
+.\"
+.\" End of "$Id: cupsd.conf.5 327 1999-05-14 17:03:06Z mike $".
+.\"
diff --git a/man/cupsd.conf.z b/man/cupsd.conf.z
new file mode 100644
index 000000000..84ac20774
--- /dev/null
+++ b/man/cupsd.conf.z
Binary files differ
diff --git a/man/cupsd.z b/man/cupsd.z
new file mode 100644
index 000000000..842424f66
--- /dev/null
+++ b/man/cupsd.z
Binary files differ
diff --git a/man/enable.8 b/man/enable.8
new file mode 100644
index 000000000..4e0d7f486
--- /dev/null
+++ b/man/enable.8
@@ -0,0 +1,64 @@
+.\"
+.\" "$Id: enable.8 380 1999-06-10 16:15:04Z mike $"
+.\"
+.\" enable/disable man page for the Common UNIX Printing System (CUPS).
+.\"
+.\" Copyright 1997-1999 by Easy Software Products.
+.\"
+.\" These coded instructions, statements, and computer programs are the
+.\" property of Easy Software Products and are protected by Federal
+.\" copyright law. Distribution and use rights are outlined in the file
+.\" "LICENSE.txt" which should have been included with this file. If this
+.\" file is missing or damaged please contact Easy Software Products
+.\" at:
+.\"
+.\" Attn: CUPS Licensing Information
+.\" Easy Software Products
+.\" 44141 Airport View Drive, Suite 204
+.\" Hollywood, Maryland 20636-3111 USA
+.\"
+.\" Voice: (301) 373-9603
+.\" EMail: cups-info@cups.org
+.\" WWW: http://www.cups.org
+.\"
+.TH enable 8 "Common UNIX Printing System" "14 May 1999" "Easy Software Products"
+.SH NAME
+disable, disable \- stop/start printers and classes
+.SH SYNOPSIS
+.B enable
+destination(s)
+.br
+.B disable
+[ \-c ] [ -h
+.I server
+] [ \-r [
+.I reason
+] ] destination(s)
+.SH DESCRIPTION
+\fBenable\fR starts the named printers or classes.
+.LP
+\fBdisable\fR stops the named printers or classes. The following options may
+be used:
+.TP 5
+\-c
+.br
+Cancels all jobs on the named destination.
+.TP 5
+\-r [ \fIreason\fR ]
+.br
+Sets the message associated with the stopped state. If no reason is specified
+then the message is set to "Reason Unknown".
+.SH COMPATIBILITY
+The CUPS versions of \fBdisable\fR and \fBenable\fR may ask the user for an
+access password depending on the printing system configuration. This differs
+from the System V versions which require the root user to execute these
+commands.
+.SH SEE ALSO
+accept(8), cancel(1), lp(1), lpadmin(8), lpstat(1), reject(8),
+CUPS Software Administrator's Manual
+.SH COPYRIGHT
+Copyright 1993-1999 by Easy Software Products, All Rights Reserved.
+
+.\"
+.\" End of "$Id: enable.8 380 1999-06-10 16:15:04Z mike $".
+.\"
diff --git a/man/enable.z b/man/enable.z
new file mode 100644
index 000000000..f2b4cfd11
--- /dev/null
+++ b/man/enable.z
Binary files differ
diff --git a/man/filter.1 b/man/filter.1
new file mode 100644
index 000000000..51d494d98
--- /dev/null
+++ b/man/filter.1
@@ -0,0 +1,95 @@
+.\"
+.\" "$Id: filter.1 327 1999-05-14 17:03:06Z mike $"
+.\"
+.\" filter man page for the Common UNIX Printing System (CUPS).
+.\"
+.\" Copyright 1997-1999 by Easy Software Products.
+.\"
+.\" These coded instructions, statements, and computer programs are the
+.\" property of Easy Software Products and are protected by Federal
+.\" copyright law. Distribution and use rights are outlined in the file
+.\" "LICENSE.txt" which should have been included with this file. If this
+.\" file is missing or damaged please contact Easy Software Products
+.\" at:
+.\"
+.\" Attn: CUPS Licensing Information
+.\" Easy Software Products
+.\" 44141 Airport View Drive, Suite 204
+.\" Hollywood, Maryland 20636-3111 USA
+.\"
+.\" Voice: (301) 373-9603
+.\" EMail: cups-info@cups.org
+.\" WWW: http://www.cups.org
+.\"
+.TH filter 1 "Common UNIX Printing System" "14 May 1999" "Easy Software Products"
+.SH NAME
+filter \- file conversion filter interfaces
+.SH SYNOPSIS
+.B filter
+job user title num-copies options
+.I [ filename ]
+.SH DESCRIPTION
+The CUPS filter interface provides a standard method for adding support for
+new document types to CUPS. Each filter is capable of converting from one
+or more input formats to another format that can either be printed directly
+or piped into another filter to get it to a printable format.
+.LP
+Filters must be capable of reading from a filename on the command-line or from
+the standard input, copying stdin to a temporary if required by the file
+format.
+.LP
+The command name (argv[0]) is set to the name of the destination printer.
+.SH ENVIRONMENT VARIABLES
+The following environment variables are defined by the CUPS server when
+executing each filter:
+.TP 5
+CHARSET
+.br
+The default text character set (typically us-ascii or iso-8859-1).
+.TP 5
+LANG
+.br
+The default language locale (typically C or en).
+.TP 5
+PATH
+.br
+The standard execution path for external programs that may be run by the filter.
+.TP 5
+PPD
+.br
+The full pathname of the PostScript Printer Description (PPD) file for
+this printer.
+.TP 5
+RIP_CACHE
+.br
+The recommended amount of memory to use for Raster Image Processors (RIPs).
+.TP 5
+SERVER_ROOT
+.br
+The root directory of the server.
+.TP 5
+SOFTWARE
+.br
+The name and version number of the server (typically CUPS/1.0).
+.TP 5
+TZ
+.br
+The timezone of the server (typically GMT).
+.TP 5
+USER
+.br
+The user executing the filter (typically root).
+.SH COMPATIBILITY
+While the filter interface is compatible with System V interface
+scripts, it will only work with the System V interface script as the
+only filter. Typically the interface script will be provided via the
+\fBlpadmin(8)\fR command using the \fI-i\fR option.
+.SH SEE ALSO
+backend(1), cupsd(8),
+CUPS Software Administrator's Manual,
+CUPS Interface Design Description
+.SH COPYRIGHT
+Copyright 1993-1999 by Easy Software Products, All Rights Reserved.
+.\"
+.\" End of "$Id: filter.1 327 1999-05-14 17:03:06Z mike $".
+.\"
diff --git a/man/filter.z b/man/filter.z
new file mode 100644
index 000000000..44203947c
--- /dev/null
+++ b/man/filter.z
Binary files differ
diff --git a/man/lp.1 b/man/lp.1
new file mode 100644
index 000000000..57dfb52e8
--- /dev/null
+++ b/man/lp.1
@@ -0,0 +1,71 @@
+.\"
+.\" "$Id: lp.1 491 1999-07-07 14:03:48Z mike $"
+.\"
+.\" lp/cancel man page for the Common UNIX Printing System (CUPS).
+.\"
+.\" Copyright 1997-1999 by Easy Software Products.
+.\"
+.\" These coded instructions, statements, and computer programs are the
+.\" property of Easy Software Products and are protected by Federal
+.\" copyright law. Distribution and use rights are outlined in the file
+.\" "LICENSE.txt" which should have been included with this file. If this
+.\" file is missing or damaged please contact Easy Software Products
+.\" at:
+.\"
+.\" Attn: CUPS Licensing Information
+.\" Easy Software Products
+.\" 44141 Airport View Drive, Suite 204
+.\" Hollywood, Maryland 20636-3111 USA
+.\"
+.\" Voice: (301) 373-9603
+.\" EMail: cups-info@cups.org
+.\" WWW: http://www.cups.org
+.\"
+.TH lp 1 "Common UNIX Printing System" "14 May 1999" "Easy Software Products"
+.SH NAME
+lp \- print files
+.br
+cancel \- cancel jobs
+.SH SYNOPSIS
+.B lp
+[ \-c ] [ \-d
+.I destination
+] [ \-m ] [ \-n
+.I num-copies
+[ \-o
+.I option
+] [ \-p/q
+.I priority
+] [ \-s ] [ \-t
+.I title
+] [
+.I file(s)
+]
+.br
+.B cancel
+[ \-a ] [ -h
+.I server
+] [
+.I id
+] [
+.I destination
+] [
+.I destination-id
+]
+.SH DESCRIPTION
+\fBlp\fR submits files for printing.
+.LP
+\fBcancel\fR cancels existing print jobs. The \fI-a\fR option will remove
+all jobs from the specified destination.
+.SH COMPATIBILITY
+Unlike the System V printing system, CUPS allows printer names to contain
+any printable character except SPACE and TAB. Also, printer and class names are
+\fBnot\fR case-sensitive.
+.SH SEE ALSO
+lpstat(1),
+CUPS Software User's Manual
+.SH COPYRIGHT
+Copyright 1993-1999 by Easy Software Products, All Rights Reserved.
+.\"
+.\" End of "$Id: lp.1 491 1999-07-07 14:03:48Z mike $".
+.\"
diff --git a/man/lp.z b/man/lp.z
new file mode 100644
index 000000000..1d45c1575
--- /dev/null
+++ b/man/lp.z
Binary files differ
diff --git a/man/lpadmin.8 b/man/lpadmin.8
new file mode 100644
index 000000000..fc492b1f8
--- /dev/null
+++ b/man/lpadmin.8
@@ -0,0 +1,124 @@
+.\"
+.\" "$Id: lpadmin.8 461 1999-06-24 17:44:19Z mike $"
+.\"
+.\" lpadmin man page for the Common UNIX Printing System (CUPS).
+.\"
+.\" Copyright 1997-1999 by Easy Software Products.
+.\"
+.\" These coded instructions, statements, and computer programs are the
+.\" property of Easy Software Products and are protected by Federal
+.\" copyright law. Distribution and use rights are outlined in the file
+.\" "LICENSE.txt" which should have been included with this file. If this
+.\" file is missing or damaged please contact Easy Software Products
+.\" at:
+.\"
+.\" Attn: CUPS Licensing Information
+.\" Easy Software Products
+.\" 44141 Airport View Drive, Suite 204
+.\" Hollywood, Maryland 20636-3111 USA
+.\"
+.\" Voice: (301) 373-9603
+.\" EMail: cups-info@cups.org
+.\" WWW: http://www.cups.org
+.\"
+.TH lpadmin 8 "Common UNIX Printing System" "22 June 1999" "Easy Software Products"
+.SH NAME
+lpadmin \- configure cups printers and classes
+.SH SYNOPSIS
+.B lpadmin
+[ -h
+.I server
+] \-d
+.I destination
+.br
+.B lpadmin
+[ -h
+.I server
+] \-p
+.I printer
+.I option(s)
+.br
+.B lpadmin
+[ -h
+.I server
+] \-x
+.I destination
+.SH DESCRIPTION
+\fBlpadmin\fR configures printer and class queues provided by CUPS. It can also
+be used to set the system default printer or class.
+.LP
+The first form of the command sets the default printer or class to
+\fIdestination\fR. Subsequent print jobs submitted via the \fIlp(1)\fR or
+\fIlpr(1)\fR commands will use this destination unless the user specifies
+otherwise.
+.LP
+The second form of the command configures the named printer. The additional
+options are described below.
+.LP
+The third form of the command deletes the printer or class \fIdestination\fR.
+Any jobs that are pending for the destination will be removed and any job that
+is currently printed will be aborted.
+.SH CONFIGURATION OPTIONS
+The following options are recognized when configuring a printer queue:
+.TP 5
+\-c \fIclass\fR
+.br
+Adds the named \fIprinter\fR to \fIclass\fR. If \fIclass\fR does not
+exist it is created automatically.
+.TP 5
+\-i \fIinterface\fR
+.br
+Sets a System V style interface script for the printer. This option cannot
+be specified with the \fI\-P\fR option (PPD file) and is intended for
+providing support for legacy printer drivers.
+.TP 5
+\-m \fImodel\fR
+.br
+Sets a standard System V interface script or PPD file from the model
+directory.
+.TP 5
+\-r \fIclass\fR
+.br
+Removes the named \fIprinter\fR from \fIclass\fR. If the resulting class
+becomes empty it is removed.
+.TP 5
+\-v \fIdevice-uri\fR
+.br
+Sets the \fIdevice-uri\fR attribute of the printer queue. If \fIdevice-uri\fR
+is a filename it is automatically converted to the form \fBfile:/file/name\fR.
+.TP 5
+\-D \fIinfo\fR
+.br
+Provides a textual description of the printer.
+.TP 5
+\-E
+.br
+Enables the printer; this is the same as running the \fIaccept(8)\fR and
+\fIenable(8)\fR programs on the printer.
+.TP 5
+\-L \fIlocation\fR
+.br
+Provides a textual location of the printer.
+.TP 5
+\-P \fIppd-file\fR
+.br
+Specifies a PostScript Printer Description file to use with the printer. If
+specified, this option overrides the \fI-i\fR option (interface script).
+.SH COMPATIBILITY
+Unlike the System V printing system, CUPS allows printer names to contain
+any printable character except SPACE and TAB. Also, printer and class names are
+\fBnot\fR case-sensitive. Finally, the CUPS version of \fBlpadmin\fR may ask the
+user for an access password depending on the printing system configuration.
+This differs from the System V version which requires the root user to execute
+this command.
+.SH LIMITATIONS
+The CUPS version of \fBlpadmin\fR does not support all of the System V or
+Solaris printing system configuration options.
+.SH SEE ALSO
+accept(8), cancel(1), disable(8), enable(8), lp(1), lpstat(1), reject(8),
+CUPS Software Administrator's Manual
+.SH COPYRIGHT
+Copyright 1993-1999 by Easy Software Products, All Rights Reserved.
+.\"
+.\" End of "$Id: lpadmin.8 461 1999-06-24 17:44:19Z mike $".
+.\"
diff --git a/man/lpadmin.z b/man/lpadmin.z
new file mode 100644
index 000000000..c7b0c61cb
--- /dev/null
+++ b/man/lpadmin.z
Binary files differ
diff --git a/man/lpc.8 b/man/lpc.8
new file mode 100644
index 000000000..0ce5029d9
--- /dev/null
+++ b/man/lpc.8
@@ -0,0 +1,79 @@
+.\"
+.\" "$Id: lpc.8 327 1999-05-14 17:03:06Z mike $"
+.\"
+.\" lpc man page for the Common UNIX Printing System (CUPS).
+.\"
+.\" Copyright 1997-1999 by Easy Software Products.
+.\"
+.\" These coded instructions, statements, and computer programs are the
+.\" property of Easy Software Products and are protected by Federal
+.\" copyright law. Distribution and use rights are outlined in the file
+.\" "LICENSE.txt" which should have been included with this file. If this
+.\" file is missing or damaged please contact Easy Software Products
+.\" at:
+.\"
+.\" Attn: CUPS Licensing Information
+.\" Easy Software Products
+.\" 44141 Airport View Drive, Suite 204
+.\" Hollywood, Maryland 20636-3111 USA
+.\"
+.\" Voice: (301) 373-9603
+.\" EMail: cups-info@cups.org
+.\" WWW: http://www.cups.org
+.\"
+.TH lpc 8 "Common UNIX Printing System" "14 May 1999" "Easy Software Products"
+.SH NAME
+lpc \- line printer control program
+.SH SYNOPSIS
+.B lpc
+[
+.I command
+[
+.I parameter(s)
+] ]
+.SH DESCRIPTION
+\fBlpc\fR provides limited control over printer and class queues provided by
+CUPS. It can also be used to query the state of queues.
+.LP
+If no command is specified on the command-line, \fRlpc\fR will display a
+prompt and accept commands from the standard input.
+.SH COMMANDS
+The \fBlpc\fR program accepts a subset of commands accepted by the Berkeley
+\fBlpc\fR program of the same name:
+.TP 5
+\fIexit
+.br
+Exits the command interpreter.
+.TP 5
+help \fI[command]\fR
+.br
+Displays a short help message.
+.TP 5
+quit
+.br
+Exits the command interpreter.
+.TP 5
+status \fI[queue]\fR
+.br
+Displays the status of one or more printer or class queues.
+.TP 5
+? \fI[command]\fR
+.br
+Display a short help message.
+.SH LIMITATIONS
+Since \fBlpc\fR is geared towards the Berkeley printing system, it is impossible
+to use \fBlpc\fR to configure printer or class queues provided by CUPS. To
+configure printer or class queues you must use the \fBlpadmin(8)\fR command
+or another CUPS-compatible client with that functionality.
+.SH COMPATIBILITY
+The CUPS version of \fBlpc\fR does not implement all of the standard Berkeley
+commands.
+.SH SEE ALSO
+accept(8), cancel(1), disable(8), enable(8), lp(1), lpr(1), lprm(1),
+lpstat(1), reject(8),
+CUPS Software Administrator's Manual
+.SH COPYRIGHT
+Copyright 1993-1999 by Easy Software Products, All Rights Reserved.
+.\"
+.\" End of "$Id: lpc.8 327 1999-05-14 17:03:06Z mike $".
+.\"
diff --git a/man/lpc.z b/man/lpc.z
new file mode 100644
index 000000000..9151be9a5
--- /dev/null
+++ b/man/lpc.z
Binary files differ
diff --git a/man/lpr.1 b/man/lpr.1
new file mode 100644
index 000000000..ad563e3eb
--- /dev/null
+++ b/man/lpr.1
@@ -0,0 +1,96 @@
+.\"
+.\" "$Id: lpr.1 491 1999-07-07 14:03:48Z mike $"
+.\"
+.\" lpr man page for the Common UNIX Printing System (CUPS).
+.\"
+.\" Copyright 1997-1999 by Easy Software Products.
+.\"
+.\" These coded instructions, statements, and computer programs are the
+.\" property of Easy Software Products and are protected by Federal
+.\" copyright law. Distribution and use rights are outlined in the file
+.\" "LICENSE.txt" which should have been included with this file. If this
+.\" file is missing or damaged please contact Easy Software Products
+.\" at:
+.\"
+.\" Attn: CUPS Licensing Information
+.\" Easy Software Products
+.\" 44141 Airport View Drive, Suite 204
+.\" Hollywood, Maryland 20636-3111 USA
+.\"
+.\" Voice: (301) 373-9603
+.\" EMail: cups-info@cups.org
+.\" WWW: http://www.cups.org
+.\"
+.TH lpr 1 "Common UNIX Printing System" "14 May 1999" "Easy Software Products"
+.SH NAME
+lpr \- print files
+.SH SYNOPSIS
+.B lpr
+[ \-P
+.I destination
+] [ \-#
+.I num-copies
+[ \-l ] [ \-o
+.I option
+] [ \-p] [ \-r ] [ \-C/J/T
+.I title
+] [
+.I file(s)
+]
+.SH DESCRIPTION
+\fBlpr\fR submits files for printing. Files named on the command line are sent
+to the named printer (or the system default destination if no destination is
+specified). If no files are listed on the command-line \fBlpr\fR reads the
+print file from the standard input.
+.SH OPTIONS
+The following options are recognized by \fBlpr\fR:
+.TP 5
+\-P \fIdestination\fR
+.br
+Prints files to the named printer.
+.TP 5
+\-# \fIcopies\fR
+.br
+Sets the number of copies to print from 1 to 100.
+.TP 5
+\-C \fIname\fR
+.br
+Sets the job name.
+.TP 5
+\-J \fIname\fR
+.br
+Sets the job name.
+.TP 5
+\-T \fIname\fR
+.br
+Sets the job name.
+.TP 5
+\-l
+.br
+Specifies that the print file is already formatted for the destination and
+should be sent without filtering. This option is equivalent to "-oraw".
+.TP 5
+\-o \fIoption\fR
+.br
+Sets the job name.
+.TP 5
+\-p
+.br
+Specifies that the print file should be formatted with a shaded header with
+the date, time, job name, and page number. This option is equivalent to
+"-oprettyprint" and is only useful when printing text files.
+.TP 5
+\-r
+.br
+Specifies that the named print files should be deleted after printing them.
+.SH COMPATIBILITY
+The "c", "d", "f", "g", "i", "m", "n", "t", "v", and "w" options are not
+supported by CUPS and will produce a warning message if used.
+.SH SEE ALSO
+cancel(1), lp(1), lpstat(1),
+CUPS Software User's Manual
+.SH COPYRIGHT
+Copyright 1993-1999 by Easy Software Products, All Rights Reserved.
+.\"
+.\" End of "$Id: lpr.1 491 1999-07-07 14:03:48Z mike $".
+.\"
diff --git a/man/lpr.z b/man/lpr.z
new file mode 100644
index 000000000..0a611435c
--- /dev/null
+++ b/man/lpr.z
Binary files differ
diff --git a/man/lprm.1 b/man/lprm.1
new file mode 100644
index 000000000..03b06eb3a
--- /dev/null
+++ b/man/lprm.1
@@ -0,0 +1,40 @@
+.\"
+.\" "$Id: lprm.1 327 1999-05-14 17:03:06Z mike $"
+.\"
+.\" lprm man page for the Common UNIX Printing System (CUPS).
+.\"
+.\" Copyright 1997-1999 by Easy Software Products.
+.\"
+.\" These coded instructions, statements, and computer programs are the
+.\" property of Easy Software Products and are protected by Federal
+.\" copyright law. Distribution and use rights are outlined in the file
+.\" "LICENSE.txt" which should have been included with this file. If this
+.\" file is missing or damaged please contact Easy Software Products
+.\" at:
+.\"
+.\" Attn: CUPS Licensing Information
+.\" Easy Software Products
+.\" 44141 Airport View Drive, Suite 204
+.\" Hollywood, Maryland 20636-3111 USA
+.\"
+.\" Voice: (301) 373-9603
+.\" EMail: cups-info@cups.org
+.\" WWW: http://www.cups.org
+.\"
+.TH lprm 1 "Common UNIX Printing System" "14 May 1999" "Easy Software Products"
+.SH NAME
+lprm \- cancel print jobs
+.SH SYNOPSIS
+.B lprm
+.SH DESCRIPTION
+\fBlpstat\fR
+.SH COMPATIBILITY
+SOMETHING
+.SH SEE ALSO
+cancel(1), lp(1), lpstat(1),
+CUPS Software User's Manual
+.SH COPYRIGHT
+Copyright 1993-1999 by Easy Software Products, All Rights Reserved.
+.\"
+.\" End of "$Id: lprm.1 327 1999-05-14 17:03:06Z mike $".
+.\"
diff --git a/man/lprm.z b/man/lprm.z
new file mode 100644
index 000000000..40f8c05b5
--- /dev/null
+++ b/man/lprm.z
Binary files differ
diff --git a/man/lpstat.1 b/man/lpstat.1
new file mode 100644
index 000000000..e107e5bfa
--- /dev/null
+++ b/man/lpstat.1
@@ -0,0 +1,115 @@
+.\"
+.\" "$Id: lpstat.1 461 1999-06-24 17:44:19Z mike $"
+.\"
+.\" lpstat man page for the Common UNIX Printing System (CUPS).
+.\"
+.\" Copyright 1997-1999 by Easy Software Products.
+.\"
+.\" These coded instructions, statements, and computer programs are the
+.\" property of Easy Software Products and are protected by Federal
+.\" copyright law. Distribution and use rights are outlined in the file
+.\" "LICENSE.txt" which should have been included with this file. If this
+.\" file is missing or damaged please contact Easy Software Products
+.\" at:
+.\"
+.\" Attn: CUPS Licensing Information
+.\" Easy Software Products
+.\" 44141 Airport View Drive, Suite 204
+.\" Hollywood, Maryland 20636-3111 USA
+.\"
+.\" Voice: (301) 373-9603
+.\" EMail: cups-info@cups.org
+.\" WWW: http://www.cups.org
+.\"
+.TH lpstat 1 "Common UNIX Printing System" "24 June 1999" "Easy Software Products"
+.SH NAME
+lpstat \- print cups status information
+.SH SYNOPSIS
+.B lpstat
+[ -a [
+.I destination(s)
+] ] [ -c [
+.I class(es)
+] ] [ -d ] [ -h
+.I server
+] [ -o [
+.I destination(s)
+] ] [ -p [
+.I printer(s)
+] ] [ -r ] [ -s ] [ -t ] [ -u [
+.I user(s)
+] ] [ -v [
+.I printer(s)
+] ]
+.SH DESCRIPTION
+\fBlpstat\fR displays status information about the current classes, jobs, and
+printers. When run with no arguments, \fBlpstat\fR will list jobs queued by
+the user. Other options include:
+.TP 5
+\-a [\fIprinter(s)\fR]
+.br
+Shows the accepting state of printer queues. If no printers are
+specified then all printers are listed.
+.TP 5
+\-c [\fIclass(es)\fR]
+.br
+Shows the printer classes and the printers that belong to them. If no
+classes are specified then all classes are listed.
+.TP 5
+\-d
+.br
+Shows the current default destination.
+.TP 5
+\-h \fIserver\fR
+.br
+Specifies the CUPS server to communicate with.
+.TP 5
+\-o [\fIdestination(s)\fR]
+.br
+Shows the jobs queue on the specified destinations. If no destinations are
+specified all jobs are shown.
+.TP 5
+\-p [\fIprinter(s)\fR]
+.br
+Shows the printers and whether or not they are enabled for printing. If
+no printers are specified then all printers are listed.
+.TP 5
+\-r
+.br
+Shows whether or not the CUPS server is running.
+.TP 5
+\-s
+.br
+Shows a status summary, including the system default destination, a
+list of classes and their member printers, and a list of printers and
+their associated devices. This is equivalent to using the "-d", "-c",
+and "-p" options.
+.TP 5
+\-t
+.br
+Shows all status information. This is equivalent to using the "-r",
+"-d", "-c", "-d", "-v", "-a", "-p", and "-o" options.
+.TP 5
+\-u [\fIuser(s)\fR]
+.br
+Shows a list of print jobs queued by the specified users. If no users
+are specified, lists the jobs queued by the current user.
+.TP 5
+\-v [\fIprinter(s)\fR]
+.br
+Shows the printers and what device they are attached to. If no printers
+are specified then all printers are listed.
+.SH COMPATIBILITY
+Unlike the System V printing system, CUPS allows printer names to contain
+any printable character except SPACE and TAB. Also, printer and class names are
+\fBnot\fR case-sensitive.
+.LP
+The "-h" option is not a standard System V option.
+.SH SEE ALSO
+cancel(1), lp(1),
+CUPS Software User's Manual
+.SH COPYRIGHT
+Copyright 1993-1999 by Easy Software Products, All Rights Reserved.
+.\"
+.\" End of "$Id: lpstat.1 461 1999-06-24 17:44:19Z mike $".
+.\"
diff --git a/man/lpstat.z b/man/lpstat.z
new file mode 100644
index 000000000..76628c04e
--- /dev/null
+++ b/man/lpstat.z
Binary files differ
diff --git a/man/mime.convs.5 b/man/mime.convs.5
new file mode 100644
index 000000000..93e530312
--- /dev/null
+++ b/man/mime.convs.5
@@ -0,0 +1,36 @@
+.\"
+.\" "$Id: mime.convs.5 327 1999-05-14 17:03:06Z mike $"
+.\"
+.\" mime.convs man page for the Common UNIX Printing System (CUPS).
+.\"
+.\" Copyright 1997-1999 by Easy Software Products.
+.\"
+.\" These coded instructions, statements, and computer programs are the
+.\" property of Easy Software Products and are protected by Federal
+.\" copyright law. Distribution and use rights are outlined in the file
+.\" "LICENSE.txt" which should have been included with this file. If this
+.\" file is missing or damaged please contact Easy Software Products
+.\" at:
+.\"
+.\" Attn: CUPS Licensing Information
+.\" Easy Software Products
+.\" 44141 Airport View Drive, Suite 204
+.\" Hollywood, Maryland 20636-3111 USA
+.\"
+.\" Voice: (301) 373-9603
+.\" EMail: cups-info@cups.org
+.\" WWW: http://www.cups.org
+.\"
+.TH mime.convs 5 "Common UNIX Printing System" "14 May 1999" "Easy Software Products"
+.SH NAME
+mime.convs \- mime type conversion file for cups
+.SH DESCRIPTION
+.SH SEE ALSO
+classes.conf(5), cupsd(8), cupsd.conf(5), mime.types(5), printers.conf(5),
+CUPS Software Administrator's Manual,
+CUPS Interface Design Description
+.SH COPYRIGHT
+Copyright 1993-1999 by Easy Software Products, All Rights Reserved.
+.\"
+.\" End of "$Id: mime.convs.5 327 1999-05-14 17:03:06Z mike $".
+.\"
diff --git a/man/mime.convs.z b/man/mime.convs.z
new file mode 100644
index 000000000..27c4895c7
--- /dev/null
+++ b/man/mime.convs.z
Binary files differ
diff --git a/man/mime.types.5 b/man/mime.types.5
new file mode 100644
index 000000000..5381b8190
--- /dev/null
+++ b/man/mime.types.5
@@ -0,0 +1,36 @@
+.\"
+.\" "$Id: mime.types.5 327 1999-05-14 17:03:06Z mike $"
+.\"
+.\" mime.types man page for the Common UNIX Printing System (CUPS).
+.\"
+.\" Copyright 1997-1999 by Easy Software Products.
+.\"
+.\" These coded instructions, statements, and computer programs are the
+.\" property of Easy Software Products and are protected by Federal
+.\" copyright law. Distribution and use rights are outlined in the file
+.\" "LICENSE.txt" which should have been included with this file. If this
+.\" file is missing or damaged please contact Easy Software Products
+.\" at:
+.\"
+.\" Attn: CUPS Licensing Information
+.\" Easy Software Products
+.\" 44141 Airport View Drive, Suite 204
+.\" Hollywood, Maryland 20636-3111 USA
+.\"
+.\" Voice: (301) 373-9603
+.\" EMail: cups-info@cups.org
+.\" WWW: http://www.cups.org
+.\"
+.TH mime.types 5 "Common UNIX Printing System" "14 May 1999" "Easy Software Products"
+.SH NAME
+mime.types \- mime type description file for cups
+.SH DESCRIPTION
+.SH SEE ALSO
+classes.conf(5), cupsd(8), cupsd.conf(5), mime.convs(5), printers.conf(5),
+CUPS Software Administrator's Manual,
+CUPS Interface Design Description
+.SH COPYRIGHT
+Copyright 1993-1999 by Easy Software Products, All Rights Reserved.
+.\"
+.\" End of "$Id: mime.types.5 327 1999-05-14 17:03:06Z mike $".
+.\"
diff --git a/man/mime.types.z b/man/mime.types.z
new file mode 100644
index 000000000..60600d78a
--- /dev/null
+++ b/man/mime.types.z
Binary files differ
diff --git a/man/printers.conf.5 b/man/printers.conf.5
new file mode 100644
index 000000000..490c50601
--- /dev/null
+++ b/man/printers.conf.5
@@ -0,0 +1,36 @@
+.\"
+.\" "$Id: printers.conf.5 327 1999-05-14 17:03:06Z mike $"
+.\"
+.\" printers.conf man page for the Common UNIX Printing System (CUPS).
+.\"
+.\" Copyright 1997-1999 by Easy Software Products.
+.\"
+.\" These coded instructions, statements, and computer programs are the
+.\" property of Easy Software Products and are protected by Federal
+.\" copyright law. Distribution and use rights are outlined in the file
+.\" "LICENSE.txt" which should have been included with this file. If this
+.\" file is missing or damaged please contact Easy Software Products
+.\" at:
+.\"
+.\" Attn: CUPS Licensing Information
+.\" Easy Software Products
+.\" 44141 Airport View Drive, Suite 204
+.\" Hollywood, Maryland 20636-3111 USA
+.\"
+.\" Voice: (301) 373-9603
+.\" EMail: cups-info@cups.org
+.\" WWW: http://www.cups.org
+.\"
+.TH printers.conf 5 "Common UNIX Printing System" "14 May 1999" "Easy Software Products"
+.SH NAME
+printers.conf \- printer configuration file for cups
+.SH DESCRIPTION
+.SH SEE ALSO
+classes.conf(5), cupsd(8), cupsd.conf(5), mime.convs(5), mime.types(5),
+CUPS Software Administrator's Manual,
+CUPS Interface Design Description
+.SH COPYRIGHT
+Copyright 1993-1999 by Easy Software Products, All Rights Reserved.
+.\"
+.\" End of "$Id: printers.conf.5 327 1999-05-14 17:03:06Z mike $".
+.\"
diff --git a/man/printers.conf.z b/man/printers.conf.z
new file mode 100644
index 000000000..43157f31c
--- /dev/null
+++ b/man/printers.conf.z
Binary files differ
diff --git a/ppd/Makefile b/ppd/Makefile
new file mode 100644
index 000000000..675b199e4
--- /dev/null
+++ b/ppd/Makefile
@@ -0,0 +1,55 @@
+#
+# "$Id$"
+#
+# PPD file makefile for the Common UNIX Printing System (CUPS).
+#
+# Copyright 1993-1999 by Easy Software Products.
+#
+# These coded instructions, statements, and computer programs are the
+# property of Easy Software Products and are protected by Federal
+# copyright law. Distribution and use rights are outlined in the file
+# "LICENSE.txt" which should have been included with this file. If this
+# file is missing or damaged please contact Easy Software Products
+# at:
+#
+# Attn: CUPS Licensing Information
+# Easy Software Products
+# 44141 Airport View Drive, Suite 204
+# Hollywood, Maryland 20636-3111 USA
+#
+# Voice: (301) 373-9603
+# EMail: cups-info@cups.org
+# WWW: http://www.cups.org
+#
+
+include ../Makedefs
+
+#
+# PPD files...
+#
+
+FILES = deskjet.ppd laserjet.ppd
+
+#
+# Make everything...
+#
+
+all:
+
+#
+# Clean all config and object files...
+#
+
+clean:
+
+#
+# Install files...
+#
+
+install:
+ -$(MKDIR) $(DATADIR)/model
+ $(CP) $(FILES) $(DATADIR)/model
+
+#
+# End of "$Id$".
+#
diff --git a/ppd/deskjet.ppd b/ppd/deskjet.ppd
new file mode 100644
index 000000000..cc712c355
--- /dev/null
+++ b/ppd/deskjet.ppd
@@ -0,0 +1,186 @@
+*PPD-Adobe: "4.3"
+*%
+*% "$Id$"
+*%
+*% Sample HP DeskJet driver PPD file for the Common UNIX Printing
+*% System (CUPS).
+*%
+*% Copyright 1997-1999 by Easy Software Products.
+*%
+*% These coded instructions, statements, and computer programs are the
+*% property of Easy Software Products and are protected by Federal
+*% copyright law. Distribution and use rights are outlined in the file
+*% "LICENSE.txt" which should have been included with this file. If this
+*% file is missing or damaged please contact Easy Software Products
+*% at:
+*%
+*% Attn: CUPS Licensing Information
+*% Easy Software Products
+*% 44145 Airport View Drive, Suite 204
+*% Hollywood, Maryland 20636-3111 USA
+*%
+*% Voice: (301) 373-9603
+*% EMail: cups-info@cups.org
+*% WWW: http://www.cups.org
+*%
+*FormatVersion: "4.3"
+*FileVersion: "1.0"
+*LanguageVersion: English
+*LanguageEncoding: ISOLatin1
+*PCFileName: "DESKJET.PPD"
+*Manufacturer: "ESP"
+*Product: "(CUPS v1.0)"
+*cupsVersion: 1.0
+*cupsManualCopies: True
+*cupsFilter: "application/vnd.cups-raster 0 rastertohp"
+*ModelName: "HP DeskJet Series"
+*ShortNickName: "HP DeskJet Series"
+*NickName: "HP DeskJet Series CUPS v1.0"
+*PSVersion: "(2017.000) 0"
+*LanguageLevel: "2"
+*ColorDevice: True
+*DefaultColorSpace: RGB
+*FileSystem: False
+*Throughput: "1"
+*LandscapeOrientation: Plus90
+*VariablePaperSize: False
+*TTRasterizer: Type42
+
+*UIConstraints: *PageSize Executive *InputSlot Envelope
+*UIConstraints: *PageSize Letter *InputSlot Envelope
+*UIConstraints: *PageSize Legal *InputSlot Envelope
+*UIConstraints: *PageSize A4 *InputSlot Envelope
+*UIConstraints: *PageSize A5 *InputSlot Envelope
+*UIConstraints: *PageSize B5 *InputSlot Envelope
+*UIConstraints: *Resolution 600dpi *ColorModel CMYK
+
+*OpenUI *PageSize/Media Size: PickOne
+*OrderDependency: 10 AnySetup *PageSize
+*DefaultPageSize: Letter
+*PageSize Letter/Letter: "<</PageSize[612 792]/ImagingBBox null>>setpagedevice"
+*PageSize Legal/Legal: "<</PageSize[612 1008]/ImagingBBox null>>setpagedevice"
+*PageSize Executive/Executive: "<</PageSize[522 756]/ImagingBBox null>>setpagedevice"
+*PageSize A4/A4: "<</PageSize[595 842]/ImagingBBox null>>setpagedevice"
+*PageSize A5/A5: "<</PageSize[420 595]/ImagingBBox null>>setpagedevice"
+*PageSize B5/B5 (JIS): "<</PageSize[516 729]/ImagingBBox null>>setpagedevice"
+*PageSize EnvISOB5/Envelope B5: "<</PageSize[499 709]/ImagingBBox null>>setpagedevice"
+*PageSize Env10/Envelope #10: "<</PageSize[297 684]/ImagingBBox null>>setpagedevice"
+*PageSize EnvC5/Envelope C5: "<</PageSize[459 649]/ImagingBBox null>>setpagedevice"
+*PageSize EnvDL/Envelope DL: "<</PageSize[312 624]/ImagingBBox null>>setpagedevice"
+*PageSize EnvMonarch/Envelope Monarch: "<</PageSize[279 540]/ImagingBBox null>>setpagedevice"
+*CloseUI: *PageSize
+
+*OpenUI *PageRegion: PickOne
+*OrderDependency: 10 AnySetup *PageRegion
+*DefaultPageRegion: Letter
+*PageRegion Letter/Letter: "<</PageSize[612 792]/ImagingBBox null>>setpagedevice"
+*PageRegion Legal/Legal: "<</PageSize[612 1008]/ImagingBBox null>>setpagedevice"
+*PageRegion Executive/Executive: "<</PageSize[522 756]/ImagingBBox null>>setpagedevice"
+*PageRegion A4/A4: "<</PageSize[595 842]/ImagingBBox null>>setpagedevice"
+*PageRegion A5/A5: "<</PageSize[421 595]/ImagingBBox null>>setpagedevice"
+*PageRegion B5/B5 (JIS): "<</PageSize[516 729]/ImagingBBox null>>setpagedevice"
+*PageRegion EnvISOB5/Envelope B5: "<</PageSize[499 709]/ImagingBBox null>>setpagedevice"
+*PageRegion Env10/Envelope #10: "<</PageSize[297 684]/ImagingBBox null>>setpagedevice"
+*PageRegion EnvC5/Envelope C5: "<</PageSize[459 649]/ImagingBBox null>>setpagedevice"
+*PageRegion EnvDL/Envelope DL: "<</PageSize[312 624]/ImagingBBox null>>setpagedevice"
+*PageRegion EnvMonarch/Envelope Monarch: "<</PageSize[279 540]/ImagingBBox null>>setpagedevice"
+*CloseUI: *PageRegion
+
+*DefaultImageableArea: Letter
+*ImageableArea Letter/US Letter: "18 36 594 756"
+*ImageableArea Legal/US Legal: "18 36 594 972"
+*ImageableArea Executive/Executive: "18 36 504 684"
+*ImageableArea A4/A4: "18 36 577 806"
+*ImageableArea A5/A5: "18 36 403 559"
+*ImageableArea B5/JIS B5: "18 36 498 693"
+*ImageableArea EnvISOB5/B5 (ISO): "18 36 463 673"
+*ImageableArea Env10/Com-10: "18 36 279 648"
+*ImageableArea EnvC5/EnvC5: "18 36 441 613"
+*ImageableArea EnvDL/EnvDL: "18 36 294 588"
+*ImageableArea EnvMonarch/Envelope Monarch: "18 36 261 504"
+
+*DefaultPaperDimension: Letter
+*PaperDimension Letter/Letter: "612 792"
+*PaperDimension Legal/Legal: "612 1008"
+*PaperDimension Executive/Executive: "522 756"
+*PaperDimension A4/A4: "595 842"
+*PaperDimension A5/A5: "421 595"
+*PaperDimension B5/B5 (JIS): "516 729"
+*PaperDimension EnvISOB5/Envelope B5: "499 709"
+*PaperDimension Env10/Envelope #10: "297 684"
+*PaperDimension EnvC5/Envelope C5: "459 649"
+*PaperDimension EnvDL/Envelope DL: "312 624"
+*PaperDimension EnvMonarch/Envelope Monarch: "279 540"
+
+*OpenUI *MediaType/Media Type: PickOne
+*OrderDependency: 10 AnySetup *MediaType
+*DefaultMediaType: Plain
+*MediaType Plain/Plain Paper: "<</MediaType(Plain)/cupsMediaType 0>>setpagedevice"
+*MediaType Bond/Bond Paper: "<</MediaType(Bond)/cupsMediaType 1>>setpagedevice"
+*MediaType Special/Special Paper: "<</MediaType(Special)/cupsMediaType 2>>setpagedevice"
+*MediaType Transparency/Transparency: "<</MediaType(Transparency)/cupsMediaType 3>>setpagedevice"
+*MediaType Glossy/Glossy Paper: "<</MediaType(Glossy)/cupsMediaType 4>>setpagedevice"
+*CloseUI: *MediaType
+
+*OpenUI *InputSlot/Media Source: PickOne
+*OrderDependency: 10 AnySetup *InputSlot
+*DefaultInputSlot: Tray
+*InputSlot Tray/Tray: "<</cupsMediaPosition 1>>setpagedevice"
+*InputSlot Manual/Manual Feed: "<</cupsMediaPosition 2>>setpagedevice"
+*InputSlot Envelope/Envelope Feed: "<</cupsMediaPosition 3>>setpagedevice"
+*CloseUI: *InputSlot
+
+*OpenUI *Resolution/Output Resolution: PickOne
+*OrderDependency: 20 AnySetup *Resolution
+*DefaultResolution: 300dpi
+*Resolution 150dpi/150 DPI: "<</HWResolution[150 150]>>setpagedevice"
+*Resolution 300dpi/300 DPI: "<</HWResolution[300 300]>>setpagedevice"
+*Resolution 600dpi/600 DPI: "<</HWResolution[600 600]/cupsColorSpace 3>>setpagedevice"
+*CloseUI: *Resolution
+
+*OpenUI *ColorModel/Output Mode: PickOne
+*OrderDependency: 10 AnySetup *ColorModel
+*DefaultColorModel: CMYK
+*ColorModel CMYK/Color: "<</cupsColorOrder 1/cupsColorSpace 8/cupsCompression 2>>setpagedevice"
+*ColorModel Gray/Grayscale: "<</cupsColorOrder 0/cupsColorSpace 3/cupsCompression 2>>setpagedevice"
+*CloseUI: *ColorModel
+
+*DefaultFont: Courier
+*Font AvantGarde-Book: Standard "(001.006S)" Standard ROM
+*Font AvantGarde-BookOblique: Standard "(001.006S)" Standard ROM
+*Font AvantGarde-Demi: Standard "(001.007S)" Standard ROM
+*Font AvantGarde-DemiOblique: Standard "(001.007S)" Standard ROM
+*Font Bookman-Demi: Standard "(001.004S)" Standard ROM
+*Font Bookman-DemiItalic: Standard "(001.004S)" Standard ROM
+*Font Bookman-Light: Standard "(001.004S)" Standard ROM
+*Font Bookman-LightItalic: Standard "(001.004S)" Standard ROM
+*Font Courier: Standard "(002.004S)" Standard ROM
+*Font Courier-Bold: Standard "(002.004S)" Standard ROM
+*Font Courier-BoldOblique: Standard "(002.004S)" Standard ROM
+*Font Courier-Oblique: Standard "(002.004S)" Standard ROM
+*Font Helvetica: Standard "(001.006S)" Standard ROM
+*Font Helvetica-Bold: Standard "(001.007S)" Standard ROM
+*Font Helvetica-BoldOblique: Standard "(001.007S)" Standard ROM
+*Font Helvetica-Narrow: Standard "(001.006S)" Standard ROM
+*Font Helvetica-Narrow-Bold: Standard "(001.007S)" Standard ROM
+*Font Helvetica-Narrow-BoldOblique: Standard "(001.007S)" Standard ROM
+*Font Helvetica-Narrow-Oblique: Standard "(001.006S)" Standard ROM
+*Font Helvetica-Oblique: Standard "(001.006S)" Standard ROM
+*Font NewCenturySchlbk-Bold: Standard "(001.009S)" Standard ROM
+*Font NewCenturySchlbk-BoldItalic: Standard "(001.007S)" Standard ROM
+*Font NewCenturySchlbk-Italic: Standard "(001.006S)" Standard ROM
+*Font NewCenturySchlbk-Roman: Standard "(001.007S)" Standard ROM
+*Font Palatino-Bold: Standard "(001.005S)" Standard ROM
+*Font Palatino-BoldItalic: Standard "(001.005S)" Standard ROM
+*Font Palatino-Italic: Standard "(001.005S)" Standard ROM
+*Font Palatino-Roman: Standard "(001.005S)" Standard ROM
+*Font Symbol: Special "(001.007S)" Special ROM
+*Font Times-Bold: Standard "(001.007S)" Standard ROM
+*Font Times-BoldItalic: Standard "(001.009S)" Standard ROM
+*Font Times-Italic: Standard "(001.007S)" Standard ROM
+*Font Times-Roman: Standard "(001.007S)" Standard ROM
+*Font ZapfChancery-MediumItalic: Standard "(001.007S)" Standard ROM
+*Font ZapfDingbats: Special "(001.004S)" Standard ROM
+*%
+*% End of "$Id$".
+*%
diff --git a/ppd/laserjet.ppd b/ppd/laserjet.ppd
new file mode 100644
index 000000000..20e0b9d78
--- /dev/null
+++ b/ppd/laserjet.ppd
@@ -0,0 +1,172 @@
+*PPD-Adobe: "4.3"
+*%
+*% "$Id$"
+*%
+*% Sample HP LaserJet driver PPD file for the Common UNIX Printing
+*% System (CUPS).
+*%
+*% Copyright 1997-1999 by Easy Software Products.
+*%
+*% These coded instructions, statements, and computer programs are the
+*% property of Easy Software Products and are protected by Federal
+*% copyright law. Distribution and use rights are outlined in the file
+*% "LICENSE.txt" which should have been included with this file. If this
+*% file is missing or damaged please contact Easy Software Products
+*% at:
+*%
+*% Attn: CUPS Licensing Information
+*% Easy Software Products
+*% 44145 Airport View Drive, Suite 204
+*% Hollywood, Maryland 20636-3111 USA
+*%
+*% Voice: (301) 373-9603
+*% EMail: cups-info@cups.org
+*% WWW: http://www.cups.org
+*%
+*FormatVersion: "4.3"
+*FileVersion: "1.0"
+*LanguageVersion: English
+*LanguageEncoding: ISOLatin1
+*PCFileName: "LASERJET.PPD"
+*Manufacturer: "ESP"
+*Product: "(CUPS v1.0)"
+*cupsVersion: 1.0
+*cupsManualCopies: False
+*cupsFilter: "application/vnd.cups-raster 0 rastertohp"
+*ModelName: "HP LaserJet Series"
+*ShortNickName: "HP LaserJet Series"
+*NickName: "HP LaserJet Series CUPS v1.0"
+*PSVersion: "(2017.000) 0"
+*LanguageLevel: "2"
+*ColorDevice: False
+*DefaultColorSpace: Gray
+*FileSystem: False
+*Throughput: "8"
+*LandscapeOrientation: Plus90
+*VariablePaperSize: False
+*TTRasterizer: Type42
+
+*UIConstraints: *PageSize Executive *InputSlot Envelope
+*UIConstraints: *PageSize Letter *InputSlot Envelope
+*UIConstraints: *PageSize Legal *InputSlot Envelope
+*UIConstraints: *PageSize A4 *InputSlot Envelope
+*UIConstraints: *PageSize A5 *InputSlot Envelope
+*UIConstraints: *PageSize B5 *InputSlot Envelope
+
+*OpenUI *PageSize/Media Size: PickOne
+*OrderDependency: 10 AnySetup *PageSize
+*DefaultPageSize: Letter
+*PageSize Letter/Letter: "<</PageSize[612 792]/ImagingBBox null>>setpagedevice"
+*PageSize Legal/Legal: "<</PageSize[612 1008]/ImagingBBox null>>setpagedevice"
+*PageSize Executive/Executive: "<</PageSize[522 756]/ImagingBBox null>>setpagedevice"
+*PageSize A4/A4: "<</PageSize[595 842]/ImagingBBox null>>setpagedevice"
+*PageSize A5/A5: "<</PageSize[420 595]/ImagingBBox null>>setpagedevice"
+*PageSize B5/B5 (JIS): "<</PageSize[516 729]/ImagingBBox null>>setpagedevice"
+*PageSize EnvISOB5/Envelope B5: "<</PageSize[499 709]/ImagingBBox null>>setpagedevice"
+*PageSize Env10/Envelope #10: "<</PageSize[297 684]/ImagingBBox null>>setpagedevice"
+*PageSize EnvC5/Envelope C5: "<</PageSize[459 649]/ImagingBBox null>>setpagedevice"
+*PageSize EnvDL/Envelope DL: "<</PageSize[312 624]/ImagingBBox null>>setpagedevice"
+*PageSize EnvMonarch/Envelope Monarch: "<</PageSize[279 540]/ImagingBBox null>>setpagedevice"
+*CloseUI: *PageSize
+
+*OpenUI *PageRegion: PickOne
+*OrderDependency: 10 AnySetup *PageRegion
+*DefaultPageRegion: Letter
+*PageRegion Letter/Letter: "<</PageSize[612 792]/ImagingBBox null>>setpagedevice"
+*PageRegion Legal/Legal: "<</PageSize[612 1008]/ImagingBBox null>>setpagedevice"
+*PageRegion Executive/Executive: "<</PageSize[522 756]/ImagingBBox null>>setpagedevice"
+*PageRegion A4/A4: "<</PageSize[595 842]/ImagingBBox null>>setpagedevice"
+*PageRegion A5/A5: "<</PageSize[421 595]/ImagingBBox null>>setpagedevice"
+*PageRegion B5/B5 (JIS): "<</PageSize[516 729]/ImagingBBox null>>setpagedevice"
+*PageRegion EnvISOB5/Envelope B5: "<</PageSize[499 709]/ImagingBBox null>>setpagedevice"
+*PageRegion Env10/Envelope #10: "<</PageSize[297 684]/ImagingBBox null>>setpagedevice"
+*PageRegion EnvC5/Envelope C5: "<</PageSize[459 649]/ImagingBBox null>>setpagedevice"
+*PageRegion EnvDL/Envelope DL: "<</PageSize[312 624]/ImagingBBox null>>setpagedevice"
+*PageRegion EnvMonarch/Envelope Monarch: "<</PageSize[279 540]/ImagingBBox null>>setpagedevice"
+*CloseUI: *PageRegion
+
+*DefaultImageableArea: Letter
+*ImageableArea Letter/US Letter: "18 36 594 756"
+*ImageableArea Legal/US Legal: "18 36 594 972"
+*ImageableArea Executive/Executive: "18 36 504 684"
+*ImageableArea A4/A4: "18 36 577 806"
+*ImageableArea A5/A5: "18 36 403 559"
+*ImageableArea B5/JIS B5: "18 36 498 693"
+*ImageableArea EnvISOB5/B5 (ISO): "18 36 463 673"
+*ImageableArea Env10/Com-10: "18 36 279 648"
+*ImageableArea EnvC5/EnvC5: "18 36 441 613"
+*ImageableArea EnvDL/EnvDL: "18 36 294 588"
+*ImageableArea EnvMonarch/Envelope Monarch: "18 36 261 504"
+
+*DefaultPaperDimension: Letter
+*PaperDimension Letter/Letter: "612 792"
+*PaperDimension Legal/Legal: "612 1008"
+*PaperDimension Executive/Executive: "522 756"
+*PaperDimension A4/A4: "595 842"
+*PaperDimension A5/A5: "421 595"
+*PaperDimension B5/B5 (JIS): "516 729"
+*PaperDimension EnvISOB5/Envelope B5: "499 709"
+*PaperDimension Env10/Envelope #10: "297 684"
+*PaperDimension EnvC5/Envelope C5: "459 649"
+*PaperDimension EnvDL/Envelope DL: "312 624"
+*PaperDimension EnvMonarch/Envelope Monarch: "279 540"
+
+*OpenUI *InputSlot/Media Source: PickOne
+*OrderDependency: 10 AnySetup *InputSlot
+*DefaultInputSlot: Default
+*InputSlot Default/Default: "<</cupsMediaPosition 0>>setpagedevice"
+*InputSlot Tray1/Tray 1: "<</cupsMediaPosition 8>>setpagedevice"
+*InputSlot Tray2/Tray 2: "<</cupsMediaPosition 1>>setpagedevice"
+*InputSlot Tray3/Tray 3: "<</cupsMediaPosition 4>>setpagedevice"
+*InputSlot Tray4/Tray 4: "<</cupsMediaPosition 5>>setpagedevice"
+*InputSlot Manual/Manual Feed: "<</cupsMediaPosition 2>>setpagedevice"
+*InputSlot Envelope/Envelope Feed: "<</cupsMediaPosition 3>>setpagedevice"
+*CloseUI: *InputSlot
+
+*OpenUI *Resolution/Output Resolution: PickOne
+*OrderDependency: 20 AnySetup *Resolution
+*DefaultResolution: 300dpi
+*Resolution 150dpi/150 DPI: "<</HWResolution[150 150]/cupsColorOrder 0/cupsColorSpace 3/cupsCompression 2>>setpagedevice"
+*Resolution 300dpi/300 DPI: "<</HWResolution[300 300]/cupsColorOrder 0/cupsColorSpace 3/cupsCompression 2>>setpagedevice"
+*Resolution 600dpi/600 DPI: "<</HWResolution[600 600]/cupsColorOrder 0/cupsColorSpace 3/cupsCompression 2>>setpagedevice"
+*CloseUI: *Resolution
+
+*DefaultFont: Courier
+*Font AvantGarde-Book: Standard "(001.006S)" Standard ROM
+*Font AvantGarde-BookOblique: Standard "(001.006S)" Standard ROM
+*Font AvantGarde-Demi: Standard "(001.007S)" Standard ROM
+*Font AvantGarde-DemiOblique: Standard "(001.007S)" Standard ROM
+*Font Bookman-Demi: Standard "(001.004S)" Standard ROM
+*Font Bookman-DemiItalic: Standard "(001.004S)" Standard ROM
+*Font Bookman-Light: Standard "(001.004S)" Standard ROM
+*Font Bookman-LightItalic: Standard "(001.004S)" Standard ROM
+*Font Courier: Standard "(002.004S)" Standard ROM
+*Font Courier-Bold: Standard "(002.004S)" Standard ROM
+*Font Courier-BoldOblique: Standard "(002.004S)" Standard ROM
+*Font Courier-Oblique: Standard "(002.004S)" Standard ROM
+*Font Helvetica: Standard "(001.006S)" Standard ROM
+*Font Helvetica-Bold: Standard "(001.007S)" Standard ROM
+*Font Helvetica-BoldOblique: Standard "(001.007S)" Standard ROM
+*Font Helvetica-Narrow: Standard "(001.006S)" Standard ROM
+*Font Helvetica-Narrow-Bold: Standard "(001.007S)" Standard ROM
+*Font Helvetica-Narrow-BoldOblique: Standard "(001.007S)" Standard ROM
+*Font Helvetica-Narrow-Oblique: Standard "(001.006S)" Standard ROM
+*Font Helvetica-Oblique: Standard "(001.006S)" Standard ROM
+*Font NewCenturySchlbk-Bold: Standard "(001.009S)" Standard ROM
+*Font NewCenturySchlbk-BoldItalic: Standard "(001.007S)" Standard ROM
+*Font NewCenturySchlbk-Italic: Standard "(001.006S)" Standard ROM
+*Font NewCenturySchlbk-Roman: Standard "(001.007S)" Standard ROM
+*Font Palatino-Bold: Standard "(001.005S)" Standard ROM
+*Font Palatino-BoldItalic: Standard "(001.005S)" Standard ROM
+*Font Palatino-Italic: Standard "(001.005S)" Standard ROM
+*Font Palatino-Roman: Standard "(001.005S)" Standard ROM
+*Font Symbol: Special "(001.007S)" Special ROM
+*Font Times-Bold: Standard "(001.007S)" Standard ROM
+*Font Times-BoldItalic: Standard "(001.009S)" Standard ROM
+*Font Times-Italic: Standard "(001.007S)" Standard ROM
+*Font Times-Roman: Standard "(001.007S)" Standard ROM
+*Font ZapfChancery-MediumItalic: Standard "(001.007S)" Standard ROM
+*Font ZapfDingbats: Special "(001.004S)" Standard ROM
+*%
+*% End of "$Id$".
+*%
diff --git a/pstoraster/Fontmap b/pstoraster/Fontmap
new file mode 100644
index 000000000..42797fe18
--- /dev/null
+++ b/pstoraster/Fontmap
@@ -0,0 +1,98 @@
+%
+% "$Id: Fontmap 569 1999-07-30 12:57:12Z mike $"
+%
+% Fontmap file for the Common UNIX Printing System (CUPS).
+%
+% Copyright 1997-1999 by Easy Software Products, all rights reserved.
+%
+% These coded instructions, statements, and computer programs are the
+% property of Easy Software Products and are protected by Federal
+% copyright law. Distribution and use rights are outlined in the file
+% "LICENSE.txt" which should have been included with this file. If this
+% file is missing or damaged please contact Easy Software Products
+% at:
+%
+% Attn: CUPS Licensing Information
+% Easy Software Products
+% 44145 Airport View Drive, Suite 204
+% Hollywood, Maryland 20636-3111 USA
+%
+% Voice: (301) 373-9603
+% EMail: cups-info@cups.org
+% WWW: http://www.cups.org
+%
+
+%
+% The Fontmap file takes lines in the following formats:
+%
+% /FontName /RealFontName [for aliases]
+% /FontName (FileName) [for actual font files]
+%
+% All Type1 fonts in the "fonts" directory (usually /usr/share/cups/fonts)
+% are automagically added with the names in the font files (that is, the
+% font filename doesn't matter, it looks at the file header instead).
+%
+
+%
+% The standard fonts included with ESP Print are the free GhostScript fonts,
+% which don't use the standard names. These aliases map the standard 39
+% fonts to the free fonts.
+%
+
+/Bookman-Demi /URWBookmanL-DemiBold ;
+/Bookman-DemiItalic /URWBookmanL-DemiBoldItal ;
+/Bookman-Light /URWBookmanL-Ligh ;
+/Bookman-LightItalic /URWBookmanL-LighItal ;
+
+/Courier /NimbusMonL-Regu ;
+/Courier-Oblique /NimbusMonL-ReguObli ;
+/Courier-Bold /NimbusMonL-Bold ;
+/Courier-BoldOblique /NimbusMonL-BoldObli ;
+
+/AvantGarde-Book /URWGothicL-Book ;
+/AvantGarde-BookOblique /URWGothicL-BookObli ;
+/AvantGarde-Demi /URWGothicL-Demi ;
+/AvantGarde-DemiOblique /URWGothicL-DemiObli ;
+
+/Helvetica /NimbusSanL-Regu ;
+/Helvetica-Oblique /NimbusSanL-ReguItal ;
+/Helvetica-Bold /NimbusSanL-Bold ;
+/Helvetica-BoldOblique /NimbusSanL-BoldItal ;
+
+/Helvetica-Narrow /NimbusSanL-ReguCond ;
+/Helvetica-Narrow-Oblique /NimbusSanL-ReguCondItal ;
+/Helvetica-Narrow-Bold /NimbusSanL-BoldCond ;
+/Helvetica-Narrow-BoldOblique /NimbusSanL-BoldCondItal ;
+
+/Palatino-Roman /URWPalladioL-Roma ;
+/Palatino-Italic /URWPalladioL-Ital ;
+/Palatino-Bold /URWPalladioL-Bold ;
+/Palatino-BoldItalic /URWPalladioL-BoldItal ;
+
+/NewCenturySchlbk-Roman /CenturySchL-Roma ;
+/NewCenturySchlbk-Italic /CenturySchL-Ital ;
+/NewCenturySchlbk-Bold /CenturySchL-Bold ;
+/NewCenturySchlbk-BoldItalic /CenturySchL-BoldItal ;
+
+/Times-Roman /NimbusRomNo9L-Regu ;
+/Times-Italic /NimbusRomNo9L-ReguItal ;
+/Times-Bold /NimbusRomNo9L-Medi ;
+/Times-BoldItalic /NimbusRomNo9L-MediItal ;
+
+/Symbol /StandardSymL ;
+
+/ZapfChancery-MediumItalic /URWChanceryL-MediItal ;
+
+/ZapfDingbats /Dingbats ;
+
+%
+% This alias is for less-intelligent PC programs like Quark and
+% Freehand which insist on using "Times" as the name for the
+% "Times-Roman" font. Go figure.
+%
+
+/Times /Times-Roman ;
+
+%
+% End of "$Id: Fontmap 569 1999-07-30 12:57:12Z mike $".
+%
diff --git a/pstoraster/Makefile b/pstoraster/Makefile
new file mode 100644
index 000000000..518d0121b
--- /dev/null
+++ b/pstoraster/Makefile
@@ -0,0 +1,167 @@
+#
+# "$Id$"
+#
+# GNU Ghostscript makefile for the Common UNIX Printing System (CUPS).
+#
+# Copyright 1993-1999 by Easy Software Products.
+#
+# These coded instructions, statements, and computer programs are the
+# property of Easy Software Products and are protected by Federal
+# copyright law. Distribution and use rights are outlined in the file
+# "LICENSE.txt" which should have been included with this file. If this
+# file is missing or damaged please contact Easy Software Products
+# at:
+#
+# Attn: CUPS Licensing Information
+# Easy Software Products
+# 44141 Airport View Drive, Suite 204
+# Hollywood, Maryland 20636-3111 USA
+#
+# Voice: (301) 373-9603
+# EMail: cups-info@cups.org
+# WWW: http://www.cups.org
+#
+# This makefile and any derivative of it may be used and distributed
+# freely under the terms of the GNU General Public License when
+# used with GNU Ghostscript or its derivatives. Use of the makefile
+# (or any derivative of it) with software other than GNU GhostScript
+# (or its derivatives) is governed by the CUPS license agreement.
+#
+
+include ../Makedefs
+
+#
+# Object files...
+#
+
+LIBOBJS = gconf.o gdevabuf.o gdevcups.o gdevddrw.o gdevdflt.o \
+ gdevemap.o gdevm1.o gdevm16.o gdevm2.o gdevm24.o \
+ gdevm32.o gdevm4.o gdevm8.o gdevmem.o gdevmpla.o \
+ gdevmrop.o gdevnfwd.o gdevpipe.o gdevprn.o gp_nofb.o \
+ gp_unifn.o gp_unifs.o gp_unix.o gsalloc.o gsbitops.o \
+ gsbittab.o gscdef.o gschar.o gschar0.o gscie.o \
+ gscolor.o gscolor1.o gscolor2.o gscoord.o gscsepr.o \
+ gsdevice.o gsdevmem.o gsdparam.o gsdps1.o gsfont.o \
+ gsfont0.o gshsb.o gsht.o gsht1.o gshtscr.o gsimage.o \
+ gsimpath.o gsinit.o gsiodev.o gsline.o gsmatrix.o \
+ gsmemory.o gsmisc.o gspaint.o gsparam.o gspath.o \
+ gspath1.o gspcolor.o gsrop.o gsroptab.o gsstate.o \
+ gstype1.o gstype42.o gsutil.o gxacpath.o gxbcache.o \
+ gxccache.o gxccman.o gxcht.o gxclbits.o gxclfile.o \
+ gxclimag.o gxclip2.o gxclist.o gxclpath.o gxclread.o \
+ gxcmap.o gxcpath.o gxctable.o gxdcconv.o gxdcolor.o \
+ gxdither.o gxfill.o gxhint1.o gxhint2.o gxhint3.o \
+ gxht.o gximage.o gximage0.o gximage1.o gximage2.o \
+ gximage3.o gximage4.o gximage5.o gxpaint.o gxpath.o \
+ gxpath2.o gxpcmap.o gxpcopy.o gxpdash.o gxstroke.o \
+ ialloc.o ibnum.o iccinit0.o iconf.o idebug.o idict.o \
+ idparam.o igc.o igcref.o igcstr.o iinit.o ilocate.o \
+ imain.o iname.o interp.o iparam.o ireclaim.o isave.o \
+ iscan.o iscanbin.o iscannum.o iscantab.o istack.o \
+ iutil.o iutil2.o sbcp.o sbhc.o sbwbs.o \
+ scfd.o scfdtab.o scfe.o scfetab.o sdctc.o sdctd.o \
+ sdcte.o seexec.o sfile.o sfilter1.o sfilter2.o shc.o \
+ shcgen.o siscale.o sjpegc.o sjpegd.o sjpege.o \
+ sjpegerr.o slzwc.o slzwce.o slzwd.o smtf.o spcxd.o \
+ spdiff.o spngp.o srld.o srle.o sstring.o stream.o \
+ szlibc.o szlibd.o szlibe.o zarith.o zarray.o zbseq.o \
+ zchar.o zchar1.o zchar2.o zchar42.o zcharout.o zcie.o \
+ zcolor.o zcolor1.o zcolor2.o zcontrol.o zcrd.o \
+ zcsindex.o zcssepr.o zdevcal.o zdevice.o zdevice2.o \
+ zdict.o zdps1.o zfbcp.o zfdctc.o zfdctd.o zfdcte.o \
+ zfdecode.o zfile.o zfileio.o zfilter.o zfilter2.o \
+ zfilterx.o zfname.o zfont.o zfont0.o zfont1.o zfont2.o \
+ zfont42.o zfproc.o zfzlib.o zgeneric.o zgstate.o zhsb.o \
+ zht.o zht1.o zht2.o zimage2.o ziodev.o ziodev2.o \
+ zmath.o zmatrix.o zmedia2.o zmisc.o zmisc1.o zmisc2.o \
+ zpacked.o zpaint.o zpath.o zpath1.o zpcolor.o zrelbit.o \
+ zstack.o zstring.o zsysvm.o ztoken.o ztype.o zupath.o \
+ zusparam.o zvmem.o zvmem2.o
+OBJS = $(LIBOBJS) genarch.o pstoraster.o
+
+#
+# Data files...
+#
+
+DFILES = Fontmap gs_btokn.ps gs_ccfnt.ps gs_cidfn.ps gs_cmap.ps \
+ gs_cmdl.ps gs_dbt_e.ps gs_diskf.ps gs_dps1.ps \
+ gs_fform.ps gs_fonts.ps gs_init.ps gs_iso_e.ps \
+ gs_kanji.ps gs_ksb_e.ps gs_l2img.ps gs_lev2.ps \
+ gs_mex_e.ps gs_mro_e.ps gs_pdfwr.ps gs_pdf.ps \
+ gs_pdf_e.ps gs_pfile.ps gs_res.ps gs_setpd.ps \
+ gs_statd.ps gs_std_e.ps gs_sym_e.ps gs_ttf.ps \
+ gs_typ42.ps gs_type1.ps gs_wan_e.ps gs_wl1_e.ps \
+ gs_wl2_e.ps gs_wl5_e.ps pdf_2ps.ps pdf_base.ps \
+ pdf_draw.ps pdf_font.ps pdf_main.ps pdf_sec.ps \
+ pfbtogs.ps
+
+#
+# Targets...
+#
+
+TARGETS = genarch arch.h libgs.a pstoraster
+
+#
+# Make everything...
+#
+
+all: $(TARGETS)
+
+#
+# Clean all config and object files...
+#
+
+clean:
+ $(RM) $(TARGETS)
+ $(RM) $(OBJS)
+
+#
+# Install files...
+#
+
+install: $(TARGETS)
+ -$(MKDIR) $(SERVERROOT)/filter
+ $(CP) pstoraster $(SERVERROOT)/filter
+ -$(LN) pstoraster $(SERVERROOT)/filter/pdftops
+ -$(MKDIR) $(DATADIR)/pstoraster
+ $(CP) $(DFILES) $(DATADIR)/pstoraster
+
+#
+# genarch - generate the architecture configuration file.
+#
+
+genarch: genarch.o
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o genarch genarch.o
+arch.h: genarch
+ echo Generating $@...
+ ./genarch arch.h
+
+#
+# libgs.a - GhostScript interpreter library...
+#
+
+libgs.a: $(LIBOBJS) ../Makedefs
+ echo Archiving $@...
+ $(RM) $@
+ $(AR) $(ARFLAGS) $@ $(LIBOBJS)
+ $(RANLIB) $@
+
+gdevcups.o: ../cups/raster.h
+gconf.o iconf.o gscdef.o: gconfig.h
+$(LIBOBJS): arch.h ../config.h ../Makedefs
+
+
+#
+# pstoraster - PostScript RIP filter.
+#
+
+pstoraster: pstoraster.o libgs.a ../Makedefs ../cups/$(LIBCUPS)
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o pstoraster pstoraster.o libgs.a \
+ $(LIBJPEG) $(LIBZ) $(LIBS) -lm
+pstoraster.o: arch.h ../config.h ../Makedefs
+
+#
+# End of "$Id$".
+#
diff --git a/pstoraster/bfont.h b/pstoraster/bfont.h
new file mode 100644
index 000000000..a653ce399
--- /dev/null
+++ b/pstoraster/bfont.h
@@ -0,0 +1,54 @@
+/* Copyright (C) 1992, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* bfont.h */
+/* Interpreter internal routines and data needed for building fonts */
+/* Requires gxfont.h */
+#include "ifont.h"
+
+/* In zfont.c */
+int add_FID(P2(ref *pfdict, gs_font *pfont));
+font_proc_make_font(zdefault_make_font);
+font_proc_make_font(zbase_make_font);
+/* The global font directory */
+extern gs_font_dir *ifont_dir;
+
+/* Structure for passing BuildChar and BuildGlyph procedures. */
+typedef struct build_proc_refs_s {
+ ref BuildChar;
+ ref BuildGlyph;
+} build_proc_refs;
+
+/* In zfont2.c */
+int build_proc_name_refs(P3(build_proc_refs *pbuild,
+ const char _ds *bcstr,
+ const char _ds *bgstr));
+int build_gs_font_procs(P2(os_ptr, build_proc_refs *));
+int build_gs_primitive_font(P5(os_ptr, gs_font_base **, font_type,
+ gs_memory_type_ptr_t, const build_proc_refs *));
+int build_gs_simple_font(P5(os_ptr, gs_font_base **, font_type,
+ gs_memory_type_ptr_t, const build_proc_refs *));
+void lookup_gs_simple_font_encoding(P1(gs_font_base *));
+int build_gs_font(P5(os_ptr, gs_font **, font_type,
+ gs_memory_type_ptr_t, const build_proc_refs *));
+int define_gs_font(P1(gs_font *));
diff --git a/pstoraster/bseq.h b/pstoraster/bseq.h
new file mode 100644
index 000000000..46dab4a60
--- /dev/null
+++ b/pstoraster/bseq.h
@@ -0,0 +1,66 @@
+/* Copyright (C) 1990, 1994 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* bseq.h */
+/* Definitions for Level 2 binary object sequences */
+
+/* Binary object sequence element types */
+typedef enum {
+ bs_null = 0,
+ bs_integer = 1,
+ bs_real = 2,
+ bs_name = 3,
+ bs_boolean = 4,
+ bs_string = 5,
+ bs_eval_name = 6,
+ bs_array = 9,
+ bs_mark = 10,
+ /*
+ * We extend the PostScript language definition by allowing
+ * dictionaries in binary object sequences. The data for
+ * a dictionary is like that for an array, with the following
+ * changes:
+ * - If the size is an even number, the value is the index of
+ * the first of a series of alternating keys and values.
+ * - If the size is 1, the value is the index of another
+ * object (which must also be a dictionary, and must not have
+ * size = 1); this object represents the same object as that one.
+ */
+ bs_dictionary = 15
+} bin_seq_type;
+#define bs_executable 128
+
+/* Definition of an object in a binary object sequence. */
+typedef struct {
+ byte tx; /* type and executable flag */
+ byte unused;
+ union {
+ bits16 w;
+ byte b[2];
+ } size;
+ union {
+ bits32 w;
+ float f;
+ byte b[4];
+ } value;
+} bin_seq_obj;
diff --git a/pstoraster/btoken.h b/pstoraster/btoken.h
new file mode 100644
index 000000000..69a7d63e9
--- /dev/null
+++ b/pstoraster/btoken.h
@@ -0,0 +1,65 @@
+/* Copyright (C) 1990 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* btoken.h */
+/* Definitions for Level 2 binary tokens */
+
+/* Binary token types */
+typedef enum {
+ bt_seq = 128,
+ bt_seq_IEEE_msb = 128, /* binary object sequence, */
+ /* IEEE floats, big-endian */
+ bt_seq_IEEE_lsb = 129, /* ditto, little-endian */
+ bt_seq_native_msb = 130, /* ditto, native floats, big-endian */
+ bt_seq_native_lsb = 131, /* ditto, little-endian */
+ bt_int32_msb = 132,
+ bt_int32_lsb = 133,
+ bt_int16_msb = 134,
+ bt_int16_lsb = 135,
+ bt_int8 = 136,
+ bt_fixed = 137,
+ bt_float_IEEE_msb = 138,
+ bt_float_IEEE_lsb = 139,
+ bt_float_native = 140,
+ bt_boolean = 141,
+ bt_string_256 = 142,
+ bt_string_64k_msb = 143,
+ bt_string_64k_lsb = 144,
+ bt_litname_system = 145,
+ bt_execname_system = 146,
+ bt_litname_user = 147,
+ bt_execname_user = 148,
+ bt_num_array = 149
+} bt_char;
+#define bt_char_min 128
+#define bt_char_max 159
+
+/* Define the number of required initial bytes for binary tokens */
+/* (including the token type byte). */
+extern const byte bin_token_bytes[]; /* in iscan2.c */
+#define bin_token_bytes_values\
+ 4, 4, 4, 4, 5, 5, 3, 3, 2, 2, 5, 5, 5,\
+ 2, 2, 3, 3, 2, 2, 2, 2, 4,\
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 /* undefined */
+#define binary_token_bytes(btchar)\
+ (bin_token_bytes[(btchar) - bt_char_min])
diff --git a/pstoraster/ctype_.h b/pstoraster/ctype_.h
new file mode 100644
index 000000000..42af5c4a3
--- /dev/null
+++ b/pstoraster/ctype_.h
@@ -0,0 +1,31 @@
+/* Copyright (C) 1993 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* ctype_.h */
+/* Wrapper for ctype.h */
+
+/* We must include std.h before any file that includes sys/types.h. */
+#include "std.h"
+
+/* ... and that's the only reason for having this file at all. */
+#include <ctype.h>
diff --git a/pstoraster/dirent_.h b/pstoraster/dirent_.h
new file mode 100644
index 000000000..548de3bab
--- /dev/null
+++ b/pstoraster/dirent_.h
@@ -0,0 +1,50 @@
+/*
+ Copyright 1993-1999 by Easy Software Products.
+ Copyright (C) 1993 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* dirent_.h */
+/* Generic substitute for Unix dirent.h */
+
+/* We must include std.h before any file that includes sys/types.h. */
+#include "std.h"
+
+#include <config.h>
+
+/* Directory entries may be defined in quite a number of different */
+/* header files. The following switches are defined in gconfig_.h. */
+#ifdef HAVE_DIRENT_H
+# include <dirent.h>
+typedef struct dirent dir_entry;
+#else
+# if HAVE_SYS_NDIR_H
+# include <sys/ndir.h>
+# endif
+# if HAVE_SYS_DIR_H
+# include <sys/dir.h>
+# endif
+# if HAVE_NDIR_H
+# include <ndir.h>
+# endif
+typedef struct direct dir_entry;
+#endif
diff --git a/pstoraster/dstack.h b/pstoraster/dstack.h
new file mode 100644
index 000000000..b897393c5
--- /dev/null
+++ b/pstoraster/dstack.h
@@ -0,0 +1,307 @@
+/* Copyright (C) 1992, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* dstack.h */
+/* Definitions for the dictionary stack */
+#include "istack.h"
+
+/* Define the dictionary stack and systemdict. */
+extern ref_stack d_stack;
+extern ref ref_systemdict;
+#define systemdict (&ref_systemdict)
+
+/* Define the dictionary stack pointers. */
+typedef s_ptr ds_ptr;
+typedef const_s_ptr const_ds_ptr;
+#define dsbot (d_stack.bot)
+#define dsp (d_stack.p)
+#define dstop (d_stack.top)
+
+/* Macro to ensure enough room on the dictionary stack */
+#define check_dstack(n)\
+ if ( dstop - dsp < (n) )\
+ { d_stack.requested = (n); return_error(e_dictstackoverflow); }
+
+/* Check whether a dictionary is one of the permanent ones on the d-stack. */
+bool dict_is_permanent_on_dstack(P1(const ref *));
+
+/*
+ * Switching between Level 1 and Level 2 involves inserting and removing
+ * globaldict on the dictionary stack. Instead of truly inserting and
+ * removing entries, we replace globaldict by a copy of systemdict in
+ * Level 1 mode. min_dstack_size, the minimum number of entries, does not
+ * change depending on language level; the countdictstack and dictstack
+ * operators must take this into account.
+ */
+extern uint min_dstack_size;
+
+/*
+ * The dictionary stack is implemented as a linked list of blocks;
+ * operators that access the entire d-stack must take this into account.
+ * These are:
+ * countdictstack dictstack
+ * In addition, name lookup requires searching the entire stack, not just
+ * the top block, and the underflow check for the dictionary stack
+ * (`end' operator) is not just a check for underflowing the top block.
+ */
+
+/*
+ * Cache a value for fast checking of def operations.
+ * If the top entry on the dictionary stack is a writable dictionary,
+ * dsspace is the space of the dictionary; if it is a non-writable
+ * dictionary, dsspace = -1. Then def is legal precisely if
+ * r_space(pvalue) <= dsspace. Note that in order for this trick to work,
+ * the result of r_space must be a signed integer; some compilers treat
+ * enums as unsigned, probably in violation of the ANSI standard.
+ */
+extern int dsspace;
+#define dtop_can_store(pvalue) ((int)r_space(pvalue) <= dsspace)
+/*
+ * Cache values for fast name lookup. If the top entry on the dictionary
+ * stack is a readable dictionary with packed keys, dtop_keys, dtop_npairs,
+ * and dtop_values are keys.value.packed, npairs, and values.value.refs
+ * for that dictionary; otherwise, these variables point to a dummy
+ * empty dictionary.
+ */
+extern const ref_packed *dtop_keys;
+extern uint dtop_npairs;
+extern ref *dtop_values;
+/*
+ * Reset the cached top values. Every routine that alters the
+ * dictionary stack (including changing the protection or size of the
+ * top dictionary on the stack) must call this.
+ */
+void dict_set_top(P0());
+
+/*
+ * Define a special fast entry for name lookup in the interpreter.
+ * The key is known to be a name; search the entire dict stack.
+ * Return the pointer to the value slot.
+ * If the name isn't found, just return 0.
+ */
+ref *dict_find_name_by_index(P1(uint nidx));
+#define dict_find_name(pnref) dict_find_name_by_index(name_index(pnref))
+
+/* Define some auxiliary macros needed for inline code. */
+
+#define hash_mod_large(hash, size) ((hash) & ((size) - 1))
+#define hash_mod_small(hash, size) ((hash) % (size))
+#define dict_round_size_large(asize)\
+ while ( asize & (asize - 1) ) asize = (asize | (asize - 1)) + 1
+#define dict_round_size_small(asize)\
+ DO_NOTHING
+#if arch_small_memory
+# define hash_mod(h, s) hash_mod_small(h, s)
+# define dict_round_size(s) dict_round_size_small(s)
+#else
+# ifdef DEBUG
+# define hash_mod(h, s)\
+ (gs_debug_c('.') ? hash_mod_small(h, s) : hash_mod_large(h, s))
+# define dict_round_size(s)\
+ if ( !gs_debug_c('.') ) dict_round_size_large(s)
+# else
+# define hash_mod(h, s) hash_mod_large(h, s)
+# define dict_round_size(s) dict_round_size_large(s)
+# endif
+#endif
+
+/* Define the hashing function for names. */
+/* We don't have to scramble the index, because */
+/* indices are assigned in a scattered order (see name_ref in iname.c). */
+#define dict_name_index_hash(nidx) (nidx)
+
+/*
+ * Define an extra-fast macro for name lookup, optimized for
+ * a single-probe lookup in the top dictionary on the stack.
+ * Amazingly enough, this seems to hit over 90% of the time
+ * (aside from operators, of course, which are handled either with
+ * the special cache pointer or with 'bind').
+ */
+#define dict_find_name_by_index_inline(nidx,htemp)\
+ (dtop_keys[htemp = hash_mod(dict_name_index_hash(nidx),\
+ dtop_npairs) + 1] == pt_tag(pt_literal_name) + (nidx) ?\
+ dtop_values + htemp : dict_find_name_by_index(nidx))
+/*
+ * Define a similar macro that only checks the top dictionary on the stack.
+ */
+#define if_dict_find_name_by_index_top(nidx,htemp,pvslot)\
+ if ( ((dtop_keys[htemp = hash_mod(dict_name_index_hash(nidx),\
+ dtop_npairs) + 1] == pt_tag(pt_literal_name) + (nidx)) ?\
+ ((pvslot) = dtop_values + (htemp), 1) :\
+ 0)\
+ )
+
+/*
+Notes on dictionary lookup performance
+--------------------------------------
+
+We mark heavily used operations with a * below; moderately heavily used
+operations with a +.
+
+The following operations change the dictionary stack:
+ +begin, +end
+ readonly (on a dictionary that is on the stack)
+ noaccess (on a dictionary that is on the stack)
+We implement cleardictstack as a series of ends.
+
+The following operations change the contents of dictionaries:
+ *def, +put
+ undef
+ restore
+ .setmaxlength
+We implement store in PostScript, and copy as a series of puts. Many
+other operators also do puts (e.g., ScaleMatrix in makefont,
+Implementation in makepattern, ...). Note that put can do an implicit
+.setmaxlength (if it has to grow the dictionary).
+
+The following operations look up keys on the dictionary stack:
+ *(interpreter name lookup)
+ load
+ where
+
+Current design
+--------------
+
+Each name has a pointer that has one of 3 states:
+ - This name has no definitions.
+ - This name has exactly one definition, in systemdict or userdict.
+ In this case, the pointer points to the value slot.
+ - This name has some other status.
+
+We cache some pointers to the top dictionary on the stack if it is a
+readable dictionary with packed keys, which allows us to do fast,
+single-probe lookups in this dictionary. We also cache a value that
+allows us to do a fast check for stores into the top dictionary
+(writability + space check).
+
+Full shallow binding
+--------------------
+
+We implement shallow binding with a pointer in each name that points to
+the value slot that holds the name's definition. If the name is
+undefined, or if we don't know where the slot is, the binding pointer
+points to a ref with a special type t__invalid, which cannot occur
+anywhere else. "Clearing" the pointer means setting it to point to this
+ref.
+
+We also maintain a pair of pointers that bracket the value region of the
+top dictionary on the stack, for fast checking in def. If the top
+dictionary is readonly or noaccess, the pointers designate an empty area.
+We call this the "def region" cache.
+
+We implement the above operations as follows:
+ begin - push the dictionary on the stack; set the pointers of
+ all name keys to point to the corresponding value slots.
+ end - pop the stack; clear the pointers of all name keys.
+ readonly - if the dictionary is the top one on the stack,
+ reset the def region cache.
+ noaccess - clear the pointers of all name keys. (This is overly
+ conservative, but this is a very rare operation.)
+ Also reset the def region cache if the dictionary is
+ the top one on the stack.
+ def - if the key is a name and its pointer points within the cached
+ def region, store the value through the pointer; otherwise,
+ look up the key in the top dictionary, store the value,
+ and if the key is a name, set its pointer to the value slot.
+ put - if the key is a name and wasn't in the dictionary before,
+ clear its pointer. (Conservative, but rare.)
+ undef - if the key is a name, clear its pointer. (Overly
+ conservative, but rare.)
+ restore - if either the old or the new value of a change is a name
+ (possibly in a packed array), clear its pointer. This is
+ conservative, but easy to detect, and probably not *too*
+ conservative.
+ .setmaxlength - clear all the pointers, like noaccess.
+ (name lookup) - fetch the value through the pointer and dispatch
+ on its type; if the type is t__invalid, do a full search
+ and set the pointer. This avoids a separate check for a
+ clear pointer in the usual case where the pointer is valid.
+ load - if the pointer is clear, do a search and set the pointer;
+ then fetch the value.
+ where - always do a full search and set the pointer.
+ (Conservative, but rare.)
+
+One place where shallow binding will result in major new overhead is the
+extra push of systemdict for loading fonts. This probably isn't a problem
+in real life.
+
+Adaptive shallow binding
+------------------------
+
+We do validity checking for the name value cache using an epoch counter.
+For each dictionary D, we keep an on-stack flag F. Each dictionary stack
+entry is <D,M,F,E> where D is the actual dictionary, M is a mark vector of
+V bits (V is a system constant, probably 64), F is D's former on-stack
+flag, and E is the epoch at which the entry was made. For each name K, we
+keep a cache <P,E> where P is a pointer to the dictionary value slot that
+holds the current value of K, and E is an epoch value; the cache is valid
+if K->E >= dsp->E. Here is what happens for each operation:
+
+****** Still need to handle names defined only in systemdict or userdict?
+
+To initialize:
+ Epoch = 0
+To clear the cache entry for K:
+ *K = <ptr to invalid value, 0>
+begin(D):
+ *++dsp = <D, {0...}, D->F, ++Epoch>
+ set D->F
+value = lookup(K):
+ if K->E >= dsp->E
+ value = *K->P
+ else
+ do lookup as usual
+ *K = <ptr to value, Epoch>
+ set dp->M[i mod V] where dp is the dstack slot of the dictionary
+ where K was found and i is the index within that dictionary
+end:
+ for each i such that dsp->M[i] is set,
+ clear the cache entry for dsp->D->keys[i, i+V, ...]
+ dsp->D->F = dsp->F
+ --dsp
+noaccess(D):
+ if D->F is set,
+ clear the cache entries for all name keys of D
+readonly(D):
+ << nothing >>
+.setmaxlength(D,N):
+ same as noaccess
+restore:
+ If either the old or the new value of a change is a name
+ (possibly in a packed array), clear its cache entry. This is
+ conservative, but easy to detect, and probably not *too*
+ conservative.
+def(K,V):
+ if K->P points into dsp->D
+ *K->P = V
+ else
+ put the new value in dsp->D
+ set *K and dsp->M[i mod V] as for a lookup
+put(D,K,V):
+ if K is already defined in D, do nothing special
+ otherwise, if D->F isn't set, do nothing special
+ otherwise, clear K's cache entry
+undef(D,K):
+ if D->F is set,
+ clear K's cache entry
+*/
diff --git a/pstoraster/errno_.h b/pstoraster/errno_.h
new file mode 100644
index 000000000..f5dbfdebe
--- /dev/null
+++ b/pstoraster/errno_.h
@@ -0,0 +1,35 @@
+/* Copyright (C) 1993 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* errno_.h */
+/* Generic substitute for Unix errno.h */
+
+/* We must include std.h before any file that includes sys/types.h. */
+#include "std.h"
+
+/* All environments provide errno.h, but in some of them, errno.h */
+/* only defines the error numbers, and doesn't declare errno. */
+#include <errno.h>
+#ifndef errno /* in case it was #defined (very implausible!) */
+extern int errno;
+#endif
diff --git a/pstoraster/errors.h b/pstoraster/errors.h
new file mode 100644
index 000000000..9c9defcce
--- /dev/null
+++ b/pstoraster/errors.h
@@ -0,0 +1,179 @@
+/* Copyright (C) 1989, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* errors.h */
+/* Definition of error codes */
+
+/*
+ * A procedure that may return an error always returns
+ * a non-negative value (zero, unless otherwise noted) for success,
+ * or negative for failure.
+ * We use ints rather than an enum to avoid a lot of casting.
+ */
+
+/*
+ * The following peculiar structure allows us to include this file
+ * wherever error code definitions are needed, and use the same file
+ * to generate the table of error names by setting INCLUDE_ERROR_NAMES.
+ */
+
+# ifdef INCLUDE_ERROR_NAMES
+
+/* Define the error name table */
+const char _ds *gs_error_names[] = {
+#define _e_(code,name) name,
+
+# else /* !INCLUDE_ERROR_NAMES */
+
+extern const char _ds *gs_error_names[];
+# define _e_(code,name)
+
+#endif /* (!)INCLUDE_ERROR_NAMES */
+
+ /* ------ PostScript Level 1 errors ------ */
+
+#define e_unknownerror (-1) /* unknown error */
+ _e_(e_unknown, "unknownerror")
+#define e_dictfull (-2)
+ _e_(e_dictfull, "dictfull")
+#define e_dictstackoverflow (-3)
+ _e_(e_dictstackoverflow, "dictstackoverflow")
+#define e_dictstackunderflow (-4)
+ _e_(e_dictstackunderflow, "dictstackunderflow")
+#define e_execstackoverflow (-5)
+ _e_(e_execstackoverflow, "execstackoverflow")
+#define e_interrupt (-6)
+/* We also need to define gs_error_interrupt, for gpcheck.h. */
+#undef gs_error_interrupt
+#define gs_error_interrupt e_interrupt
+ _e_(e_interrupt, "interrupt")
+#define e_invalidaccess (-7)
+ _e_(e_invalidaccess, "invalidaccess")
+#define e_invalidexit (-8)
+ _e_(e_invalidexit, "invalidexit")
+#define e_invalidfileaccess (-9)
+ _e_(e_invalidfileaccess, "invalidfileaccess")
+#define e_invalidfont (-10)
+ _e_(e_invalidfont, "invalidfont")
+#define e_invalidrestore (-11)
+ _e_(e_invalidrestore, "invalidrestore")
+#define e_ioerror (-12)
+ _e_(e_ioerror, "ioerror")
+#define e_limitcheck (-13)
+ _e_(e_limitcheck, "limitcheck")
+#define e_nocurrentpoint (-14)
+ _e_(e_nocurrentpoint, "nocurrentpoint")
+#define e_rangecheck (-15)
+ _e_(e_rangecheck, "rangecheck")
+#define e_stackoverflow (-16)
+ _e_(e_stackoverflow, "stackoverflow")
+#define e_stackunderflow (-17)
+ _e_(e_stackunderflow, "stackunderflow")
+#define e_syntaxerror (-18)
+ _e_(e_syntaxerror, "syntaxerror")
+#define e_timeout (-19)
+ _e_(e_timeout, "timeout")
+#define e_typecheck (-20)
+ _e_(e_typecheck, "typecheck")
+#define e_undefined (-21)
+ _e_(e_undefined, "undefined")
+#define e_undefinedfilename (-22)
+ _e_(e_undefinedfilename, "undefinedfilename")
+#define e_undefinedresult (-23)
+ _e_(e_undefinedresult, "undefinedresult")
+#define e_unmatchedmark (-24)
+ _e_(e_unmatchedmark, "unmatchedmark")
+#define e_VMerror (-25)
+ _e_(e_VMerror, "VMerror")
+
+ /* ------ Additional Level 2 and DPS errors ------ */
+
+#define e_configurationerror (-26)
+ _e_(e_configurationerror, "configurationerror")
+#define e_invalidcontext (-27)
+ _e_(e_invalidcontext, "invalidcontext")
+#define e_undefinedresource (-28)
+ _e_(e_undefinedresource, "undefinedresource")
+#define e_unregistered (-29)
+ _e_(e_unregistered, "unregistered")
+
+# ifdef INCLUDE_ERROR_NAMES
+
+/* End of error name table */
+ 0
+};
+
+# endif /* INCLUDE_ERROR_NAMES */
+
+ /* ------ Pseudo-errors used internally ------ */
+
+/*
+ * Internal code for a fatal error.
+ * gs_interpret also returns this for a .quit with a positive exit code.
+ */
+#define e_Fatal (-100)
+
+/*
+ * Internal code for the .quit operator.
+ * The real quit code is an integer on the operand stack.
+ * gs_interpret returns this only for a .quit with a zero exit code.
+ */
+#define e_Quit (-101)
+
+/*
+ * Internal code for a normal exit from the interpreter.
+ * Do not use outside of interp.c.
+ */
+#define e_InterpreterExit (-102)
+
+/*
+ * Internal code that indicates that a procedure has been inserted
+ * on the e-stack at (former) esp+2, to be executed before retrying
+ * the current token. This is used for color remapping
+ * involving a call back into the interpreter -- inelegant, but effective.
+ */
+#define e_InsertProc (-103)
+
+/*
+ * Internal code to indicate we have underflowed the top block
+ * of the e-stack.
+ */
+#define e_ExecStackUnderflow (-104)
+
+/*
+ * Internal code for the vmreclaim operator with a positive operand.
+ * We need to handle this as an error because otherwise the interpreter
+ * won't reload enough of its state when the operator returns.
+ */
+#define e_VMreclaim (-105)
+
+/*
+ * Internal code for requesting more input from run_string.
+ */
+#define e_NeedInput (-106)
+
+/*
+ * Define which error codes require re-executing the current object.
+ */
+#define error_is_interrupt(ecode)\
+ ((ecode) == e_interrupt || (ecode) == e_timeout)
diff --git a/pstoraster/estack.h b/pstoraster/estack.h
new file mode 100644
index 000000000..3dfd42b75
--- /dev/null
+++ b/pstoraster/estack.h
@@ -0,0 +1,137 @@
+/* Copyright (C) 1989, 1992, 1993, 1994 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* estack.h */
+/* Definitions for the execution stack */
+#include "istack.h"
+
+/* Define the execution stack pointers. */
+typedef s_ptr es_ptr;
+typedef const_s_ptr const_es_ptr;
+extern ref_stack e_stack;
+#define esbot (e_stack.bot)
+#define esp (e_stack.p)
+#define estop (e_stack.top)
+
+/*
+ * To improve performance, we cache the currentfile pointer
+ * (i.e., `shallow-bind' it in Lisp terminology). The invariant is as
+ * follows: either esfile points to the currentfile slot on the estack
+ * (i.e., the topmost slot with an executable file), or it is 0.
+ * To maintain the invariant, it is sufficient that whenever a routine
+ * pushes or pops anything on the estack, if the object *might* be
+ * an executable file, invoke esfile_clear_cache(); alternatively,
+ * immediately after pushing an object, invoke esfile_check_cache().
+ */
+extern ref *esfile;
+#define esfile_clear_cache() (esfile = 0)
+#define esfile_set_cache(pref) (esfile = (pref))
+#define esfile_check_cache()\
+ if ( r_has_type_attrs(esp, t_file, a_executable) )\
+ esfile_set_cache(esp)
+
+/*
+ * The execution stack is used for three purposes:
+ *
+ * - Procedures being executed are held here. They always have
+ * type = t_array, t_mixedarray, or t_shortarray, with a_executable set.
+ * More specifically, the e-stack holds the as yet unexecuted tail of the
+ * procedure.
+ *
+ * - if, ifelse, etc. push arguments to be executed here.
+ * They may be any kind of object whatever.
+ *
+ * - Control operators (filenameforall, for, repeat, loop, forall,
+ * pathforall, run, stopped, ...) mark the stack by pushing
+ * an object with type = t_null, attrs = a_executable, size = es_xxx
+ * (see below), and value.opproc = a cleanup procedure that will get called
+ * whenever the execution stack is about to get cut back beyond this point
+ * (either for normal completion of the operator, or any kind of exit).
+ * (Executable null objects can't ever appear on the e-stack otherwise:
+ * if a control operator pushes one, it gets popped immediately.)
+ * The cleanup procedure is called with esp pointing just BELOW the mark,
+ * i.e., the mark has already been popped.
+ *
+ * The loop operators also push whatever state they need,
+ * followed by an operator object that handles continuing the loop.
+ *
+ * Note that there are many internal looping operators -- for example,
+ * all the 'show' operators can behave like loops, since they may call out
+ * to BuildChar procedures.
+ */
+
+/* Macro for marking the execution stack */
+#define make_mark_estack(ep, es_idx, proc)\
+ make_tasv(ep, t_null, a_executable, es_idx, opproc, proc)
+#define push_mark_estack(es_idx, proc)\
+ (++esp, make_mark_estack(esp, es_idx, proc))
+#define r_is_estack_mark(ep)\
+ r_has_type_attrs(ep, t_null, a_executable)
+#define estack_mark_index(ep) r_size(ep)
+
+/* Macro for pushing an operator on the execution stack */
+/* to represent a continuation procedure */
+#define make_op_estack(ep, proc)\
+ make_oper(ep, 0, proc)
+#define push_op_estack(proc)\
+ (++esp, make_op_estack(esp, proc))
+
+/* Macro to ensure enough room on the execution stack */
+#define check_estack(n)\
+ if ( esp > estop - (n) )\
+ { int es_code_ = ref_stack_extend(&e_stack, n);\
+ if ( es_code_ < 0 ) return es_code_;\
+ }
+
+/* Macro to ensure enough entries on the execution stack */
+#define check_esp(n)\
+ if ( esp < esbot + ((n) - 1) )\
+ { e_stack.requested = (n); return_error(e_ExecStackUnderflow); }
+
+/* Define the various kinds of execution stack marks. */
+#define es_other 0 /* internal use */
+#define es_show 1 /* show operators */
+#define es_for 2 /* iteration operators */
+#define es_stopped 3 /* stopped operator */
+
+/* Pop a given number of elements off the execution stack, */
+/* executing cleanup procedures as necessary. */
+void pop_estack(P1(uint));
+
+/*
+ * The execution stack is implemented as a linked list of blocks;
+ * operators that can push or pop an unbounded number of values, or that
+ * access the entire o-stack, must take this into account. These are:
+ * exit .stop .instopped countexecstack execstack currentfile
+ * pop_estack(exit, stop, error recovery)
+ * gs_show_find(all the show operators)
+ * In addition, for e-stack entries created by control operators, we must
+ * ensure that the mark and its data are never separated. We do this
+ * by ensuring that when splitting the top block, at least N items
+ * are kept in the new top block above the bottommost retained mark,
+ * where N is the largest number of data items associated with a mark.
+ * Finally, in order to avoid specific checks for underflowing a block,
+ * we put a guard entry at the bottom of each block except the top one
+ * that contains a procedure that returns an internal "exec stack block
+ * underflow" error.
+ */
diff --git a/pstoraster/files.h b/pstoraster/files.h
new file mode 100644
index 000000000..f7c320997
--- /dev/null
+++ b/pstoraster/files.h
@@ -0,0 +1,142 @@
+/* Copyright (C) 1989, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* files.h */
+/* Common declarations for zfile.c and zfileio.c */
+/* Requires stream.h */
+
+/*
+ * In many places below, a do {...} while (0) avoids problems with a possible
+ * enclosing 'if'.
+ */
+
+/*
+ * File objects store a pointer to a stream in value.pfile.
+ * A file object is valid if its "size" matches the read_id or write_id
+ * (as appropriate) in the stream it points to. This arrangement
+ * allows us to detect closed files reliably, while allowing us to
+ * reuse closed streams for new files.
+ */
+#define fptr(pref) (pref)->value.pfile
+#define make_file(pref,a,id,s)\
+ make_tasv(pref,t_file,a,id,pfile,s)
+
+/* The stdxxx files. We have to access them through procedures, */
+/* because they might have to be opened when referenced. */
+int zget_stdin(P1(stream **));
+int zget_stdout(P1(stream **));
+int zget_stderr(P1(stream **));
+extern bool gs_stdin_is_interactive;
+/* An invalid (closed) file. */
+extern stream *invalid_file_entry;
+
+/* Macros for checking file validity. */
+#define file_is_valid(svar,op)\
+ (svar = fptr(op), (svar->read_id | svar->write_id) == r_size(op))
+#define check_file(svar,op)\
+ do\
+ { check_type(*(op), t_file);\
+ if ( !file_is_valid(svar, op) ) return_error(e_invalidaccess);\
+ }\
+ while (0)
+
+/*
+ * If a file is open for both reading and writing, its read_id, write_id,
+ * and stream procedures and modes reflect the current mode of use;
+ * an id check failure will switch it to the other mode.
+ */
+int file_switch_to_read(P1(const ref *));
+#define check_read_file(svar,op)\
+ do\
+ { check_read_type(*(op), t_file);\
+ check_read_known_file(svar, op, return);\
+ }\
+ while (0)
+#define check_read_known_file(svar,op,error_return)\
+ check_read_known_file_else(svar, op, error_return, svar = invalid_file_entry)
+/* The do... avoids problems with a possible enclosed 'if'. */
+#define check_read_known_file_else(svar,op,error_return,invalid_action)\
+ do\
+ { svar = fptr(op);\
+ if ( svar->read_id != r_size(op) )\
+ { if ( svar->read_id == 0 && svar->write_id == r_size(op) )\
+ { int fcode = file_switch_to_read(op);\
+ if ( fcode < 0 ) error_return(fcode);\
+ }\
+ else do { invalid_action; } while (0); /* closed or reopened file */\
+ }\
+ }\
+ while (0)
+int file_switch_to_write(P1(const ref *));
+#define check_write_file(svar,op)\
+ do\
+ { check_write_type(*(op), t_file);\
+ check_write_known_file(svar, op, return);\
+ }\
+ while (0)
+#define check_write_known_file(svar,op,error_return)\
+ do\
+ { svar = fptr(op);\
+ if ( svar->write_id != r_size(op) )\
+ { int fcode = file_switch_to_write(op);\
+ if ( fcode < 0 ) error_return(fcode);\
+ }\
+ }\
+ while (0)
+
+/* Data exported by zfile.c. */
+ /* for zfilter.c and ziodev.c */
+extern const uint file_default_buffer_size;
+
+/* Procedures exported by zfile.c. */
+ /* for gs.c */
+FILE *lib_fopen(P1(const char *));
+ /* for gsmain.c */
+int lib_file_open(P6(const char *, uint, byte *, uint, uint *, ref *));
+ /* for iccinit.c */
+int file_read_string(P3(const byte *, uint, ref *));
+ /* for os_open in ziodev.c */
+#ifdef iodev_proc_fopen /* in gxiodev.h */
+int file_open_stream(P6(const char *, uint, const char *, uint,
+ stream **, iodev_proc_fopen_t));
+#endif
+ /* for zfilter.c */
+int filter_open(P6(const char *, uint, ref *, const stream_procs _ds *,
+ const stream_template *, const stream_state *));
+ /* for zfileio.c */
+void make_stream_file(P3(ref *, stream *, const char *));
+ /* for ziodev.c */
+int file_close_finish(P1(stream *));
+int file_close_disable(P1(stream *));
+int file_close_file(P1(stream *));
+ /* for gsmain.c, interp.c */
+int file_close(P1(ref *));
+ /* for ziodev.c */
+stream *file_alloc_stream(P2(gs_memory_t *, client_name_t));
+ /* for isave.c */
+void file_save(P0());
+/*void file_restore(P1(const alloc_save_t *));*/
+
+/* Procedures exported by zfileio.c. */
+ /* for ziodev.c */
+int zreadline_from(P5(stream *, byte *, uint, uint *, bool *));
diff --git a/pstoraster/fname.h b/pstoraster/fname.h
new file mode 100644
index 000000000..84e2852ea
--- /dev/null
+++ b/pstoraster/fname.h
@@ -0,0 +1,38 @@
+/* Copyright (C) 1993 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* fname.h */
+/* File name parsing interface */
+/* Requires gxiodev.h */
+
+/* Parsed file name type. Note that the file name may be either a */
+/* gs_string (no terminator) or a C string (null terminator). */
+typedef struct parsed_file_name_s {
+ gx_io_device *iodev;
+ const char *fname;
+ uint len;
+} parsed_file_name;
+int parse_file_name(P2(const ref *, parsed_file_name *));
+int parse_real_file_name(P3(const ref *, parsed_file_name *, client_name_t));
+int terminate_file_name(P2(parsed_file_name *, client_name_t));
+void free_file_name(P2(parsed_file_name *, client_name_t));
diff --git a/pstoraster/gconf.c b/pstoraster/gconf.c
new file mode 100644
index 000000000..1a6641cc3
--- /dev/null
+++ b/pstoraster/gconf.c
@@ -0,0 +1,126 @@
+/* Copyright (C) 1989, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gconf.c */
+/* Configuration tables */
+#include "gx.h"
+#include "gscdefs.h" /* interface */
+#include "gconfig.h" /* for #defines */
+/*
+ * Since we only declare variables of type gx_device *,
+ * it should be sufficient to define struct gx_device_s as
+ * an abstract (undefined) structure. However, the VAX VMS compiler
+ * isn't happy with this, so we have to include the full definition.
+ */
+#include "gxdevice.h"
+#include "gxiodev.h"
+
+/*
+ * The makefile generates the file gconfig.h, which consists of
+ * lines of the form
+ * device_(gs_xxx_device)
+ * for each installed device;
+ * emulator_("emulator")
+ * for each known emulator;
+ * init_(gs_xxx_init)
+ * for each initialization procedure;
+ * io_device_(gs_iodev_xxx)
+ * for each known IODevice;
+ * oper_(xxx_op_defs)
+ * for each operator option;
+ * psfile_("gs_xxxx.ps")
+ * for each optional initialization file.
+ *
+ * We include this file multiple times to generate various different
+ * source structures. (It's a hack, but we haven't come up with anything
+ * more satisfactory.)
+ */
+
+/* ---------------- Resources (devices, inits, IODevices) ---------------- */
+
+/* Declare devices, init procedures, and IODevices as extern. */
+#define device_(dev) extern far_data gx_device dev;
+#define init_(proc) extern void proc(P1(gs_memory_t *));
+#define io_device_(iodev) extern gx_io_device iodev;
+#include "gconfig.h"
+#undef init_
+#undef io_device_
+#undef device_
+
+/* Set up the initialization procedure table. */
+extern_gx_init_table();
+#define init_(proc) proc,
+void (*gx_init_table[])(P1(gs_memory_t *)) = {
+#include "gconfig.h"
+ 0
+};
+#undef init_
+
+/* Set up the IODevice table. The first entry must be %os%, */
+/* since it is the default for files with no explicit device specified. */
+extern_gx_io_device_table();
+extern gx_io_device gs_iodev_os;
+#define io_device_(iodev) &iodev,
+gx_io_device *gx_io_device_table[] = {
+ &gs_iodev_os,
+#include "gconfig.h"
+ 0
+};
+#undef io_device_
+uint gx_io_device_table_count = countof(gx_io_device_table) - 1;
+
+/* Set up the device table. */
+#define device_(dev) &dev,
+private const gx_device *gx_device_list[] = {
+#include "gconfig.h"
+ 0
+};
+#undef device_
+
+/*
+ * Allocate structure descriptors for the devices.
+ * We can't fill in the structure sizes, because we don't know them
+ * statically, and we also don't know statically which devices are
+ * forwarders; so we fill all of this in when we need to
+ * (in gs_copydevice in gsdevice.c).
+ */
+#define device_(dev) { 0 },
+/* Because of a bug in the Borland C++ 4.5 compiler, */
+/* we can't declare the following far_data but not static. */
+static /*private*/ far_data gs_memory_struct_type_t gx_device_st_list[] = {
+#include "gconfig.h"
+ { 0 }
+};
+#undef device_
+
+/* Return the list of device prototypes, the list of their structure */
+/* descriptors, and (as the value) the length of the lists. */
+extern_gs_lib_device_list();
+int
+gs_lib_device_list(const gx_device ***plist, gs_memory_struct_type_t **pst)
+{ if ( plist != 0 )
+ *plist = gx_device_list;
+ if ( pst != 0 )
+ *pst = gx_device_st_list;
+ return countof(gx_device_list) - 1;
+}
diff --git a/pstoraster/gconfig.h b/pstoraster/gconfig.h
new file mode 100644
index 000000000..4988155ff
--- /dev/null
+++ b/pstoraster/gconfig.h
@@ -0,0 +1,196 @@
+/*
+ * "$Id$"
+ *
+ * GNU GhostScript configuration file for the Common UNIX Printing
+ * System (CUPS).
+ *
+ * This file is normally generated by a lot of echogs and genconf
+ * commands...
+ *
+ * Copyright 1997-1999 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ */
+
+#include <config.h>
+
+#ifdef device_
+device_(gs_cups_device)
+device_(gs_nullpage_device)
+#endif
+#ifdef oper_
+# ifdef HAVE_LIBZ
+oper_(zfzlib_op_defs)
+# endif /* HAVE_LIBZ */
+oper_(zcie_l2_op_defs)
+oper_(zcrd_l2_op_defs)
+oper_(zfont0_op_defs)
+oper_(zchar2_op_defs)
+# ifdef HAVE_LIBJPEG
+oper_(zfdcte_op_defs)
+oper_(zfdctd_op_defs)
+# endif /* HAVE_LIBJPEG */
+oper_(zdevice2_l2_op_defs)
+oper_(ziodev2_l2_op_defs)
+oper_(zmedia2_l2_op_defs)
+#endif
+#ifdef io_device_
+io_device_(gs_iodev_null)
+io_device_(gs_iodev_ram)
+io_device_(gs_iodev_calendar)
+#endif
+#ifdef psfile_
+psfile_("gs_setpd.ps")
+#endif
+#ifdef oper_
+oper_(zbseq_l2_op_defs)
+#endif
+#ifdef psfile_
+psfile_("gs_btokn.ps")
+#endif
+#ifdef init_
+init_(gs_gscolor1_init)
+#endif
+#ifdef oper_
+oper_(zcolor1_op_defs)
+oper_(zht1_op_defs)
+oper_(zupath_l2_op_defs)
+oper_(zvmem2_op_defs)
+oper_(ireclaim_l2_op_defs)
+oper_(zchar2_l2_op_defs)
+oper_(zdps1_l2_op_defs)
+#endif
+#ifdef psfile_
+psfile_("gs_dps1.ps")
+#endif
+#ifdef oper_
+oper_(zfdecode_op_defs)
+oper_(zfilter2_op_defs)
+oper_(zarith_op_defs)
+oper_(zarray_op_defs)
+oper_(zcontrol_op_defs)
+oper_(zdict_op_defs)
+oper_(zfile_op_defs)
+oper_(zfileio_op_defs)
+oper_(zfilter_op_defs)
+oper_(zfproc_op_defs)
+oper_(zgeneric_op_defs)
+oper_(ziodev_op_defs)
+oper_(zmath_op_defs)
+oper_(zmisc_op_defs)
+oper_(zpacked_op_defs)
+oper_(zrelbit_op_defs)
+oper_(zstack_op_defs)
+oper_(zstring_op_defs)
+oper_(zsysvm_op_defs)
+oper_(ztoken_op_defs)
+oper_(ztype_op_defs)
+oper_(zusparam_op_defs)
+oper_(zvmem_op_defs)
+oper_(zchar_op_defs)
+oper_(zcolor_op_defs)
+oper_(zdevice_op_defs)
+oper_(zfont_op_defs)
+oper_(zfont2_op_defs)
+oper_(zgstate_op_defs)
+oper_(zht_op_defs)
+oper_(zmatrix_op_defs)
+oper_(zpaint_op_defs)
+oper_(zpath_op_defs)
+#endif
+#ifdef io_device_
+io_device_(gs_iodev_stdin)
+io_device_(gs_iodev_stdout)
+io_device_(gs_iodev_stderr)
+io_device_(gs_iodev_lineedit)
+io_device_(gs_iodev_statementedit)
+#endif
+#ifdef oper_
+oper_(zfbcp_op_defs)
+oper_(zhsb_op_defs)
+oper_(zpath1_op_defs)
+oper_(zchar1_op_defs)
+oper_(zfont1_op_defs)
+oper_(zmisc1_op_defs)
+#endif
+#ifdef psfile_
+psfile_("gs_type1.ps")
+#endif
+#ifdef emulator_
+emulator_("PostScript")
+emulator_("PostScriptLevel1")
+#endif
+#ifdef oper_
+oper_(zpcolor_l2_op_defs)
+oper_(zmisc2_op_defs)
+oper_(zcolor2_l2_op_defs)
+oper_(zcsindex_l2_op_defs)
+oper_(zht2_l2_op_defs)
+oper_(zimage2_l2_op_defs)
+#endif
+#ifdef psfile_
+psfile_("gs_lev2.ps")
+psfile_("gs_res.ps")
+#endif
+#ifdef oper_
+oper_(zcssepr_l2_op_defs)
+oper_(zfilterx_op_defs)
+#endif
+#ifdef emulator_
+emulator_("PostScriptLevel2")
+#endif
+#ifdef psfile_
+psfile_("gs_mex_e.ps")
+psfile_("gs_mro_e.ps")
+psfile_("gs_pdf_e.ps")
+psfile_("gs_wan_e.ps")
+psfile_("gs_pdf.ps")
+psfile_("gs_l2img.ps")
+psfile_("pdf_base.ps")
+psfile_("pdf_draw.ps")
+psfile_("pdf_font.ps")
+psfile_("pdf_main.ps")
+psfile_("pdf_sec.ps")
+psfile_("pdf_2ps.ps")
+#endif
+#ifdef emulator_
+emulator_("PDF")
+#endif
+#ifdef io_device_
+io_device_(gs_iodev_pipe)
+#endif
+#ifdef oper_
+oper_(zchar42_op_defs)
+oper_(zfont42_op_defs)
+#endif
+#ifdef psfile_
+psfile_("gs_typ42.ps")
+psfile_("gs_ttf.ps")
+#endif
+#ifdef init_
+init_(gs_climag_init)
+init_(gs_clpath_init)
+init_(gs_gscolor_init)
+init_(gs_roplib_init)
+#endif
+#define GS_LIB_DEFAULT DATADIR "/ghostscript:" DATADIR "/fonts"
+#define GS_DOCDIR DATADIR "/ghostscript"
+#define GS_INIT "gs_init.ps"
+
+/*
+ * End of "$Id$".
+ */
diff --git a/pstoraster/gconfigv.h b/pstoraster/gconfigv.h
new file mode 100644
index 000000000..c2f16d45d
--- /dev/null
+++ b/pstoraster/gconfigv.h
@@ -0,0 +1,3 @@
+#define USE_ASM (-0)
+#define USE_FPU (2-0)
+#define EXTEND_NAMES 0
diff --git a/pstoraster/gdebug.h b/pstoraster/gdebug.h
new file mode 100644
index 000000000..aeba8a7aa
--- /dev/null
+++ b/pstoraster/gdebug.h
@@ -0,0 +1,117 @@
+/* Copyright (C) 1989, 1992, 1993, 1994, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gdebug.h */
+/* Debugging machinery definitions */
+
+#ifndef gdebug_INCLUDED
+# define gdebug_INCLUDED
+
+/* Define the array of debugging flags, indexed by character code. */
+extern char gs_debug[128];
+#define gs_debug_c(c)\
+ ((c)>='a' && (c)<='z' ? gs_debug[c] | gs_debug[(c)^32] : gs_debug[c])
+#ifdef DEBUG
+# define gs_if_debug_c(c) gs_debug_c(c)
+#else
+# define gs_if_debug_c(c) 0
+#endif
+/* Define an alias for a specialized debugging flag */
+/* that used to be a separate variable. */
+#define gs_log_errors gs_debug['#']
+
+/* If debugging, direct all error output to gs_debug_out. */
+extern FILE *gs_debug_out;
+#ifdef DEBUG
+#undef dstderr
+#define dstderr gs_debug_out
+#undef estderr
+#define estderr gs_debug_out
+#endif
+
+/* Redefine eprintf_program_name and lprintf_file_and_line as procedures */
+/* so one can set breakpoints on them. */
+#undef eprintf_program_name
+extern void eprintf_program_name(P2(FILE *, const char *));
+#undef lprintf_file_and_line
+extern void lprintf_file_and_line(P3(FILE *, const char *, int));
+
+/* Insert code conditionally if debugging. */
+#ifdef DEBUG
+# define do_debug(x) x
+#else
+# define do_debug(x)
+#endif
+
+/* Debugging printout macros. */
+#ifdef DEBUG
+# define if_debug0(c,s)\
+ if (gs_debug_c(c)) dprintf(s)
+# define if_debug1(c,s,a1)\
+ if (gs_debug_c(c)) dprintf1(s,a1)
+# define if_debug2(c,s,a1,a2)\
+ if (gs_debug_c(c)) dprintf2(s,a1,a2)
+# define if_debug3(c,s,a1,a2,a3)\
+ if (gs_debug_c(c)) dprintf3(s,a1,a2,a3)
+# define if_debug4(c,s,a1,a2,a3,a4)\
+ if (gs_debug_c(c)) dprintf4(s,a1,a2,a3,a4)
+# define if_debug5(c,s,a1,a2,a3,a4,a5)\
+ if (gs_debug_c(c)) dprintf5(s,a1,a2,a3,a4,a5)
+# define if_debug6(c,s,a1,a2,a3,a4,a5,a6)\
+ if (gs_debug_c(c)) dprintf6(s,a1,a2,a3,a4,a5,a6)
+# define if_debug7(c,s,a1,a2,a3,a4,a5,a6,a7)\
+ if (gs_debug_c(c)) dprintf7(s,a1,a2,a3,a4,a5,a6,a7)
+# define if_debug8(c,s,a1,a2,a3,a4,a5,a6,a7,a8)\
+ if (gs_debug_c(c)) dprintf8(s,a1,a2,a3,a4,a5,a6,a7,a8)
+# define if_debug9(c,s,a1,a2,a3,a4,a5,a6,a7,a8,a9)\
+ if (gs_debug_c(c)) dprintf9(s,a1,a2,a3,a4,a5,a6,a7,a8,a9)
+# define if_debug10(c,s,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10)\
+ if (gs_debug_c(c)) dprintf10(s,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10)
+# define if_debug11(c,s,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11)\
+ if (gs_debug_c(c)) dprintf11(s,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11)
+# define if_debug12(c,s,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12)\
+ if (gs_debug_c(c)) dprintf12(s,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12)
+#else
+# define if_debug0(c,s) DO_NOTHING
+# define if_debug1(c,s,a1) DO_NOTHING
+# define if_debug2(c,s,a1,a2) DO_NOTHING
+# define if_debug3(c,s,a1,a2,a3) DO_NOTHING
+# define if_debug4(c,s,a1,a2,a3,a4) DO_NOTHING
+# define if_debug5(c,s,a1,a2,a3,a4,a5) DO_NOTHING
+# define if_debug6(c,s,a1,a2,a3,a4,a5,a6) DO_NOTHING
+# define if_debug7(c,s,a1,a2,a3,a4,a5,a6,a7) DO_NOTHING
+# define if_debug8(c,s,a1,a2,a3,a4,a5,a6,a7,a8) DO_NOTHING
+# define if_debug9(c,s,a1,a2,a3,a4,a5,a6,a7,a8,a9) DO_NOTHING
+# define if_debug10(c,s,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10) DO_NOTHING
+# define if_debug11(c,s,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) DO_NOTHING
+# define if_debug12(c,s,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12) DO_NOTHING
+#endif
+
+/* Debugging support procedures in gsmisc.c */
+void debug_dump_bytes(P3(const byte *from, const byte *to,
+ const char *msg));
+void debug_dump_bitmap(P4(const byte *from, uint raster, uint height,
+ const char *msg));
+void debug_print_string(P2(const byte *str, uint len));
+
+#endif /* gdebug_INCLUDED */
diff --git a/pstoraster/gdevabuf.c b/pstoraster/gdevabuf.c
new file mode 100644
index 000000000..11e509083
--- /dev/null
+++ b/pstoraster/gdevabuf.c
@@ -0,0 +1,355 @@
+/* Copyright (C) 1994, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gdevabuf.c */
+/* Alpha-buffering memory devices */
+#include "memory_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gxdevice.h"
+#include "gxdevmem.h" /* semi-public definitions */
+#include "gdevmem.h" /* private definitions */
+
+/* ================ Alpha devices ================ */
+
+/*
+ * These devices store 2 or 4 bits of alpha. They are a hybrid of a
+ * monobit device (for color mapping) and a 2- or 4-bit device (for painting).
+ * Currently, we only use them for character rasterizing, but they might be
+ * useful for other things someday.
+ */
+
+/* We can't initialize the device descriptor statically very well, */
+/* so we patch up the image2 or image4 descriptor. */
+private dev_proc_map_rgb_color(mem_alpha_map_rgb_color);
+private dev_proc_map_color_rgb(mem_alpha_map_color_rgb);
+private dev_proc_map_rgb_alpha_color(mem_alpha_map_rgb_alpha_color);
+private dev_proc_get_alpha_bits(mem_alpha_get_alpha_bits);
+private dev_proc_copy_alpha(mem_alpha_copy_alpha);
+
+void
+gs_make_mem_alpha_device(gx_device_memory *adev, gs_memory_t *mem,
+ gx_device *target, int alpha_bits)
+{ gs_make_mem_device(adev, gdev_mem_device_for_bits(alpha_bits),
+ mem, 0, target);
+ /* This is a black-and-white device ... */
+ adev->color_info = gdev_mem_device_for_bits(1)->color_info;
+ /* ... but it has multiple bits per pixel ... */
+ adev->color_info.depth = alpha_bits;
+ /* ... and different color mapping. */
+ set_dev_proc(adev, map_rgb_color, mem_alpha_map_rgb_color);
+ set_dev_proc(adev, map_color_rgb, mem_alpha_map_color_rgb);
+ set_dev_proc(adev, map_rgb_alpha_color, mem_alpha_map_rgb_alpha_color);
+ set_dev_proc(adev, get_alpha_bits, mem_alpha_get_alpha_bits);
+ set_dev_proc(adev, copy_alpha, mem_alpha_copy_alpha);
+}
+
+/* Reimplement color mapping. */
+private gx_color_index
+mem_alpha_map_rgb_color(gx_device *dev, gx_color_value r, gx_color_value g,
+ gx_color_value b)
+{ gx_color_index color = gx_forward_map_rgb_color(dev, r, g, b);
+ return (color == 0 || color == gx_no_color_index ? color :
+ (gx_color_index)((1 << mdev->log2_alpha_bits) - 1));
+}
+private int
+mem_alpha_map_color_rgb(gx_device *dev, gx_color_index color,
+ gx_color_value prgb[3])
+{ return
+ gx_forward_map_color_rgb(dev,
+ (color == 0 ? color : (gx_color_index)1),
+ prgb);
+}
+private gx_color_index
+mem_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_forward_map_rgb_color(dev, r, g, b);
+ return (color == 0 || color == gx_no_color_index ? color :
+ (gx_color_index)(alpha >> (gx_color_value_bits -
+ mdev->log2_alpha_bits)));
+}
+private int
+mem_alpha_get_alpha_bits(gx_device *dev, graphics_object_type type)
+{ return 1 << mdev->log2_alpha_bits;
+}
+/* Implement alpha copying. */
+private int
+mem_alpha_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)
+{ /* Just use copy_color. */
+ return (color == 0 ?
+ (*dev_proc(dev, fill_rectangle))(dev, x, y, width, height,
+ color) :
+ (*dev_proc(dev, copy_color))(dev, data, data_x, raster, id,
+ x, y, width, height));
+}
+
+/* ================ Alpha-buffer device ================ */
+
+/*
+ * This device converts graphics sampled at a higher resolution to
+ * alpha values at a lower resolution. It does this by accumulating
+ * the bits of a band and then converting the band to alphas.
+ * In order to make this work, the client of the device must promise
+ * only to visit each band at most once, except possibly for a single
+ * scan line overlapping the adjacent band, and must promise only to write
+ * a single color into the output. In particular, this works
+ * within a single call on gx_fill_path (if the fill loop is constrained
+ * to process bands of limited height on each pass) or a single masked image
+ * scanned in Y order, but not across such calls and not for other
+ * kinds of painting operations.
+ *
+ * We implement this device as a subclass of a monobit memory device.
+ * (We put its state in the definition of gx_device_memory just because
+ * actual subclassing introduces a lot of needless boilerplate.)
+ * We only allocate enough bits for one band. The height of the band
+ * must be a multiple of the Y scale factor; the minimum height
+ * of the band is twice the Y scale factor.
+ *
+ * The bits in storage are actually a sliding window on the true
+ * oversampled image. To avoid having to copy the bits around when we
+ * move the window, we adjust the mapping between the client's Y values
+ * and our own, as follows:
+ * Client Stored
+ * ------ ------
+ * y0..y0+m-1 n-m..n-1
+ * y0+m..y0+n-1 0..n-m-1
+ * where n and m are multiples of the Y scale factor and 0 <= m <= n <=
+ * the height of the band. (In the device structure, m is called
+ * mapped_start and n is called mapped_height.) This allows us to slide
+ * the window incrementally in either direction without copying any bits.
+ */
+
+/* Procedures */
+private dev_proc_close_device(mem_abuf_close);
+private dev_proc_copy_mono(mem_abuf_copy_mono);
+private dev_proc_fill_rectangle(mem_abuf_fill_rectangle);
+
+/* The device descriptor. */
+private const gx_device_memory far_data mem_alpha_buffer_device =
+ mem_device("image(alpha buffer)", 0, 1,
+ gx_forward_map_rgb_color, gx_forward_map_color_rgb,
+ mem_abuf_copy_mono, gx_default_copy_color, mem_abuf_fill_rectangle,
+ gx_no_strip_copy_rop);
+
+/* Make an alpha-buffer memory device. */
+/* We use abuf instead of alpha_buffer because */
+/* gcc under VMS only retains 23 characters of procedure names. */
+void
+gs_make_mem_abuf_device(gx_device_memory *adev, gs_memory_t *mem,
+ gx_device *target, const gs_log2_scale_point *pscale,
+ int alpha_bits, int mapped_x)
+{ gs_make_mem_device(adev, &mem_alpha_buffer_device, mem, 0, target);
+ adev->max_fill_band = 1 << pscale->y;
+ adev->log2_scale = *pscale;
+ adev->log2_alpha_bits = alpha_bits >> 1; /* works for 1,2,4 */
+ adev->mapped_x = mapped_x;
+ set_dev_proc(adev, close_device, mem_abuf_close);
+}
+
+/* Test whether a device is an alpha-buffering device. */
+bool
+gs_device_is_abuf(const gx_device *dev)
+{ /* We can't just compare the procs, or even an individual proc, */
+ /* because we might be tracing. Instead, check the identity of */
+ /* the device name. */
+ return dev->dname == mem_alpha_buffer_device.dname;
+}
+
+/* Internal routine to flush a block of the buffer. */
+/* A block is a group of scan lines whose initial Y is a multiple */
+/* of the Y scale and whose height is equal to the Y scale. */
+private int
+abuf_flush_block(gx_device_memory *adev, int y)
+{ gx_device *target = adev->target;
+ int block_height = 1 << adev->log2_scale.y;
+ int alpha_bits = 1 << adev->log2_alpha_bits;
+ int ddepth =
+ (adev->width >> adev->log2_scale.x) << adev->log2_alpha_bits;
+ uint draster = bitmap_raster(ddepth);
+ int buffer_y = y - adev->mapped_y + adev->mapped_start;
+ byte *bits;
+
+ if ( buffer_y >= adev->height )
+ buffer_y -= adev->height;
+ bits = scan_line_base(adev, buffer_y);
+ { /*
+ * Many bits are typically zero. Save time by computing
+ * an accurate X bounding box before compressing.
+ * Unfortunately, in order to deal with alpha nibble swapping
+ * (see gsbitops.c), we can't expand the box only to pixel
+ * boundaries:
+ int alpha_mask = -1 << adev->log2_alpha_bits;
+ * Instead, we must expand it to byte boundaries,
+ */
+ int alpha_mask = ~7;
+ gs_int_rect bbox;
+ int width;
+
+ bits_bounding_box(bits, block_height, adev->raster, &bbox);
+ bbox.p.x &= alpha_mask;
+ bbox.q.x = (bbox.q.x + ~alpha_mask) & alpha_mask;
+ width = bbox.q.x - bbox.p.x;
+ bits_compress_scaled(bits, bbox.p.x, width, block_height,
+ adev->raster, bits, draster, &adev->log2_scale,
+ adev->log2_alpha_bits);
+ return (*dev_proc(target, copy_alpha))(target,
+ bits, 0, draster, gx_no_bitmap_id,
+ (adev->mapped_x + bbox.p.x) >>
+ adev->log2_scale.x,
+ y >> adev->log2_scale.y,
+ width >> adev->log2_scale.x, 1,
+ adev->save_color, alpha_bits);
+ }
+}
+/* Flush the entire buffer. */
+private int
+abuf_flush(gx_device_memory *adev)
+{ int y, code = 0;
+ int block_height = 1 << adev->log2_scale.y;
+ for ( y = 0; y < adev->mapped_height; y += block_height )
+ if ( (code = abuf_flush_block(adev, adev->mapped_y + y)) < 0 )
+ return code;
+ adev->mapped_height = adev->mapped_start = 0;
+ return 0;
+}
+
+/* Close the device, flushing the buffer. */
+private int
+mem_abuf_close(gx_device *dev)
+{ int code = abuf_flush(mdev);
+ if ( code < 0 )
+ return code;
+ return mem_close(dev);
+}
+
+/*
+ * Framework for mapping a requested imaging operation to the buffer.
+ * For now, we assume top-to-bottom transfers and use a very simple algorithm.
+ */
+typedef struct y_transfer_s {
+ int y_next;
+ int height_left;
+ int transfer_y;
+ int transfer_height;
+} y_transfer;
+private void near
+y_transfer_init(y_transfer *pyt, gx_device *dev, int ty, int th)
+{ int bh = 1 << mdev->log2_scale.y;
+ if ( ty < mdev->mapped_y || ty > mdev->mapped_y + mdev->mapped_height )
+ { abuf_flush(mdev);
+ mdev->mapped_y = ty & -bh;
+ mdev->mapped_height = bh;
+ memset(scan_line_base(mdev, 0), 0, bh * mdev->raster);
+ }
+ pyt->y_next = ty;
+ pyt->height_left = th;
+ pyt->transfer_height = 0;
+}
+/* while ( yt.height_left > 0 ) { y_transfer_next(&yt, mdev); ... } */
+private void near
+y_transfer_next(y_transfer *pyt, gx_device *dev)
+{ int my = mdev->mapped_y, mh = mdev->mapped_height;
+ int ms = mdev->mapped_start;
+ int ty = pyt->y_next += pyt->transfer_height;
+ int th = pyt->height_left;
+ int bh = 1 << mdev->log2_scale.y;
+ /* From here on, we know that my <= ty <= my + mh. */
+ int tby, tbh;
+ if ( ty == my + mh )
+ { /* Add a new block at my1. */
+ if ( mh == mdev->height )
+ { abuf_flush_block(mdev, my);
+ mdev->mapped_y = my += bh;
+ if ( (mdev->mapped_start = ms += bh) == mh )
+ mdev->mapped_start = ms = 0;
+ }
+ else
+ { /* Because we currently never extend backwards, */
+ /* we know we can't wrap around in this case. */
+ mdev->mapped_height = mh += bh;
+ }
+ memset(scan_line_base(mdev, (ms == 0 ? mh : ms) - bh),
+ 0, bh * mdev->raster);
+ }
+ /* Now we know that my <= ty < my + mh. */
+ tby = ty - my + ms;
+ if ( tby < mdev->height )
+ { tbh = mdev->height - ms;
+ if ( tbh > mh ) tbh = mh;
+ tbh -= tby - ms;
+ }
+ else /* wrap around */
+ { tby -= mdev->height;
+ tbh = ms + mh - dev->height - tby;
+ }
+ if_debug7('v', "[v]my=%d, mh=%d, ms=%d, ty=%d, th=%d, tby=%d, tbh=%d\n",
+ my, mh, ms, ty, th, tby, tbh);
+ if ( tbh > th ) tbh = th;
+ pyt->height_left = th - tbh;
+ pyt->transfer_y = tby;
+ pyt->transfer_height = tbh;
+}
+
+/* Copy a monobit image. */
+private int
+mem_abuf_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 zero, gx_color_index one)
+{ y_transfer yt;
+ if ( zero != gx_no_color_index || one == gx_no_color_index )
+ return_error(gs_error_undefinedresult);
+ x -= mdev->mapped_x;
+ fit_copy_xwh(dev, base, sourcex, sraster, id, x, y, w, h); /* don't limit y */
+ mdev->save_color = one;
+ y_transfer_init(&yt, dev, y, h);
+ while ( yt.height_left > 0 )
+ { y_transfer_next(&yt, dev);
+ (*dev_proc(&mem_mono_device, copy_mono))(dev,
+ base + (yt.y_next - y) * sraster,
+ sourcex, sraster, gx_no_bitmap_id,
+ x, yt.transfer_y, w, yt.transfer_height,
+ gx_no_color_index, (gx_color_index)1);
+ }
+ return 0;
+}
+
+/* Fill a rectangle. */
+private int
+mem_abuf_fill_rectangle(gx_device *dev, int x, int y, int w, int h,
+ gx_color_index color)
+{ y_transfer yt;
+ x -= mdev->mapped_x;
+ fit_fill_xyw(dev, x, y, w, h); /* don't limit h */
+ /* or check w <= 0, h <= 0 */
+ mdev->save_color = color;
+ y_transfer_init(&yt, dev, y, h);
+ while ( yt.height_left > 0 )
+ { y_transfer_next(&yt, dev);
+ (*dev_proc(&mem_mono_device, fill_rectangle))(dev,
+ x, yt.transfer_y, w, yt.transfer_height,
+ (gx_color_index)1);
+ }
+ return 0;
+}
diff --git a/pstoraster/gdevcups.c b/pstoraster/gdevcups.c
new file mode 100644
index 000000000..1d4b8f202
--- /dev/null
+++ b/pstoraster/gdevcups.c
@@ -0,0 +1,2459 @@
+/*
+ * "$Id$"
+ *
+ * GNU Ghostscript raster output driver for the Common UNIX Printing
+ * System (CUPS).
+ *
+ * Copyright 1993-1999 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * This code and any derivative of it may be used and distributed
+ * freely under the terms of the GNU General Public License when
+ * used with GNU Ghostscript or its derivatives. Use of the code
+ * (or any derivative of it) with software other than GNU
+ * GhostScript (or its derivatives) is governed by the CUPS license
+ * agreement.
+ *
+ * Contents:
+ *
+ * cups_close() - Close the output file.
+ * cups_get_matrix() - Generate the default page matrix.
+ * cups_get_params() - Get pagedevice parameters.
+ * cups_map_color_rgb() - Map a color index to an RGB color.
+ * cups_map_rgb_color() - Map an RGB color to a color index. We map the
+ * RGB color to the output colorspace & bits (we
+ * figure out the format when we output a page).
+ * cups_open() - Open the output file and initialize things.
+ * cups_print_pages() - Send one or more pages to the output file.
+ * cups_put_params() - Set pagedevice parameters.
+ * cups_set_color_info() - Set the color information structure based on
+ * the required output.
+ * cups_print_chunked() - Print a page of chunked pixels.
+ * cups_print_banded() - Print a page of banded pixels.
+ * cups_print_planar() - Print a page of planar pixels.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "std.h" /* to stop stdlib.h redefining types */
+#include "gdevprn.h"
+#include "gsparam.h"
+
+#include <stdlib.h>
+#include <cups/raster.h>
+#include <cups/ppd.h>
+#include <math.h>
+
+
+/*
+ * Macros...
+ */
+
+#define x_dpi (pdev->HWResolution[0])
+#define y_dpi (pdev->HWResolution[1])
+#define cups ((gx_device_cups *)pdev)
+
+/*
+ * Macros from <macros.h>; we can't include <macros.h> because it also
+ * defines DEBUG, one of our flags to insert various debugging code.
+ */
+
+#ifndef max
+# define max(a,b) ((a)<(b) ? (b) : (a))
+#endif /* !max */
+
+#ifndef min
+# define min(a,b) ((a)>(b) ? (b) : (a))
+#endif /* !min */
+
+#ifndef abs
+# define abs(x) ((x)>=0 ? (x) : -(x))
+#endif /* !abs */
+
+
+/*
+ * Procedures
+ */
+
+private dev_proc_close_device(cups_close);
+private dev_proc_get_initial_matrix(cups_get_matrix);
+private int cups_get_params(gx_device *, gs_param_list *);
+private dev_proc_map_color_rgb(cups_map_color_rgb);
+private dev_proc_map_cmyk_color(cups_map_cmyk_color);
+private dev_proc_map_rgb_color(cups_map_rgb_color);
+private dev_proc_open_device(cups_open);
+private int cups_print_pages(gx_device_printer *, FILE *, int);
+private int cups_put_params(gx_device *, gs_param_list *);
+private void cups_set_color_info(gx_device *);
+private dev_proc_sync_output(cups_sync_output);
+
+/*
+ * The device descriptors...
+ */
+
+typedef struct gx_device_cups_s
+{
+ gx_device_common; /* Standard GhostScript device stuff */
+ gx_prn_device_common; /* Standard printer device stuff */
+ int page; /* Page number */
+ cups_raster_t *stream; /* Raster stream */
+ ppd_file_t *ppd; /* PPD file for this printer */
+ cups_page_header_t header; /* PostScript page device info */
+} gx_device_cups;
+
+private gx_device_procs cups_procs =
+{
+ cups_open,
+ cups_get_matrix,
+ cups_sync_output,
+ gdev_prn_output_page,
+ cups_close,
+ cups_map_rgb_color,
+ cups_map_color_rgb,
+ NULL, /* fill_rectangle */
+ NULL, /* tile_rectangle */
+ NULL, /* copy_mono */
+ NULL, /* copy_color */
+ NULL, /* draw_line */
+ gx_default_get_bits,
+ cups_get_params,
+ cups_put_params,
+ NULL,
+ 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 */
+};
+
+gx_device_cups gs_cups_device =
+{
+ prn_device_body_copies(gx_device_cups, cups_procs, "cups", 85, 110, 100, 100,
+ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, cups_print_pages),
+ 0, /* page */
+ NULL, /* stream */
+ NULL, /* ppd */
+ { /* header */
+ "", /* MediaClass */
+ "", /* MediaColor */
+ "", /* MediaType */
+ "", /* OutputType */
+ 0, /* AdvanceDistance */
+ CUPS_ADVANCE_NONE, /* AdvanceMedia */
+ CUPS_FALSE, /* Collate */
+ CUPS_CUT_NONE, /* CutMedia */
+ CUPS_FALSE, /* Duplex */
+ { 100, 100 }, /* HWResolution */
+ { 0, 0, 612, 792 }, /* ImagingBoundingBox */
+ CUPS_FALSE, /* InsertSheet */
+ CUPS_JOG_NONE, /* Jog */
+ CUPS_EDGE_TOP, /* LeadingEdge */
+ { 0, 0 }, /* Margins */
+ CUPS_FALSE, /* ManualFeed */
+ 0, /* MediaPosition */
+ 0, /* MediaWeight */
+ CUPS_FALSE, /* MirrorPrint */
+ CUPS_FALSE, /* NegativePrint */
+ 1, /* NumCopies */
+ CUPS_ORIENT_0, /* Orientation */
+ CUPS_FALSE, /* OutputFaceUp */
+ { 612, 792 }, /* PageSize */
+ CUPS_FALSE, /* Separations */
+ CUPS_FALSE, /* TraySwitch */
+ CUPS_FALSE, /* Tumble */
+ 850, /* cupsWidth */
+ 1100, /* cupsHeight */
+ 0, /* cupsMediaType */
+ 1, /* cupsBitsPerColor */
+ 1, /* cupsBitsPerPixel */
+ 107, /* cupsBytesPerLine */
+ CUPS_ORDER_CHUNKED, /* cupsColorOrder */
+ CUPS_CSPACE_K, /* cupsColorSpace */
+ 0, /* cupsCompression */
+ 0, /* cupsRowCount */
+ 0, /* cupsRowFeed */
+ 0 /* cupsRowStep */
+ }
+};
+
+/*
+ * Color lookup tables...
+ */
+
+static gx_color_value lut_color_rgb[256];
+static unsigned char lut_rgb_color[gx_max_color_value + 1];
+static int cupsHaveProfile = 0;
+static int cupsMatrix[3][3][gx_max_color_value + 1];
+static int cupsDensity[gx_max_color_value + 1];
+
+
+/*
+ * Local functions...
+ */
+
+static void cups_print_chunked(gx_device_printer *, unsigned char *);
+static void cups_print_banded(gx_device_printer *, unsigned char *,
+ unsigned char *, int);
+static void cups_print_planar(gx_device_printer *, unsigned char *,
+ unsigned char *, int);
+
+/*static void cups_set_margins(gx_device *);*/
+
+
+/*
+ * 'cups_close()' - Close the output file.
+ */
+
+private int
+cups_close(gx_device *pdev) /* I - Device info */
+{
+#ifdef DEBUG
+ fprintf(stderr, "DEBUG: cups_close(%08x)\n", pdev);
+#endif /* DEBUG */
+
+ if (cups->stream != NULL)
+ {
+ cupsRasterClose(cups->stream);
+ cups->stream = NULL;
+ }
+
+#if 0 /* Can't do this here because put_params() might close the device */
+ if (cups->ppd != NULL)
+ {
+ ppdClose(cups->ppd);
+ cups->ppd = NULL;
+ }
+#endif /* 0 */
+
+ return (gdev_prn_close(pdev));
+}
+
+
+/*
+ * 'cups_get_matrix()' - Generate the default page matrix.
+ */
+
+private void
+cups_get_matrix(gx_device *pdev, /* I - Device info */
+ gs_matrix *pmat) /* O - Physical transform matrix */
+{
+#ifdef DEBUG
+ fprintf(stderr, "DEBUG: cups_get_matrix(%08x, %08x)\n", pdev, pmat);
+#endif /* DEBUG */
+
+ /*
+ * Set the raster width and height...
+ */
+
+ cups->header.cupsWidth = cups->width;
+ cups->header.cupsHeight = cups->height;
+
+ /*
+ * Set the transform matrix...
+ */
+
+ pmat->xx = (float)cups->header.HWResolution[0] / 72.0;
+ pmat->xy = 0.0;
+ pmat->yx = 0.0;
+ pmat->yy = -(float)cups->header.HWResolution[1] / 72.0;
+ pmat->tx = -(float)cups->header.HWResolution[0] * pdev->HWMargins[0] / 72.0;
+ pmat->ty = (float)cups->header.HWResolution[1] *
+ ((float)cups->header.PageSize[1] - pdev->HWMargins[3]) / 72.0;
+
+#ifdef DEBUG
+ fprintf(stderr, "DEBUG: width = %d, height = %d\n", cups->width,
+ cups->height);
+ fprintf(stderr, "DEBUG: PageSize = [ %d %d ], HWResolution = [ %d %d ]\n",
+ cups->header.PageSize[0], cups->header.PageSize[1],
+ cups->header.HWResolution[0], cups->header.HWResolution[1]);
+ fprintf(stderr, "DEBUG: matrix = [ %.3f %.3f %.3f %.3f %.3f %.3f ]\n",
+ pmat->xx, pmat->xy, pmat->yx, pmat->yy, pmat->tx, pmat->ty);
+#endif /* DEBUG */
+}
+
+
+/*
+ * 'cups_get_params()' - Get pagedevice parameters.
+ */
+
+private int /* O - Error status */
+cups_get_params(gx_device *pdev, /* I - Device info */
+ gs_param_list *plist) /* I - Parameter list */
+{
+ int code; /* Return code */
+
+
+#ifdef DEBUG
+ fprintf(stderr, "DEBUG: cups_get_params(%08x, %08x)\n", pdev, plist);
+#endif /* DEBUG */
+
+ /*
+ * First process the "standard" page device parameters...
+ */
+
+ if ((code = gdev_prn_get_params(pdev, plist)) < 0)
+ return (code);
+
+ /*
+ * Then write the CUPS-specific parameters...
+ */
+
+ if ((code = param_write_int(plist, "cupsWidth",
+ (int *)&(cups->header.cupsWidth))) < 0)
+ return (code);
+
+ if ((code = param_write_int(plist, "cupsHeight",
+ (int *)&(cups->header.cupsHeight))) < 0)
+ return (code);
+
+ if ((code = param_write_int(plist, "cupsMediaType",
+ (int *)&(cups->header.cupsMediaType))) < 0)
+ return (code);
+
+ if ((code = param_write_int(plist, "cupsBitsPerColor",
+ (int *)&(cups->header.cupsBitsPerColor))) < 0)
+ return (code);
+
+ if ((code = param_write_int(plist, "cupsBitsPerPixel",
+ (int *)&(cups->header.cupsBitsPerPixel))) < 0)
+ return (code);
+
+ if ((code = param_write_int(plist, "cupsBytesPerLine",
+ (int *)&(cups->header.cupsBytesPerLine))) < 0)
+ return (code);
+
+ if ((code = param_write_int(plist, "cupsColorOrder",
+ (int *)&(cups->header.cupsColorOrder))) < 0)
+ return (code);
+
+ if ((code = param_write_int(plist, "cupsColorSpace",
+ (int *)&(cups->header.cupsColorSpace))) < 0)
+ return (code);
+
+ if ((code = param_write_int(plist, "cupsCompression",
+ (int *)&(cups->header.cupsCompression))) < 0)
+ return (code);
+
+ if ((code = param_write_int(plist, "cupsRowCount",
+ (int *)&(cups->header.cupsRowCount))) < 0)
+ return (code);
+
+ if ((code = param_write_int(plist, "cupsRowFeed",
+ (int *)&(cups->header.cupsRowFeed))) < 0)
+ return (code);
+
+ if ((code = param_write_int(plist, "cupsRowStep",
+ (int *)&(cups->header.cupsRowStep))) < 0)
+ return (code);
+
+ return (0);
+}
+
+
+/*
+ * 'cups_map_color_rgb()' - Map a color index to an RGB color.
+ */
+
+private int
+cups_map_color_rgb(gx_device *pdev, /* I - Device info */
+ gx_color_index color, /* I - Color index */
+ gx_color_value prgb[3]) /* O - RGB values */
+{
+ unsigned char c0, c1, c2, c3; /* Color index components */
+ gx_color_value k, divk; /* Black & divisor */
+
+
+#ifdef DEBUG
+ fprintf(stderr, "DEBUG: cups_map_color_rgb(%08x, %d, %08x)\n", pdev,
+ color, prgb);
+#endif /* DEBUG */
+
+ /*
+ * Setup the color info data as needed...
+ */
+
+ if (pdev->color_info.num_components == 0)
+ cups_set_color_info(pdev);
+
+#ifdef DEBUG
+ fprintf(stderr, "DEBUG: COLOR %08x = ", color);
+#endif /* DEBUG */
+
+ /*
+ * Extract the color components from the color index...
+ */
+
+ switch (cups->header.cupsBitsPerColor)
+ {
+ case 1 :
+ c3 = color & 1;
+ color >>= 1;
+ c2 = color & 1;
+ color >>= 1;
+ c1 = color & 1;
+ color >>= 1;
+ c0 = color;
+ break;
+ case 2 :
+ c3 = color & 3;
+ color >>= 2;
+ c2 = color & 3;
+ color >>= 2;
+ c2 = color & 3;
+ color >>= 2;
+ c0 = color;
+ break;
+ case 4 :
+ c3 = color & 15;
+ color >>= 4;
+ c2 = color & 15;
+ color >>= 4;
+ c1 = color & 15;
+ color >>= 4;
+ c0 = color;
+ break;
+ case 8 :
+ c3 = color & 255;
+ color >>= 8;
+ c2 = color & 255;
+ color >>= 8;
+ c1 = color & 255;
+ color >>= 8;
+ c0 = color;
+ break;
+ }
+
+ /*
+ * Convert the color components to RGB...
+ */
+
+ switch (cups->header.cupsColorSpace)
+ {
+ case CUPS_CSPACE_K :
+ case CUPS_CSPACE_WHITE :
+ case CUPS_CSPACE_GOLD :
+ case CUPS_CSPACE_SILVER :
+ prgb[0] =
+ prgb[1] =
+ prgb[2] = lut_color_rgb[c3];
+ break;
+
+ case CUPS_CSPACE_W :
+ prgb[0] =
+ prgb[1] =
+ prgb[2] = lut_color_rgb[c3];
+ break;
+
+ case CUPS_CSPACE_RGB :
+ prgb[0] = lut_color_rgb[c1];
+ prgb[1] = lut_color_rgb[c2];
+ prgb[2] = lut_color_rgb[c3];
+ break;
+
+ case CUPS_CSPACE_CMY :
+ prgb[0] = lut_color_rgb[c1];
+ prgb[1] = lut_color_rgb[c2];
+ prgb[2] = lut_color_rgb[c3];
+ break;
+
+ case CUPS_CSPACE_YMC :
+ prgb[0] = lut_color_rgb[c3];
+ prgb[1] = lut_color_rgb[c2];
+ prgb[2] = lut_color_rgb[c1];
+ break;
+
+ case CUPS_CSPACE_KCMY :
+ k = lut_color_rgb[c0];
+ divk = gx_max_color_value - k;
+ if (divk == 0)
+ {
+ prgb[0] = 0;
+ prgb[1] = 0;
+ prgb[2] = 0;
+ }
+ else
+ {
+ prgb[0] = gx_max_color_value + divk -
+ gx_max_color_value * c1 / divk;
+ prgb[1] = gx_max_color_value + divk -
+ gx_max_color_value * c2 / divk;
+ prgb[2] = gx_max_color_value + divk -
+ gx_max_color_value * c3 / divk;
+ }
+ break;
+
+ case CUPS_CSPACE_CMYK :
+ k = lut_color_rgb[c3];
+ divk = gx_max_color_value - k;
+ if (divk == 0)
+ {
+ prgb[0] = 0;
+ prgb[1] = 0;
+ prgb[2] = 0;
+ }
+ else
+ {
+ prgb[0] = gx_max_color_value + divk -
+ gx_max_color_value * c0 / divk;
+ prgb[1] = gx_max_color_value + divk -
+ gx_max_color_value * c1 / divk;
+ prgb[2] = gx_max_color_value + divk -
+ gx_max_color_value * c2 / divk;
+ }
+ break;
+
+ case CUPS_CSPACE_YMCK :
+ case CUPS_CSPACE_GMCK :
+ case CUPS_CSPACE_GMCS :
+ k = lut_color_rgb[c3];
+ divk = gx_max_color_value - k;
+ if (divk == 0)
+ {
+ prgb[0] = 0;
+ prgb[1] = 0;
+ prgb[2] = 0;
+ }
+ else
+ {
+ prgb[0] = gx_max_color_value + divk -
+ gx_max_color_value * c2 / divk;
+ prgb[1] = gx_max_color_value + divk -
+ gx_max_color_value * c1 / divk;
+ prgb[2] = gx_max_color_value + divk -
+ gx_max_color_value * c0 / divk;
+ }
+ break;
+ }
+
+#ifdef DEBUG
+ fprintf(stderr, "%d,%d,%d\n", prgb[0], prgb[1], prgb[2]);
+#endif /* DEBUG */
+
+ return (0);
+}
+
+
+/*
+ * 'cups_map_rgb_color()' - Map an RGB color to a color index. We map the
+ * RGB color to the output colorspace & bits (we
+ * figure out the format when we output a page).
+ */
+
+private gx_color_index /* O - Color index */
+cups_map_rgb_color(gx_device *pdev, /* I - Device info */
+ gx_color_value r, /* I - Red value */
+ gx_color_value g, /* I - Green value */
+ gx_color_value b) /* I - Blue value */
+{
+ gx_color_index i; /* Temporary index */
+ gx_color_value ic, im, iy, ik; /* Integral CMYKcm values */
+ float divk, /* Black "divisor" */
+ diff; /* Average color difference */
+ int tc, tm, ty, tk; /* Temporary color values */
+
+
+#ifdef DEBUG
+ fprintf(stderr, "DEBUG: cups_map_rgb_color(%08x, %d, %d, %d)\n", pdev, r, g, b);
+#endif /* DEBUG */
+
+ /*
+ * Setup the color info data as needed...
+ */
+
+ if (pdev->color_info.num_components == 0)
+ cups_set_color_info(pdev);
+
+ /*
+ * Do color correction as needed...
+ */
+
+ if (cupsHaveProfile)
+ {
+ /*
+ * Compute CMYK values...
+ */
+
+ ic = gx_max_color_value - r;
+ im = gx_max_color_value - g;
+ iy = gx_max_color_value - b;
+ ik = min(ic, min(im, iy));
+ ic -= ik;
+ im -= ik;
+ iy -= ik;
+
+ /*
+ * Color correct CMY...
+ */
+
+ tc = cupsMatrix[0][0][ic] +
+ cupsMatrix[0][1][im] +
+ cupsMatrix[0][2][iy] +
+ ik;
+ tm = cupsMatrix[1][0][ic] +
+ cupsMatrix[1][1][im] +
+ cupsMatrix[1][2][iy] +
+ ik;
+ ty = cupsMatrix[2][0][ic] +
+ cupsMatrix[2][1][im] +
+ cupsMatrix[2][2][iy] +
+ ik;
+
+ /*
+ * Density correct combined CMYK...
+ */
+
+ if (tc < 0)
+ r = gx_max_color_value;
+ else if (tc > gx_max_color_value)
+ r = gx_max_color_value - cupsDensity[gx_max_color_value];
+ else
+ r = gx_max_color_value - cupsDensity[tc];
+
+ if (tm < 0)
+ g = gx_max_color_value;
+ else if (tm > gx_max_color_value)
+ g = gx_max_color_value - cupsDensity[gx_max_color_value];
+ else
+ g = gx_max_color_value - cupsDensity[tm];
+
+ if (ty < 0)
+ b = gx_max_color_value;
+ else if (ty > gx_max_color_value)
+ b = gx_max_color_value - cupsDensity[gx_max_color_value];
+ else
+ b = gx_max_color_value - cupsDensity[ty];
+ }
+
+ /*
+ * Convert the RGB color to a color index...
+ */
+
+ switch (cups->header.cupsColorSpace)
+ {
+ case CUPS_CSPACE_W :
+ i = lut_rgb_color[(r * 31 + g * 61 + b * 8) / 100];
+ break;
+
+ case CUPS_CSPACE_RGB :
+ ic = lut_rgb_color[r];
+ im = lut_rgb_color[g];
+ iy = lut_rgb_color[b];
+
+ switch (cups->header.cupsBitsPerColor)
+ {
+ case 1 :
+ i = (((ic << 1) | im) << 1) | iy;
+ break;
+ case 2 :
+ i = (((ic << 2) | im) << 2) | iy;
+ break;
+ case 4 :
+ i = (((ic << 4) | im) << 4) | iy;
+ break;
+ case 8 :
+ i = (((ic << 8) | im) << 8) | iy;
+ break;
+ }
+ break;
+
+ case CUPS_CSPACE_K :
+ case CUPS_CSPACE_WHITE :
+ case CUPS_CSPACE_GOLD :
+ case CUPS_CSPACE_SILVER :
+ i = lut_rgb_color[gx_max_color_value - (r * 31 + g * 61 + b * 8) / 100];
+ break;
+
+ case CUPS_CSPACE_CMY :
+ if (cups->header.cupsBitsPerColor == 1)
+ {
+ ic = lut_rgb_color[gx_max_color_value - r];
+ im = lut_rgb_color[gx_max_color_value - g];
+ iy = lut_rgb_color[gx_max_color_value - b];
+ }
+ else
+ {
+ ic = gx_max_color_value - r;
+ im = gx_max_color_value - g;
+ iy = gx_max_color_value - b;
+ ik = min(ic, min(im, iy));
+
+ ic = lut_rgb_color[(gx_color_value)((float)(gx_max_color_value - g / 4) /
+ (float)gx_max_color_value * (float)(ic - ik)) + ik];
+ im = lut_rgb_color[(gx_color_value)((float)(gx_max_color_value - b / 4) /
+ (float)gx_max_color_value * (float)(im - ik)) + ik];
+ iy = lut_rgb_color[(gx_color_value)((float)(gx_max_color_value - r / 4) /
+ (float)gx_max_color_value * (float)(iy - ik)) + ik];
+ }
+
+ switch (cups->header.cupsBitsPerColor)
+ {
+ case 1 :
+ i = (((ic << 1) | im) << 1) | iy;
+ break;
+ case 2 :
+ i = (((ic << 2) | im) << 2) | iy;
+ break;
+ case 4 :
+ i = (((ic << 4) | im) << 4) | iy;
+ break;
+ case 8 :
+ i = (((ic << 8) | im) << 8) | iy;
+ break;
+ }
+ break;
+
+ case CUPS_CSPACE_YMC :
+ if (cups->header.cupsBitsPerColor == 1)
+ {
+ ic = lut_rgb_color[gx_max_color_value - r];
+ im = lut_rgb_color[gx_max_color_value - g];
+ iy = lut_rgb_color[gx_max_color_value - b];
+ }
+ else
+ {
+ ic = gx_max_color_value - r;
+ im = gx_max_color_value - g;
+ iy = gx_max_color_value - b;
+ ik = min(ic, min(im, iy));
+
+ ic = lut_rgb_color[(gx_color_value)((float)(gx_max_color_value - g / 4) /
+ (float)gx_max_color_value * (float)(ic - ik)) + ik];
+ im = lut_rgb_color[(gx_color_value)((float)(gx_max_color_value - b / 4) /
+ (float)gx_max_color_value * (float)(im - ik)) + ik];
+ iy = lut_rgb_color[(gx_color_value)((float)(gx_max_color_value - r / 4) /
+ (float)gx_max_color_value * (float)(iy - ik)) + ik];
+ }
+
+ switch (cups->header.cupsBitsPerColor)
+ {
+ case 1 :
+ i = (((iy << 1) | im) << 1) | ic;
+ break;
+ case 2 :
+ i = (((iy << 2) | im) << 2) | ic;
+ break;
+ case 4 :
+ i = (((iy << 4) | im) << 4) | ic;
+ break;
+ case 8 :
+ i = (((iy << 8) | im) << 8) | ic;
+ break;
+ }
+ break;
+
+ case CUPS_CSPACE_CMYK :
+ ic = gx_max_color_value - r;
+ im = gx_max_color_value - g;
+ iy = gx_max_color_value - b;
+ ik = min(ic, min(im, iy));
+
+ if (ik > 0)
+ {
+ diff = 1.0 - (float)(max(ic, max(im, iy)) - ik) /
+ (float)gx_max_color_value;
+ ik = (int)(diff * (float)ik);
+ }
+
+ if (ik == gx_max_color_value)
+ {
+ ik = lut_rgb_color[ik];
+ ic = 0;
+ im = 0;
+ iy = 0;
+ }
+ else if (cups->header.cupsBitsPerColor == 1)
+ {
+ ic = lut_rgb_color[ic - ik];
+ im = lut_rgb_color[im - ik];
+ iy = lut_rgb_color[iy - ik];
+ ik = lut_rgb_color[ik];
+ }
+ else
+ {
+ divk = (float)gx_max_color_value / (float)(gx_max_color_value - ik);
+ tc = (float)(ic - ik) * divk;
+ tm = (float)(im - ik) * divk;
+ ty = (float)(iy - ik) * divk;
+
+ if (tc >= gx_max_color_value)
+ ic = lut_rgb_color[gx_max_color_value];
+ else
+ ic = lut_rgb_color[tc];
+
+ if (tm >= gx_max_color_value)
+ im = lut_rgb_color[gx_max_color_value];
+ else
+ im = lut_rgb_color[tm];
+
+ if (ty >= gx_max_color_value)
+ iy = lut_rgb_color[gx_max_color_value];
+ else
+ iy = lut_rgb_color[ty];
+
+ ik = lut_rgb_color[ik];
+ }
+
+ switch (cups->header.cupsBitsPerColor)
+ {
+ case 1 :
+ i = (((((ic << 1) | im) << 1) | iy) << 1) | ik;
+ break;
+ case 2 :
+ i = (((((ic << 2) | im) << 2) | iy) << 2) | ik;
+ break;
+ case 4 :
+ i = (((((ic << 4) | im) << 4) | iy) << 4) | ik;
+ break;
+ case 8 :
+ i = (((((ic << 8) | im) << 8) | iy) << 8) | ik;
+ break;
+ }
+
+ if (gs_log_errors > 1)
+ fprintf(stderr, "DEBUG: CMY (%d,%d,%d) -> CMYK %08.8x (%d,%d,%d,%d)\n",
+ r, g, b, i, ic, im, iy, ik);
+ break;
+
+ case CUPS_CSPACE_YMCK :
+ case CUPS_CSPACE_GMCK :
+ case CUPS_CSPACE_GMCS :
+ ic = gx_max_color_value - r;
+ im = gx_max_color_value - g;
+ iy = gx_max_color_value - b;
+ ik = min(ic, min(im, iy));
+
+ if (ik > 0)
+ {
+ diff = 1.0 - (float)(max(ic, max(im, iy)) - ik) /
+ (float)gx_max_color_value;
+ ik = (int)(diff * (float)ik);
+ }
+
+ if (ik == gx_max_color_value)
+ {
+ ik = lut_rgb_color[ik];
+ ic = 0;
+ im = 0;
+ iy = 0;
+ }
+ else if (cups->header.cupsBitsPerColor == 1)
+ {
+ ic = lut_rgb_color[ic - ik];
+ im = lut_rgb_color[im - ik];
+ iy = lut_rgb_color[iy - ik];
+ ik = lut_rgb_color[ik];
+ }
+ else
+ {
+ divk = (float)gx_max_color_value / (float)(gx_max_color_value - ik);
+ tc = (float)(ic - ik) * divk;
+ tm = (float)(im - ik) * divk;
+ ty = (float)(iy - ik) * divk;
+
+ if (tc >= gx_max_color_value)
+ ic = lut_rgb_color[gx_max_color_value];
+ else
+ ic = lut_rgb_color[tc];
+
+ if (tm >= gx_max_color_value)
+ im = lut_rgb_color[gx_max_color_value];
+ else
+ im = lut_rgb_color[tm];
+
+ if (ty >= gx_max_color_value)
+ iy = lut_rgb_color[gx_max_color_value];
+ else
+ iy = lut_rgb_color[ty];
+
+ ik = lut_rgb_color[ik];
+ }
+
+ switch (cups->header.cupsBitsPerColor)
+ {
+ case 1 :
+ i = (((((iy << 1) | im) << 1) | ic) << 1) | ik;
+ break;
+ case 2 :
+ i = (((((iy << 2) | im) << 2) | ic) << 2) | ik;
+ break;
+ case 4 :
+ i = (((((iy << 4) | im) << 4) | ic) << 4) | ik;
+ break;
+ case 8 :
+ i = (((((iy << 8) | im) << 8) | ic) << 8) | ik;
+ break;
+ }
+ break;
+
+ case CUPS_CSPACE_KCMYcm :
+ if (cups->header.cupsBitsPerColor == 1)
+ {
+ ic = gx_max_color_value - r;
+ im = gx_max_color_value - g;
+ iy = gx_max_color_value - b;
+ ik = min(ic, min(im, iy));
+
+ if (ik > 0)
+ {
+ diff = 1.0 - (float)(max(ic, max(im, iy)) - ik) / (float)ik;
+ ik = (int)(diff * (float)ik);
+ }
+
+ ic = lut_rgb_color[ic - ik];
+ im = lut_rgb_color[im - ik];
+ iy = lut_rgb_color[iy - ik];
+ ik = lut_rgb_color[ik];
+ if (ik)
+ i = 32;
+ else if (ic && im)
+ i = 17;
+ else if (ic && iy)
+ i = 6;
+ else if (im && iy)
+ i = 12;
+ else if (ic)
+ i = 16;
+ else if (im)
+ i = 8;
+ else if (iy)
+ i = 4;
+ else
+ i = 0;
+ break;
+ }
+
+ case CUPS_CSPACE_KCMY :
+ ic = gx_max_color_value - r;
+ im = gx_max_color_value - g;
+ iy = gx_max_color_value - b;
+ ik = min(ic, min(im, iy));
+
+ if (ik > 0)
+ {
+ diff = 1.0 - (float)(max(ic, max(im, iy)) - ik) /
+ (float)gx_max_color_value;
+ ik = (int)(diff * (float)ik);
+ }
+
+ if (ik == gx_max_color_value)
+ {
+ ik = lut_rgb_color[ik];
+ ic = 0;
+ im = 0;
+ iy = 0;
+ }
+ else if (cups->header.cupsBitsPerColor == 1)
+ {
+ ic = lut_rgb_color[ic - ik];
+ im = lut_rgb_color[im - ik];
+ iy = lut_rgb_color[iy - ik];
+ ik = lut_rgb_color[ik];
+ }
+ else
+ {
+ divk = (float)gx_max_color_value / (float)(gx_max_color_value - ik);
+ tc = (float)(ic - ik) * divk;
+ tm = (float)(im - ik) * divk;
+ ty = (float)(iy - ik) * divk;
+
+ if (tc >= gx_max_color_value)
+ ic = lut_rgb_color[gx_max_color_value];
+ else
+ ic = lut_rgb_color[tc];
+
+ if (tm >= gx_max_color_value)
+ im = lut_rgb_color[gx_max_color_value];
+ else
+ im = lut_rgb_color[tm];
+
+ if (ty >= gx_max_color_value)
+ iy = lut_rgb_color[gx_max_color_value];
+ else
+ iy = lut_rgb_color[ty];
+
+ ik = lut_rgb_color[ik];
+ }
+
+ switch (cups->header.cupsBitsPerColor)
+ {
+ case 1 :
+ i = (((((ik << 1) | ic) << 1) | im) << 1) | iy;
+ break;
+ case 2 :
+ i = (((((ik << 2) | ic) << 2) | im) << 2) | iy;
+ break;
+ case 4 :
+ i = (((((ik << 4) | ic) << 4) | im) << 4) | iy;
+ break;
+ case 8 :
+ i = (((((ik << 8) | ic) << 8) | im) << 8) | iy;
+ break;
+ }
+ break;
+ }
+
+#ifdef DEBUG
+ fprintf(stderr, "DEBUG: RGB %d,%d,%d = %08x\n", r, g, b, i);
+#endif /* DEBUG */
+
+ return (i);
+}
+
+
+/*
+ * 'cups_open()' - Open the output file and initialize things.
+ */
+
+private int /* O - Error status */
+cups_open(gx_device *pdev) /* I - Device info */
+{
+ int code; /* Return status */
+
+
+#ifdef DEBUG
+ fprintf(stderr, "DEBUG: cups_open(%08x)\n", pdev);
+#endif /* DEBUG */
+
+ if (cups->page == 0)
+ {
+ fputs("INFO: Processing page 1...\n", stderr);
+ cups->page = 1;
+ }
+
+ if (pdev->color_info.num_components == 0)
+ cups_set_color_info(pdev);
+
+ if ((code = gdev_prn_open(pdev)) != 0)
+ return (code);
+
+ if (cups->ppd == NULL)
+ cups->ppd = ppdOpenFile(getenv("PPD"));
+
+ return (0);
+}
+
+
+/*
+ * 'cups_print_pages()' - Send one or more pages to the output file.
+ */
+
+private int /* O - 0 if everything is OK */
+cups_print_pages(gx_device_printer *pdev, /* I - Device info */
+ FILE *fp, /* I - Output file */
+ int num_copies) /* I - Number of copies */
+{
+ int copy; /* Copy number */
+ int srcbytes; /* Byte width of scanline */
+ int x, y; /* Current position in image */
+ unsigned char *src, /* Scanline data */
+ *dst; /* Bitmap data */
+
+
+#ifdef DEBUG
+ fprintf(stderr, "DEBUG: cups_print_pages(%08x, %08x, %d)\n", pdev, fp,
+ num_copies);
+#endif /* DEBUG */
+
+ /*
+ * Figure out the number of bytes per line...
+ */
+
+ switch (cups->header.cupsColorOrder)
+ {
+ case CUPS_ORDER_CHUNKED :
+ cups->header.cupsBytesPerLine = (cups->header.cupsBitsPerPixel *
+ cups->header.cupsWidth + 7) / 8;
+ break;
+
+ case CUPS_ORDER_BANDED :
+ cups->header.cupsBytesPerLine = (cups->header.cupsBitsPerPixel *
+ cups->header.cupsWidth + 7) / 8 *
+ cups->color_info.num_components;
+ break;
+
+ case CUPS_ORDER_PLANAR :
+ cups->header.cupsBytesPerLine = (cups->header.cupsBitsPerPixel *
+ cups->header.cupsWidth + 7) / 8;
+ break;
+ }
+
+ /*
+ * Compute the width of a scanline and allocate input/output buffers...
+ */
+
+ srcbytes = gdev_prn_raster(pdev);
+
+#ifdef DEBUG
+ fprintf(stderr, "DEBUG: cupsBitsPerPixel = %d, cupsWidth = %d, cupsBytesPerLine = %d, srcbytes = %d\n",
+ cups->header.cupsBitsPerPixel, cups->header.cupsWidth,
+ cups->header.cupsBytesPerLine, srcbytes);
+#endif /* DEBUG */
+
+ src = (unsigned char *)gs_malloc(srcbytes, 1, "cups_print_pages");
+
+ if (src == NULL) /* can't allocate input buffer */
+ return_error(gs_error_VMerror);
+
+ if (cups->header.cupsColorOrder != CUPS_ORDER_CHUNKED)
+ {
+ /*
+ * Need an output buffer, too...
+ */
+
+ dst = (unsigned char *)gs_malloc(cups->header.cupsBytesPerLine, 1,
+ "cups_print_pages");
+
+ if (dst == NULL) /* can't allocate working area */
+ return_error(gs_error_VMerror);
+ }
+ else
+ dst = NULL;
+
+ /*
+ * See if the stream has been initialized yet...
+ */
+
+ if (cups->stream == NULL)
+ {
+ if (fp == NULL)
+ cups->stream = cupsRasterOpen(1, CUPS_RASTER_WRITE);
+ else
+ cups->stream = cupsRasterOpen(fileno(fp), CUPS_RASTER_WRITE);
+
+ if (cups->stream == NULL)
+ {
+ perror("ERROR: Unable to open raster stream - ");
+ gs_exit(0);
+ }
+ }
+
+ /*
+ * Output a page of graphics...
+ */
+
+ if (num_copies < 1)
+ num_copies = 1;
+
+ if (cups->ppd != NULL && !cups->ppd->manual_copies)
+ {
+ cups->header.NumCopies = num_copies;
+ num_copies = 1;
+ }
+
+#ifdef DEBUG
+ fprintf(stderr, "DEBUG: cupsWidth = %d, cupsHeight = %d, cupsBytesPerLine = %d\n",
+ cups->header.cupsWidth, cups->header.cupsHeight,
+ cups->header.cupsBytesPerLine);
+#endif /* DEBUG */
+
+ for (copy = num_copies; copy > 0; copy --)
+ {
+ cupsRasterWriteHeader(cups->stream, &(cups->header));
+
+ if (pdev->color_info.num_components == 1)
+ cups_print_chunked(pdev, src);
+ else
+ switch (cups->header.cupsColorOrder)
+ {
+ case CUPS_ORDER_CHUNKED :
+ cups_print_chunked(pdev, src);
+ break;
+ case CUPS_ORDER_BANDED :
+ cups_print_banded(pdev, src, dst, srcbytes);
+ break;
+ case CUPS_ORDER_PLANAR :
+ cups_print_planar(pdev, src, dst, srcbytes);
+ break;
+ }
+ }
+
+ /*
+ * Free temporary storage and return...
+ */
+
+ gs_free((char *)src, srcbytes, 1, "cups_print_pages");
+ if (dst)
+ gs_free((char *)dst, cups->header.cupsBytesPerLine, 1, "cups_print_pages");
+
+ cups->page ++;
+ fprintf(stderr, "INFO: Processing page %d...\n", cups->page);
+
+ return (0);
+}
+
+
+/*
+ * 'cups_put_params()' - Set pagedevice parameters.
+ */
+
+private int /* O - Error status */
+cups_put_params(gx_device *pdev, /* I - Device info */
+ gs_param_list *plist) /* I - Parameter list */
+{
+ int i; /* Looping var */
+ float margins[4]; /* Physical margins of print */
+ ppd_size_t *size; /* Page size */
+ int olddepth; /* Old depth value */
+ int code; /* Error code */
+ int intval; /* Integer value */
+ bool boolval; /* Boolean value */
+ float floatval; /* Floating point value */
+ gs_param_string stringval; /* String value */
+ gs_param_float_array arrayval; /* Float array value */
+
+
+#ifdef DEBUG
+ fprintf(stderr, "DEBUG: cups_put_params(%08x, %08x)\n", pdev, plist);
+#endif /* DEBUG */
+
+ /*
+ * Process other options for CUPS...
+ */
+
+#define stringoption(name, sname) \
+ if ((code = param_read_string(plist, sname, &stringval)) < 0) \
+ { \
+ param_signal_error(plist, sname, code); \
+ return (code); \
+ } \
+ else if (code == 0) \
+ { \
+ strncpy(cups->header.name, (const char *)stringval.data, \
+ stringval.size); \
+ cups->header.name[stringval.size] = '\0'; \
+ }
+
+#define intoption(name, sname, type) \
+ if ((code = param_read_int(plist, sname, &intval)) < 0) \
+ { \
+ param_signal_error(plist, sname, code); \
+ return (code); \
+ } \
+ else if (code == 0) \
+ cups->header.name = (type)intval;
+
+#define floatoption(name, sname) \
+ if ((code = param_read_float(plist, sname, &floatval)) < 0) \
+ { \
+ param_signal_error(plist, sname, code); \
+ return (code); \
+ } \
+ else if (code == 0) \
+ cups->header.name = (unsigned)floatval;
+
+#define booloption(name, sname) \
+ if ((code = param_read_bool(plist, sname, &boolval)) < 0) \
+ { \
+ if ((code = param_read_null(plist, sname)) < 0) \
+ { \
+ param_signal_error(plist, sname, code); \
+ return (code); \
+ } \
+ if (code == 0) \
+ cups->header.name = CUPS_FALSE; \
+ } \
+ else if (code == 0) \
+ cups->header.name = (cups_bool_t)boolval;
+
+#define arrayoption(name, sname, count) \
+ if ((code = param_read_float_array(plist, sname, &arrayval)) < 0) \
+ { \
+ if ((code = param_read_null(plist, sname)) < 0) \
+ { \
+ param_signal_error(plist, sname, code); \
+ return (code); \
+ } \
+ if (code == 0) \
+ for (i = 0; i < count; i ++) \
+ cups->header.name[i] = 0; \
+ } \
+ else if (code == 0) \
+ { \
+ for (i = 0; i < count; i ++) \
+ cups->header.name[i] = (unsigned)arrayval.data[i]; \
+ }
+
+ stringoption(MediaClass, "MediaClass")
+ stringoption(MediaColor, "MediaColor")
+ stringoption(MediaType, "MediaType")
+ stringoption(OutputType, "OutputType")
+ floatoption(AdvanceDistance, "AdvanceDistance")
+ intoption(AdvanceMedia, "AdvanceMedia", cups_adv_t)
+ booloption(Collate, "Collate")
+ intoption(CutMedia, "CutMedia", cups_cut_t)
+ booloption(Duplex, "Duplex")
+ arrayoption(ImagingBoundingBox, "ImagingBoundingBox", 4)
+ booloption(InsertSheet, "InsertSheet")
+ intoption(Jog, "Jog", cups_jog_t)
+ intoption(LeadingEdge, "LeadingEdge", cups_edge_t)
+ arrayoption(Margins, "Margins", 2)
+ booloption(ManualFeed, "ManualFeed")
+ intoption(MediaPosition, "cupsMediaPosition", unsigned)
+ floatoption(MediaWeight, "MediaWeight")
+ booloption(MirrorPrint, "MirrorPrint")
+ booloption(NegativePrint, "NegativePrint")
+ intoption(NumCopies, "NumCopies", unsigned)
+ intoption(Orientation, "Orientation", cups_orient_t)
+ booloption(OutputFaceUp, "OutputFaceUp")
+ booloption(Separations, "Separations")
+ booloption(TraySwitch, "TraySwitch")
+ booloption(Tumble, "Tumble")
+ intoption(cupsWidth, "cupsWidth", unsigned)
+ intoption(cupsHeight, "cupsHeight", unsigned)
+ intoption(cupsMediaType, "cupsMediaType", unsigned)
+ intoption(cupsBitsPerColor, "cupsBitsPerColor", unsigned)
+ intoption(cupsBitsPerPixel, "cupsBitsPerPixel", unsigned)
+ intoption(cupsBytesPerLine, "cupsBytesPerLine", unsigned)
+ intoption(cupsColorOrder, "cupsColorOrder", cups_order_t)
+ intoption(cupsColorSpace, "cupsColorSpace", cups_cspace_t)
+ intoption(cupsCompression, "cupsCompression", unsigned)
+ intoption(cupsRowCount, "cupsRowCount", unsigned)
+ intoption(cupsRowFeed, "cupsRowFeed", unsigned)
+ intoption(cupsRowStep, "cupsRowStep", unsigned)
+
+ /*
+ * Then process standard page device options...
+ */
+
+ if ((code = gdev_prn_put_params(pdev, plist)) < 0)
+ return (code);
+
+ cups->header.HWResolution[0] = pdev->HWResolution[0];
+ cups->header.HWResolution[1] = pdev->HWResolution[1];
+
+ cups->header.PageSize[0] = pdev->MediaSize[0];
+ cups->header.PageSize[1] = pdev->MediaSize[1];
+
+ /*
+ * Check for a change in color depth...
+ */
+
+ olddepth = pdev->color_info.depth;
+ cups_set_color_info(pdev);
+
+ if (olddepth != pdev->color_info.depth && pdev->is_open)
+ gs_closedevice(pdev);
+
+ /*
+ * Compute the page margins...
+ */
+
+ if (cups->ppd != NULL)
+ {
+ /*
+ * Set the margins from the PPD file...
+ */
+
+ for (i = cups->ppd->num_sizes, size = cups->ppd->sizes;
+ i > 0;
+ i --, size ++)
+ if (size->width == cups->header.PageSize[0] &&
+ size->length == cups->header.PageSize[1])
+ break;
+
+ if (i == 0)
+ {
+ /*
+ * Pull margins from custom page size (0 or whatever is defined
+ * by the PPD file...
+ */
+
+ margins[0] = cups->ppd->custom_margins[0] / 72.0;
+ margins[1] = cups->ppd->custom_margins[1] / 72.0;
+ margins[2] = cups->ppd->custom_margins[2] / 72.0;
+ margins[3] = cups->ppd->custom_margins[3] / 72.0;
+ }
+ else
+ {
+ /*
+ * Pull the margins from the size entry; since the margins are not
+ * like the bounding box we have to adjust the top and right values
+ * accordingly.
+ */
+
+ margins[0] = size->left / 72.0;
+ margins[1] = size->bottom / 72.0;
+ margins[2] = (size->width - size->right) / 72.0;
+ margins[3] = (size->length - size->top) / 72.0;
+ }
+ }
+ else
+ {
+ /*
+ * Set default margins of 0.0...
+ */
+
+ memset(margins, 0, sizeof(margins));
+ }
+
+#ifdef DEBUG
+ fprintf(stderr, "DEBUG: ppd = %08x\n", cups->ppd);
+ fprintf(stderr, "DEBUG: MediaSize = [ %.3f %.3f ]\n",
+ pdev->MediaSize[0], pdev->MediaSize[1]);
+ fprintf(stderr, "DEBUG: margins = [ %.3f %.3f %.3f %.3f ]\n",
+ margins[0], margins[1], margins[2], margins[3]);
+ fprintf(stderr, "DEBUG: HWResolution = [ %.3f %.3f ]\n",
+ pdev->HWResolution[0], pdev->HWResolution[1]);
+ fprintf(stderr, "DEBUG: width = %d, height = %d\n",
+ pdev->width, pdev->height);
+ fprintf(stderr, "DEBUG: HWMargins = [ %.3f %.3f %.3f %.3f ]\n",
+ pdev->HWMargins[0], pdev->HWMargins[1],
+ pdev->HWMargins[2], pdev->HWMargins[3]);
+#endif /* DEBUG */
+
+ /*
+ * Set the margins and update the bitmap size...
+ */
+
+ gx_device_set_margins(pdev, margins, false);
+
+ if ((code = gdev_prn_put_params(pdev, plist)) < 0)
+ return (code);
+
+ return (0);
+}
+
+
+/*
+ * 'cups_set_color_info()' - Set the color information structure based on
+ * the required output.
+ */
+
+private void
+cups_set_color_info(gx_device *pdev) /* I - Device info */
+{
+ int i, j, k; /* Looping vars */
+ float d, g; /* Density and gamma correction */
+ char resolution[41]; /* Resolution string */
+ ppd_profile_t *profile; /* Color profile information */
+
+
+#ifdef DEBUG
+ fprintf(stderr, "DEBUG: cups_set_color_info(%08x)\n", pdev);
+#endif /* DEBUG */
+
+ switch (cups->header.cupsColorSpace)
+ {
+ default :
+ case CUPS_CSPACE_W :
+ case CUPS_CSPACE_K :
+ case CUPS_CSPACE_WHITE :
+ case CUPS_CSPACE_GOLD :
+ case CUPS_CSPACE_SILVER :
+ cups->header.cupsBitsPerPixel = cups->header.cupsBitsPerColor;
+ cups->color_info.depth = cups->header.cupsBitsPerPixel;
+ cups->color_info.num_components = 1;
+ break;
+
+ case CUPS_CSPACE_CMY :
+ case CUPS_CSPACE_YMC :
+ case CUPS_CSPACE_RGB :
+ if (cups->header.cupsColorOrder != CUPS_ORDER_CHUNKED)
+ cups->header.cupsBitsPerPixel = cups->header.cupsBitsPerColor;
+ else if (cups->header.cupsBitsPerColor < 8)
+ cups->header.cupsBitsPerPixel = 4 * cups->header.cupsBitsPerColor;
+ else
+ cups->header.cupsBitsPerPixel = 3 * cups->header.cupsBitsPerColor;
+
+ if (cups->header.cupsBitsPerColor < 8)
+ cups->color_info.depth = 4 * cups->header.cupsBitsPerColor;
+ else
+ cups->color_info.depth = 3 * cups->header.cupsBitsPerColor;
+
+ cups->color_info.num_components = 3;
+ break;
+
+ case CUPS_CSPACE_KCMYcm :
+ if (cups->header.cupsBitsPerColor == 1)
+ {
+ cups->header.cupsBitsPerPixel = 8;
+ cups->color_info.depth = 4;
+ cups->color_info.num_components = 4;
+ break;
+ }
+
+ case CUPS_CSPACE_CMYK :
+ case CUPS_CSPACE_YMCK :
+ case CUPS_CSPACE_KCMY :
+ case CUPS_CSPACE_GMCK :
+ case CUPS_CSPACE_GMCS :
+ if (cups->header.cupsColorOrder != CUPS_ORDER_CHUNKED)
+ cups->header.cupsBitsPerPixel = cups->header.cupsBitsPerColor;
+ else
+ cups->header.cupsBitsPerPixel = 4 * cups->header.cupsBitsPerColor;
+
+ cups->color_info.depth = 4 * cups->header.cupsBitsPerColor;
+ cups->color_info.num_components = 4;
+ break;
+ }
+
+ if (cups->color_info.num_components > 1)
+ {
+ cups->color_info.max_gray = (1 << cups->header.cupsBitsPerColor) - 1;
+ cups->color_info.max_color = (1 << cups->header.cupsBitsPerColor) - 1;
+ cups->color_info.dither_grays = (1 << cups->header.cupsBitsPerColor);
+ cups->color_info.dither_colors = (1 << cups->header.cupsBitsPerColor);
+ }
+ else
+ {
+ cups->color_info.max_gray = (1 << cups->header.cupsBitsPerColor) - 1;
+ cups->color_info.max_color = 0;
+ cups->color_info.dither_grays = (1 << cups->header.cupsBitsPerColor);
+ cups->color_info.dither_colors = 0;
+ }
+
+ /*
+ * Compute the lookup tables...
+ */
+
+ for (i = 0; i <= gx_max_color_value; i ++)
+ lut_rgb_color[i] = cups->color_info.max_gray * i / gx_max_color_value;
+
+ for (i = 0; i < cups->color_info.dither_grays; i ++)
+ lut_color_rgb[i] = gx_max_color_value * i / cups->color_info.max_gray;
+
+#ifdef DEBUG
+ fprintf(stderr, "DEBUG: num_components = %d, depth = %d\n",
+ cups->color_info.num_components, cups->color_info.depth);
+ fprintf(stderr, "DEBUG: cupsColorSpace = %d, cupsColorOrder = %d\n",
+ cups->header.cupsColorSpace, cups->header.cupsColorOrder);
+ fprintf(stderr, "DEBUG: cupsBitsPerPixel = %d, cupsBitsPerColor = %d\n",
+ cups->header.cupsBitsPerPixel, cups->header.cupsBitsPerColor);
+ fprintf(stderr, "DEBUG: max_gray = %d, dither_grays = %d\n",
+ cups->color_info.max_gray, cups->color_info.dither_grays);
+ fprintf(stderr, "DEBUG: max_color = %d, dither_colors = %d\n",
+ cups->color_info.max_color, cups->color_info.dither_colors);
+#endif /* DEBUG */
+
+ /*
+ * Set the color profile as needed...
+ */
+
+ cupsHaveProfile = 0;
+
+ if (cups->ppd != NULL && cups->header.cupsBitsPerColor == 8)
+ {
+ /*
+ * Find the appropriate color profile...
+ */
+
+ if (pdev->HWResolution[0] != pdev->HWResolution[1])
+ sprintf(resolution, "%.0fx%.0fdpi", pdev->HWResolution[0],
+ pdev->HWResolution[1]);
+ else
+ sprintf(resolution, "%.0fdpi", pdev->HWResolution[0]);
+
+ for (i = 0, profile = cups->ppd->profiles;
+ i < cups->ppd->num_profiles;
+ i ++, profile ++)
+ if ((strcmp(profile->resolution, resolution) == 0 ||
+ profile->resolution[0] == '-') &&
+ (strcmp(profile->media_type, cups->header.MediaType) == 0 ||
+ profile->media_type[0] == '-'))
+ break;
+
+ /*
+ * If we found a color profile, use it!
+ */
+
+ if (i < cups->ppd->num_profiles)
+ {
+#ifdef DEBUG
+ fputs("DEBUG: Using color profile!\n", stderr);
+#endif /* DEBUG */
+
+ cupsHaveProfile = 1;
+
+ for (i = 0; i < 3; i ++)
+ for (j = 0; j < 3; j ++)
+ for (k = 0; k <= gx_max_color_value; k ++)
+ {
+ cupsMatrix[i][j][k] = (int)((float)k * profile->matrix[i][j] + 0.5);
+
+#ifdef DEBUG
+ if ((k & 4095) == 0)
+ fprintf(stderr, "DEBUG: cupsMatrix[%d][%d][%d] = %d\n",
+ i, j, k, cupsMatrix[i][j][k]);
+#endif /* DEBUG */
+ }
+
+ d = profile->density;
+ g = profile->gamma;
+
+ for (k = 0; k <= gx_max_color_value; k ++)
+ {
+ cupsDensity[k] = (int)((float)gx_max_color_value * d *
+ pow((float)k / (float)gx_max_color_value, g) +
+ 0.5);
+#ifdef DEBUG
+ if ((k & 4095) == 0)
+ fprintf(stderr, "DEBUG: cupsDensity[%d] = %d\n", k, cupsDensity[k]);
+#endif /* DEBUG */
+ }
+ }
+ }
+}
+
+
+/*
+ * 'cups_sync_output()' - Keep the user informed of our status...
+ */
+
+private int /* O - Error status */
+cups_sync_output(gx_device *pdev) /* I - Device info */
+{
+ fprintf(stderr, "INFO: Processing page %d...\n", cups->page);
+
+ return (0);
+}
+
+
+/*
+ * 'cups_print_chunked()' - Print a page of chunked pixels.
+ */
+
+static void
+cups_print_chunked(gx_device_printer *pdev, /* I - Printer device */
+ unsigned char *src) /* I - Scanline buffer */
+{
+ int y; /* Looping var */
+ unsigned char *srcptr; /* Pointer to data */
+
+
+ /*
+ * Loop through the page bitmap and write chunked pixels (the format
+ * is identical to GhostScript's...
+ */
+
+ for (y = 0; y < cups->height; y ++)
+ {
+ /*
+ * Grab the scanline data...
+ */
+
+ if (gdev_prn_get_bits((gx_device_printer *)pdev, y, src, &srcptr) < 0)
+ {
+ fprintf(stderr, "ERROR: Unable to get scanline %d!\n", y);
+ gs_exit(1);
+ }
+
+ /*
+ * Write the scanline data to the raster stream...
+ */
+
+ cupsRasterWritePixels(cups->stream, srcptr, cups->header.cupsBytesPerLine);
+ }
+}
+
+
+/*
+ * 'cups_print_banded()' - Print a page of banded pixels.
+ */
+
+static void
+cups_print_banded(gx_device_printer *pdev, /* I - Printer device */
+ unsigned char *src, /* I - Scanline buffer */
+ unsigned char *dst, /* I - Bitmap buffer */
+ int srcbytes) /* I - Number of bytes in src */
+{
+ int x; /* Looping var */
+ int y; /* Looping var */
+ int bandbytes; /* Bytes per band */
+ unsigned char bit; /* Current bit */
+ unsigned char temp; /* Temporary variable */
+ unsigned char *srcptr; /* Pointer to data */
+ unsigned char *cptr, *mptr, *yptr, *kptr; /* Pointer to components */
+ unsigned char *lcptr, *lmptr; /* ... */
+
+
+ /*
+ * Loop through the page bitmap and write banded pixels... We have
+ * to separate each chunked color as needed...
+ */
+
+ bandbytes = cups->header.cupsBytesPerLine / pdev->color_info.num_components;
+
+ for (y = 0; y < cups->height; y ++)
+ {
+ /*
+ * Grab the scanline data...
+ */
+
+ if (gdev_prn_get_bits((gx_device_printer *)pdev, y, src, &srcptr) < 0)
+ {
+ fprintf(stderr, "ERROR: Unable to get scanline %d!\n", y);
+ gs_exit(1);
+ }
+
+ /*
+ * Separate the chunked colors into their components...
+ */
+
+ if (srcptr[0] == 0 && memcmp(srcptr, srcptr + 1, srcbytes - 1) == 0)
+ memset(dst, 0, cups->header.cupsBytesPerLine);
+ else
+ switch (cups->header.cupsBitsPerColor)
+ {
+ case 1 :
+ memset(dst, 0, cups->header.cupsBytesPerLine);
+
+ switch (cups->header.cupsColorSpace)
+ {
+ case CUPS_CSPACE_RGB :
+ case CUPS_CSPACE_CMY :
+ case CUPS_CSPACE_YMC :
+ for (x = cups->width, cptr = dst, mptr = cptr + bandbytes,
+ yptr = mptr + bandbytes, bit = 128;
+ x > 0;
+ x --, srcptr ++)
+ {
+ if (*srcptr & 0x40)
+ *cptr |= bit;
+ if (*srcptr & 0x20)
+ *mptr |= bit;
+ if (*srcptr & 0x10)
+ *yptr |= bit;
+
+ bit >>= 1;
+ x --;
+ if (x == 0)
+ break;
+
+ if (*srcptr & 0x4)
+ *cptr |= bit;
+ if (*srcptr & 0x2)
+ *mptr |= bit;
+ if (*srcptr & 0x1)
+ *yptr |= bit;
+
+ if (bit > 1)
+ bit >>= 1;
+ else
+ {
+ cptr ++;
+ mptr ++;
+ yptr ++;
+ bit = 128;
+ }
+ }
+ break;
+ case CUPS_CSPACE_CMYK :
+ case CUPS_CSPACE_YMCK :
+ case CUPS_CSPACE_KCMY :
+ for (x = cups->width, cptr = dst, mptr = cptr + bandbytes,
+ yptr = mptr + bandbytes, kptr = yptr + bandbytes,
+ bit = 128;
+ x > 0;
+ x --, srcptr ++)
+ {
+ if (*srcptr & 0x80)
+ *cptr |= bit;
+ if (*srcptr & 0x40)
+ *mptr |= bit;
+ if (*srcptr & 0x20)
+ *yptr |= bit;
+ if (*srcptr & 0x10)
+ *kptr |= bit;
+
+ bit >>= 1;
+ x --;
+ if (x == 0)
+ break;
+
+ if (*srcptr & 0x8)
+ *cptr |= bit;
+ if (*srcptr & 0x4)
+ *mptr |= bit;
+ if (*srcptr & 0x2)
+ *yptr |= bit;
+ if (*srcptr & 0x1)
+ *kptr |= bit;
+
+ if (bit > 1)
+ bit >>= 1;
+ else
+ {
+ cptr ++;
+ mptr ++;
+ yptr ++;
+ kptr ++;
+ bit = 128;
+ }
+ }
+ break;
+ case CUPS_CSPACE_KCMYcm :
+ for (x = cups->width, cptr = dst, mptr = cptr + bandbytes,
+ yptr = mptr + bandbytes, kptr = yptr + bandbytes,
+ lcptr = kptr + bandbytes, lmptr = lcptr + bandbytes,
+ bit = 128;
+ x > 0;
+ x --, srcptr ++)
+ {
+ if (*srcptr & 0x20)
+ *cptr |= bit;
+ if (*srcptr & 0x10)
+ *mptr |= bit;
+ if (*srcptr & 0x08)
+ *yptr |= bit;
+ if (*srcptr & 0x04)
+ *kptr |= bit;
+ if (*srcptr & 0x02)
+ *lcptr |= bit;
+ if (*srcptr & 0x01)
+ *lmptr |= bit;
+
+ if (bit > 1)
+ bit >>= 1;
+ else
+ {
+ cptr ++;
+ mptr ++;
+ yptr ++;
+ kptr ++;
+ lcptr ++;
+ lmptr ++;
+ bit = 128;
+ }
+ }
+ break;
+ }
+ break;
+
+ case 2 :
+ memset(dst, 0, cups->header.cupsBytesPerLine);
+
+ switch (cups->header.cupsColorSpace)
+ {
+ case CUPS_CSPACE_RGB :
+ case CUPS_CSPACE_CMY :
+ case CUPS_CSPACE_YMC :
+ for (x = cups->width, cptr = dst, mptr = cptr + bandbytes,
+ yptr = mptr + bandbytes, bit = 0xc0;
+ x > 0;
+ x --, srcptr ++)
+ switch (bit)
+ {
+ case 0xc0 :
+ if (temp = *srcptr & 0x30)
+ *cptr |= temp << 2;
+ if (temp = *srcptr & 0x0c)
+ *mptr |= temp << 4;
+ if (temp = *srcptr & 0x03)
+ *yptr |= temp << 6;
+
+ bit = 0x30;
+ break;
+ case 0x30 :
+ if (temp = *srcptr & 0x30)
+ *cptr |= temp;
+ if (temp = *srcptr & 0x0c)
+ *mptr |= temp << 2;
+ if (temp = *srcptr & 0x03)
+ *yptr |= temp << 4;
+
+ bit = 0x0c;
+ break;
+ case 0x0c :
+ if (temp = *srcptr & 0x30)
+ *cptr |= temp >> 2;
+ if (temp = *srcptr & 0x0c)
+ *mptr |= temp;
+ if (temp = *srcptr & 0x03)
+ *yptr |= temp << 2;
+
+ bit = 0x03;
+ break;
+ case 0x03 :
+ if (temp = *srcptr & 0x30)
+ *cptr |= temp >> 4;
+ if (temp = *srcptr & 0x0c)
+ *mptr |= temp >> 2;
+ if (temp = *srcptr & 0x03)
+ *yptr |= temp;
+
+ bit = 0xc0;
+ cptr ++;
+ mptr ++;
+ yptr ++;
+ break;
+ }
+ break;
+ case CUPS_CSPACE_CMYK :
+ case CUPS_CSPACE_YMCK :
+ case CUPS_CSPACE_KCMY :
+ for (x = cups->width, cptr = dst, mptr = cptr + bandbytes,
+ yptr = mptr + bandbytes, kptr = yptr + bandbytes,
+ bit = 0xc0;
+ x > 0;
+ x --, srcptr ++)
+ switch (bit)
+ {
+ case 0xc0 :
+ if (temp = *srcptr & 0xc0)
+ *cptr |= temp;
+ if (temp = *srcptr & 0x30)
+ *mptr |= temp << 2;
+ if (temp = *srcptr & 0x0c)
+ *yptr |= temp << 4;
+ if (temp = *srcptr & 0x03)
+ *kptr |= temp << 6;
+
+ bit = 0x30;
+ break;
+ case 0x30 :
+ if (temp = *srcptr & 0xc0)
+ *cptr |= temp >> 2;
+ if (temp = *srcptr & 0x30)
+ *mptr |= temp;
+ if (temp = *srcptr & 0x0c)
+ *yptr |= temp << 2;
+ if (temp = *srcptr & 0x03)
+ *kptr |= temp << 4;
+
+ bit = 0x0c;
+ break;
+ case 0x0c :
+ if (temp = *srcptr & 0xc0)
+ *cptr |= temp >> 4;
+ if (temp = *srcptr & 0x30)
+ *mptr |= temp >> 2;
+ if (temp = *srcptr & 0x0c)
+ *yptr |= temp;
+ if (temp = *srcptr & 0x03)
+ *kptr |= temp << 2;
+
+ bit = 0x03;
+ break;
+ case 0x03 :
+ if (temp = *srcptr & 0xc0)
+ *cptr |= temp >> 6;
+ if (temp = *srcptr & 0x30)
+ *mptr |= temp >> 4;
+ if (temp = *srcptr & 0x0c)
+ *yptr |= temp >> 2;
+ if (temp = *srcptr & 0x03)
+ *kptr |= temp;
+
+ bit = 0xc0;
+ cptr ++;
+ mptr ++;
+ yptr ++;
+ kptr ++;
+ break;
+ }
+ break;
+ }
+ break;
+
+ case 4 :
+ memset(dst, 0, cups->header.cupsBytesPerLine);
+
+ switch (cups->header.cupsColorSpace)
+ {
+ case CUPS_CSPACE_RGB :
+ case CUPS_CSPACE_CMY :
+ case CUPS_CSPACE_YMC :
+ for (x = cups->width, cptr = dst, mptr = cptr + bandbytes,
+ yptr = mptr + bandbytes, bit = 0xf0;
+ x > 0;
+ x --, srcptr += 2)
+ switch (bit)
+ {
+ case 0xf0 :
+ if (temp = srcptr[0] & 0x0f)
+ *cptr |= temp << 4;
+ if (temp = srcptr[1] & 0xf0)
+ *mptr |= temp;
+ if (temp = srcptr[1] & 0x0f)
+ *yptr |= temp << 4;
+
+ bit = 0x0f;
+ break;
+ case 0x0f :
+ if (temp = srcptr[0] & 0x0f)
+ *cptr |= temp;
+ if (temp = srcptr[1] & 0xf0)
+ *mptr |= temp >> 4;
+ if (temp = srcptr[1] & 0x0f)
+ *yptr |= temp;
+
+ bit = 0xf0;
+ cptr ++;
+ mptr ++;
+ yptr ++;
+ kptr ++;
+ break;
+ }
+ break;
+ case CUPS_CSPACE_CMYK :
+ case CUPS_CSPACE_YMCK :
+ case CUPS_CSPACE_KCMY :
+ case CUPS_CSPACE_KCMYcm :
+ for (x = cups->width, cptr = dst, mptr = cptr + bandbytes,
+ yptr = mptr + bandbytes, kptr = yptr + bandbytes,
+ bit = 0xf0;
+ x > 0;
+ x --, srcptr += 2)
+ switch (bit)
+ {
+ case 0xf0 :
+ if (temp = srcptr[0] & 0xf0)
+ *cptr |= temp;
+ if (temp = srcptr[0] & 0x0f)
+ *mptr |= temp << 4;
+ if (temp = srcptr[1] & 0xf0)
+ *yptr |= temp;
+ if (temp = srcptr[1] & 0x0f)
+ *kptr |= temp << 4;
+
+ bit = 0x0f;
+ break;
+ case 0x0f :
+ if (temp = srcptr[0] & 0xf0)
+ *cptr |= temp >> 4;
+ if (temp = srcptr[0] & 0x0f)
+ *mptr |= temp;
+ if (temp = srcptr[1] & 0xf0)
+ *yptr |= temp >> 4;
+ if (temp = srcptr[1] & 0x0f)
+ *kptr |= temp;
+
+ bit = 0xf0;
+ cptr ++;
+ mptr ++;
+ yptr ++;
+ kptr ++;
+ break;
+ }
+ break;
+ }
+ break;
+
+ case 8 :
+ switch (cups->header.cupsColorSpace)
+ {
+ case CUPS_CSPACE_RGB :
+ case CUPS_CSPACE_CMY :
+ case CUPS_CSPACE_YMC :
+ for (x = cups->width, cptr = dst, mptr = cptr + bandbytes,
+ yptr = mptr + bandbytes;
+ x > 0;
+ x --)
+ {
+ *cptr++ = *srcptr++;
+ *mptr++ = *srcptr++;
+ *yptr++ = *srcptr++;
+ }
+ break;
+ case CUPS_CSPACE_CMYK :
+ case CUPS_CSPACE_YMCK :
+ case CUPS_CSPACE_KCMY :
+ case CUPS_CSPACE_KCMYcm :
+ for (x = cups->width, cptr = dst, mptr = cptr + bandbytes,
+ yptr = mptr + bandbytes, kptr = yptr + bandbytes;
+ x > 0;
+ x --)
+ {
+ *cptr++ = *srcptr++;
+ *mptr++ = *srcptr++;
+ *yptr++ = *srcptr++;
+ *kptr++ = *srcptr++;
+ }
+ break;
+ }
+ break;
+ }
+
+ /*
+ * Write the bitmap data to the raster stream...
+ */
+
+ cupsRasterWritePixels(cups->stream, dst, cups->header.cupsBytesPerLine);
+ }
+}
+
+
+/*
+ * 'cups_print_planar()' - Print a page of planar pixels.
+ */
+
+static void
+cups_print_planar(gx_device_printer *pdev, /* I - Printer device */
+ unsigned char *src, /* I - Scanline buffer */
+ unsigned char *dst, /* I - Bitmap buffer */
+ int srcbytes) /* I - Number of bytes in src */
+{
+ int x; /* Looping var */
+ int y; /* Looping var */
+ int z; /* Looping var */
+ unsigned char srcbit; /* Current source bit */
+ unsigned char dstbit; /* Current destination bit */
+ unsigned char temp; /* Temporary variable */
+ unsigned char *srcptr; /* Pointer to data */
+ unsigned char *dstptr; /* Pointer to bitmap */
+
+
+ /*
+ * Loop through the page bitmap and write planar pixels... We have
+ * to separate each chunked color as needed...
+ */
+
+ for (z = 0; z < pdev->color_info.num_components; z ++)
+ for (y = 0; y < cups->height; y ++)
+ {
+ /*
+ * Grab the scanline data...
+ */
+
+ if (gdev_prn_get_bits((gx_device_printer *)pdev, y, src, &srcptr) < 0)
+ {
+ fprintf(stderr, "ERROR: Unable to get scanline %d!\n", y);
+ gs_exit(1);
+ }
+
+ /*
+ * Pull the individual color planes out of the pixels...
+ */
+
+ if (srcptr[0] == 0 && memcmp(srcptr, srcptr + 1, srcbytes - 1) == 0)
+ memset(dst, 0, cups->header.cupsBytesPerLine);
+ else
+ switch (cups->header.cupsBitsPerColor)
+ {
+ case 1 :
+ memset(dst, 0, cups->header.cupsBytesPerLine);
+
+ switch (cups->header.cupsColorSpace)
+ {
+ case CUPS_CSPACE_RGB :
+ case CUPS_CSPACE_CMY :
+ case CUPS_CSPACE_YMC :
+ for (dstptr = dst, x = cups->width, srcbit = 64 >> z,
+ dstbit = 128;
+ x > 0;
+ x --)
+ {
+ if (*srcptr & srcbit)
+ *dstptr |= dstbit;
+
+ if (srcbit >= 16)
+ srcbit >>= 4;
+ else
+ {
+ srcbit = 64 >> z;
+ srcptr ++;
+ }
+
+ if (dstbit > 1)
+ dstbit >>= 1;
+ else
+ {
+ dstbit = 128;
+ dstptr ++;
+ }
+ }
+ break;
+ case CUPS_CSPACE_CMYK :
+ case CUPS_CSPACE_YMCK :
+ case CUPS_CSPACE_KCMY :
+ for (dstptr = dst, x = cups->width, srcbit = 128 >> z,
+ dstbit = 128;
+ x > 0;
+ x --)
+ {
+ if (*srcptr & srcbit)
+ *dstptr |= dstbit;
+
+ if (srcbit >= 16)
+ srcbit >>= 4;
+ else
+ {
+ srcbit = 128 >> z;
+ srcptr ++;
+ }
+
+ if (dstbit > 1)
+ dstbit >>= 1;
+ else
+ {
+ dstbit = 128;
+ dstptr ++;
+ }
+ }
+ break;
+ case CUPS_CSPACE_KCMYcm :
+ for (dstptr = dst, x = cups->width, srcbit = 32 >> z,
+ dstbit = 128;
+ x > 0;
+ x --, srcptr ++)
+ {
+ if (*srcptr & srcbit)
+ *dstptr |= dstbit;
+
+ if (dstbit > 1)
+ dstbit >>= 1;
+ else
+ {
+ dstbit = 128;
+ dstptr ++;
+ }
+ }
+ break;
+ }
+ break;
+
+ case 2 :
+ memset(dst, 0, cups->header.cupsBytesPerLine);
+
+ switch (cups->header.cupsColorSpace)
+ {
+ case CUPS_CSPACE_RGB :
+ case CUPS_CSPACE_CMY :
+ case CUPS_CSPACE_YMC :
+ for (dstptr = dst, x = cups->width, srcbit = 48 >> (z * 2),
+ dstbit = 0xc0;
+ x > 0;
+ x --, srcptr ++)
+ {
+ if (temp = *srcptr & srcbit)
+ {
+ if (srcbit == dstbit)
+ *dstptr |= temp;
+ else
+ {
+ switch (srcbit)
+ {
+ case 0x30 :
+ temp >>= 4;
+ break;
+ case 0x0c :
+ temp >>= 2;
+ break;
+ }
+
+ switch (dstbit)
+ {
+ case 0xc0 :
+ *dstptr |= temp << 6;
+ break;
+ case 0x30 :
+ *dstptr |= temp << 4;
+ break;
+ case 0x0c :
+ *dstptr |= temp << 2;
+ break;
+ case 0x03 :
+ *dstptr |= temp;
+ break;
+ }
+ }
+ }
+
+ if (dstbit > 0x03)
+ dstbit >>= 2;
+ else
+ {
+ dstbit = 0xc0;
+ dstptr ++;
+ }
+ }
+ break;
+ case CUPS_CSPACE_CMYK :
+ case CUPS_CSPACE_YMCK :
+ case CUPS_CSPACE_KCMY :
+ case CUPS_CSPACE_KCMYcm :
+ for (dstptr = dst, x = cups->width, srcbit = 192 >> (z * 2),
+ dstbit = 0xc0;
+ x > 0;
+ x --, srcptr ++)
+ {
+ if (temp = *srcptr & srcbit)
+ {
+ if (srcbit == dstbit)
+ *dstptr |= temp;
+ else
+ {
+ switch (srcbit)
+ {
+ case 0xc0 :
+ temp >>= 6;
+ break;
+ case 0x30 :
+ temp >>= 4;
+ break;
+ case 0x0c :
+ temp >>= 2;
+ break;
+ }
+
+ switch (dstbit)
+ {
+ case 0xc0 :
+ *dstptr |= temp << 6;
+ break;
+ case 0x30 :
+ *dstptr |= temp << 4;
+ break;
+ case 0x0c :
+ *dstptr |= temp << 2;
+ break;
+ case 0x03 :
+ *dstptr |= temp;
+ break;
+ }
+ }
+ }
+
+ if (dstbit > 0x03)
+ dstbit >>= 2;
+ else
+ {
+ dstbit = 0xc0;
+ dstptr ++;
+ }
+ }
+ break;
+ }
+ break;
+
+ case 4 :
+ memset(dst, 0, cups->header.cupsBytesPerLine);
+
+ switch (cups->header.cupsColorSpace)
+ {
+ case CUPS_CSPACE_RGB :
+ case CUPS_CSPACE_CMY :
+ case CUPS_CSPACE_YMC :
+ if (z > 0)
+ srcptr ++;
+
+ if (z == 1)
+ srcbit = 0xf0;
+ else
+ srcbit = 0x0f;
+
+ for (dstptr = dst, x = cups->width, dstbit = 0xf0;
+ x > 0;
+ x --, srcptr += 2)
+ {
+ if (temp = *srcptr & srcbit)
+ {
+ if (srcbit == dstbit)
+ *dstptr |= temp;
+ else
+ {
+ if (srcbit == 0xf0)
+ temp >>= 4;
+
+ if (dstbit == 0xf0)
+ *dstptr |= temp << 4;
+ else
+ *dstptr |= temp;
+ }
+ }
+
+ if (dstbit == 0xf0)
+ dstbit = 0x0f;
+ else
+ {
+ dstbit = 0xf0;
+ dstptr ++;
+ }
+ }
+ break;
+ case CUPS_CSPACE_CMYK :
+ case CUPS_CSPACE_YMCK :
+ case CUPS_CSPACE_KCMY :
+ case CUPS_CSPACE_KCMYcm :
+ if (z > 1)
+ srcptr ++;
+
+ if (z & 1)
+ srcbit = 0x0f;
+ else
+ srcbit = 0xf0;
+
+ for (dstptr = dst, x = cups->width, dstbit = 0xf0;
+ x > 0;
+ x --, srcptr += 2)
+ {
+ if (temp = *srcptr & srcbit)
+ {
+ if (srcbit == dstbit)
+ *dstptr |= temp;
+ else
+ {
+ if (srcbit == 0xf0)
+ temp >>= 4;
+
+ if (dstbit == 0xf0)
+ *dstptr |= temp << 4;
+ else
+ *dstptr |= temp;
+ }
+ }
+
+ if (dstbit == 0xf0)
+ dstbit = 0x0f;
+ else
+ {
+ dstbit = 0xf0;
+ dstptr ++;
+ }
+ }
+ break;
+ }
+ break;
+
+ case 8 :
+ for (srcptr += z, dstptr = dst, x = cups->header.cupsBytesPerLine;
+ x > 0;
+ srcptr += pdev->color_info.num_components, x --)
+ *dstptr++ = *srcptr;
+ break;
+ }
+
+ /*
+ * Write the bitmap data to the raster stream...
+ */
+
+ cupsRasterWritePixels(cups->stream, dst, cups->header.cupsBytesPerLine);
+ }
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/pstoraster/gdevddrw.c b/pstoraster/gdevddrw.c
new file mode 100644
index 000000000..85536350a
--- /dev/null
+++ b/pstoraster/gdevddrw.c
@@ -0,0 +1,470 @@
+/* Copyright (C) 1989, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gdevddrw.c */
+/* Default polygon and image drawing device procedures */
+#include "math_.h"
+#include "gx.h"
+#include "gpcheck.h"
+#include "gserrors.h"
+#include "gxfixed.h"
+#include "gxmatrix.h"
+#include "gxdcolor.h"
+#include "gxdevice.h"
+
+/* ---------------- Polygon and line drawing ---------------- */
+
+/* Define the 'remainder' analogue of fixed_mult_quo. */
+private fixed
+fixed_mult_rem(fixed a, fixed b, fixed c)
+{ double prod = (double)a * b;
+ return (fixed)(prod - floor(prod / c) * c);
+}
+
+/*
+ * Fill a trapezoid. Requires:
+ * {left,right}->start.y <= ybot <= ytop <= {left,right}->end.y.
+ * Lines where left.x >= right.x will not be drawn. Thanks to Paul Haeberli
+ * for an early floating point version of this algorithm.
+ */
+typedef struct trap_line_s {
+ int di; fixed df; /* dx/dy ratio = di + df/h */
+ fixed ldi, ldf; /* increment per scan line = ldi + ldf/h */
+ fixed x, xf; /* current value */
+ fixed h;
+} trap_line;
+int
+gx_default_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)
+{ const fixed ymin = fixed_pixround(ybot) + fixed_half;
+ const fixed ymax = fixed_pixround(ytop);
+ if ( ymin >= ymax ) return 0; /* no scan lines to sample */
+ { int iy = fixed2int_var(ymin);
+ const int iy1 = fixed2int_var(ymax);
+ trap_line l, r;
+ int rxl, rxr, ry;
+ const fixed
+ x0l = left->start.x, x1l = left->end.x,
+ x0r = right->start.x, x1r = right->end.x,
+ dxl = x1l - x0l, dxr = x1r - x0r;
+ const fixed /* partial pixel offset to first line to sample */
+ ysl = ymin - left->start.y,
+ ysr = ymin - right->start.y;
+ fixed fxl;
+ bool fill_direct = color_writes_pure(pdevc, lop);
+ gx_color_index cindex;
+ dev_proc_fill_rectangle((*fill_rect));
+ int max_rect_height = 1; /* max height to do fill as rectangle */
+ int code;
+
+ if_debug2('z', "[z]y=[%d,%d]\n", iy, iy1);
+
+ if ( fill_direct )
+ cindex = pdevc->colors.pure,
+ fill_rect = dev_proc(dev, fill_rectangle);
+ l.h = left->end.y - left->start.y;
+ r.h = right->end.y - right->start.y;
+ l.x = x0l + (fixed_half - fixed_epsilon);
+ r.x = x0r + (fixed_half - fixed_epsilon);
+ ry = iy;
+
+#define fill_trap_rect(x,y,w,h)\
+ (fill_direct ?\
+ (swap_axes ? (*fill_rect)(dev, y, x, h, w, cindex) :\
+ (*fill_rect)(dev, x, y, w, h, cindex)) :\
+ swap_axes ? gx_fill_rectangle_device_rop(y, x, h, w, pdevc, dev, lop) :\
+ gx_fill_rectangle_device_rop(x, y, w, h, pdevc, dev, lop))
+
+ /* Compute the dx/dy ratios. */
+ /* dx# = dx#i + (dx#f / h#). */
+#define compute_dx(tl, d, ys)\
+ if ( d >= 0 )\
+ { if ( d < tl.h ) tl.di = 0, tl.df = d;\
+ else tl.di = (int)(d / tl.h), tl.df = d - tl.di * tl.h,\
+ tl.x += ys * tl.di;\
+ }\
+ else\
+ { if ( (tl.df = d + tl.h) >= 0 /* d >= -tl.h */ ) tl.di = -1, tl.x -= ys;\
+ else tl.di = (int)-((tl.h - 1 - d) / tl.h), tl.df = d - tl.di * tl.h,\
+ tl.x += ys * tl.di;\
+ }
+
+ /* Compute the x offsets at the first scan line to sample. */
+ /* We need to be careful in computing ys# * dx#f {/,%} h# */
+ /* because the multiplication may overflow. We know that */
+ /* all the quantities involved are non-negative, and that */
+ /* ys# is usually than 1 (as a fixed, of course); this gives us */
+ /* a cheap conservative check for overflow in the multiplication. */
+#define ymult_limit (max_fixed / fixed_1)
+#define ymult_quo(ys, tl)\
+ (ys < fixed_1 && tl.df < ymult_limit ? ys * tl.df / tl.h :\
+ fixed_mult_quo(ys, tl.df, tl.h))
+
+ /*
+ * It's worth checking for dxl == dxr, since this is the case
+ * for parallelograms (including stroked lines).
+ * Also check for left or right vertical edges.
+ */
+ if ( fixed_floor(l.x) == fixed_pixround(x1l) )
+ { /* Left edge is vertical, we don't need to increment. */
+ l.di = 0, l.df = 0;
+ fxl = 0;
+ }
+ else
+ { compute_dx(l, dxl, ysl);
+ fxl = ymult_quo(ysl, l);
+ l.x += fxl;
+ }
+ if ( fixed_floor(r.x) == fixed_pixround(x1r) )
+ { /* Right edge is vertical. If both are vertical, */
+ /* we have a rectangle. */
+ if ( l.di == 0 && l.df == 0 )
+ max_rect_height = max_int;
+ else
+ r.di = 0, r.df = 0;
+ }
+ /* The test for fxl != 0 is required because the right edge */
+ /* might cross some pixel centers even if the left edge doesn't. */
+ else if ( dxr == dxl && fxl != 0 )
+ { if ( l.di == 0 )
+ r.di = 0, r.df = l.df;
+ else /* too hard to do adjustments right */
+ compute_dx(r, dxr, ysr);
+ if ( ysr == ysl && r.h == l.h )
+ r.x += fxl;
+ else
+ r.x += ymult_quo(ysr, r);
+ }
+ else
+ { compute_dx(r, dxr, ysr);
+ r.x += ymult_quo(ysr, r);
+ }
+ rxl = fixed2int_var(l.x);
+ rxr = fixed2int_var(r.x);
+
+ /*
+ * Take a shortcut if we're only sampling a single scan line,
+ * or if we have a rectangle.
+ */
+ if ( iy1 - iy <= max_rect_height )
+ { iy = iy1;
+ if_debug2('z', "[z]rectangle, x=[%d,%d]\n", rxl, rxr);
+ goto last;
+ }
+
+ /* Compute one line's worth of dx/dy. */
+ /* dx# * fixed_1 = ld#i + (ld#f / h#). */
+#define compute_ldx(tl, ys)\
+ if ( tl.df < ymult_limit )\
+ { if ( tl.df == 0 ) /* vertical edge, worth checking for */\
+ tl.ldi = int2fixed(tl.di),\
+ tl.ldf = 0,\
+ tl.xf = -tl.h;\
+ else\
+ tl.ldi = int2fixed(tl.di) + int2fixed(tl.df) / tl.h,\
+ tl.ldf = int2fixed(tl.df) % tl.h,\
+ tl.xf = (ys < fixed_1 ? ys * tl.df % tl.h :\
+ fixed_mult_rem(ys, tl.df, tl.h)) - tl.h;\
+ }\
+ else\
+ tl.ldi = int2fixed(tl.di) + fixed_mult_quo(fixed_1, tl.df, tl.h),\
+ tl.ldf = fixed_mult_rem(fixed_1, tl.df, tl.h),\
+ tl.xf = fixed_mult_rem(ys, tl.df, tl.h) - tl.h
+ compute_ldx(l, ysl);
+ if ( dxr == dxl && ysr == ysl && r.h == l.h )
+ r.ldi = l.ldi, r.ldf = l.ldf, r.xf = l.xf;
+ else
+ { compute_ldx(r, ysr);
+ }
+#undef compute_ldx
+
+ while ( ++iy != iy1 )
+ { int ixl, ixr;
+#define step_line(tl)\
+ tl.x += tl.ldi;\
+ if ( (tl.xf += tl.ldf) >= 0 ) tl.xf -= tl.h, tl.x++;
+ step_line(l);
+ step_line(r);
+#undef step_line
+ ixl = fixed2int_var(l.x);
+ ixr = fixed2int_var(r.x);
+ if ( ixl != rxl || ixr != rxr )
+ { code = fill_trap_rect(rxl, ry, rxr - rxl, iy - ry);
+ if ( code < 0 ) goto xit;
+ rxl = ixl, rxr = ixr, ry = iy;
+ }
+ }
+last: code = fill_trap_rect(rxl, ry, rxr - rxl, iy - ry);
+xit: if ( code < 0 && fill_direct )
+ return_error(code);
+ return_if_interrupt();
+ return code;
+ }
+}
+
+/* Fill a parallelogram whose points are p, p+a, p+b, and p+a+b. */
+/* We should swap axes to get best accuracy, but we don't. */
+/* We must be very careful to follow the center-of-pixel rule in all cases. */
+int
+gx_default_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)
+{ fixed t;
+ fixed qx, qy, ym;
+ dev_proc_fill_trapezoid((*fill_trapezoid));
+ gs_fixed_edge left, right;
+ int code;
+
+ /* Ensure ay >= 0, by >= 0. */
+ if ( ay < 0 )
+ px += ax, py += ay, ax = -ax, ay = -ay;
+ if ( by < 0 )
+ px += bx, py += by, bx = -bx, by = -by;
+ qx = px + ax + bx;
+ /* Make a special fast check for rectangles. */
+ if ( (ay | bx) == 0 || (by | ax) == 0 )
+ { /* If a point falls exactly on the middle of a pixel, */
+ /* we must round it down, not up. */
+ int rx = fixed2int_pixround(px);
+ int ry = fixed2int_pixround(py);
+ /* Exactly one of (ax,bx) and one of (ay,by) is non-zero. */
+ int w = fixed2int_pixround(qx) - rx;
+ if ( w < 0 ) rx += w, w = -w;
+ return gx_fill_rectangle_device_rop(rx, ry, w,
+ fixed2int_pixround(py + ay + by) - ry,
+ pdevc, dev, lop);
+ }
+ /* Not a rectangle. Ensure ax <= bx. */
+#define swap(r, s) (t = r, r = s, s = t)
+ if ( ax > bx )
+ swap(ax, bx), swap(ay, by);
+ fill_trapezoid = dev_proc(dev, fill_trapezoid);
+ qy = py + ay + by;
+ left.start.x = right.start.x = px;
+ left.start.y = right.start.y = py;
+ left.end.x = px + ax;
+ left.end.y = py + ay;
+ right.end.x = px + bx;
+ right.end.y = py + by;
+#define rounded_same(p1, p2)\
+ (fixed_pixround(p1) == fixed_pixround(p2))
+ if ( ay < by )
+ { if ( !rounded_same(py, left.end.y) )
+ { code = (*fill_trapezoid)(dev, &left, &right, py, left.end.y,
+ false, pdevc, lop);
+ if ( code < 0 )
+ return code;
+ }
+ left.start = left.end;
+ left.end.x = qx, left.end.y = qy;
+ ym = right.end.y;
+ if ( !rounded_same(left.start.y, ym) )
+ { code = (*fill_trapezoid)(dev, &left, &right, left.start.y, ym,
+ false, pdevc, lop);
+ if ( code < 0 )
+ return code;
+ }
+ right.start = right.end;
+ right.end.x = qx, right.end.y = qy;
+ }
+ else
+ { if ( !rounded_same(py, right.end.y) )
+ { code = (*fill_trapezoid)(dev, &left, &right, py, right.end.y,
+ false, pdevc, lop);
+ if ( code < 0 )
+ return code;
+ }
+ right.start = right.end;
+ right.end.x = qx, right.end.y = qy;
+ ym = left.end.y;
+ if ( !rounded_same(right.start.y, ym) )
+ { code = (*fill_trapezoid)(dev, &left, &right, right.start.y, ym,
+ false, pdevc, lop);
+ if ( code < 0 )
+ return code;
+ }
+ left.start = left.end;
+ left.end.x = qx, left.end.y = qy;
+ }
+ if ( !rounded_same(ym, qy) )
+ return (*fill_trapezoid)(dev, &left, &right, ym, qy,
+ false, pdevc, lop);
+ else
+ return 0;
+#undef rounded_same
+#undef swap
+}
+
+/* Fill a triangle whose points are p, p+a, and p+b. */
+/* We should swap axes to get best accuracy, but we don't. */
+int
+gx_default_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)
+{ fixed t;
+ fixed ym;
+ dev_proc_fill_trapezoid((*fill_trapezoid)) =
+ dev_proc(dev, fill_trapezoid);
+ gs_fixed_edge left, right;
+ int code;
+
+ /* Ensure ay >= 0, by >= 0. */
+ if ( ay < 0 )
+ px += ax, py += ay, bx -= ax, by -= ay, ax = -ax, ay = -ay;
+ if ( by < 0 )
+ px += bx, py += by, ax -= bx, ay -= by, bx = -bx, by = -by;
+ /* Ensure ax <= bx. */
+#define swap(r, s) (t = r, r = s, s = t)
+ if ( ax > bx )
+ swap(ax, bx), swap(ay, by);
+#undef swap
+ left.start.y = right.start.y = py;
+ /* Make a special check for a flat bottom or top, */
+ /* which we can handle with a single call on fill_trapezoid. */
+ if ( ay < by )
+ { right.end.x = px + bx, right.end.y = py + by;
+ if ( ay == 0 )
+ { if ( ax < 0 )
+ left.start.x = px + ax, right.start.x = px;
+ else
+ left.start.x = px, right.start.x = px + ax;
+ left.end.x = right.end.x, left.end.y = right.end.y;
+ ym = py;
+ }
+ else
+ { left.start.x = right.start.x = px;
+ left.end.x = px + ax, left.end.y = py + ay;
+ code = (*fill_trapezoid)(dev, &left, &right, py, left.end.y,
+ false, pdevc, lop);
+ if ( code < 0 )
+ return code;
+ left.start = left.end;
+ left.end = right.end;
+ ym = left.start.x;
+ }
+ }
+ else if ( by < ay )
+ { left.end.x = px + ax, left.end.y = py + by;
+ if ( by == 0 )
+ { if ( bx < 0 )
+ left.start.x = px + bx, right.start.x = px;
+ else
+ left.start.x = px, right.start.x = px + bx;
+ right.end.x = left.end.x, right.end.y = left.end.y;
+ ym = py;
+ }
+ else
+ { left.start.x = right.start.x = px;
+ right.end.x = px + bx, right.end.y = py + by;
+ code = (*fill_trapezoid)(dev, &left, &right, py, right.end.y,
+ false, pdevc, lop);
+ if ( code < 0 )
+ return code;
+ right.start = right.end;
+ right.end = left.end;
+ ym = right.start.x;
+ }
+ }
+ else /* ay == by */
+ { left.start.x = right.start.x = px;
+ left.end.x = px + ax, left.end.y = py + ay;
+ right.end.x = px + bx, right.end.y = py + by;
+ ym = py;
+ }
+ return (*fill_trapezoid)(dev, &left, &right, ym, right.end.y,
+ false, pdevc, lop);
+}
+
+/* Draw a one-pixel-wide line. */
+int
+gx_default_draw_thin_line(gx_device *dev,
+ fixed fx0, fixed fy0, fixed fx1, fixed fy1,
+ const gx_device_color *pdevc, gs_logical_operation_t lop)
+{ int ix = fixed2int_var(fx0);
+ int iy = fixed2int_var(fy0);
+ int itox = fixed2int_var(fx1);
+ int itoy = fixed2int_var(fy1);
+
+ return_if_interrupt();
+ if ( itoy == iy ) /* horizontal line */
+ { return (ix <= itox ?
+ gx_fill_rectangle_device_rop(ix, iy, itox - ix + 1, 1,
+ pdevc, dev, lop) :
+ gx_fill_rectangle_device_rop(itox, iy, ix - itox + 1, 1,
+ pdevc, dev, lop)
+ );
+ }
+ if ( itox == ix ) /* vertical line */
+ { return (iy <= itoy ?
+ gx_fill_rectangle_device_rop(ix, iy, 1, itoy - iy + 1,
+ pdevc, dev, lop) :
+ gx_fill_rectangle_device_rop(ix, itoy, 1, iy - itoy + 1,
+ pdevc, dev, lop)
+ );
+ }
+ if ( color_writes_pure(pdevc, lop) &&
+ (*dev_proc(dev, draw_line))(dev, ix, iy, itox, itoy,
+ pdevc->colors.pure) >= 0
+ )
+ return 0;
+ { fixed h = fy1 - fy0;
+ fixed w = fx1 - fx0;
+ fixed tf;
+ bool swap_axes;
+ gs_fixed_edge left, right;
+
+#define fswap(a, b) tf = a, a = b, b = tf
+ if ( (w < 0 ? -w : w) <= (h < 0 ? -h : h) )
+ { if ( h < 0 )
+ fswap(fx0, fx1), fswap(fy0, fy1),
+ h = -h;
+ right.start.x = (left.start.x = fx0 - fixed_half) + fixed_1;
+ right.end.x = (left.end.x = fx1 - fixed_half) + fixed_1;
+ left.start.y = right.start.y = fy0;
+ left.end.y = right.end.y = fy1;
+ swap_axes = false;
+ }
+ else
+ { if ( w < 0 )
+ fswap(fx0, fx1), fswap(fy0, fy1),
+ w = -w;
+ right.start.x = (left.start.x = fy0 - fixed_half) + fixed_1;
+ right.end.x = (left.end.x = fy1 - fixed_half) + fixed_1;
+ left.start.y = right.start.y = fx0;
+ left.end.y = right.end.y = fx1;
+ swap_axes = true;
+ }
+ return (*dev_proc(dev, fill_trapezoid))(dev, &left, &right,
+ left.start.y, left.end.y,
+ swap_axes, pdevc, lop);
+#undef fswap
+ }
+}
+
+/* Stub out the obsolete procedure. */
+int
+gx_default_draw_line(gx_device *dev,
+ int x0, int y0, int x1, int y1, gx_color_index color)
+{ return -1;
+}
diff --git a/pstoraster/gdevdflt.c b/pstoraster/gdevdflt.c
new file mode 100644
index 000000000..27bcc9a3b
--- /dev/null
+++ b/pstoraster/gdevdflt.c
@@ -0,0 +1,878 @@
+/* Copyright (C) 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gdevdflt.c */
+/* Default device implementation */
+#include "gx.h"
+#include "gpcheck.h"
+#include "gserrors.h"
+#include "gxdevice.h"
+#include "gxdevmem.h"
+#include "gxcpath.h"
+
+/* ---------------- Default device procedures ---------------- */
+
+/* Define the default implementations of RasterOp procedures. */
+/* If the RasterOp option is linked in, it initializes these */
+/* to different values. */
+dev_proc_copy_rop((*gx_default_copy_rop_proc)) = gx_no_copy_rop;
+dev_proc_copy_rop((*gx_forward_copy_rop_proc)) = gx_no_copy_rop;
+dev_proc_strip_copy_rop((*gx_default_strip_copy_rop_proc)) = gx_no_strip_copy_rop;
+dev_proc_strip_copy_rop((*gx_forward_strip_copy_rop_proc)) = gx_no_strip_copy_rop;
+
+/* Fill in NULL procedures in a device procedure record. */
+void
+gx_device_fill_in_procs(register gx_device *dev)
+{ gx_device_set_procs(dev);
+ fill_dev_proc(dev, open_device, gx_default_open_device);
+ fill_dev_proc(dev, get_initial_matrix, gx_default_get_initial_matrix);
+ fill_dev_proc(dev, sync_output, gx_default_sync_output);
+ fill_dev_proc(dev, output_page, gx_default_output_page);
+ fill_dev_proc(dev, close_device, gx_default_close_device);
+ fill_dev_proc(dev, map_rgb_color, gx_default_map_rgb_color);
+ fill_dev_proc(dev, map_color_rgb, gx_default_map_color_rgb);
+ /* NOT fill_rectangle */
+ fill_dev_proc(dev, tile_rectangle, gx_default_tile_rectangle);
+ fill_dev_proc(dev, copy_mono, gx_default_copy_mono);
+ fill_dev_proc(dev, copy_color, gx_default_copy_color);
+ fill_dev_proc(dev, draw_line, gx_default_draw_line);
+ fill_dev_proc(dev, get_bits, gx_default_get_bits);
+ fill_dev_proc(dev, get_params, gx_default_get_params);
+ fill_dev_proc(dev, put_params, gx_default_put_params);
+ fill_dev_proc(dev, map_cmyk_color, gx_default_map_cmyk_color);
+ fill_dev_proc(dev, get_xfont_procs, gx_default_get_xfont_procs);
+ fill_dev_proc(dev, get_xfont_device, gx_default_get_xfont_device);
+ fill_dev_proc(dev, map_rgb_alpha_color, gx_default_map_rgb_alpha_color);
+ fill_dev_proc(dev, get_page_device, gx_default_get_page_device);
+ fill_dev_proc(dev, get_alpha_bits, gx_default_get_alpha_bits);
+ fill_dev_proc(dev, copy_alpha, gx_default_copy_alpha);
+ fill_dev_proc(dev, get_band, gx_default_get_band);
+ fill_dev_proc(dev, copy_rop, gx_default_copy_rop_proc);
+ fill_dev_proc(dev, fill_path, gx_default_fill_path);
+ fill_dev_proc(dev, stroke_path, gx_default_stroke_path);
+ fill_dev_proc(dev, fill_mask, gx_default_fill_mask);
+ fill_dev_proc(dev, fill_trapezoid, gx_default_fill_trapezoid);
+ fill_dev_proc(dev, fill_parallelogram, gx_default_fill_parallelogram);
+ fill_dev_proc(dev, fill_triangle, gx_default_fill_triangle);
+ fill_dev_proc(dev, draw_thin_line, gx_default_draw_thin_line);
+ fill_dev_proc(dev, begin_image, gx_default_begin_image);
+ fill_dev_proc(dev, image_data, gx_default_image_data);
+ fill_dev_proc(dev, end_image, gx_default_end_image);
+ fill_dev_proc(dev, strip_tile_rectangle, gx_default_strip_tile_rectangle);
+ fill_dev_proc(dev, strip_copy_rop, gx_default_strip_copy_rop_proc);
+}
+
+int
+gx_default_open_device(gx_device *dev)
+{ return 0;
+}
+
+/* Get the initial matrix for a device with inverted Y. */
+/* This includes essentially all printers and displays. */
+void
+gx_default_get_initial_matrix(gx_device *dev, register gs_matrix *pmat)
+{ pmat->xx = dev->HWResolution[0] / 72.0; /* x_pixels_per_inch */
+ pmat->xy = 0;
+ pmat->yx = 0;
+ pmat->yy = dev->HWResolution[1] / -72.0; /* y_pixels_per_inch */
+ /****** tx/y is WRONG for devices with ******/
+ /****** arbitrary initial matrix ******/
+ pmat->tx = 0;
+ pmat->ty = dev->height;
+}
+/* Get the initial matrix for a device with upright Y. */
+/* This includes just a few printers and window systems. */
+void
+gx_upright_get_initial_matrix(gx_device *dev, register gs_matrix *pmat)
+{ pmat->xx = dev->HWResolution[0] / 72.0; /* x_pixels_per_inch */
+ pmat->xy = 0;
+ pmat->yx = 0;
+ pmat->yy = dev->HWResolution[1] / 72.0; /* y_pixels_per_inch */
+ /****** tx/y is WRONG for devices with ******/
+ /****** arbitrary initial matrix ******/
+ pmat->tx = 0;
+ pmat->ty = 0;
+}
+
+int
+gx_default_sync_output(gx_device *dev)
+{ return 0;
+}
+
+int
+gx_default_output_page(gx_device *dev, int num_copies, int flush)
+{ return (*dev_proc(dev, sync_output))(dev);
+}
+
+int
+gx_default_close_device(gx_device *dev)
+{ return 0;
+}
+
+/* By default, implement tile_rectangle using strip_tile_rectangle. */
+int
+gx_default_tile_rectangle(gx_device *dev, const gx_tile_bitmap *tile,
+ int x, int y, int w, int h, gx_color_index color0, gx_color_index color1,
+ int px, int py)
+{ gx_strip_bitmap tiles;
+ *(gx_tile_bitmap *)&tiles = *tile;
+ tiles.shift = tiles.rep_shift = 0;
+ return (*dev_proc(dev, strip_tile_rectangle))
+ (dev, &tiles, x, y, w, h, color0, color1, px, py);
+}
+
+/* Implement copy_mono by filling lots of small rectangles. */
+/* This is very inefficient, but it works as a default. */
+int
+gx_default_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)
+{ byte invert;
+ gx_color_index color;
+ dev_proc_fill_rectangle((*fill));
+ const byte *row;
+ int iy;
+
+ fit_copy(dev, data, dx, raster, id, x, y, w, h);
+ fill = dev_proc(dev, fill_rectangle);
+ if ( one != gx_no_color_index )
+ { invert = 0;
+ color = one;
+ if ( zero != gx_no_color_index )
+ { int code = (*fill)(dev, x, y, w, h, zero);
+ if ( code < 0 )
+ return code;
+ }
+ }
+ else
+ { invert = 0xff;
+ color = zero;
+ }
+ for ( row = data, iy = 0; iy < h; row += raster, iy++ )
+ { int ix;
+ for ( ix = 0; ix < w; )
+ { int i0;
+#define row_bit(i) ((row[(i) >> 3] ^ invert) & (0x80 >> ((i) & 7)))
+ while ( ix < w && row_bit(ix + dx) == 0 )
+ ix++;
+ for ( i0 = ix; ix < w && row_bit(ix + dx) != 0; )
+ ix++;
+ if ( ix > i0 )
+ { int code = (*fill)(dev, i0 + x, iy + y, ix - i0, 1, color);
+ if ( code < 0 )
+ return code;
+ }
+#undef row_bit
+ }
+ }
+ return 0;
+}
+
+/* Implement copy_color by filling lots of small rectangles. */
+/* This is very inefficient, but it works as a default. */
+int
+gx_default_copy_color(gx_device *dev, const byte *data,
+ int dx, int raster, gx_bitmap_id id,
+ int x, int y, int w, int h)
+{ int depth = dev->color_info.depth;
+ byte mask;
+ dev_proc_fill_rectangle((*fill));
+ const byte *row;
+ int iy;
+
+ if ( depth == 1 )
+ return (*dev_proc(dev, copy_mono))(dev, data, dx, raster, id,
+ x, y, w, h,
+ (gx_color_index)0, (gx_color_index)1);
+ fit_copy(dev, data, dx, raster, id, x, y, w, h);
+ fill = dev_proc(dev, fill_rectangle);
+ mask = (byte)((1 << depth) - 1);
+ for ( row = data, iy = 0; iy < h; row += raster, ++iy )
+ { int ix;
+ gx_color_index c0 = gx_no_color_index;
+ const byte *ptr = row + ((dx * depth) >> 3);
+ int i0;
+
+ for ( i0 = ix = 0; ix < w; ++ix )
+ { gx_color_index color;
+
+ if ( depth >= 8 )
+ { color = *ptr++;
+ switch ( depth )
+ {
+ case 32: color = (color << 8) + *ptr++;
+ case 24: color = (color << 8) + *ptr++;
+ case 16: color = (color << 8) + *ptr++;
+ }
+ }
+ else
+ { uint dbit = (-(ix + dx + 1) * depth) & 7;
+ color = (*ptr >> dbit) & mask;
+ if ( dbit == 0 )
+ ptr++;
+ }
+ if ( color != c0 )
+ { if ( ix > i0 )
+ { int code = (*fill)
+ (dev, i0 + x, iy + y, ix - i0, 1, c0);
+ if ( code < 0 )
+ return code;
+ }
+ c0 = color;
+ i0 = ix;
+ }
+ }
+ if ( ix > i0 )
+ { int code = (*fill)(dev, i0 + x, iy + y, ix - i0, 1, c0);
+ if ( code < 0 )
+ return code;
+ }
+ }
+ return 0;
+}
+
+int
+gx_default_get_bits(gx_device *dev, int y, byte *data, byte **actual_data)
+{ return_error(gs_error_unknownerror);
+}
+
+gx_xfont_procs *
+gx_default_get_xfont_procs(gx_device *dev)
+{ return NULL;
+}
+
+gx_device *
+gx_default_get_xfont_device(gx_device *dev)
+{ return dev;
+}
+
+gx_device *
+gx_default_get_page_device(gx_device *dev)
+{ return NULL;
+}
+gx_device *
+gx_page_device_get_page_device(gx_device *dev)
+{ return dev;
+}
+
+int
+gx_default_get_alpha_bits(gx_device *dev, graphics_object_type type)
+{ return 1;
+}
+
+int
+gx_no_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)
+{ return_error(gs_error_unknownerror);
+}
+
+int
+gx_default_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 = &gs_memory_default; /* dev might not have one */
+ int bpp = dev->color_info.depth;
+ uint in_size = gx_device_raster(dev, false);
+ byte *lin;
+ uint out_size;
+ byte *lout;
+ int code = 0;
+ gx_color_value color_rgb[3];
+ 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, map_color_rgb))(dev, color, color_rgb);
+ for ( ry = y; ry < y + height; row += raster, ++ry )
+ { byte *line;
+ int sx, rx;
+ declare_line_accum(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);
+blend: if ( alpha == 15 )
+ { /* Just write the new color. */
+ composite = color;
+ }
+ else
+ { if ( previous == gx_no_color_index )
+ { /* Extract the old color. */
+ if ( bpp < 8 )
+ { const uint bit = rx * bpp;
+ const byte *src = line + (bit >> 3);
+ previous =
+ (*src >> (8 - (bit + bpp))) &
+ ((1 << bpp) - 1);
+ }
+ else
+ { const byte *src = line + (rx * (bpp >> 3));
+ previous = 0;
+ switch ( bpp >> 3 )
+ {
+ case 4:
+ previous += (gx_color_index)*src++ << 24;
+ case 3:
+ previous += (gx_color_index)*src++ << 16;
+ case 2:
+ previous += (gx_color_index)*src++ << 8;
+ case 1:
+ previous += *src++;
+ }
+ }
+ }
+ if ( alpha == 0 )
+ { /* Just write the old color. */
+ composite = previous;
+ }
+ else
+ { /* Blend RGB values. */
+ gx_color_value rgb[3];
+
+ (*dev_proc(dev, map_color_rgb))(dev, previous, rgb);
+#if arch_ints_are_short
+# define b_int long
+#else
+# define b_int int
+#endif
+#define make_shade(old, clr, alpha, amax) \
+ (old) + (((b_int)(clr) - (b_int)(old)) * (alpha) / (amax))
+ rgb[0] = make_shade(rgb[0], color_rgb[0], alpha, 15);
+ rgb[1] = make_shade(rgb[1], color_rgb[1], alpha, 15);
+ rgb[2] = make_shade(rgb[2], color_rgb[2], alpha, 15);
+#undef b_int
+#undef make_shade
+ composite =
+ (*dev_proc(dev, map_rgb_color))(dev, rgb[0],
+ rgb[1], rgb[2]);
+ if ( composite == gx_no_color_index )
+ { /* The device can't represent this color. */
+ /* Move the alpha value towards 0 or 1. */
+ if ( alpha == 7 ) /* move 1/2 towards 1 */
+ ++alpha;
+ alpha = (alpha & 8) | (alpha >> 1);
+ goto blend;
+ }
+ }
+ }
+ 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;
+ }
+}
+
+int
+gx_default_get_band(gx_device *dev, int y, int *band_start)
+{ return 0;
+}
+
+int
+gx_no_copy_rop(gx_device *dev,
+ const byte *sdata, int sourcex, uint sraster, gx_bitmap_id id,
+ const gx_color_index *scolors,
+ const gx_tile_bitmap *texture, const gx_color_index *tcolors,
+ int x, int y, int width, int height,
+ int phase_x, int phase_y, gs_logical_operation_t lop)
+{ return_error(gs_error_unknownerror); /* not implemented */
+}
+int
+gx_default_copy_rop(gx_device *dev,
+ const byte *sdata, int sourcex, uint sraster, gx_bitmap_id id,
+ const gx_color_index *scolors,
+ const gx_tile_bitmap *texture, const gx_color_index *tcolors,
+ int x, int y, int width, int height,
+ int phase_x, int phase_y, gs_logical_operation_t lop)
+{ return (*gx_default_copy_rop_proc)
+ (dev, sdata, sourcex, sraster, id, scolors, texture, tcolors,
+ x, y, width, height, phase_x, phase_y, lop);
+}
+
+int
+gx_default_fill_mask(gx_device *orig_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 *dev;
+ gx_device_clip cdev;
+ gx_color_index colors[2];
+ gx_strip_bitmap *tile;
+
+ if ( gx_dc_is_pure(pdcolor) )
+ { tile = 0;
+ colors[0] = gx_no_color_index;
+ colors[1] = gx_dc_pure_color(pdcolor);
+ }
+ else if ( gx_dc_is_binary_halftone(pdcolor) )
+ { tile = gx_dc_binary_tile(pdcolor);
+ colors[0] = gx_dc_binary_color0(pdcolor);
+ colors[1] = gx_dc_binary_color1(pdcolor);
+ }
+ else
+ return_error(gs_error_unknownerror); /* not implemented */
+ if ( pcpath != 0 )
+ { gx_make_clip_path_device(&cdev, pcpath);
+ cdev.target = orig_dev;
+ dev = (gx_device *)&cdev;
+ (*dev_proc(dev, open_device))(dev);
+ }
+ else
+ dev = orig_dev;
+ if ( depth > 1 )
+ { /****** CAN'T DO ROP OR HALFTONE WITH ALPHA ******/
+ return (*dev_proc(dev, copy_alpha))
+ (dev, data, dx, raster, id, x, y, w, h, colors[1], depth);
+ }
+ if ( lop != lop_default )
+ { gx_color_index scolors[2];
+
+ scolors[0] = 1;
+ scolors[1] = 0;
+ return (*dev_proc(dev, strip_copy_rop))
+ (dev, data, dx, raster, id, scolors, tile, colors,
+ x, y, w, h,
+ gx_dc_phase(pdcolor).x, gx_dc_phase(pdcolor).y, lop);
+ }
+ if ( tile == 0 )
+ { return (*dev_proc(dev, copy_mono))
+ (dev, data, dx, raster, id, x, y, w, h,
+ gx_no_color_index, colors[1]);
+ }
+ /*
+ * Use the same approach as the default copy_mono (above). We
+ * should really clip to the intersection of the bounding boxes of
+ * the device and the clipping path, but it's too much work.
+ */
+ fit_copy(orig_dev, data, dx, raster, id, x, y, w, h);
+ { dev_proc_strip_tile_rectangle((*tile_proc)) =
+ dev_proc(dev, strip_tile_rectangle);
+ const byte *row;
+ int iy;
+
+ for ( row = data, iy = 0; iy < h; row += raster, iy++ )
+ { int ix;
+ for ( ix = 0; ix < w; )
+ { int i0;
+#define row_bit(i) (row[(i) >> 3] & (0x80 >> ((i) & 7)))
+ while ( ix < w && row_bit(ix + dx) == 0 )
+ ix++;
+ for ( i0 = ix; ix < w && row_bit(ix + dx) != 0; )
+ ix++;
+ if ( ix > i0 )
+ { int code = (*tile_proc)
+ (dev, tile, i0 + x, iy + y, ix - i0, 1,
+ colors[0], colors[1],
+ gx_dc_phase(pdcolor).x, gx_dc_phase(pdcolor).y);
+ if ( code < 0 )
+ return code;
+ }
+#undef row_bit
+ }
+ }
+ }
+ return 0;
+}
+
+/* Default implementation of strip_tile_rectangle */
+int
+gx_default_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)
+{ /* Fill the rectangle in chunks. */
+ int width = tiles->size.x;
+ int height = tiles->size.y;
+ int raster = tiles->raster;
+ int rwidth = tiles->rep_width;
+ int rheight = tiles->rep_height;
+ int shift = tiles->shift;
+ int xoff =
+ (shift == 0 ? px :
+ px + (y + py) / rheight * tiles->rep_shift);
+ int irx = ((rwidth & (rwidth - 1)) == 0 ? /* power of 2 */
+ (x + xoff) & (rwidth - 1) :
+ (x + xoff) % rwidth);
+ int ry = ((rheight & (rheight - 1)) == 0 ? /* power of 2 */
+ (y + py) & (rheight - 1) :
+ (y + py) % rheight);
+ int icw = width - irx;
+ int ch = height - ry;
+ byte *row = tiles->data + ry * raster;
+ dev_proc_copy_mono((*proc_mono));
+ dev_proc_copy_color((*proc_color));
+ int code;
+
+#ifdef DEBUG
+if ( gs_debug_c('t') )
+ { int ptx, pty;
+ const byte *ptp = tiles->data;
+ dprintf3("[t]tile %dx%d raster=%d;",
+ tiles->size.x, tiles->size.y, tiles->raster);
+ dprintf6(" x,y=%d,%d w,h=%d,%d p=%d,%d\n",
+ x, y, w, h, px, py);
+ for ( pty = 0; pty < tiles->size.y; pty++ )
+ { dprintf(" ");
+ for ( ptx = 0; ptx < tiles->raster; ptx++ )
+ dprintf1("%3x", *ptp++);
+ }
+ dputc('\n');
+ }
+#endif
+
+ /*
+ * We should really make the following check before doing
+ * all the computations above, but since we expect devices
+ * to implement strip_tile_rectangle rather than tile_rectangle,
+ * the check will rarely succeed.
+ */
+ if ( dev_proc(dev, tile_rectangle) != gx_default_tile_rectangle )
+ { if ( shift == 0 )
+ { /*
+ * Temporarily patch the tile_rectangle procedure in the
+ * device so we don't get into a recursion loop if the
+ * device has a tile_rectangle procedure that conditionally
+ * calls the strip_tile_rectangle procedure.
+ */
+ dev_proc_tile_rectangle((*tile_proc)) =
+ dev_proc(dev, tile_rectangle);
+ int code;
+
+ set_dev_proc(dev, tile_rectangle, gx_default_tile_rectangle);
+ code = (*tile_proc)
+ (dev, (const gx_tile_bitmap *)tiles, x, y, w, h,
+ color0, color1, px, py);
+ set_dev_proc(dev, tile_rectangle, tile_proc);
+ return code;
+ }
+ /* We should probably optimize this case too, for the benefit */
+ /* of window systems, but we don't yet. */
+ }
+
+ if ( color0 == gx_no_color_index && color1 == gx_no_color_index )
+ proc_color = dev_proc(dev, copy_color);
+ else
+ proc_color = 0, proc_mono = dev_proc(dev, copy_mono);
+
+/****** SHOULD ALSO PASS id IF COPYING A FULL TILE ******/
+#define real_copy_tile(srcx, tx, ty, tw, th)\
+ code =\
+ (proc_color != 0 ?\
+ (*proc_color)(dev, row, srcx, raster, gx_no_bitmap_id, tx, ty, tw, th) :\
+ (*proc_mono)(dev, row, srcx, raster, gx_no_bitmap_id, tx, ty, tw, th, color0, color1));\
+ if ( code < 0 ) return_error(code);\
+ return_if_interrupt()
+#ifdef DEBUG
+#define copy_tile(sx, tx, ty, tw, th)\
+ if ( gs_debug_c('t') )\
+ dprintf5(" copy sx=%d x=%d y=%d w=%d h=%d\n",\
+ sx, tx, ty, tw, th);\
+ real_copy_tile(sx, tx, ty, tw, th)
+#else
+#define copy_tile(sx, tx, ty, tw, th)\
+ real_copy_tile(sx, tx, ty, tw, th)
+#endif
+ if ( ch >= h )
+ { /* Shallow operation */
+ if ( icw >= w )
+ { /* Just one (partial) tile to transfer. */
+ copy_tile(irx, x, y, w, h);
+ }
+ else
+ { int ex = x + w;
+ int fex = ex - width;
+ int cx = x + icw;
+
+ copy_tile(irx, x, y, icw, h);
+ while ( cx <= fex )
+ { copy_tile(0, cx, y, width, h);
+ cx += width;
+ }
+ if ( cx < ex )
+ { copy_tile(0, cx, y, ex - cx, h);
+ }
+ }
+ }
+ else if ( icw >= w && shift == 0 )
+ { /* Narrow operation, no shift */
+ int ey = y + h;
+ int fey = ey - height;
+ int cy = y + ch;
+
+ copy_tile(irx, x, y, w, ch);
+ row = tiles->data;
+ do
+ { ch = (cy > fey ? ey - cy : height);
+ copy_tile(irx, x, cy, w, ch);
+ }
+ while ( (cy += ch) < ey );
+ }
+ else
+ { /* Full operation. If shift != 0, some scan lines */
+ /* may be narrow. We could test shift == 0 in advance */
+ /* and use a slightly faster loop, but right now */
+ /* we don't bother. */
+ int ex = x + w, ey = y + h;
+ int fex = ex - width, fey = ey - height;
+ int cx, cy;
+
+ for ( cy = y; ; )
+ { if ( icw >= w )
+ { copy_tile(irx, x, cy, w, ch);
+ }
+ else
+ { copy_tile(irx, x, cy, icw, ch);
+ cx = x + icw;
+ while ( cx <= fex )
+ { copy_tile(0, cx, cy, width, ch);
+ cx += width;
+ }
+ if ( cx < ex )
+ { copy_tile(0, cx, cy, ex - cx, ch);
+ }
+ }
+ if ( (cy += ch) >= ey ) break;
+ ch = (cy > fey ? ey - cy : height);
+ if ( (irx += shift) >= rwidth )
+ irx -= rwidth;
+ icw = width - irx;
+ row = tiles->data;
+ }
+ }
+#undef copy_tile
+#undef real_copy_tile
+ return 0;
+}
+
+int
+gx_no_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)
+{ return_error(gs_error_unknownerror); /* not implemented */
+}
+int
+gx_default_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)
+{ return (*gx_default_strip_copy_rop_proc)
+ (dev, sdata, sourcex, sraster, id, scolors, textures, tcolors,
+ x, y, width, height, phase_x, phase_y, lop);
+}
+
+/* The following is not really a device procedure. See gxdevice.h. */
+
+/* Create an ordinary memory device for page or band buffering. */
+int
+gx_default_make_buffer_device(gx_device_memory *mdev,
+ gx_device *target, gs_memory_t *mem, bool for_band)
+{ const gx_device_memory *mdproto =
+ gdev_mem_device_for_bits(target->color_info.depth);
+
+ if ( mdproto == 0 )
+ return_error(gs_error_rangecheck);
+ if ( target == (gx_device *)mdev )
+ mdev->std_procs = mdproto->std_procs;
+ else
+ gs_make_mem_device(mdev, mdproto, mem, (for_band ? 1 : 0),
+ (target == (gx_device *)mdev ? 0 : target));
+ return 0;
+}
+
+/* ---------------- Default per-instance procedures ---------------- */
+
+int
+gx_default_install(gx_device *dev, gs_state *pgs)
+{ return 0;
+}
+
+int
+gx_default_begin_page(gx_device *dev, gs_state *pgs)
+{ return 0;
+}
+
+int
+gx_default_end_page(gx_device *dev, int reason, gs_state *pgs)
+{ return (reason != 2 ? 1 : 0);
+}
+
+/* ---------------- Unaligned copy operations ---------------- */
+
+/*
+ * Implementing unaligned operations in terms of the standard aligned
+ * operations requires adjusting the bitmap origin and/or the raster
+ * to be aligned. Adjusting the origin is simple, but non-portable,
+ * since there is no portable way to determine the alignment of a pointer.
+ * Adjusting the raster requires doing the operation one scan line at a time.
+ */
+
+int
+gx_copy_mono_unaligned(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)
+{ dev_proc_copy_mono((*copy_mono)) = dev_proc(dev, copy_mono);
+ uint offset = (uint)(data - (const byte *)0) & (align_bitmap_mod - 1);
+ int step = raster & (align_bitmap_mod - 1);
+
+ /* Adjust the origin. */
+ data -= offset;
+ dx += offset << 3;
+
+ /* Adjust the raster. */
+ if ( !step )
+ { /* No adjustment needed. */
+ return (*copy_mono)(dev, data, dx, raster, id,
+ x, y, w, h, zero, one);
+ }
+
+ /* Do the transfer one scan line at a time. */
+ { const byte *p = data;
+ int d = dx;
+ int code = 0;
+ int i;
+
+ for ( i = 0; i < h && code >= 0;
+ ++i, p += raster - step, d += step << 3
+ )
+ code = (*copy_mono)(dev, p, d, raster, gx_no_bitmap_id,
+ x, y + i, w, 1, zero, one);
+ return code;
+ }
+}
+
+int
+gx_copy_color_unaligned(gx_device *dev, const byte *data,
+ int data_x, int raster, gx_bitmap_id id,
+ int x, int y, int width, int height)
+{ dev_proc_copy_color((*copy_color)) = dev_proc(dev, copy_color);
+ int depth = dev->color_info.depth;
+ uint offset = (uint)(data - (const byte *)0) & (align_bitmap_mod - 1);
+ int step = raster & (align_bitmap_mod - 1);
+
+ /*
+ * Adjust the origin.
+ * We have to do something very special for 24-bit data,
+ * because that is the only depth that doesn't divide
+ * align_bitmap_mod exactly. In particular, we need to find
+ * M*B + R == 0 mod 3, where M is align_bitmap_mod, R is the
+ * offset value just calculated, and B is an integer unknown;
+ * the new value of offset will be M*B + R.
+ */
+ if ( depth == 24 )
+ offset += (offset % 3) *
+ (align_bitmap_mod * (3 - (align_bitmap_mod % 3)));
+ data -= offset;
+ data_x += (offset << 3) / depth;
+
+ /* Adjust the raster. */
+ if ( !step )
+ { /* No adjustment needed. */
+ return (*copy_color)(dev, data, data_x, raster, id,
+ x, y, width, height);
+ }
+
+ /* Do the transfer one scan line at a time. */
+ { const byte *p = data;
+ int d = data_x;
+ int dstep = (step << 3) / depth;
+ int code = 0;
+ int i;
+
+ for ( i = 0; i < height && code >= 0;
+ ++i, p += raster - step, d += dstep
+ )
+ code = (*copy_color)(dev, p, d, raster, gx_no_bitmap_id,
+ x, y + i, width, 1);
+ return code;
+ }
+}
+
+int
+gx_copy_alpha_unaligned(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)
+{ dev_proc_copy_alpha((*copy_alpha)) = dev_proc(dev, copy_alpha);
+ uint offset = (uint)(data - (const byte *)0) & (align_bitmap_mod - 1);
+ int step = raster & (align_bitmap_mod - 1);
+
+ /* Adjust the origin. */
+ data -= offset;
+ data_x += (offset << 3) / depth;
+
+ /* Adjust the raster. */
+ if ( !step )
+ { /* No adjustment needed. */
+ return (*copy_alpha)(dev, data, data_x, raster, id,
+ x, y, width, height, color, depth);
+ }
+
+ /* Do the transfer one scan line at a time. */
+ { const byte *p = data;
+ int d = data_x;
+ int dstep = (step << 3) / depth;
+ int code = 0;
+ int i;
+
+ for ( i = 0; i < height && code >= 0;
+ ++i, p += raster - step, d += dstep
+ )
+ code = (*copy_alpha)(dev, p, d, raster, gx_no_bitmap_id,
+ x, y + i, width, 1, color, depth);
+ return code;
+ }
+}
diff --git a/pstoraster/gdevemap.c b/pstoraster/gdevemap.c
new file mode 100644
index 000000000..939a83c9d
--- /dev/null
+++ b/pstoraster/gdevemap.c
@@ -0,0 +1,64 @@
+/* Copyright (C) 1993 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gdevemap.c */
+/* Mappings between StandardEncoding and ISOLatin1Encoding */
+#include "std.h"
+
+const byte far_data gs_map_std_to_iso[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, 173, 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,
+ 0, 161, 162, 163, 0, 165, 0, 167, 164, 0, 0, 171, 0, 0, 0, 0,
+ 0, 0, 0, 0, 183, 0, 182, 0, 0, 0, 0, 187, 0, 0, 0, 191,
+ 0, 145, 180, 147, 148, 175, 150, 151, 168, 0, 154, 184, 0, 157, 158, 159,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 198, 0, 170, 0, 0, 0, 0, 0, 216, 0, 186, 0, 0, 0, 0,
+ 0, 230, 0, 0, 0, 144, 0, 0, 0, 248, 0, 223, 0, 0, 0, 0
+};
+
+const byte far_data gs_map_iso_to_std[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,
+ 245, 193, 194, 195, 196, 197, 198, 199, 200, 0, 202, 203, 0, 205, 206, 207,
+ 32, 161, 162, 163, 168, 165, 0, 167, 200, 0, 227, 171, 0, 45, 0, 197,
+ 0, 0, 0, 0, 194, 0, 182, 180, 203, 0, 235, 187, 0, 0, 0, 191,
+ 0, 0, 0, 0, 0, 0, 225, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 233, 0, 0, 0, 0, 0, 0, 251,
+ 0, 0, 0, 0, 0, 0, 241, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 249, 0, 0, 0, 0, 0, 0, 0
+};
diff --git a/pstoraster/gdevht.h b/pstoraster/gdevht.h
new file mode 100644
index 000000000..77bf5f28b
--- /dev/null
+++ b/pstoraster/gdevht.h
@@ -0,0 +1,47 @@
+/* Copyright (C) 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gdevht.h */
+/* Definitions for halftoning device */
+/* Requires gxdevice.h */
+#include "gzht.h"
+
+/*
+ * A halftoning device converts between a non-halftoned device color space
+ * (e.g., 8-bit gray) and a halftoned space (e.g., 1-bit black and white).
+ * Currently, the target space must not exceed 8 bits per pixel, so that
+ * we can pack two target colors and a halftone level into a gx_color_index.
+ */
+#define ht_target_max_depth 8
+#define ht_level_depth (sizeof(gx_color_index) * 8 - ht_target_max_depth * 2)
+typedef struct gx_device_ht_s {
+ gx_device_forward_common;
+ /* Following are set before opening. */
+ const gx_device_halftone *dev_ht;
+ gs_int_point ht_phase; /* halftone phase from gstate */
+ /* Following are computed when device is opened. */
+ gs_int_point phase; /* halftone tile offset */
+} gx_device_ht;
+
+/* Macro for casting gx_device argument */
+#define htdev ((gx_device_ht *)dev)
diff --git a/pstoraster/gdevm1.c b/pstoraster/gdevm1.c
new file mode 100644
index 000000000..f0787d56b
--- /dev/null
+++ b/pstoraster/gdevm1.c
@@ -0,0 +1,689 @@
+/* Copyright (C) 1989, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gdevm1.c */
+/* Monobit "memory" (stored bitmap) device */
+#include "memory_.h"
+#include "gx.h"
+#include "gxdevice.h"
+#include "gxdevmem.h" /* semi-public definitions */
+#include "gdevmem.h" /* private definitions */
+
+extern dev_proc_strip_copy_rop(mem_mono_strip_copy_rop); /* in gdevmrop.c */
+
+/* Optionally, use the slow RasterOp implementations for testing. */
+/*#define USE_COPY_ROP*/
+
+#ifdef USE_COPY_ROP
+#include "gsrop.h"
+#endif
+
+/* ================ Standard (byte-oriented) device ================ */
+
+/* We went to a lot of trouble to optimize mem_mono_tile_rectangle. */
+/* It has a substantial effect on the total time at high resolutions. */
+/* However, it takes quite a lot of code, so we omit it on 16-bit systems. */
+#define OPTIMIZE_TILE (arch_sizeof_int > 2)
+
+/* Procedures */
+private dev_proc_map_rgb_color(mem_mono_map_rgb_color);
+private dev_proc_map_color_rgb(mem_mono_map_color_rgb);
+private dev_proc_copy_mono(mem_mono_copy_mono);
+private dev_proc_fill_rectangle(mem_mono_fill_rectangle);
+#if OPTIMIZE_TILE
+private dev_proc_strip_tile_rectangle(mem_mono_strip_tile_rectangle);
+#else
+# define mem_mono_strip_tile_rectangle gx_default_strip_tile_rectangle
+#endif
+
+/* The device descriptor. */
+/* The instance is public. */
+const gx_device_memory far_data mem_mono_device =
+ mem_full_alpha_device("image1", 0, 1, mem_open,
+ mem_mono_map_rgb_color, mem_mono_map_color_rgb,
+ mem_mono_copy_mono, gx_default_copy_color, mem_mono_fill_rectangle,
+ mem_get_bits, gx_default_map_cmyk_color, gx_no_copy_alpha,
+ mem_mono_strip_tile_rectangle, mem_mono_strip_copy_rop);
+
+/* Map color to/from RGB. This may be inverted. */
+private gx_color_index
+mem_mono_map_rgb_color(gx_device *dev, gx_color_value r, gx_color_value g,
+ gx_color_value b)
+{ return (gx_default_w_b_map_rgb_color(dev, r, g, b) ^
+ mdev->palette.data[0]) & 1;
+}
+private int
+mem_mono_map_color_rgb(gx_device *dev, gx_color_index color,
+ gx_color_value prgb[3])
+{ return gx_default_w_b_map_color_rgb(dev,
+ (color ^ mdev->palette.data[0]) & 1,
+ prgb);
+}
+
+/* Fill a rectangle with a color. */
+private int
+mem_mono_fill_rectangle(gx_device *dev, int x, int y, int w, int h,
+ gx_color_index color)
+{
+#ifdef USE_COPY_ROP
+ return mem_mono_copy_rop(dev, NULL, 0, 0, gx_no_bitmap_id, NULL,
+ NULL, NULL,
+ x, y, w, h, 0, 0,
+ (color ? rop3_1 : rop3_0));
+#else
+ fit_fill(dev, x, y, w, h);
+ bits_fill_rectangle(scan_line_base(mdev, y), x, mdev->raster,
+ -(mono_fill_chunk)color, w, h);
+ return 0;
+#endif
+}
+
+/* Convert x coordinate to byte offset in scan line. */
+#define x_to_byte(x) ((x) >> 3)
+
+/* Copy a monochrome bitmap. */
+#undef mono_masks
+#define mono_masks mono_copy_masks
+
+/* Fetch a chunk from the source. */
+/* The source data are always stored big-endian. */
+/* Note that the macros always cast cptr, */
+/* so it doesn't matter what the type of cptr is. */
+/* cshift = chunk_bits - shift. */
+#undef chunk
+#if arch_is_big_endian
+# define chunk uint
+# define cfetch_right(cptr, shift, cshift)\
+ (cfetch_aligned(cptr) >> shift)
+# define cfetch_left(cptr, shift, cshift)\
+ (cfetch_aligned(cptr) << shift)
+/* Fetch a chunk that straddles a chunk boundary. */
+# define cfetch2(cptr, cskew, skew)\
+ (cfetch_left(cptr, cskew, skew) +\
+ cfetch_right((const chunk *)(cptr) + 1, skew, cskew))
+#else /* little-endian */
+# define chunk bits16
+private const bits16 right_masks2[9] = {
+ 0xffff, 0x7f7f, 0x3f3f, 0x1f1f, 0x0f0f, 0x0707, 0x0303, 0x0101, 0x0000
+};
+private const bits16 left_masks2[9] = {
+ 0xffff, 0xfefe, 0xfcfc, 0xf8f8, 0xf0f0, 0xe0e0, 0xc0c0, 0x8080, 0x0000
+};
+# define ccont(cptr, off) (((const chunk *)(cptr))[off])
+# define cfetch_right(cptr, shift, cshift)\
+ ((shift) < 8 ?\
+ ((ccont(cptr, 0) >> (shift)) & right_masks2[shift]) +\
+ (ccont(cptr, 0) << (cshift)) :\
+ ((chunk)*(const byte *)(cptr) << (cshift)) & 0xff00)
+# define cfetch_left(cptr, shift, cshift)\
+ ((shift) < 8 ?\
+ ((ccont(cptr, 0) << (shift)) & left_masks2[shift]) +\
+ (ccont(cptr, 0) >> (cshift)) :\
+ ((ccont(cptr, 0) & 0xff00) >> (cshift)) & 0xff)
+/* Fetch a chunk that straddles a chunk boundary. */
+/* We can avoid testing the shift amount twice */
+/* by expanding the cfetch_left/right macros in-line. */
+# define cfetch2(cptr, cskew, skew)\
+ ((cskew) < 8 ?\
+ ((ccont(cptr, 0) << (cskew)) & left_masks2[cskew]) +\
+ (ccont(cptr, 0) >> (skew)) +\
+ (((chunk)(((const byte *)(cptr))[2]) << (cskew)) & 0xff00) :\
+ (((ccont(cptr, 0) & 0xff00) >> (skew)) & 0xff) +\
+ ((ccont(cptr, 1) >> (skew)) & right_masks2[skew]) +\
+ (ccont(cptr, 1) << (cskew)))
+#endif
+/* Since source and destination are both always big-endian, */
+/* fetching an aligned chunk never requires byte swapping. */
+# define cfetch_aligned(cptr)\
+ (*(const chunk *)(cptr))
+
+/* copy_function and copy_shift get added together for dispatch */
+typedef enum {
+ copy_or = 0, copy_store, copy_and, copy_funny
+} copy_function;
+/* copy_right/left is not an enum, because compilers complain about */
+/* an enumeration clash when these are added to a copy_function. */
+#define copy_right ((copy_function)0)
+#define copy_left ((copy_function)4)
+typedef struct {
+ short invert;
+ ushort op; /* copy_function */
+} copy_mode;
+/* Map from <c0,c1> to copy_mode. */
+#define cm(i,op) { i, (ushort)op }
+private copy_mode copy_modes[9] = {
+ cm(-1, copy_funny), /* NN */
+ cm(-1, copy_and), /* N0 */
+ cm(0, copy_or), /* N1 */
+ cm(0, copy_and), /* 0N */
+ cm(0, copy_funny), /* 00 */
+ cm(0, copy_store), /* 01 */
+ cm(-1, copy_or), /* 1N */
+ cm(-1, copy_store), /* 10 */
+ cm(0, copy_funny), /* 11 */
+};
+private int
+mem_mono_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 zero, gx_color_index one)
+{
+#ifdef USE_COPY_ROP
+ return mem_mono_copy_rop(dev, base, sourcex, sraster, id, NULL,
+ NULL, NULL,
+ x, y, w, h, 0, 0,
+ ((zero == gx_no_color_index ? rop3_D :
+ zero == 0 ? rop3_0 : rop3_1) & ~rop3_S) |
+ ((one == gx_no_color_index ? rop3_D :
+ one == 0 ? rop3_0 : rop3_1) & rop3_S));
+#else /* !USE_COPY_ROP */
+ register const byte *bptr; /* actually chunk * */
+ int dbit, wleft;
+ uint mask;
+ copy_mode mode;
+#define function (copy_function)(mode.op)
+ declare_scan_ptr_as(dbptr, byte *);
+#define optr ((chunk *)dbptr)
+ register int skew;
+ register uint invert;
+ fit_copy(dev, base, sourcex, sraster, id, x, y, w, h);
+#if gx_no_color_index_value != -1 /* hokey! */
+ if ( zero == gx_no_color_index ) zero = -1;
+ if ( one == gx_no_color_index ) one = -1;
+#endif
+#define izero (int)zero
+#define ione (int)one
+ mode = copy_modes[izero + izero + izero + ione + 4];
+#undef izero
+#undef ione
+ invert = (uint)(int)mode.invert; /* load register */
+ setup_rect_as(dbptr, byte *);
+ bptr = base + ((sourcex & ~chunk_align_bit_mask) >> 3);
+ dbit = x & chunk_align_bit_mask;
+ skew = dbit - (sourcex & chunk_align_bit_mask);
+/* Macros for writing partial chunks. */
+/* The destination pointer is always named optr, */
+/* and must be declared as chunk *. */
+/* cinvert may be temporarily redefined. */
+#define cinvert(bits) ((bits) ^ invert)
+#define write_or_masked(bits, mask, off)\
+ optr[off] |= (cinvert(bits) & mask)
+#define write_store_masked(bits, mask, off)\
+ optr[off] = ((optr[off] & ~mask) | (cinvert(bits) & mask))
+#define write_and_masked(bits, mask, off)\
+ optr[off] &= (cinvert(bits) | ~mask)
+/* Macros for writing full chunks. */
+#define write_or(bits) *optr |= cinvert(bits)
+#define write_store(bits) *optr = cinvert(bits)
+#define write_and(bits) *optr &= cinvert(bits)
+/* Macro for incrementing to next chunk. */
+#define next_x_chunk\
+ bptr += chunk_bytes; dbptr += chunk_bytes
+/* Common macro for the end of each scan line. */
+#define end_y_loop(sdelta, ddelta)\
+ if ( --h == 0 ) break;\
+ bptr += sdelta; dbptr += ddelta
+ if ( (wleft = w + dbit - chunk_bits) <= 0 )
+ { /* The entire operation fits in one (destination) chunk. */
+ set_mono_thin_mask(mask, w, dbit);
+#define write_single(wr_op, src)\
+ for ( ; ; )\
+ { wr_op(src, mask, 0);\
+ end_y_loop(sraster, draster);\
+ }
+#define write1_loop(src)\
+ switch ( function ) {\
+ case copy_or: write_single(write_or_masked, src); break;\
+ case copy_store: write_single(write_store_masked, src); break;\
+ case copy_and: write_single(write_and_masked, src); break;\
+ default: goto funny;\
+ }
+ if ( skew >= 0 ) /* single -> single, right/no shift */
+ { if ( skew == 0 ) /* no shift */
+ { write1_loop(cfetch_aligned(bptr));
+ }
+ else /* right shift */
+ { int cskew = chunk_bits - skew;
+ write1_loop(cfetch_right(bptr, skew, cskew));
+ }
+ }
+ else if ( wleft <= skew ) /* single -> single, left shift */
+ { int cskew = chunk_bits + skew;
+ skew = -skew;
+ write1_loop(cfetch_left(bptr, skew, cskew));
+ }
+ else /* double -> single */
+ { int cskew = -skew;
+ skew += chunk_bits;
+ write1_loop(cfetch2(bptr, cskew, skew));
+ }
+#undef write1_loop
+#undef write_single
+ }
+ else if ( wleft <= skew )
+ { /* 1 source chunk -> 2 destination chunks. */
+ /* This is an important special case for */
+ /* both characters and halftone tiles. */
+ uint rmask;
+ int cskew = chunk_bits - skew;
+ set_mono_left_mask(mask, dbit);
+ set_mono_right_mask(rmask, wleft);
+#undef cinvert
+#define cinvert(bits) (bits) /* pre-inverted here */
+#if arch_is_big_endian /* no byte swapping */
+# define write_1to2(wr_op)\
+ for ( ; ; )\
+ { register uint bits = cfetch_aligned(bptr) ^ invert;\
+ wr_op(bits >> skew, mask, 0);\
+ wr_op(bits << cskew, rmask, 1);\
+ end_y_loop(sraster, draster);\
+ }
+#else /* byte swapping */
+# define write_1to2(wr_op)\
+ for ( ; ; )\
+ { wr_op(cfetch_right(bptr, skew, cskew) ^ invert, mask, 0);\
+ wr_op(cfetch_left(bptr, cskew, skew) ^ invert, rmask, 1);\
+ end_y_loop(sraster, draster);\
+ }
+#endif
+ switch ( function )
+ {
+ case copy_or: write_1to2(write_or_masked); break;
+ case copy_store: write_1to2(write_store_masked); break;
+ case copy_and: write_1to2(write_and_masked); break;
+ default: goto funny;
+ }
+#undef cinvert
+#define cinvert(bits) ((bits) ^ invert)
+#undef write_1to2
+ }
+ else
+ { /* More than one source chunk and more than one */
+ /* destination chunk are involved. */
+ uint rmask;
+ int words = (wleft & ~chunk_bit_mask) >> 3;
+ uint sskip = sraster - words;
+ uint dskip = draster - words;
+ register uint bits;
+ set_mono_left_mask(mask, dbit);
+ set_mono_right_mask(rmask, wleft & chunk_bit_mask);
+ if ( skew == 0 ) /* optimize the aligned case */
+ {
+#define write_aligned(wr_op, wr_op_masked)\
+ for ( ; ; )\
+ { int count = wleft;\
+ /* Do first partial chunk. */\
+ wr_op_masked(cfetch_aligned(bptr), mask, 0);\
+ /* Do full chunks. */\
+ while ( (count -= chunk_bits) >= 0 )\
+ { next_x_chunk; wr_op(cfetch_aligned(bptr)); }\
+ /* Do last chunk */\
+ if ( count > -chunk_bits )\
+ { wr_op_masked(cfetch_aligned(bptr + chunk_bytes), rmask, 1); }\
+ end_y_loop(sskip, dskip);\
+ }
+ switch ( function )
+ {
+ case copy_or:
+ write_aligned(write_or, write_or_masked);
+ break;
+ case copy_store:
+ write_aligned(write_store, write_store_masked);
+ break;
+ case copy_and:
+ write_aligned(write_and, write_and_masked);
+ break;
+ default:
+ goto funny;
+ }
+#undef write_aligned
+ }
+ else /* not aligned */
+ { int ccase =
+ (skew >= 0 ? copy_right :
+ ((bptr += chunk_bytes), copy_left))
+ + (int)function;
+ int cskew = -skew & chunk_bit_mask;
+ skew &= chunk_bit_mask;
+ for ( ; ; )
+ { int count = wleft;
+#define prefetch_right\
+ bits = cfetch_right(bptr, skew, cskew)
+#define prefetch_left\
+ bits = cfetch2(bptr - chunk_bytes, cskew, skew)
+#define write_unaligned(wr_op, wr_op_masked)\
+ wr_op_masked(bits, mask, 0);\
+ /* Do full chunks. */\
+ while ( count >= chunk_bits )\
+ { bits = cfetch2(bptr, cskew, skew);\
+ next_x_chunk; wr_op(bits); count -= chunk_bits;\
+ }\
+ /* Do last chunk */\
+ if ( count > 0 )\
+ { bits = cfetch_left(bptr, cskew, skew);\
+ if ( count > skew ) bits += cfetch_right(bptr + chunk_bytes, skew, cskew);\
+ wr_op_masked(bits, rmask, 1);\
+ }
+ switch ( ccase )
+ {
+ case copy_or + copy_left:
+ prefetch_left; goto uor;
+ case copy_or + copy_right:
+ prefetch_right;
+uor: write_unaligned(write_or, write_or_masked);
+ break;
+ case copy_store + copy_left:
+ prefetch_left; goto ustore;
+ case copy_store + copy_right:
+ prefetch_right;
+ustore: write_unaligned(write_store, write_store_masked);
+ break;
+ case copy_and + copy_left:
+ prefetch_left; goto uand;
+ case copy_and + copy_right:
+ prefetch_right;
+uand: write_unaligned(write_and, write_and_masked);
+ break;
+ default:
+ goto funny;
+ }
+ end_y_loop(sskip, dskip);
+#undef write_unaligned
+#undef prefetch_left
+#undef prefetch_right
+ }
+ }
+ }
+#undef end_y_loop
+#undef next_x_chunk
+ return 0;
+ /* Handle the funny cases that aren't supposed to happen. */
+funny: return (invert ? -1 : mem_mono_fill_rectangle(dev, x, y, w, h, zero));
+#undef optr
+#endif /* !USE_COPY_ROP */
+}
+
+#if OPTIMIZE_TILE /**************** ****************/
+
+/* Strip-tile with a monochrome halftone. */
+/* This is a performance bottleneck for monochrome devices, */
+/* so we re-implement it, even though it takes a lot of code. */
+private int
+mem_mono_strip_tile_rectangle(gx_device *dev,
+ register const gx_strip_bitmap *tiles,
+ int tx, int y, int tw, int th, gx_color_index color0, gx_color_index color1,
+ int px, int py)
+{
+#ifdef USE_COPY_ROP
+ return mem_mono_strip_copy_rop(dev, NULL, 0, 0, tile->id, NULL,
+ tiles, NULL,
+ tx, y, tw, th, px, py,
+ ((color0 == gx_no_color_index ? rop3_D :
+ color0 == 0 ? rop3_0 : rop3_1) & ~rop3_T) |
+ ((color1 == gx_no_color_index ? rop3_D :
+ color1 == 0 ? rop3_0 : rop3_1) & rop3_T));
+#else /* !USE_COPY_ROP */
+ register uint invert;
+ int sraster;
+ uint tile_bits_size;
+ const byte *base;
+ const byte *end;
+ int x, rw, w, h;
+ register const byte *bptr; /* actually chunk * */
+ int dbit, wleft;
+ uint mask;
+ byte *dbase;
+ declare_scan_ptr_as(dbptr, byte *);
+#define optr ((chunk *)dbptr)
+ register int skew;
+
+ /* This implementation doesn't handle strips yet. */
+ if ( color0 != (color1 ^ 1) || tiles->shift != 0 )
+ return gx_default_strip_tile_rectangle(dev, tiles, tx, y, tw, th,
+ color0, color1, px, py);
+ fit_fill(dev, tx, y, tw, th);
+ invert = -(uint)color0;
+ sraster = tiles->raster;
+ base = tiles->data + ((y + py) % tiles->rep_height) * sraster;
+ tile_bits_size = tiles->size.y * sraster;
+ end = tiles->data + tile_bits_size;
+#undef end_y_loop
+#define end_y_loop(sdelta, ddelta)\
+ if ( --h == 0 ) break;\
+ if ( end - bptr <= sdelta ) /* wrap around */\
+ bptr -= tile_bits_size;\
+ bptr += sdelta; dbptr += ddelta
+ draster = mdev->raster;
+ dbase = scan_line_base(mdev, y);
+ x = tx;
+ rw = tw;
+ /*
+ * The outermost loop here works horizontally, one iteration per
+ * copy of the tile. Note that all iterations except the first
+ * have sourcex = 0.
+ */
+ { int sourcex = (x + px) % tiles->rep_width;
+ w = tiles->size.x - sourcex;
+ bptr = base + ((sourcex & ~chunk_align_bit_mask) >> 3);
+ dbit = x & chunk_align_bit_mask;
+ skew = dbit - (sourcex & chunk_align_bit_mask);
+ }
+outer: if ( w > rw )
+ w = rw;
+ h = th;
+ dbptr = dbase + ((x >> 3) & -chunk_align_bytes);
+ if ( (wleft = w + dbit - chunk_bits) <= 0 )
+ { /* The entire operation fits in one (destination) chunk. */
+ set_mono_thin_mask(mask, w, dbit);
+#define write1_loop(src)\
+ for ( ; ; )\
+ { write_store_masked(src, mask, 0);\
+ end_y_loop(sraster, draster);\
+ }
+ if ( skew >= 0 ) /* single -> single, right/no shift */
+ { if ( skew == 0 ) /* no shift */
+ { write1_loop(cfetch_aligned(bptr));
+ }
+ else /* right shift */
+ { int cskew = chunk_bits - skew;
+ write1_loop(cfetch_right(bptr, skew, cskew));
+ }
+ }
+ else if ( wleft <= skew ) /* single -> single, left shift */
+ { int cskew = chunk_bits + skew;
+ skew = -skew;
+ write1_loop(cfetch_left(bptr, skew, cskew));
+ }
+ else /* double -> single */
+ { int cskew = -skew;
+ skew += chunk_bits;
+ write1_loop(cfetch2(bptr, cskew, skew));
+ }
+#undef write1_loop
+ }
+ else if ( wleft <= skew )
+ { /* 1 source chunk -> 2 destination chunks. */
+ /* This is an important special case for */
+ /* both characters and halftone tiles. */
+ uint rmask;
+ int cskew = chunk_bits - skew;
+ set_mono_left_mask(mask, dbit);
+ set_mono_right_mask(rmask, wleft);
+#if arch_is_big_endian /* no byte swapping */
+#undef cinvert
+#define cinvert(bits) (bits) /* pre-inverted here */
+ for ( ; ; )
+ { register uint bits = cfetch_aligned(bptr) ^ invert;
+ write_store_masked(bits >> skew, mask, 0);
+ write_store_masked(bits << cskew, rmask, 1);
+ end_y_loop(sraster, draster);
+ }
+#undef cinvert
+#define cinvert(bits) ((bits) ^ invert)
+#else /* byte swapping */
+ for ( ; ; )
+ { write_store_masked(cfetch_right(bptr, skew, cskew), mask, 0);
+ write_store_masked(cfetch_left(bptr, cskew, skew), rmask, 1);
+ end_y_loop(sraster, draster);
+ }
+#endif
+ }
+ else
+ { /* More than one source chunk and more than one */
+ /* destination chunk are involved. */
+ uint rmask;
+ int words = (wleft & ~chunk_bit_mask) >> 3;
+ uint sskip = sraster - words;
+ uint dskip = draster - words;
+ register uint bits;
+#define next_x_chunk\
+ bptr += chunk_bytes; dbptr += chunk_bytes
+
+ set_mono_right_mask(rmask, wleft & chunk_bit_mask);
+ if ( skew == 0 ) /* optimize the aligned case */
+ { if ( dbit == 0 )
+ mask = 0;
+ else
+ set_mono_left_mask(mask, dbit);
+ for ( ; ; )
+ { int count = wleft;
+ /* Do first partial chunk. */
+ if ( mask )
+ write_store_masked(cfetch_aligned(bptr), mask, 0);
+ else
+ write_store(cfetch_aligned(bptr));
+ /* Do full chunks. */
+ while ( (count -= chunk_bits) >= 0 )
+ { next_x_chunk;
+ write_store(cfetch_aligned(bptr));
+ }
+ /* Do last chunk */
+ if ( count > -chunk_bits )
+ { write_store_masked(cfetch_aligned(bptr + chunk_bytes), rmask, 1);
+ }
+ end_y_loop(sskip, dskip);
+ }
+ }
+ else /* not aligned */
+ { bool case_right =
+ (skew >= 0 ? true :
+ ((bptr += chunk_bytes), false));
+ int cskew = -skew & chunk_bit_mask;
+
+ skew &= chunk_bit_mask;
+ set_mono_left_mask(mask, dbit);
+ for ( ; ; )
+ { int count = wleft;
+ if ( case_right )
+ bits = cfetch_right(bptr, skew, cskew);
+ else
+ bits = cfetch2(bptr - chunk_bytes, cskew, skew);
+ write_store_masked(bits, mask, 0);
+ /* Do full chunks. */
+ while ( count >= chunk_bits )
+ { bits = cfetch2(bptr, cskew, skew);
+ next_x_chunk;
+ write_store(bits);
+ count -= chunk_bits;
+ }
+ /* Do last chunk */
+ if ( count > 0 )
+ { bits = cfetch_left(bptr, cskew, skew);
+ if ( count > skew )
+ bits += cfetch_right(bptr + chunk_bytes, skew, cskew);
+ write_store_masked(bits, rmask, 1);
+ }
+ end_y_loop(sskip, dskip);
+ }
+ }
+ }
+#undef end_y_loop
+#undef next_x_chunk
+#undef optr
+ if ( (rw -= w) > 0 )
+ { x += w;
+ w = tiles->size.x;
+ bptr = base;
+ skew = dbit = x & chunk_align_bit_mask;
+ goto outer;
+ }
+ return 0;
+#endif /* !USE_COPY_ROP */
+}
+
+#endif /**************** ****************/
+
+/* ================ "Word"-oriented device ================ */
+
+/* Note that on a big-endian machine, this is the same as the */
+/* standard byte-oriented-device. */
+
+#if !arch_is_big_endian
+
+/* Procedures */
+private dev_proc_copy_mono(mem1_word_copy_mono);
+private dev_proc_fill_rectangle(mem1_word_fill_rectangle);
+#define mem1_word_strip_tile_rectangle gx_default_strip_tile_rectangle
+
+/* Here is the device descriptor. */
+const gx_device_memory far_data mem_mono_word_device =
+ mem_full_alpha_device("image1w", 0, 1, mem_open,
+ mem_mono_map_rgb_color, mem_mono_map_color_rgb,
+ mem1_word_copy_mono, gx_default_copy_color, mem1_word_fill_rectangle,
+ mem_word_get_bits, gx_default_map_cmyk_color, gx_no_copy_alpha,
+ mem1_word_strip_tile_rectangle, gx_no_strip_copy_rop);
+
+/* Fill a rectangle with a color. */
+private int
+mem1_word_fill_rectangle(gx_device *dev, int x, int y, int w, int h,
+ gx_color_index color)
+{ byte *base;
+ uint raster;
+ fit_fill(dev, x, y, w, h);
+ base = scan_line_base(mdev, y);
+ raster = mdev->raster;
+ mem_swap_byte_rect(base, raster, x, w, h, true);
+ bits_fill_rectangle(base, x, raster, -(mono_fill_chunk)color, w, h);
+ mem_swap_byte_rect(base, raster, x, w, h, true);
+ return 0;
+}
+
+/* Copy a bitmap. */
+private int
+mem1_word_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 zero, gx_color_index one)
+{ byte *row;
+ uint raster;
+ bool store;
+ fit_copy(dev, base, sourcex, sraster, id, x, y, w, h);
+ row = scan_line_base(mdev, y);
+ raster = mdev->raster;
+ store = (zero != gx_no_color_index && one != gx_no_color_index);
+ mem_swap_byte_rect(row, raster, x, w, h, store);
+ mem_mono_copy_mono(dev, base, sourcex, sraster, id,
+ x, y, w, h, zero, one);
+ mem_swap_byte_rect(row, raster, x, w, h, false);
+ return 0;
+}
+
+#endif /* !arch_is_big_endian */
diff --git a/pstoraster/gdevm16.c b/pstoraster/gdevm16.c
new file mode 100644
index 000000000..149a72129
--- /dev/null
+++ b/pstoraster/gdevm16.c
@@ -0,0 +1,154 @@
+/* Copyright (C) 1994, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gdevm16.c */
+/* 16-bit-per-pixel "memory" (stored bitmap) device */
+#include "memory_.h"
+#include "gx.h"
+#include "gxdevice.h"
+#include "gxdevmem.h" /* semi-public definitions */
+#include "gdevmem.h" /* private definitions */
+
+#undef chunk
+#define chunk byte
+
+/* The 16 bits are divided 5 for red, 6 for green, and 5 for blue. */
+/* Note that the bits must always be kept in big-endian order. */
+
+/* Procedures */
+declare_mem_map_procs(mem_true16_map_rgb_color, mem_true16_map_color_rgb);
+declare_mem_procs(mem_true16_copy_mono, mem_true16_copy_color, mem_true16_fill_rectangle);
+
+/* The device descriptor. */
+const gx_device_memory far_data mem_true16_device =
+ mem_device("image16", 16, 0,
+ mem_true16_map_rgb_color, mem_true16_map_color_rgb,
+ mem_true16_copy_mono, mem_true16_copy_color, mem_true16_fill_rectangle,
+ gx_no_strip_copy_rop);
+
+/* Map a r-g-b color to a color index. */
+private gx_color_index
+mem_true16_map_rgb_color(gx_device *dev, gx_color_value r, gx_color_value g,
+ gx_color_value b)
+{ return ((r >> (gx_color_value_bits - 5)) << 11) +
+ ((g >> (gx_color_value_bits - 6)) << 5) +
+ (b >> (gx_color_value_bits - 5));
+}
+
+/* Map a color index to a r-g-b color. */
+private int
+mem_true16_map_color_rgb(gx_device *dev, gx_color_index color,
+ gx_color_value prgb[3])
+{ ushort value = color >> 11;
+ prgb[0] = ((value << 11) + (value << 6) + (value << 1) + (value >> 4)) >> (16 - gx_color_value_bits);
+ value = (color >> 6) & 0x7f;
+ prgb[1] = ((value << 10) + (value << 4) + (value >> 2)) >> (16 - gx_color_value_bits);
+ value = color & 0x3f;
+ prgb[2] = ((value << 11) + (value << 6) + (value << 1) + (value >> 4)) >> (16 - gx_color_value_bits);
+ return 0;
+}
+
+/* Convert x coordinate to byte offset in scan line. */
+#undef x_to_byte
+#define x_to_byte(x) ((x) << 1)
+
+/* Fill a rectangle with a color. */
+private int
+mem_true16_fill_rectangle(gx_device *dev,
+ int x, int y, int w, int h, gx_color_index color)
+{
+#if arch_is_big_endian
+# define color16 ((ushort)color)
+#else
+ ushort color16 = ((uint)(byte)color << 8) + ((ushort)color >> 8);
+#endif
+ declare_scan_ptr(dest);
+ fit_fill(dev, x, y, w, h);
+ setup_rect(dest);
+ while ( h-- > 0 )
+ { ushort *pptr = (ushort *)dest;
+ int cnt = w;
+ do { *pptr++ = color16; } while ( --cnt > 0 );
+ inc_ptr(dest, draster);
+ }
+ return 0;
+#undef color16
+}
+
+/* Copy a monochrome bitmap. */
+private int
+mem_true16_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 zero, gx_color_index one)
+{
+#if arch_is_big_endian
+# define zero16 ((ushort)zero)
+# define one16 ((ushort)one)
+#else
+ ushort zero16 = ((uint)(byte)zero << 8) + ((ushort)zero >> 8);
+ ushort one16 = ((uint)(byte)one << 8) + ((ushort)one >> 8);
+#endif
+ const byte *line;
+ int first_bit;
+ declare_scan_ptr(dest);
+ fit_copy(dev, base, sourcex, sraster, id, x, y, w, h);
+ setup_rect(dest);
+ line = base + (sourcex >> 3);
+ first_bit = 0x80 >> (sourcex & 7);
+ while ( h-- > 0 )
+ { register ushort *pptr = (ushort *)dest;
+ const byte *sptr = line;
+ register int sbyte = *sptr++;
+ register int bit = first_bit;
+ int count = w;
+ do
+ { if ( sbyte & bit )
+ { if ( one != gx_no_color_index )
+ *pptr = one16;
+ }
+ else
+ { if ( zero != gx_no_color_index )
+ *pptr = zero16;
+ }
+ if ( (bit >>= 1) == 0 )
+ bit = 0x80, sbyte = *sptr++;
+ pptr++;
+ }
+ while ( --count > 0 );
+ line += sraster;
+ inc_ptr(dest, draster);
+ }
+ return 0;
+#undef zero16
+#undef one16
+}
+
+/* Copy a color bitmap. */
+private int
+mem_true16_copy_color(gx_device *dev,
+ const byte *base, int sourcex, int sraster, gx_bitmap_id id,
+ int x, int y, int w, int h)
+{ fit_copy(dev, base, sourcex, sraster, id, x, y, w, h);
+ mem_copy_byte_rect(mdev, base, sourcex, sraster, x, y, w, h);
+ return 0;
+}
diff --git a/pstoraster/gdevm2.c b/pstoraster/gdevm2.c
new file mode 100644
index 000000000..f68ccea72
--- /dev/null
+++ b/pstoraster/gdevm2.c
@@ -0,0 +1,244 @@
+/* Copyright (C) 1994, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gdevm2.c */
+/* 2-bit-per-pixel "memory" (stored bitmap) device */
+#include "memory_.h"
+#include "gx.h"
+#include "gxdevice.h"
+#include "gxdevmem.h" /* semi-public definitions */
+#include "gdevmem.h" /* private definitions */
+
+/**************** NOTE: copy_rop only works for gray scale ****************/
+extern dev_proc_strip_copy_rop(mem_gray_strip_copy_rop);
+
+/* ================ Standard (byte-oriented) device ================ */
+
+#undef chunk
+#define chunk byte
+#define fpat(byt) mono_fill_make_pattern(byt)
+
+/* Procedures */
+declare_mem_procs(mem_mapped2_copy_mono, mem_mapped2_copy_color, mem_mapped2_fill_rectangle);
+
+/* The device descriptor. */
+const gx_device_memory far_data mem_mapped2_device =
+ mem_device("image2", 2, 0,
+ mem_mapped_map_rgb_color, mem_mapped_map_color_rgb,
+ mem_mapped2_copy_mono, mem_mapped2_copy_color, mem_mapped2_fill_rectangle,
+ mem_gray_strip_copy_rop);
+
+/* Convert x coordinate to byte offset in scan line. */
+#undef x_to_byte
+#define x_to_byte(x) ((x) >> 2)
+
+/* Define the 2-bit fill patterns. */
+static const mono_fill_chunk tile_patterns[4] =
+{ fpat(0x00), fpat(0x55), fpat(0xaa), fpat(0xff)
+};
+
+/* Fill a rectangle with a color. */
+private int
+mem_mapped2_fill_rectangle(gx_device *dev,
+ int x, int y, int w, int h, gx_color_index color)
+{ fit_fill(dev, x, y, w, h);
+ bits_fill_rectangle(scan_line_base(mdev, y), x << 1, mdev->raster,
+ tile_patterns[color], w << 1, h);
+ return 0;
+}
+
+/* Copy a bitmap. */
+private int
+mem_mapped2_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 zero, gx_color_index one)
+{ const byte *line;
+ int first_bit;
+ byte first_mask, b0, b1, bxor, left_mask, right_mask;
+ static const byte btab[4] = { 0, 0x55, 0xaa, 0xff };
+ static const byte bmask[4] = { 0xc0, 0x30, 0xc, 3 };
+ static const byte lmask[4] = { 0, 0xc0, 0xf0, 0xfc };
+ declare_scan_ptr(dest);
+
+ fit_copy(dev, base, sourcex, sraster, id, x, y, w, h);
+ setup_rect(dest);
+ line = base + (sourcex >> 3);
+ first_bit = 0x80 >> (sourcex & 7);
+ first_mask = bmask[x & 3];
+ left_mask = lmask[x & 3];
+ right_mask = ~lmask[(x + w) & 3];
+ if ( (x & 3) + w <= 4 )
+ left_mask = right_mask = left_mask | right_mask;
+ b0 = btab[zero & 3];
+ b1 = btab[one & 3];
+ bxor = b0 ^ b1;
+ while ( h-- > 0 )
+ { register byte *pptr = (byte *)dest;
+ const byte *sptr = line;
+ register int sbyte = *sptr++;
+ register int bit = first_bit;
+ register byte mask = first_mask;
+ int count = w;
+
+ /* We have 4 cases, of which only 2 really matter. */
+ if ( one != gx_no_color_index )
+ { if ( zero != gx_no_color_index )
+ { /* Copying an opaque bitmap. */
+ byte data =
+ (*pptr & left_mask) | (b0 & ~left_mask);
+ do
+ { if ( sbyte & bit )
+ data ^= bxor & mask;
+ if ( (bit >>= 1) == 0 )
+ bit = 0x80, sbyte = *sptr++;
+ if ( (mask >>= 2) == 0 )
+ mask = 0xc0, *pptr++ = data, data = b0;
+ }
+ while ( --count > 0 );
+ if ( mask != 0xc0 )
+ *pptr =
+ (*pptr & right_mask) | (data & ~right_mask);
+ }
+ else
+ { /* Filling a mask. */
+ do
+ { if ( sbyte & bit )
+ *pptr = (*pptr & ~mask) + (b1 & mask);
+ if ( (bit >>= 1) == 0 )
+ bit = 0x80, sbyte = *sptr++;
+ if ( (mask >>= 2) == 0 )
+ mask = 0xc0, pptr++;
+ }
+ while ( --count > 0 );
+ }
+ }
+ else
+ { /* Some other case. */
+ do
+ { if ( !(sbyte & bit) )
+ { if ( zero != gx_no_color_index )
+ *pptr = (*pptr & ~mask) + (b0 & mask);
+ }
+ if ( (bit >>= 1) == 0 )
+ bit = 0x80, sbyte = *sptr++;
+ if ( (mask >>= 2) == 0 )
+ mask = 0xc0, pptr++;
+ }
+ while ( --count > 0 );
+ }
+ line += sraster;
+ inc_ptr(dest, draster);
+ }
+ return 0;
+}
+
+/* Copy a color bitmap. */
+private int
+mem_mapped2_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 code;
+ fit_copy(dev, base, sourcex, sraster, id, x, y, w, h);
+ /* Use monobit copy_mono. */
+ /* Patch the width in the device temporarily. */
+ dev->width <<= 1;
+ code = (*dev_proc(&mem_mono_device, copy_mono))
+ (dev, base, sourcex << 1, sraster, id,
+ x << 1, y, w << 1, h, (gx_color_index)0, (gx_color_index)1);
+ /* Restore the correct width. */
+ dev->width >>= 1;
+ return code;
+}
+
+/* ================ "Word"-oriented device ================ */
+
+/* Note that on a big-endian machine, this is the same as the */
+/* standard byte-oriented-device. */
+
+#if !arch_is_big_endian
+
+/* Procedures */
+declare_mem_procs(mem2_word_copy_mono, mem2_word_copy_color, mem2_word_fill_rectangle);
+
+/* Here is the device descriptor. */
+const gx_device_memory far_data mem_mapped2_word_device =
+ mem_full_device("image2w", 2, 0, mem_open,
+ mem_mapped_map_rgb_color, mem_mapped_map_color_rgb,
+ mem2_word_copy_mono, mem2_word_copy_color, mem2_word_fill_rectangle,
+ mem_word_get_bits, gx_default_map_cmyk_color,
+ gx_default_strip_tile_rectangle, gx_no_strip_copy_rop);
+
+/* Fill a rectangle with a color. */
+private int
+mem2_word_fill_rectangle(gx_device *dev, int x, int y, int w, int h,
+ gx_color_index color)
+{ byte *base;
+ uint raster;
+ fit_fill(dev, x, y, w, h);
+ base = scan_line_base(mdev, y);
+ raster = mdev->raster;
+ mem_swap_byte_rect(base, raster, x << 1, w << 1, h, true);
+ bits_fill_rectangle(base, x << 1, raster,
+ tile_patterns[color], w << 1, h);
+ mem_swap_byte_rect(base, raster, x << 1, w << 1, h, true);
+ return 0;
+}
+
+/* Copy a bitmap. */
+private int
+mem2_word_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 zero, gx_color_index one)
+{ byte *row;
+ uint raster;
+ bool store;
+ fit_copy(dev, base, sourcex, sraster, id, x, y, w, h);
+ row = scan_line_base(mdev, y);
+ raster = mdev->raster;
+ store = (zero != gx_no_color_index && one != gx_no_color_index);
+ mem_swap_byte_rect(row, raster, x << 1, w << 1, h, store);
+ mem_mapped2_copy_mono(dev, base, sourcex, sraster, id,
+ x, y, w, h, zero, one);
+ mem_swap_byte_rect(row, raster, x << 1, w << 1, h, false);
+ return 0;
+}
+
+/* Copy a color bitmap. */
+private int
+mem2_word_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 code;
+ fit_copy(dev, base, sourcex, sraster, id, x, y, w, h);
+ /* Use monobit copy_mono. */
+ /* Patch the width in the device temporarily. */
+ dev->width <<= 1;
+ code = (*dev_proc(&mem_mono_word_device, copy_mono))
+ (dev, base, sourcex << 1, sraster, id,
+ x << 1, y, w << 1, h, (gx_color_index)0, (gx_color_index)1);
+ /* Restore the correct width. */
+ dev->width >>= 1;
+ return code;
+}
+
+#endif /* !arch_is_big_endian */
diff --git a/pstoraster/gdevm24.c b/pstoraster/gdevm24.c
new file mode 100644
index 000000000..110845525
--- /dev/null
+++ b/pstoraster/gdevm24.c
@@ -0,0 +1,484 @@
+/* Copyright (C) 1994, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gdevm24.c */
+/* 24-bit-per-pixel "memory" (stored bitmap) device */
+#include "memory_.h"
+#include "gx.h"
+#include "gxdevice.h"
+#include "gxdevmem.h" /* semi-public definitions */
+#include "gdevmem.h" /* private definitions */
+
+extern dev_proc_strip_copy_rop(mem_gray8_rgb24_strip_copy_rop); /* in gdevmrop.c */
+#define mem_true24_strip_copy_rop mem_gray8_rgb24_strip_copy_rop
+
+/* ================ Standard (byte-oriented) device ================ */
+
+#undef chunk
+#define chunk byte
+
+/* Procedures */
+declare_mem_procs(mem_true24_copy_mono, mem_true24_copy_color, mem_true24_fill_rectangle);
+private dev_proc_copy_alpha(mem_true24_copy_alpha);
+
+/* The device descriptor. */
+const gx_device_memory far_data mem_true24_device =
+ mem_full_alpha_device("image24", 24, 0, mem_open,
+ gx_default_rgb_map_rgb_color, gx_default_rgb_map_color_rgb,
+ mem_true24_copy_mono, mem_true24_copy_color, mem_true24_fill_rectangle,
+ mem_get_bits, gx_default_map_cmyk_color, mem_true24_copy_alpha,
+ gx_default_strip_tile_rectangle, mem_true24_strip_copy_rop);
+
+/* Convert x coordinate to byte offset in scan line. */
+#undef x_to_byte
+#define x_to_byte(x) ((x) * 3)
+
+/* Unpack a color into its bytes. */
+#define declare_unpack_color(r, g, b, color)\
+ byte r = (byte)(color >> 16);\
+ byte g = (byte)((uint)color >> 8);\
+ byte b = (byte)color
+/* Put a 24-bit color into the bitmap. */
+#define put3(ptr, r, g, b)\
+ (ptr)[0] = r, (ptr)[1] = g, (ptr)[2] = b
+/* Put 4 bytes of color into the bitmap. */
+#define putw(ptr, wxyz)\
+ *(bits32 *)(ptr) = (wxyz)
+/* Load the 3-word 24-bit-color cache. */
+/* Free variables: [m]dev, rgbr, gbrg, brgb. */
+#if arch_is_big_endian
+# define set_color24_cache(crgb, r, g, b)\
+ mdev->color24.rgbr = rgbr = ((bits32)(crgb) << 8) | (r),\
+ mdev->color24.gbrg = gbrg = (rgbr << 8) | (g),\
+ mdev->color24.brgb = brgb = (gbrg << 8) | (b),\
+ mdev->color24.rgb = (crgb)
+#else
+# define set_color24_cache(crgb, r, g, b)\
+ mdev->color24.rgbr = rgbr =\
+ ((bits32)(r) << 24) | ((bits32)(b) << 16) |\
+ ((bits16)(g) << 8) | (r),\
+ mdev->color24.brgb = brgb = (rgbr << 8) | (b),\
+ mdev->color24.gbrg = gbrg = (brgb << 8) | (g),\
+ mdev->color24.rgb = (crgb)
+#endif
+
+/* Fill a rectangle with a color. */
+private int
+mem_true24_fill_rectangle(gx_device *dev,
+ int x, int y, int w, int h, gx_color_index color)
+{ declare_unpack_color(r, g, b, color);
+ declare_scan_ptr(dest);
+ fit_fill_xywh(dev, x, y, w, h); /* don't check w <= 0 or h <= 0 */
+ setup_rect(dest);
+ if ( w >= 5 )
+ { if ( r == g && r == b)
+ {
+#if 1
+ /* We think we can do better than the library's memset.... */
+ int bcntm7 = w * 3 - 7;
+ register bits32 cword = color | (color << 24);
+ while ( h-- > 0 )
+ { register byte *pptr = dest;
+ byte *limit = pptr + bcntm7;
+ /* We want to store full words, but we have to */
+ /* guarantee that they are word-aligned. */
+ switch ( x & 3 )
+ {
+ case 3: *pptr++ = (byte)cword;
+ case 2: *pptr++ = (byte)cword;
+ case 1: *pptr++ = (byte)cword;
+ case 0: ;
+ }
+ /* Even with w = 5, we always store at least */
+ /* 3 full words, regardless of the starting x. */
+ *(bits32 *)pptr =
+ ((bits32 *)pptr)[1] =
+ ((bits32 *)pptr)[2] = cword;
+ pptr += 12;
+ while ( pptr < limit )
+ { *(bits32 *)pptr =
+ ((bits32 *)pptr)[1] = cword;
+ pptr += 8;
+ }
+ switch ( pptr - limit )
+ {
+ case 0: pptr[6] = (byte)cword;
+ case 1: pptr[5] = (byte)cword;
+ case 2: pptr[4] = (byte)cword;
+ case 3: *(bits32 *)pptr = cword;
+ break;
+ case 4: pptr[2] = (byte)cword;
+ case 5: pptr[1] = (byte)cword;
+ case 6: pptr[0] = (byte)cword;
+ case 7: ;
+ }
+ inc_ptr(dest, draster);
+ }
+#else
+ int bcnt = w * 3;
+ while ( h-- > 0 )
+ { memset(dest, r, bcnt);
+ inc_ptr(dest, draster);
+ }
+#endif
+ }
+ else
+ { int x3 = -x & 3, ww = w - x3; /* we know ww >= 2 */
+ bits32 rgbr, gbrg, brgb;
+ if ( mdev->color24.rgb == color )
+ rgbr = mdev->color24.rgbr,
+ gbrg = mdev->color24.gbrg,
+ brgb = mdev->color24.brgb;
+ else
+ set_color24_cache(color, r, g, b);
+ while ( h-- > 0 )
+ { register byte *pptr = dest;
+ int w1 = ww;
+ switch ( x3 )
+ {
+ case 1:
+ put3(pptr, r, g, b);
+ pptr += 3; break;
+ case 2:
+ pptr[0] = r; pptr[1] = g;
+ putw(pptr + 2, brgb);
+ pptr += 6; break;
+ case 3:
+ pptr[0] = r;
+ putw(pptr + 1, gbrg);
+ putw(pptr + 5, brgb);
+ pptr += 9; break;
+ case 0:
+ ;
+ }
+ while ( w1 >= 4 )
+ { putw(pptr, rgbr);
+ putw(pptr + 4, gbrg);
+ putw(pptr + 8, brgb);
+ pptr += 12;
+ w1 -= 4;
+ }
+ switch ( w1 )
+ {
+ case 1:
+ put3(pptr, r, g, b);
+ break;
+ case 2:
+ putw(pptr, rgbr);
+ pptr[4] = g; pptr[5] = b;
+ break;
+ case 3:
+ putw(pptr, rgbr);
+ putw(pptr + 4, gbrg);
+ pptr[8] = b;
+ break;
+ case 0:
+ ;
+ }
+ inc_ptr(dest, draster);
+ }
+ }
+ }
+ else if ( h > 0 ) /* w < 5 */
+ switch ( w )
+ {
+ case 4:
+ do
+ { dest[9] = dest[6] = dest[3] = dest[0] = r;
+ dest[10] = dest[7] = dest[4] = dest[1] = g;
+ dest[11] = dest[8] = dest[5] = dest[2] = b;
+ inc_ptr(dest, draster);
+ }
+ while ( --h );
+ break;
+ case 3:
+ do
+ { dest[6] = dest[3] = dest[0] = r;
+ dest[7] = dest[4] = dest[1] = g;
+ dest[8] = dest[5] = dest[2] = b;
+ inc_ptr(dest, draster);
+ }
+ while ( --h );
+ break;
+ case 2:
+ do
+ { dest[3] = dest[0] = r;
+ dest[4] = dest[1] = g;
+ dest[5] = dest[2] = b;
+ inc_ptr(dest, draster);
+ }
+ while ( --h );
+ break;
+ case 1:
+ do
+ { dest[0] = r, dest[1] = g, dest[2] = b;
+ inc_ptr(dest, draster);
+ }
+ while ( --h );
+ break;
+ case 0:
+ default:
+ ;
+ }
+ return 0;
+}
+
+/* Copy a monochrome bitmap. */
+private int
+mem_true24_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 zero, gx_color_index one)
+{ const byte *line;
+ int sbit;
+ int first_bit;
+ declare_scan_ptr(dest);
+
+ fit_copy(dev, base, sourcex, sraster, id, x, y, w, h);
+ setup_rect(dest);
+ line = base + (sourcex >> 3);
+ sbit = sourcex & 7;
+ first_bit = 0x80 >> sbit;
+ if ( zero != gx_no_color_index )
+ { /* Loop for halftones or inverted masks */
+ /* (never used). */
+ declare_unpack_color(r0, g0, b0, zero);
+ declare_unpack_color(r1, g1, b1, one);
+ while ( h-- > 0 )
+ { register byte *pptr = dest;
+ const byte *sptr = line;
+ register int sbyte = *sptr++;
+ register int bit = first_bit;
+ int count = w;
+ do
+ { if ( sbyte & bit )
+ { if ( one != gx_no_color_index )
+ put3(pptr, r1, g1, b1);
+ }
+ else
+ put3(pptr, r0, g0, b0);
+ pptr += 3;
+ if ( (bit >>= 1) == 0 )
+ bit = 0x80, sbyte = *sptr++;
+ }
+ while ( --count > 0 );
+ line += sraster;
+ inc_ptr(dest, draster);
+ }
+ }
+ else if ( one != gx_no_color_index )
+ { /* Loop for character and pattern masks. */
+ /* This is used heavily. */
+ declare_unpack_color(r1, g1, b1, one);
+ int first_mask = first_bit << 1;
+ int first_count, first_skip;
+ if ( sbit + w > 8 )
+ first_mask -= 1,
+ first_count = 8 - sbit;
+ else
+ first_mask -= first_mask >> w,
+ first_count = w;
+ first_skip = first_count * 3;
+ while ( h-- > 0 )
+ { register byte *pptr = dest;
+ const byte *sptr = line;
+ register int sbyte = *sptr++ & first_mask;
+ int count = w - first_count;
+ if ( sbyte )
+ { register int bit = first_bit;
+ do
+ { if ( sbyte & bit )
+ put3(pptr, r1, g1, b1);
+ pptr += 3;
+ }
+ while ( (bit >>= 1) & first_mask );
+ }
+ else
+ pptr += first_skip;
+ while ( count >= 8 )
+ { sbyte = *sptr++;
+ if ( sbyte & 0xf0 )
+ { if ( sbyte & 0x80 )
+ put3(pptr, r1, g1, b1);
+ if ( sbyte & 0x40 )
+ put3(pptr + 3, r1, g1, b1);
+ if ( sbyte & 0x20 )
+ put3(pptr + 6, r1, g1, b1);
+ if ( sbyte & 0x10 )
+ put3(pptr + 9, r1, g1, b1);
+ }
+ if ( sbyte & 0xf )
+ { if ( sbyte & 8 )
+ put3(pptr + 12, r1, g1, b1);
+ if ( sbyte & 4 )
+ put3(pptr + 15, r1, g1, b1);
+ if ( sbyte & 2 )
+ put3(pptr + 18, r1, g1, b1);
+ if ( sbyte & 1 )
+ put3(pptr + 21, r1, g1, b1);
+ }
+ pptr += 24;
+ count -= 8;
+ }
+ if ( count > 0 )
+ { register int bit = 0x80;
+ sbyte = *sptr++;
+ do
+ { if ( sbyte & bit )
+ put3(pptr, r1, g1, b1);
+ pptr += 3;
+ bit >>= 1;
+ }
+ while ( --count > 0 );
+ }
+ line += sraster;
+ inc_ptr(dest, draster);
+ }
+ }
+ return 0;
+}
+
+/* Copy a color bitmap. */
+private int
+mem_true24_copy_color(gx_device *dev,
+ const byte *base, int sourcex, int sraster, gx_bitmap_id id,
+ int x, int y, int w, int h)
+{ fit_copy(dev, base, sourcex, sraster, id, x, y, w, h);
+ mem_copy_byte_rect(mdev, base, sourcex, sraster, x, y, w, h);
+ return 0;
+}
+
+/* Copy an alpha map. */
+private int
+mem_true24_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)
+{ const byte *line;
+ declare_scan_ptr(dest);
+ declare_unpack_color(r, g, b, color);
+
+ fit_copy(dev, base, sourcex, sraster, id, x, y, w, h);
+ setup_rect(dest);
+ line = base;
+ while ( h-- > 0 )
+ { register byte *pptr = dest;
+ int sx;
+
+ for ( sx = sourcex; sx < sourcex + w; ++sx, pptr += 3 )
+ { int alpha2, alpha;
+
+ if ( depth == 2 ) /* map 0 - 3 to 0 - 15 */
+ alpha =
+ ((line[sx >> 2] >> ((3 - (sx & 3)) << 1)) & 3) * 5;
+ else
+ alpha2 = line[sx >> 1],
+ alpha = (sx & 1 ? alpha2 & 0xf : alpha2 >> 4);
+ if ( alpha == 15 )
+ { /* Just write the new color. */
+ put3(pptr, r, g, b);
+ }
+ else if ( alpha != 0 )
+ { /* Blend RGB values. */
+#define make_shade(old, clr, alpha, amax) \
+ (old) + (((int)(clr) - (int)(old)) * (alpha) / (amax))
+ pptr[0] = make_shade(pptr[0], r, alpha, 15);
+ pptr[1] = make_shade(pptr[1], g, alpha, 15);
+ pptr[2] = make_shade(pptr[2], b, alpha, 15);
+#undef make_shade
+ }
+ }
+ line += sraster;
+ inc_ptr(dest, draster);
+ }
+ return 0;
+}
+
+/* ================ "Word"-oriented device ================ */
+
+/* Note that on a big-endian machine, this is the same as the */
+/* standard byte-oriented-device. */
+
+#if !arch_is_big_endian
+
+/* Procedures */
+declare_mem_procs(mem24_word_copy_mono, mem24_word_copy_color, mem24_word_fill_rectangle);
+
+/* Here is the device descriptor. */
+const gx_device_memory far_data mem_true24_word_device =
+ mem_full_device("image24w", 24, 0, mem_open,
+ gx_default_rgb_map_rgb_color, gx_default_rgb_map_color_rgb,
+ mem24_word_copy_mono, mem24_word_copy_color, mem24_word_fill_rectangle,
+ mem_word_get_bits, gx_default_map_cmyk_color,
+ gx_default_strip_tile_rectangle, gx_no_strip_copy_rop);
+
+/* Fill a rectangle with a color. */
+private int
+mem24_word_fill_rectangle(gx_device *dev, int x, int y, int w, int h,
+ gx_color_index color)
+{ byte *base;
+ uint raster;
+ fit_fill(dev, x, y, w, h);
+ base = scan_line_base(mdev, y);
+ raster = mdev->raster;
+ mem_swap_byte_rect(base, raster, x * 24, w * 24, h, true);
+ mem_true24_fill_rectangle(dev, x, y, w, h, color);
+ mem_swap_byte_rect(base, raster, x * 24, w * 24, h, false);
+ return 0;
+}
+
+/* Copy a bitmap. */
+private int
+mem24_word_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 zero, gx_color_index one)
+{ byte *row;
+ uint raster;
+ bool store;
+ fit_copy(dev, base, sourcex, sraster, id, x, y, w, h);
+ row = scan_line_base(mdev, y);
+ raster = mdev->raster;
+ store = (zero != gx_no_color_index && one != gx_no_color_index);
+ mem_swap_byte_rect(row, raster, x * 24, w * 24, h, store);
+ mem_true24_copy_mono(dev, base, sourcex, sraster, id,
+ x, y, w, h, zero, one);
+ mem_swap_byte_rect(row, raster, x * 24, w * 24, h, false);
+ return 0;
+}
+
+/* Copy a color bitmap. */
+private int
+mem24_word_copy_color(gx_device *dev,
+ const byte *base, int sourcex, int sraster, gx_bitmap_id id,
+ int x, int y, int w, int h)
+{ byte *row;
+ uint raster;
+ fit_copy(dev, base, sourcex, sraster, id, x, y, w, h);
+ row = scan_line_base(mdev, y);
+ raster = mdev->raster;
+ mem_swap_byte_rect(row, raster, x * 24, w * 24, h, true);
+ bytes_copy_rectangle(row + x * 3, raster, base + sourcex * 3, sraster,
+ w * 3, h);
+ mem_swap_byte_rect(row, raster, x * 24, w * 24, h, false);
+ return 0;
+}
+
+#endif /* !arch_is_big_endian */
diff --git a/pstoraster/gdevm32.c b/pstoraster/gdevm32.c
new file mode 100644
index 000000000..2e732915e
--- /dev/null
+++ b/pstoraster/gdevm32.c
@@ -0,0 +1,233 @@
+/* Copyright (C) 1994, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gdevm32.c */
+/* 32-bit-per-pixel "memory" (stored bitmap) device */
+#include "memory_.h"
+#include "gx.h"
+#include "gxdevice.h"
+#include "gxdevmem.h" /* semi-public definitions */
+#include "gdevmem.h" /* private definitions */
+
+/* ================ Standard (byte-oriented) device ================ */
+
+#undef chunk
+#define chunk byte
+
+/* Procedures */
+declare_mem_procs(mem_true32_copy_mono, mem_true32_copy_color, mem_true32_fill_rectangle);
+
+/* The device descriptor. */
+const gx_device_memory far_data mem_true32_device =
+ mem_full_device("image32", 24, 8, mem_open,
+ gx_default_map_rgb_color, gx_default_map_color_rgb,
+ mem_true32_copy_mono, mem_true32_copy_color, mem_true32_fill_rectangle,
+ mem_get_bits, gx_default_cmyk_map_cmyk_color,
+ gx_default_strip_tile_rectangle, gx_no_strip_copy_rop);
+
+/* Convert x coordinate to byte offset in scan line. */
+#undef x_to_byte
+#define x_to_byte(x) ((x) << 2)
+
+/* Swap the bytes of a color if needed. */
+#define color_swap_bytes(color)\
+ (((color) >> 24) + (((color) >> 8) & 0xff00) +\
+ (((color) & 0xff00) << 8) + ((color) << 24))
+#if arch_is_big_endian
+# define arrange_bytes(color) (color)
+#else
+# define arrange_bytes(color) color_swap_bytes(color)
+#endif
+
+/* Fill a rectangle with a color. */
+private int
+mem_true32_fill_rectangle(gx_device *dev,
+ int x, int y, int w, int h, gx_color_index color)
+{ bits32 a_color;
+ declare_scan_ptr(dest);
+
+ fit_fill(dev, x, y, w, h);
+ a_color = arrange_bytes(color);
+ setup_rect(dest);
+ if ( w <= 4 )
+ switch ( w )
+ {
+ /*case 0:*/ /* not possible */
+#define dest32 ((bits32 *)dest)
+ case 1:
+ do
+ { dest32[0] = a_color;
+ inc_ptr(dest, draster);
+ }
+ while ( --h > 0 );
+ break;
+ case 2:
+ do
+ { dest32[1] = dest32[0] = a_color;
+ inc_ptr(dest, draster);
+ }
+ while ( --h > 0 );
+ break;
+ case 3:
+ do
+ { dest32[2] = dest32[1] = dest32[0] = a_color;
+ inc_ptr(dest, draster);
+ }
+ while ( --h > 0 );
+ break;
+ case 4:
+ do
+ { dest32[3] = dest32[2] = dest32[1] = dest32[0] = a_color;
+ inc_ptr(dest, draster);
+ }
+ while ( --h > 0 );
+ break;
+ default: /* not possible */
+ ;
+ }
+ else if ( a_color == 0 )
+ do
+ { memset(dest, 0, w << 2);
+ inc_ptr(dest, draster);
+ }
+ while ( --h > 0 );
+ else
+ do
+ { bits32 *pptr = dest32;
+ int cnt = w;
+ do
+ { pptr[3] = pptr[2] = pptr[1] = pptr[0] = a_color;
+ pptr += 4;
+ }
+ while ( (cnt -= 4) > 4 );
+ do { *pptr++ = a_color; } while ( --cnt > 0 );
+ inc_ptr(dest, draster);
+ }
+ while ( --h > 0 );
+#undef dest32
+ return 0;
+}
+
+/* Copy a monochrome bitmap. */
+private int
+mem_true32_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 zero, gx_color_index one)
+{ bits32 a_zero = arrange_bytes(zero);
+ bits32 a_one = arrange_bytes(one);
+ const byte *line;
+ int first_bit;
+ declare_scan_ptr(dest);
+ fit_copy(dev, base, sourcex, sraster, id, x, y, w, h);
+ setup_rect(dest);
+ line = base + (sourcex >> 3);
+ first_bit = 0x80 >> (sourcex & 7);
+ while ( h-- > 0 )
+ { register bits32 *pptr = (bits32 *)dest;
+ const byte *sptr = line;
+ register int sbyte = *sptr++;
+ register int bit = first_bit;
+ int count = w;
+ do
+ { if ( sbyte & bit )
+ { if ( one != gx_no_color_index )
+ *pptr = a_one;
+ }
+ else
+ { if ( zero != gx_no_color_index )
+ *pptr = a_zero;
+ }
+ if ( (bit >>= 1) == 0 )
+ bit = 0x80, sbyte = *sptr++;
+ pptr++;
+ }
+ while ( --count > 0 );
+ line += sraster;
+ inc_ptr(dest, draster);
+ }
+ return 0;
+}
+
+/* Copy a color bitmap. */
+private int
+mem_true32_copy_color(gx_device *dev,
+ const byte *base, int sourcex, int sraster, gx_bitmap_id id,
+ int x, int y, int w, int h)
+{ fit_copy(dev, base, sourcex, sraster, id, x, y, w, h);
+ mem_copy_byte_rect(mdev, base, sourcex, sraster, x, y, w, h);
+ return 0;
+}
+
+/* ================ "Word"-oriented device ================ */
+
+/* Note that on a big-endian machine, this is the same as the */
+/* standard byte-oriented-device. */
+
+#if !arch_is_big_endian
+
+/* Procedures */
+declare_mem_procs(mem32_word_copy_mono, mem32_word_copy_color, mem32_word_fill_rectangle);
+
+/* Here is the device descriptor. */
+const gx_device_memory far_data mem_true32_word_device =
+ mem_full_device("image32w", 24, 8, mem_open,
+ gx_default_map_rgb_color, gx_default_map_color_rgb,
+ mem32_word_copy_mono, mem32_word_copy_color, mem32_word_fill_rectangle,
+ mem_word_get_bits, gx_default_cmyk_map_cmyk_color,
+ gx_default_strip_tile_rectangle, gx_no_strip_copy_rop);
+
+/* Fill a rectangle with a color. */
+private int
+mem32_word_fill_rectangle(gx_device *dev, int x, int y, int w, int h,
+ gx_color_index color)
+{ return mem_true32_fill_rectangle(dev, x, y, w, h,
+ color_swap_bytes(color));
+}
+
+/* Copy a bitmap. */
+private int
+mem32_word_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 zero, gx_color_index one)
+{ return mem_true32_copy_mono(dev, base, sourcex, sraster, id,
+ x, y, w, h, color_swap_bytes(zero),
+ color_swap_bytes(one));
+}
+
+/* Copy a color bitmap. */
+private int
+mem32_word_copy_color(gx_device *dev,
+ const byte *base, int sourcex, int sraster, gx_bitmap_id id,
+ int x, int y, int w, int h)
+{ byte *row;
+ uint raster;
+ fit_copy(dev, base, sourcex, sraster, id, x, y, w, h);
+ row = scan_line_base(mdev, y);
+ raster = mdev->raster;
+ bytes_copy_rectangle(row + (x << 2), raster, base + (sourcex << 2),
+ sraster, w << 2, h);
+ mem_swap_byte_rect(row, raster, x << 5, w << 5, h, false);
+ return 0;
+}
+
+#endif /* !arch_is_big_endian */
diff --git a/pstoraster/gdevm4.c b/pstoraster/gdevm4.c
new file mode 100644
index 000000000..61b4f637e
--- /dev/null
+++ b/pstoraster/gdevm4.c
@@ -0,0 +1,207 @@
+/* Copyright (C) 1992, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gdevm4.c */
+/* 4-bit-per-pixel "memory" (stored bitmap) device */
+#include "memory_.h"
+#include "gx.h"
+#include "gxdevice.h"
+#include "gxdevmem.h" /* semi-public definitions */
+#include "gdevmem.h" /* private definitions */
+
+/**************** NOTE: copy_rop only works for gray scale ****************/
+extern dev_proc_strip_copy_rop(mem_gray_strip_copy_rop);
+
+/* ================ Standard (byte-oriented) device ================ */
+
+#undef chunk
+#define chunk byte
+#define fpat(byt) mono_fill_make_pattern(byt)
+
+/* Procedures */
+declare_mem_procs(mem_mapped4_copy_mono, mem_mapped4_copy_color, mem_mapped4_fill_rectangle);
+
+/* The device descriptor. */
+const gx_device_memory far_data mem_mapped4_device =
+ mem_device("image4", 4, 0,
+ mem_mapped_map_rgb_color, mem_mapped_map_color_rgb,
+ mem_mapped4_copy_mono, mem_mapped4_copy_color, mem_mapped4_fill_rectangle,
+ mem_gray_strip_copy_rop);
+
+/* Convert x coordinate to byte offset in scan line. */
+#undef x_to_byte
+#define x_to_byte(x) ((x) >> 1)
+
+/* Define the 4-bit fill patterns. */
+static const mono_fill_chunk tile_patterns[16] =
+ { fpat(0x00), fpat(0x11), fpat(0x22), fpat(0x33),
+ fpat(0x44), fpat(0x55), fpat(0x66), fpat(0x77),
+ fpat(0x88), fpat(0x99), fpat(0xaa), fpat(0xbb),
+ fpat(0xcc), fpat(0xdd), fpat(0xee), fpat(0xff)
+ };
+
+
+/* Fill a rectangle with a color. */
+private int
+mem_mapped4_fill_rectangle(gx_device *dev,
+ int x, int y, int w, int h, gx_color_index color)
+{ fit_fill(dev, x, y, w, h);
+ bits_fill_rectangle(scan_line_base(mdev, y), x << 2, mdev->raster,
+ tile_patterns[color], w << 2, h);
+ return 0;
+}
+
+/* Copy a bitmap. */
+private int
+mem_mapped4_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 zero, gx_color_index one)
+{ const byte *line;
+ int first_bit;
+ byte first_mask, b0, b1;
+ declare_scan_ptr(dest);
+ fit_copy(dev, base, sourcex, sraster, id, x, y, w, h);
+ setup_rect(dest);
+ line = base + (sourcex >> 3);
+ first_bit = 0x80 >> (sourcex & 7);
+ first_mask = (x & 1 ? 0xf : 0xf0);
+ b0 = ((byte)zero << 4) + (byte)zero;
+ b1 = ((byte)one << 4) + (byte)one;
+ while ( h-- > 0 )
+ { register byte *pptr = (byte *)dest;
+ const byte *sptr = line;
+ register int sbyte = *sptr++;
+ register int bit = first_bit;
+ register byte mask = first_mask;
+ int count = w;
+ do
+ { if ( sbyte & bit )
+ { if ( one != gx_no_color_index )
+ *pptr = (*pptr & ~mask) + (b1 & mask);
+ }
+ else
+ { if ( zero != gx_no_color_index )
+ *pptr = (*pptr & ~mask) + (b0 & mask);
+ }
+ if ( (bit >>= 1) == 0 )
+ bit = 0x80, sbyte = *sptr++;
+ if ( (mask = ~mask) == 0xf0 )
+ pptr++;
+ }
+ while ( --count > 0 );
+ line += sraster;
+ inc_ptr(dest, draster);
+ }
+ return 0;
+}
+
+/* Copy a color bitmap. */
+private int
+mem_mapped4_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 code;
+ fit_copy(dev, base, sourcex, sraster, id, x, y, w, h);
+ /* Use monobit copy_mono. */
+ /* Patch the width in the device temporarily. */
+ dev->width <<= 2;
+ code = (*dev_proc(&mem_mono_device, copy_mono))
+ (dev, base, sourcex << 2, sraster, id,
+ x << 2, y, w << 2, h, (gx_color_index)0, (gx_color_index)1);
+ /* Restore the correct width. */
+ dev->width >>= 2;
+ return code;
+}
+
+/* ================ "Word"-oriented device ================ */
+
+/* Note that on a big-endian machine, this is the same as the */
+/* standard byte-oriented-device. */
+
+#if !arch_is_big_endian
+
+/* Procedures */
+declare_mem_procs(mem4_word_copy_mono, mem4_word_copy_color, mem4_word_fill_rectangle);
+
+/* Here is the device descriptor. */
+const gx_device_memory far_data mem_mapped4_word_device =
+ mem_full_device("image4w", 4, 0, mem_open,
+ mem_mapped_map_rgb_color, mem_mapped_map_color_rgb,
+ mem4_word_copy_mono, mem4_word_copy_color, mem4_word_fill_rectangle,
+ mem_word_get_bits, gx_default_map_cmyk_color,
+ gx_default_strip_tile_rectangle, gx_no_strip_copy_rop);
+
+/* Fill a rectangle with a color. */
+private int
+mem4_word_fill_rectangle(gx_device *dev, int x, int y, int w, int h,
+ gx_color_index color)
+{ byte *base;
+ uint raster;
+ fit_fill(dev, x, y, w, h);
+ base = scan_line_base(mdev, y);
+ raster = mdev->raster;
+ mem_swap_byte_rect(base, raster, x << 2, w << 2, h, true);
+ bits_fill_rectangle(base, x << 2, raster,
+ tile_patterns[color], w << 2, h);
+ mem_swap_byte_rect(base, raster, x << 2, w << 2, h, true);
+ return 0;
+}
+
+/* Copy a bitmap. */
+private int
+mem4_word_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 zero, gx_color_index one)
+{ byte *row;
+ uint raster;
+ bool store;
+ fit_copy(dev, base, sourcex, sraster, id, x, y, w, h);
+ row = scan_line_base(mdev, y);
+ raster = mdev->raster;
+ store = (zero != gx_no_color_index && one != gx_no_color_index);
+ mem_swap_byte_rect(row, raster, x << 2, w << 2, h, store);
+ mem_mapped4_copy_mono(dev, base, sourcex, sraster, id,
+ x, y, w, h, zero, one);
+ mem_swap_byte_rect(row, raster, x << 2, w << 2, h, false);
+ return 0;
+}
+
+/* Copy a color bitmap. */
+private int
+mem4_word_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 code;
+ fit_copy(dev, base, sourcex, sraster, id, x, y, w, h);
+ /* Use monobit copy_mono. */
+ /* Patch the width in the device temporarily. */
+ dev->width <<= 2;
+ code = (*dev_proc(&mem_mono_word_device, copy_mono))
+ (dev, base, sourcex << 2, sraster, id,
+ x << 2, y, w << 2, h, (gx_color_index)0, (gx_color_index)1);
+ /* Restore the correct width. */
+ dev->width >>= 2;
+ return code;
+}
+
+#endif /* !arch_is_big_endian */
diff --git a/pstoraster/gdevm8.c b/pstoraster/gdevm8.c
new file mode 100644
index 000000000..e24957d33
--- /dev/null
+++ b/pstoraster/gdevm8.c
@@ -0,0 +1,225 @@
+/* Copyright (C) 1994, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gdevm8.c */
+/* 8-bit-per-pixel "memory" (stored bitmap) device */
+#include "memory_.h"
+#include "gx.h"
+#include "gxdevice.h"
+#include "gxdevmem.h" /* semi-public definitions */
+#include "gdevmem.h" /* private definitions */
+
+/**************** NOTE: copy_rop only works for gray scale ****************/
+extern dev_proc_strip_copy_rop(mem_gray8_rgb24_strip_copy_rop); /* in gdevmrop.c */
+#define mem_gray8_strip_copy_rop mem_gray8_rgb24_strip_copy_rop
+
+/* ================ Standard (byte-oriented) device ================ */
+
+#undef chunk
+#define chunk byte
+
+/* Procedures */
+declare_mem_procs(mem_mapped8_copy_mono, mem_mapped8_copy_color, mem_mapped8_fill_rectangle);
+
+/* The device descriptor. */
+const gx_device_memory far_data mem_mapped8_device =
+ mem_device("image8", 8, 0,
+ mem_mapped_map_rgb_color, mem_mapped_map_color_rgb,
+ mem_mapped8_copy_mono, mem_mapped8_copy_color, mem_mapped8_fill_rectangle,
+ mem_gray8_strip_copy_rop);
+
+/* Convert x coordinate to byte offset in scan line. */
+#undef x_to_byte
+#define x_to_byte(x) (x)
+
+/* Fill a rectangle with a color. */
+private int
+mem_mapped8_fill_rectangle(gx_device *dev,
+ int x, int y, int w, int h, gx_color_index color)
+{ fit_fill(dev, x, y, w, h);
+ bytes_fill_rectangle(scan_line_base(mdev, y) + x, mdev->raster,
+ (byte)color, w, h);
+ return 0;
+}
+
+/* Copy a monochrome bitmap. */
+/* We split up this procedure because of limitations in the bcc32 compiler. */
+private void mapped8_copy01(P9(chunk *, const byte *, int, int, uint,
+ int, int, byte, byte));
+private void mapped8_copyN1(P8(chunk *, const byte *, int, int, uint,
+ int, int, byte));
+private void mapped8_copy0N(P8(chunk *, const byte *, int, int, uint,
+ int, int, byte));
+private int
+mem_mapped8_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 zero, gx_color_index one)
+{ const byte *line;
+ int first_bit;
+ declare_scan_ptr(dest);
+ fit_copy(dev, base, sourcex, sraster, id, x, y, w, h);
+ setup_rect(dest);
+ line = base + (sourcex >> 3);
+ first_bit = 0x80 >> (sourcex & 7);
+#define is_color(c) ((int)(c) != (int)gx_no_color_index)
+ if ( is_color(one) )
+ { if ( is_color(zero) )
+ mapped8_copy01(dest, line, first_bit, sraster, draster,
+ w, h, (byte)zero, (byte)one);
+ else
+ mapped8_copyN1(dest, line, first_bit, sraster, draster,
+ w, h, (byte)one);
+ }
+ else if ( is_color(zero) )
+ mapped8_copy0N(dest, line, first_bit, sraster, draster,
+ w, h, (byte)zero);
+#undef is_color
+ return 0;
+}
+/* Macros for copy loops */
+#define COPY_BEGIN\
+ while ( h-- > 0 )\
+ { register byte *pptr = dest;\
+ const byte *sptr = line;\
+ register int sbyte = *sptr;\
+ register uint bit = first_bit;\
+ int count = w;\
+ do\
+ {
+#define COPY_END\
+ if ( (bit >>= 1) == 0 )\
+ bit = 0x80, sbyte = *++sptr;\
+ pptr++;\
+ }\
+ while ( --count > 0 );\
+ line += sraster;\
+ inc_ptr(dest, draster);\
+ }
+/* Halftone coloring */
+private void
+mapped8_copy01(chunk *dest, const byte *line, int first_bit,
+ int sraster, uint draster, int w, int h, byte b0, byte b1)
+{ COPY_BEGIN
+ *pptr = (sbyte & bit ? b1 : b0);
+ COPY_END
+}
+/* Stenciling */
+private void
+mapped8_copyN1(chunk *dest, const byte *line, int first_bit,
+ int sraster, uint draster, int w, int h, byte b1)
+{ COPY_BEGIN
+ if ( sbyte & bit )
+ *pptr = b1;
+ COPY_END
+}
+/* Reverse stenciling (probably never used) */
+private void
+mapped8_copy0N(chunk *dest, const byte *line, int first_bit,
+ int sraster, uint draster, int w, int h, byte b0)
+{ COPY_BEGIN
+ if ( !(sbyte & bit) )
+ *pptr = b0;
+ COPY_END
+}
+#undef COPY_BEGIN
+#undef COPY_END
+
+/* Copy a color bitmap. */
+private int
+mem_mapped8_copy_color(gx_device *dev,
+ const byte *base, int sourcex, int sraster, gx_bitmap_id id,
+ int x, int y, int w, int h)
+{ fit_copy(dev, base, sourcex, sraster, id, x, y, w, h);
+ mem_copy_byte_rect(mdev, base, sourcex, sraster, x, y, w, h);
+ return 0;
+}
+
+/* ================ "Word"-oriented device ================ */
+
+/* Note that on a big-endian machine, this is the same as the */
+/* standard byte-oriented-device. */
+
+#if !arch_is_big_endian
+
+/* Procedures */
+declare_mem_procs(mem8_word_copy_mono, mem8_word_copy_color, mem8_word_fill_rectangle);
+
+/* Here is the device descriptor. */
+const gx_device_memory far_data mem_mapped8_word_device =
+ mem_full_device("image8w", 8, 0, mem_open,
+ mem_mapped_map_rgb_color, mem_mapped_map_color_rgb,
+ mem8_word_copy_mono, mem8_word_copy_color, mem8_word_fill_rectangle,
+ mem_word_get_bits, gx_default_map_cmyk_color,
+ gx_default_strip_tile_rectangle, gx_no_strip_copy_rop);
+
+/* Fill a rectangle with a color. */
+private int
+mem8_word_fill_rectangle(gx_device *dev, int x, int y, int w, int h,
+ gx_color_index color)
+{ byte *base;
+ uint raster;
+ fit_fill(dev, x, y, w, h);
+ base = scan_line_base(mdev, y);
+ raster = mdev->raster;
+ mem_swap_byte_rect(base, raster, x << 3, w << 3, h, true);
+ bytes_fill_rectangle(base + x, raster, (byte)color, w, h);
+ mem_swap_byte_rect(base, raster, x << 3, w << 3, h, true);
+ return 0;
+}
+
+/* Copy a bitmap. */
+private int
+mem8_word_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 zero, gx_color_index one)
+{ byte *row;
+ uint raster;
+ bool store;
+ fit_copy(dev, base, sourcex, sraster, id, x, y, w, h);
+ row = scan_line_base(mdev, y);
+ raster = mdev->raster;
+ store = (zero != gx_no_color_index && one != gx_no_color_index);
+ mem_swap_byte_rect(row, raster, x << 3, w << 3, h, store);
+ mem_mapped8_copy_mono(dev, base, sourcex, sraster, id,
+ x, y, w, h, zero, one);
+ mem_swap_byte_rect(row, raster, x << 3, w << 3, h, false);
+ return 0;
+}
+
+/* Copy a color bitmap. */
+private int
+mem8_word_copy_color(gx_device *dev,
+ const byte *base, int sourcex, int sraster, gx_bitmap_id id,
+ int x, int y, int w, int h)
+{ byte *row;
+ uint raster;
+ fit_copy(dev, base, sourcex, sraster, id, x, y, w, h);
+ row = scan_line_base(mdev, y);
+ raster = mdev->raster;
+ mem_swap_byte_rect(row, raster, x << 3, w << 3, h, true);
+ mem_copy_byte_rect(mdev, base, sourcex, sraster, x, y, w, h);
+ mem_swap_byte_rect(row, raster, x << 3, w << 3, h, false);
+ return 0;
+}
+
+#endif /* !arch_is_big_endian */
diff --git a/pstoraster/gdevmem.c b/pstoraster/gdevmem.c
new file mode 100644
index 000000000..ee5c3d75a
--- /dev/null
+++ b/pstoraster/gdevmem.c
@@ -0,0 +1,363 @@
+/* Copyright (C) 1989, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gdevmem.c */
+/* Generic "memory" (stored bitmap) device */
+#include "memory_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gsstruct.h"
+#include "gxdevice.h"
+#include "gxdevmem.h" /* semi-public definitions */
+#include "gdevmem.h" /* private definitions */
+
+/* Structure descriptor */
+public_st_device_memory();
+
+/* GC procedures */
+#define mptr ((gx_device_memory *)vptr)
+private ENUM_PTRS_BEGIN(device_memory_enum_ptrs) {
+ return (*st_device_forward.enum_ptrs)(vptr, sizeof(gx_device_forward), index-2, pep);
+ }
+ case 0:
+ *pep = (mptr->foreign_bits ? NULL : (void *)mptr->base);
+ break;
+ ENUM_STRING_PTR(1, gx_device_memory, palette);
+ENUM_PTRS_END
+private RELOC_PTRS_BEGIN(device_memory_reloc_ptrs) {
+ if ( !mptr->foreign_bits )
+ { byte *base_old = mptr->base;
+ long reloc;
+ int y;
+ RELOC_PTR(gx_device_memory, base);
+ reloc = base_old - mptr->base;
+ for ( y = 0; y < mptr->height; y++ )
+ mptr->line_ptrs[y] -= reloc;
+ /* Relocate line_ptrs, which also points into the data area. */
+ mptr->line_ptrs = (byte **)((byte *)mptr->line_ptrs - reloc);
+ }
+ RELOC_CONST_STRING_PTR(gx_device_memory, palette);
+ (*st_device_forward.reloc_ptrs)(vptr, sizeof(gx_device_forward), gcst);
+} RELOC_PTRS_END
+#undef mptr
+
+/* Define the palettes for monobit devices. */
+private const byte b_w_palette_string[6] = { 0xff,0xff,0xff, 0,0,0 };
+const gs_const_string mem_mono_b_w_palette = { b_w_palette_string, 6 };
+private const byte w_b_palette_string[6] = { 0,0,0, 0xff,0xff,0xff };
+const gs_const_string mem_mono_w_b_palette = { w_b_palette_string, 6 };
+
+/* ------ Generic code ------ */
+
+/* Return the appropriate memory device for a given */
+/* number of bits per pixel (0 if none suitable). */
+const gx_device_memory *
+gdev_mem_device_for_bits(int bits_per_pixel)
+{ switch ( bits_per_pixel )
+ {
+ case 1: return &mem_mono_device;
+ case 2: return &mem_mapped2_device;
+ case 4: return &mem_mapped4_device;
+ case 8: return &mem_mapped8_device;
+ case 16: return &mem_true16_device;
+ case 24: return &mem_true24_device;
+ case 32: return &mem_true32_device;
+ default: return 0;
+ }
+}
+/* Do the same for a word-oriented device. */
+const gx_device_memory *
+gdev_mem_word_device_for_bits(int bits_per_pixel)
+{ switch ( bits_per_pixel )
+ {
+ case 1: return &mem_mono_word_device;
+ case 2: return &mem_mapped2_word_device;
+ case 4: return &mem_mapped4_word_device;
+ case 8: return &mem_mapped8_word_device;
+ case 24: return &mem_true24_word_device;
+ case 32: return &mem_true32_word_device;
+ default: return 0;
+ }
+}
+
+/* Make a memory device. */
+/* Note that the default for monobit devices is white = 0, black = 1. */
+void
+gs_make_mem_device(gx_device_memory *dev, const gx_device_memory *mdproto,
+ gs_memory_t *mem, int page_device, gx_device *target)
+{ *dev = *mdproto;
+ dev->memory = mem;
+ dev->stype = &st_device_memory;
+ switch ( page_device )
+ {
+ case -1:
+ dev->std_procs.get_page_device = gx_default_get_page_device;
+ break;
+ case 1:
+ dev->std_procs.get_page_device = gx_page_device_get_page_device;
+ break;
+ }
+ dev->target = target;
+ if ( target != 0 )
+ { /* Forward the color mapping operations to the target. */
+ gx_device_forward_color_procs((gx_device_forward *)dev);
+ }
+ if ( dev->color_info.depth == 1 )
+ gdev_mem_mono_set_inverted(dev,
+ (target == 0 ||
+ (*dev_proc(target, map_rgb_color))
+ (target, (gx_color_value)0, (gx_color_value)0,
+ (gx_color_value)0) != 0));
+}
+/* Make a monobit memory device. This is never a page device. */
+/* Note that white=0, black=1. */
+void
+gs_make_mem_mono_device(gx_device_memory *dev, gs_memory_t *mem,
+ gx_device *target)
+{ *dev = mem_mono_device;
+ dev->memory = mem;
+ dev->std_procs.get_page_device = gx_default_get_page_device;
+ mdev->target = target;
+ gdev_mem_mono_set_inverted(dev, true);
+}
+
+
+/* Define whether a monobit memory device is inverted (black=1). */
+void
+gdev_mem_mono_set_inverted(gx_device_memory *dev, bool black_is_1)
+{ if ( black_is_1 )
+ dev->palette = mem_mono_b_w_palette;
+ else
+ dev->palette = mem_mono_w_b_palette;
+}
+
+/* Compute the size of the bitmap storage, */
+/* including the space for the scan line pointer table. */
+/* Note that scan lines are padded to a multiple of align_bitmap_mod bytes, */
+/* and additional padding may be needed if the pointer table */
+/* must be aligned to an even larger modulus. */
+private ulong
+mem_bitmap_bits_size(const gx_device_memory *dev)
+{ return round_up((ulong)dev->height * gdev_mem_raster(dev),
+ max(align_bitmap_mod, arch_align_ptr_mod));
+}
+ulong
+gdev_mem_bitmap_size(const gx_device_memory *dev)
+{ return mem_bitmap_bits_size(dev) +
+ (ulong)dev->height * sizeof(byte *);
+}
+
+/* Open a memory device, allocating the data area if appropriate, */
+/* and create the scan line table. */
+private void mem_set_line_ptrs(P3(gx_device_memory *, byte **, byte *));
+int
+mem_open(gx_device *dev)
+{ if ( mdev->bitmap_memory != 0 )
+ { /* Allocate the data now. */
+ ulong size = gdev_mem_bitmap_size(mdev);
+ if ( (uint)size != size )
+ return_error(gs_error_limitcheck);
+ mdev->base = gs_alloc_bytes(mdev->bitmap_memory, (uint)size,
+ "mem_open");
+ if ( mdev->base == 0 )
+ return_error(gs_error_VMerror);
+ mdev->foreign_bits = false;
+ }
+/*
+ * Macro for adding an offset to a pointer when setting up the
+ * scan line table. This isn't just pointer arithmetic, because of
+ * the segmenting considerations discussed in gdevmem.h.
+ */
+#define huge_ptr_add(base, offset)\
+ ((void *)((byte huge *)(base) + (offset)))
+ mem_set_line_ptrs(mdev,
+ huge_ptr_add(mdev->base,
+ mem_bitmap_bits_size(mdev)),
+ mdev->base);
+ return 0;
+}
+/* Set up the scan line pointers of a memory device. */
+/* Sets line_ptrs, base, raster; uses width, height, color_info.depth. */
+private void
+mem_set_line_ptrs(gx_device_memory *devm, byte **line_ptrs, byte *base)
+{ byte **pptr = devm->line_ptrs = line_ptrs;
+ byte **pend = pptr + devm->height;
+ byte *scan_line = devm->base = base;
+ uint raster = devm->raster = gdev_mem_raster(devm);
+
+ while ( pptr < pend )
+ { *pptr++ = scan_line;
+ scan_line = huge_ptr_add(scan_line, raster);
+ }
+}
+
+/* Return the initial transformation matrix */
+void
+mem_get_initial_matrix(gx_device *dev, gs_matrix *pmat)
+{ pmat->xx = mdev->initial_matrix.xx;
+ pmat->xy = mdev->initial_matrix.xy;
+ pmat->yx = mdev->initial_matrix.yx;
+ pmat->yy = mdev->initial_matrix.yy;
+ pmat->tx = mdev->initial_matrix.tx;
+ pmat->ty = mdev->initial_matrix.ty;
+}
+
+/* Test whether a device is a memory device */
+bool
+gs_device_is_memory(const gx_device *dev)
+{ /* We can't just compare the procs, or even an individual proc, */
+ /* because we might be tracing. Instead, check the identity of */
+ /* the device name. */
+ const gx_device_memory *bdev =
+ gdev_mem_device_for_bits(dev->color_info.depth);
+ if ( bdev != 0 && bdev->dname == dev->dname )
+ return true;
+ bdev = gdev_mem_word_device_for_bits(dev->color_info.depth);
+ return (bdev != 0 && bdev->dname == dev->dname);
+}
+
+/* Close a memory device, freeing the data area if appropriate. */
+int
+mem_close(gx_device *dev)
+{ if ( mdev->bitmap_memory != 0 )
+ gs_free_object(mdev->bitmap_memory, mdev->base, "mem_close");
+ return 0;
+}
+
+/* Copy a scan line to a client. */
+#undef chunk
+#define chunk byte
+int
+mem_get_bits(gx_device *dev, int y, byte *str, byte **actual_data)
+{ byte *src;
+ if ( y < 0 || y >= dev->height )
+ return_error(gs_error_rangecheck);
+ src = scan_line_base(mdev, y);
+ if ( actual_data == 0 )
+ memcpy(str, src, gx_device_raster(dev, 0));
+ else
+ *actual_data = src;
+ return 0;
+}
+
+#if !arch_is_big_endian
+
+/* Swap byte order in a rectangular subset of a bitmap. */
+/* If store = true, assume the rectangle will be overwritten, */
+/* so don't swap any bytes where it doesn't matter. */
+/* The caller has already done a fit_fill or fit_copy. */
+void
+mem_swap_byte_rect(byte *base, uint raster, int x, int w, int h, bool store)
+{ int xbit = x & 31;
+ if ( store )
+ { if ( xbit + w > 64 )
+ { /* Operation spans multiple words. */
+ /* Just swap the words at the left and right edges. */
+ if ( xbit != 0 )
+ mem_swap_byte_rect(base, raster, x, 1, h, false);
+ x += w - 1;
+ xbit = x & 31;
+ if ( xbit == 31 )
+ return;
+ w = 1;
+ }
+ }
+ /* Swap the entire rectangle (or what's left of it). */
+ { byte *row = base + ((x >> 5) << 2);
+ int nw = (xbit + w + 31) >> 5;
+ int ny;
+ for ( ny = h; ny > 0; row += raster, --ny )
+ { int nx = nw;
+ bits32 *pw = (bits32 *)row;
+ do
+ { bits32 w = *pw;
+ *pw++ = (w >> 24) + ((w >> 8) & 0xff00) +
+ ((w & 0xff00) << 8) + (w << 24);
+ }
+ while ( --nx);
+ }
+ }
+}
+
+/* Copy a word-oriented scan line to the client, swapping bytes as needed. */
+int
+mem_word_get_bits(gx_device *dev, int y, byte *str, byte **actual_data)
+{ byte *src;
+ uint raster = gx_device_raster(dev, 0); /* only doing 1 scan line */
+ if ( y < 0 || y >= dev->height )
+ return_error(gs_error_rangecheck);
+ src = scan_line_base(mdev, y);
+ /* We use raster << 3 rather than dev->width so that */
+ /* the right thing will happen if depth > 1. */
+ mem_swap_byte_rect(src, raster, 0, raster << 3, 1, false);
+ memcpy(str, src, raster);
+ if ( actual_data != 0 )
+ *actual_data = str;
+ mem_swap_byte_rect(src, raster, 0, raster << 3, 1, false);
+ return 0;
+}
+
+#endif /* !arch_is_big_endian */
+
+/* Map a r-g-b color to a color index for a mapped color memory device */
+/* (2, 4, or 8 bits per pixel.) */
+/* This requires searching the palette. */
+gx_color_index
+mem_mapped_map_rgb_color(gx_device *dev, gx_color_value r, gx_color_value g,
+ gx_color_value b)
+{ byte br = gx_color_value_to_byte(r);
+ byte bg = gx_color_value_to_byte(g);
+ byte bb = gx_color_value_to_byte(b);
+ register const byte *pptr = mdev->palette.data;
+ int cnt = mdev->palette.size;
+ const byte *which = 0; /* initialized only to pacify gcc */
+ int best = 256*3;
+
+ while ( (cnt -= 3) >= 0 )
+ { register int diff = *pptr - br;
+ if ( diff < 0 ) diff = -diff;
+ if ( diff < best ) /* quick rejection */
+ { int dg = pptr[1] - bg;
+ if ( dg < 0 ) dg = -dg;
+ if ( (diff += dg) < best ) /* quick rejection */
+ { int db = pptr[2] - bb;
+ if ( db < 0 ) db = -db;
+ if ( (diff += db) < best )
+ which = pptr, best = diff;
+ }
+ }
+ pptr += 3;
+ }
+ return (gx_color_index)((which - mdev->palette.data) / 3);
+}
+
+/* Map a color index to a r-g-b color for a mapped color memory device. */
+int
+mem_mapped_map_color_rgb(gx_device *dev, gx_color_index color,
+ gx_color_value prgb[3])
+{ const byte *pptr = mdev->palette.data + (int)color * 3;
+ prgb[0] = gx_color_value_from_byte(pptr[0]);
+ prgb[1] = gx_color_value_from_byte(pptr[1]);
+ prgb[2] = gx_color_value_from_byte(pptr[2]);
+ return 0;
+}
diff --git a/pstoraster/gdevmem.h b/pstoraster/gdevmem.h
new file mode 100644
index 000000000..02db91379
--- /dev/null
+++ b/pstoraster/gdevmem.h
@@ -0,0 +1,217 @@
+/* Copyright (C) 1991, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gdevmem.h */
+/* Private definitions for memory devices. */
+
+#include "gsbitops.h"
+
+/*
+ The representation for a "memory" device is simply a
+ contiguous bitmap stored in something like the PostScript
+ representation, i.e., each scan line (in left-to-right order), padded
+ to a multiple of bitmap_align_mod bytes, followed immediately by
+ the next one.
+
+ The representation of strings in the interpreter limits
+ the size of a string to 64K-1 bytes, which means we can't simply use
+ a string for the contents of a memory device.
+ We get around this problem by making the client read out the
+ contents of a memory device bitmap in pieces.
+
+ On 80x86 PCs running in 16-bit mode, there may be no way to
+ obtain a contiguous block of storage larger than 64K bytes,
+ which typically isn't big enough for a full-screen bitmap.
+ We take the following compromise position: if the PC is running in
+ native mode (pseudo-segmenting), we limit the bitmap to 64K;
+ if the PC is running in protected mode (e.g., under MS Windows),
+ we assume that blocks larger than 64K have sequential segment numbers,
+ and that the client arranges things so that an individual scan line,
+ the scan line pointer table, and any single call on a drawing routine
+ do not cross a segment boundary.
+
+ Even though the scan lines are stored contiguously, we store a table
+ of their base addresses, because indexing into it is faster than
+ the multiplication that would otherwise be needed.
+*/
+
+/*
+ * Macros for scan line access.
+ * x_to_byte is different for each number of bits per pixel.
+ * Note that these macros depend on the definition of chunk:
+ * each procedure that uses the scanning macros should #define
+ * (not typedef) chunk as either uint or byte.
+ */
+#define declare_scan_ptr(ptr) declare_scan_ptr_as(ptr, chunk *)
+#define declare_scan_ptr_as(ptr,ptype)\
+ register ptype ptr; uint draster
+#define setup_rect(ptr) setup_rect_as(ptr, chunk *)
+#define setup_rect_as(ptr,ptype)\
+ draster = mdev->raster;\
+ ptr = (ptype)(scan_line_base(mdev, y) +\
+ (x_to_byte(x) & -chunk_align_bytes))
+
+/* ------ Generic macros ------ */
+
+/* Macro for declaring the essential device procedures. */
+dev_proc_get_initial_matrix(mem_get_initial_matrix);
+dev_proc_close_device(mem_close);
+#define declare_mem_map_procs(map_rgb_color, map_color_rgb)\
+ private dev_proc_map_rgb_color(map_rgb_color);\
+ private dev_proc_map_color_rgb(map_color_rgb)
+#define declare_mem_procs(copy_mono, copy_color, fill_rectangle)\
+ private dev_proc_copy_mono(copy_mono);\
+ private dev_proc_copy_color(copy_color);\
+ private dev_proc_fill_rectangle(fill_rectangle)
+
+/* The following are used for all except planar or word-oriented devices. */
+dev_proc_open_device(mem_open);
+dev_proc_get_bits(mem_get_bits);
+/* The following are for word-oriented devices. */
+#if arch_is_big_endian
+# define mem_word_get_bits mem_get_bits
+#else
+dev_proc_get_bits(mem_word_get_bits);
+#endif
+/* The following are used for the non-true-color devices. */
+dev_proc_map_rgb_color(mem_mapped_map_rgb_color);
+dev_proc_map_color_rgb(mem_mapped_map_color_rgb);
+
+/*
+ * Macro for generating the device descriptor.
+ * Various compilers have problems with the obvious definition
+ * for max_value, namely:
+ * (depth >= 8 ? 255 : (1 << depth) - 1)
+ * I tried changing (1 << depth) to (1 << (depth & 15)) to forestall bogus
+ * error messages about invalid shift counts, but the H-P compiler chokes
+ * on this. Since the only values of depth we ever plan to support are
+ * powers of 2 (and 24), we just go ahead and enumerate them.
+ */
+#define max_value_gray(rgb_depth, gray_depth)\
+ (gray_depth ? (1 << gray_depth) - 1 : max_value_rgb(rgb_depth, 0))
+#define max_value_rgb(rgb_depth, gray_depth)\
+ (rgb_depth >= 8 ? 255 : rgb_depth == 4 ? 15 : rgb_depth == 2 ? 3 :\
+ rgb_depth == 1 ? 1 : (1 << gray_depth) - 1)
+#define mem_full_alpha_device(name, rgb_depth, gray_depth, open, map_rgb_color, map_color_rgb, copy_mono, copy_color, fill_rectangle, get_bits, map_cmyk_color, copy_alpha, strip_tile_rectangle, strip_copy_rop)\
+{ std_device_dci_body(gx_device_memory, 0, name,\
+ 0, 0, 72, 72,\
+ (rgb_depth ? 3 : 0) + (gray_depth ? 1 : 0), /* num_components */\
+ rgb_depth + gray_depth, /* depth */\
+ max_value_gray(rgb_depth, gray_depth), /* max_gray */\
+ max_value_rgb(rgb_depth, gray_depth), /* max_color */\
+ max_value_gray(rgb_depth, gray_depth) + 1, /* dither_grays */\
+ max_value_rgb(rgb_depth, gray_depth) + 1 /* dither_colors */\
+ ),\
+ { open, /* differs */\
+ mem_get_initial_matrix,\
+ gx_default_sync_output,\
+ gx_default_output_page,\
+ mem_close,\
+ map_rgb_color, /* differs */\
+ map_color_rgb, /* differs */\
+ fill_rectangle, /* differs */\
+ gx_default_tile_rectangle,\
+ copy_mono, /* differs */\
+ copy_color, /* differs */\
+ gx_default_draw_line,\
+ get_bits, /* differs */\
+ gx_default_get_params,\
+ gx_default_put_params,\
+ map_cmyk_color, /* differs */\
+ gx_forward_get_xfont_procs,\
+ gx_forward_get_xfont_device,\
+ gx_default_map_rgb_alpha_color,\
+ gx_forward_get_page_device,\
+ gx_default_get_alpha_bits, /* default is no alpha */\
+ copy_alpha, /* differs */\
+ gx_default_get_band,\
+ gx_default_copy_rop,\
+ gx_default_fill_path,\
+ gx_default_stroke_path,\
+ gx_default_fill_mask,\
+ gx_default_fill_trapezoid,\
+ gx_default_fill_parallelogram,\
+ gx_default_fill_triangle,\
+ gx_default_draw_thin_line,\
+ gx_default_begin_image,\
+ gx_default_image_data,\
+ gx_default_end_image,\
+ strip_tile_rectangle, /* differs */\
+ strip_copy_rop /* differs */\
+ },\
+ 0, /* target */\
+ mem_device_init_private /* see gxdevmem.h */\
+}
+#define mem_full_device(name, rgb_depth, gray_depth, open, map_rgb_color, map_color_rgb, copy_mono, copy_color, fill_rectangle, get_bits, map_cmyk_color, strip_tile_rectangle, strip_copy_rop)\
+ mem_full_alpha_device(name, rgb_depth, gray_depth, open, map_rgb_color,\
+ map_color_rgb, copy_mono, copy_color, fill_rectangle,\
+ get_bits, map_cmyk_color, gx_default_copy_alpha,\
+ strip_tile_rectangle, strip_copy_rop)
+#define mem_device(name, rgb_depth, gray_depth, map_rgb_color, map_color_rgb, copy_mono, copy_color, fill_rectangle, strip_copy_rop)\
+ mem_full_device(name, rgb_depth, gray_depth, mem_open, map_rgb_color,\
+ map_color_rgb, copy_mono, copy_color, fill_rectangle,\
+ mem_get_bits, gx_default_map_cmyk_color,\
+ gx_default_strip_tile_rectangle, strip_copy_rop)
+
+/* Swap a rectangle of bytes, for converting between word- and */
+/* byte-oriented representation. */
+void mem_swap_byte_rect(P6(byte *, uint, int, int, int, bool));
+
+/* Copy a rectangle of bytes from a source to a destination. */
+#define mem_copy_byte_rect(mdev, base, sourcex, sraster, x, y, w, h)\
+ bytes_copy_rectangle(scan_line_base(mdev, y) + x_to_byte(x),\
+ (mdev)->raster,\
+ base + x_to_byte(sourcex), sraster,\
+ x_to_byte(w), h)
+
+/* Macro for casting gx_device argument */
+#define mdev ((gx_device_memory *)dev)
+
+/* ------ Implementations ------ */
+
+extern const gx_device_memory far_data mem_mono_device;
+extern const gx_device_memory far_data mem_mapped2_device;
+extern const gx_device_memory far_data mem_mapped4_device;
+extern const gx_device_memory far_data mem_mapped8_device;
+extern const gx_device_memory far_data mem_true16_device;
+extern const gx_device_memory far_data mem_true24_device;
+extern const gx_device_memory far_data mem_true32_device;
+extern const gx_device_memory far_data mem_planar_device;
+#if arch_is_big_endian
+# define mem_mono_word_device mem_mono_device
+# define mem_mapped2_word_device mem_mapped2_device
+# define mem_mapped4_word_device mem_mapped4_device
+# define mem_mapped8_word_device mem_mapped8_device
+# define mem_true24_word_device mem_true24_device
+# define mem_true32_word_device mem_true32_device
+#else
+extern const gx_device_memory far_data mem_mono_word_device;
+extern const gx_device_memory far_data mem_mapped2_word_device;
+extern const gx_device_memory far_data mem_mapped4_word_device;
+extern const gx_device_memory far_data mem_mapped8_word_device;
+extern const gx_device_memory far_data mem_true24_word_device;
+extern const gx_device_memory far_data mem_true32_word_device;
+#endif
+/* Provide standard palettes for 1-bit devices. */
+extern const gs_const_string mem_mono_b_w_palette; /* black=1, white=0 */
+extern const gs_const_string mem_mono_w_b_palette; /* black=0, white=1 */
diff --git a/pstoraster/gdevmpla.c b/pstoraster/gdevmpla.c
new file mode 100644
index 000000000..66df9173c
--- /dev/null
+++ b/pstoraster/gdevmpla.c
@@ -0,0 +1,182 @@
+/* Copyright (C) 1993, 1994 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gdevmpla.c */
+/* Any-depth planar "memory" (stored bitmap) devices */
+#include "memory_.h"
+#include "gx.h"
+#include "gxdevice.h"
+#include "gxdevmem.h" /* semi-public definitions */
+#include "gdevmem.h" /* private definitions */
+
+/*
+ * Planar memory devices store the bits by planes instead of by chunks.
+ * The plane corresponding to the least significant bit of the color index
+ * is stored first.
+ *
+ * The current implementations are quite inefficient.
+ * We may improve them someday if anyone cares.
+ */
+
+/* Procedures */
+declare_mem_map_procs(mem_planar_map_rgb_color, mem_planar_map_color_rgb);
+declare_mem_procs(mem_planar_copy_mono, mem_planar_copy_color, mem_planar_fill_rectangle);
+
+/* The device descriptor. */
+/* The instance is public. */
+/* The default instance has depth = 1, but clients may set this */
+/* to other values before opening the device. */
+private dev_proc_open_device(mem_planar_open);
+private dev_proc_get_bits(mem_planar_get_bits);
+const gx_device_memory far_data mem_planar_device =
+ mem_full_device("image(planar)", 0, 1, mem_planar_open,
+ mem_planar_map_rgb_color, mem_planar_map_color_rgb,
+ mem_planar_copy_mono, mem_planar_copy_color, mem_planar_fill_rectangle,
+ mem_planar_get_bits, gx_default_map_cmyk_color,
+ gx_default_strip_tile_rectangle, gx_no_strip_copy_rop);
+
+/* Open a planar memory device. */
+private int
+mem_planar_open(gx_device *dev)
+{ /* Temporarily reset the parameters, and call */
+ /* the generic open procedure. */
+ int depth = dev->color_info.depth;
+ int height = dev->height;
+ int code;
+
+ dev->height *= depth;
+ dev->color_info.depth = 1;
+ code = mem_open(dev);
+ dev->height = height;
+ dev->color_info.depth = depth;
+ return code;
+}
+
+/* Map a r-g-b color to a color index. */
+private gx_color_index
+mem_planar_map_rgb_color(gx_device *dev, gx_color_value r, gx_color_value g,
+ gx_color_value b)
+{ int depth = dev->color_info.depth;
+ return (*dev_proc(gdev_mem_device_for_bits(depth), map_rgb_color))
+ (dev, r, g, b);
+}
+
+/* Map a color index to a r-g-b color. */
+private int
+mem_planar_map_color_rgb(gx_device *dev, gx_color_index color,
+ gx_color_value prgb[3])
+{ int depth = dev->color_info.depth;
+ return (*dev_proc(gdev_mem_device_for_bits(depth), map_color_rgb))
+ (dev, color, prgb);
+}
+
+/* Fill a rectangle with a color. */
+private int
+mem_planar_fill_rectangle(gx_device *dev,
+ int x, int y, int w, int h, gx_color_index color)
+{ byte **ptrs = mdev->line_ptrs;
+ int i;
+ for ( i = 0; i < dev->color_info.depth;
+ i++, mdev->line_ptrs += dev->height
+ )
+ (*dev_proc(&mem_mono_device, fill_rectangle))(dev,
+ x, y, w, h, (color >> i) & 1);
+ mdev->line_ptrs = ptrs;
+ return 0;
+}
+
+/* Copy a bitmap. */
+private int
+mem_planar_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 zero, gx_color_index one)
+{ byte **ptrs = mdev->line_ptrs;
+ int i;
+ for ( i = 0; i < dev->color_info.depth;
+ i++, mdev->line_ptrs += dev->height
+ )
+ (*dev_proc(&mem_mono_device, copy_mono))(dev,
+ base, sourcex, sraster, id, x, y, w, h,
+ (zero == gx_no_color_index ? gx_no_color_index :
+ (zero >> i) & 1),
+ (one == gx_no_color_index ? gx_no_color_index :
+ (one >> i) & 1));
+ mdev->line_ptrs = ptrs;
+ return 0;
+}
+
+/* Copy a color bitmap. */
+/* This is very slow and messy. */
+private int
+mem_planar_copy_color(gx_device *dev,
+ const byte *base, int sourcex, int sraster, gx_bitmap_id id,
+ int x, int y, int w, int h)
+{ byte **ptrs = mdev->line_ptrs;
+ int depth = dev->color_info.depth;
+ int wleft = w;
+ int hleft = h;
+ const byte *srow = base;
+ int ynext = y;
+#define max_w 32
+ union _b {
+ long l[max_w / sizeof(long)];
+ byte b[max_w / 8];
+ } buf;
+
+ while ( wleft > max_w )
+ { mem_planar_copy_color(dev, base,
+ sourcex + wleft - max_w, sraster, gx_no_bitmap_id,
+ x + wleft - max_w, y, max_w, h);
+ wleft -= max_w;
+ }
+ for ( ; hleft > 0;
+ srow += sraster, ynext++, hleft--,
+ mdev->line_ptrs += dev->height
+ )
+ { int i;
+ for ( i = 0; i < depth;
+ i++, mdev->line_ptrs += dev->height
+ )
+ { int sx, bx;
+ memset(buf.b, 0, sizeof(buf.b));
+ for ( sx = 0, bx = sourcex * depth + depth - 1 - i;
+ sx < w; sx++, bx += depth
+ )
+ if ( srow[bx >> 3] & (0x80 >> (bx & 7)) )
+ buf.b[sx >> 3] |= 0x80 >> (sx & 7);
+ (*dev_proc(&mem_mono_device, copy_mono))(dev,
+ buf.b, 0, sizeof(buf), gx_no_bitmap_id,
+ x, ynext, w, 1,
+ (gx_color_index)0, (gx_color_index)1);
+ }
+ mdev->line_ptrs = ptrs;
+ }
+ return 0;
+}
+
+/* Copy bits back from a planar memory device. */
+/****** NOT IMPLEMENTED YET ******/
+private int
+mem_planar_get_bits(gx_device *dev, int y, byte *str, byte **actual_data)
+{ return -1;
+}
diff --git a/pstoraster/gdevmrop.c b/pstoraster/gdevmrop.c
new file mode 100644
index 000000000..b9a0c1b57
--- /dev/null
+++ b/pstoraster/gdevmrop.c
@@ -0,0 +1,1013 @@
+/* Copyright (C) 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gdevmrop.c */
+/* RasterOp / transparency / render algorithm implementation for */
+/* memory devices */
+#include "memory_.h"
+#include "gx.h"
+#include "gsbittab.h"
+#include "gserrors.h"
+#include "gsropt.h"
+#include "gxdcolor.h"
+#include "gxdevice.h"
+#include "gxdevmem.h"
+#include "gxdevrop.h"
+#include "gdevmrop.h"
+
+#define mdev ((gx_device_memory *)dev)
+
+/**************** NOTE: ****************
+ * The 2- and 4-bit cases don't handle transparency right.
+ * The 8- and 24-bit cases haven't been tested.
+ * The 16- and 32-bit cases aren't implemented.
+ ***************** ****************/
+
+/* Forward references */
+private gs_rop3_t gs_transparent_rop(P3(gs_rop3_t rop, bool source_transparent,
+ bool pattern_transparent));
+
+#define chunk byte
+
+/* Calculate the X offset for a given Y value, */
+/* taking shift into account if necessary. */
+#define x_offset(px, ty, textures)\
+ ((textures)->shift == 0 ? (px) :\
+ (px) + (ty) / (textures)->rep_height * (textures)->rep_shift)
+
+/* ---------------- Initialization ---------------- */
+
+void
+gs_roplib_init(gs_memory_t *mem)
+{ /* Replace the default and forwarding copy_rop procedures. */
+ gx_default_copy_rop_proc = gx_real_default_copy_rop;
+ gx_forward_copy_rop_proc = gx_forward_copy_rop;
+ gx_default_strip_copy_rop_proc = gx_real_default_strip_copy_rop;
+ gx_forward_strip_copy_rop_proc = gx_forward_strip_copy_rop;
+}
+
+/* ---------------- Debugging aids ---------------- */
+
+#ifdef DEBUG
+
+private void
+trace_copy_rop(const char *cname, 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)
+{ dprintf4("%s: dev=0x%lx(%s) depth=%d\n",
+ cname, (ulong)dev, dev->dname, dev->color_info.depth);
+ dprintf4(" source data=0x%lx x=%d raster=%u id=%lu colors=",
+ (ulong)sdata, sourcex, sraster, (ulong)id);
+ if ( scolors )
+ dprintf2("(%lu,%lu);\n", scolors[0], scolors[1]);
+ else
+ dputs("none;\n");
+ if ( textures )
+ dprintf8(" textures=0x%lx size=%dx%d(%dx%d) raster=%u shift=%d(%d)",
+ (ulong)textures, textures->size.x, textures->size.y,
+ textures->rep_width, textures->rep_height, textures->raster,
+ textures->shift, textures->rep_shift);
+ else
+ dputs(" textures=none");
+ if ( tcolors )
+ dprintf2(" colors=(%lu,%lu)\n", tcolors[0], tcolors[1]);
+ else
+ dputs(" colors=none\n");
+ dprintf7(" rect=(%d,%d),(%d,%d) phase=(%d,%d) op=0x%x\n",
+ x, y, x + width, y + height, phase_x, phase_y,
+ (uint)lop);
+ if ( gs_debug_c('B') )
+ { if ( sdata )
+ debug_dump_bitmap(sdata, sraster, height, "source bits");
+ if ( textures && textures->data )
+ debug_dump_bitmap(textures->data, textures->raster,
+ textures->size.y, "textures bits");
+ }
+}
+
+#endif
+
+/* ---------------- Monobit RasterOp ---------------- */
+
+int
+mem_mono_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)
+{ gs_rop3_t rop = (gs_rop3_t)(lop & lop_rop_mask);
+ gx_strip_bitmap no_texture;
+ bool invert;
+ uint draster = mdev->raster;
+ uint traster;
+ int line_count;
+ byte *drow;
+ const byte *srow;
+ int ty;
+
+ /* If map_rgb_color isn't the default one for monobit memory */
+ /* devices, palette might not be set; set it now if needed. */
+ if ( mdev->palette.data == 0 )
+ gdev_mem_mono_set_inverted(mdev,
+ (*dev_proc(dev, map_rgb_color))
+ (dev, (gx_color_value)0,
+ (gx_color_value)0, (gx_color_value)0)
+ != 0);
+ invert = mdev->palette.data[0] != 0;
+
+#ifdef DEBUG
+ if ( gs_debug_c('b') )
+ trace_copy_rop("mem_mono_strip_copy_rop",
+ dev, sdata, sourcex, sraster,
+ id, scolors, textures, tcolors,
+ x, y, width, height, phase_x, phase_y, lop);
+ if ( gs_debug_c('B') )
+ debug_dump_bitmap(scan_line_base(mdev, y), mdev->raster,
+ height, "initial dest bits");
+#endif
+
+ /* Handle source and destination transparency. */
+ rop = gs_transparent_rop(rop, lop & lop_S_transparent,
+ lop & lop_T_transparent);
+
+ /*
+ * RasterOp is defined as operating in RGB space; in the monobit
+ * case, this means black = 0, white = 1. However, most monobit
+ * devices use the opposite convention. To make this work,
+ * we must precondition the Boolean operation by swapping the
+ * order of bits end-for-end and then inverting.
+ */
+
+ if ( invert )
+ rop = (gs_rop3_t)(byte_reverse_bits[rop] ^ 0xff);
+
+ /* Modify the raster operation according to the source palette. */
+ if ( scolors != 0 )
+ { /* Source with palette. */
+ switch ( (int)((scolors[1] << 1) + scolors[0]) )
+ {
+ case 0: rop = (gs_rop3_t)rop3_know_S_0(rop); break;
+ case 1: rop = (gs_rop3_t)rop3_invert_S(rop); break;
+ case 2: break;
+ case 3: rop = (gs_rop3_t)rop3_know_S_1(rop); break;
+ }
+ }
+
+ /* Modify the raster operation according to the texture palette. */
+ if ( tcolors != 0 )
+ { /* Texture with palette. */
+ switch ( (int)((tcolors[1] << 1) + tcolors[0]) )
+ {
+ case 0: rop = (gs_rop3_t)rop3_know_T_0(rop); break;
+ case 1: rop = (gs_rop3_t)rop3_invert_T(rop); break;
+ case 2: break;
+ case 3: rop = (gs_rop3_t)rop3_know_T_1(rop); break;
+ }
+ }
+
+ /* Handle constant source and/or texture. */
+ if ( rop3_uses_S(rop) )
+ { fit_copy(dev, sdata, sourcex, sraster, id,
+ x, y, width, height);
+ }
+ else
+ { /* Source is not used; sdata et al may be garbage. */
+ sdata = mdev->base; /* arbitrary, as long as all */
+ /* accesses are valid */
+ sourcex = x; /* guarantee no source skew */
+ sraster = 0;
+ fit_fill(dev, x, y, width, height);
+ }
+ if ( !rop3_uses_T(rop) )
+ { /* Texture is not used; texture may be garbage. */
+ no_texture.data = mdev->base; /* arbitrary */
+ no_texture.raster = 0;
+ no_texture.size.x = width;
+ no_texture.size.y = height;
+ no_texture.rep_width = no_texture.rep_height = 1;
+ no_texture.rep_shift = no_texture.shift = 0;
+ textures = &no_texture;
+ }
+
+#ifdef DEBUG
+ if_debug1('b', "final rop=0x%x\n", rop);
+#endif
+
+ /* Set up transfer parameters. */
+ line_count = height;
+ srow = sdata;
+ drow = scan_line_base(mdev, y);
+ traster = textures->raster;
+ ty = y + phase_y;
+
+ /* Loop over scan lines. */
+ for ( ; line_count-- > 0; drow += draster, srow += sraster, ++ty )
+ { /* Loop over copies of the tile. */
+ int sx = sourcex;
+ int dx = x;
+ int w = width;
+ const byte *trow =
+ textures->data + (ty % textures->size.y) * traster;
+ int xoff = x_offset(phase_x, ty, textures);
+ int nw;
+
+ for ( ; w > 0; sx += nw, dx += nw, w -= nw )
+ { int dbit = dx & 7;
+ int sbit = sx & 7;
+ int sskew = sbit - dbit;
+ int tx = (dx + xoff) % textures->rep_width;
+ int tbit = tx & 7;
+ int tskew = tbit - dbit;
+ int left = nw = min(w, textures->size.x - tx);
+ byte lmask = 0xff >> dbit;
+ byte rmask = 0xff << (~(dbit + nw - 1) & 7);
+ byte mask = lmask;
+ int nx = 8 - dbit;
+ byte *dptr = drow + (dx >> 3);
+ const byte *sptr = srow + (sx >> 3);
+ const byte *tptr = trow + (tx >> 3);
+
+ if ( sskew < 0 )
+ --sptr, sskew += 8;
+ if ( tskew < 0 )
+ --tptr, tskew += 8;
+ for ( ; left > 0;
+ left -= nx, mask = 0xff, nx = 8,
+ ++dptr, ++sptr, ++tptr
+ )
+ { byte dbyte = *dptr;
+#define fetch1(ptr, skew)\
+ (skew ? (ptr[0] << skew) + (ptr[1] >> (8 - skew)) : *ptr)
+ byte sbyte = fetch1(sptr, sskew);
+ byte tbyte = fetch1(tptr, tskew);
+#undef fetch1
+ byte result =
+ (*rop_proc_tab[rop])(dbyte, sbyte, tbyte);
+ if ( left <= nx )
+ mask &= rmask;
+ *dptr = (mask == 0xff ? result :
+ (result & mask) | (dbyte & ~mask));
+ }
+ }
+ }
+#ifdef DEBUG
+ if ( gs_debug_c('B') )
+ debug_dump_bitmap(scan_line_base(mdev, y), mdev->raster,
+ height, "final dest bits");
+#endif
+ return 0;
+}
+
+/* ---------------- Fake RasterOp for 2- and 4-bit devices ---------------- */
+
+int
+mem_gray_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)
+{ gx_color_index scolors2[2];
+ const gx_color_index *real_scolors = scolors;
+ gx_color_index tcolors2[2];
+ const gx_color_index *real_tcolors = tcolors;
+ gx_strip_bitmap texture2;
+ const gx_strip_bitmap *real_texture = textures;
+ long tdata;
+ int depth = dev->color_info.depth;
+ int log2_depth = depth >> 1; /* works for 2, 4 */
+ gx_color_index max_pixel = (1 << depth) - 1;
+ int code;
+
+#ifdef DEBUG
+ if ( gs_debug_c('b') )
+ trace_copy_rop("mem_gray_strip_copy_rop",
+ dev, sdata, sourcex, sraster,
+ id, scolors, textures, tcolors,
+ x, y, width, height, phase_x, phase_y, lop);
+#endif
+ if ( scolors )
+ { /* We can't handle "real" source colors. */
+ if ( (scolors[0] | scolors[1]) & ~max_pixel )
+ return_error(gs_error_rangecheck);
+ scolors2[0] = scolors[0] & 1;
+ scolors2[1] = scolors[1] & 1;
+ real_scolors = scolors2;
+ }
+ if ( textures )
+ { texture2 = *textures;
+ texture2.size.x <<= log2_depth;
+ texture2.rep_width <<= log2_depth;
+ texture2.shift <<= log2_depth;
+ texture2.rep_shift <<= log2_depth;
+ real_texture = &texture2;
+ }
+ if ( tcolors )
+ { /* We can't handle monobit textures. */
+ if ( tcolors[0] != tcolors[1] )
+ return_error(gs_error_rangecheck);
+ /* For polybit textures with colors other than */
+ /* all 0s or all 1s, fabricate the data. */
+ if ( tcolors[0] != 0 && tcolors[0] != max_pixel )
+ { real_tcolors = 0;
+ *(byte *)&tdata = (byte)tcolors[0] << (8 - depth);
+ texture2.data = (byte *)&tdata;
+ texture2.raster = align_bitmap_mod;
+ texture2.size.x = texture2.rep_width = depth;
+ texture2.size.y = texture2.rep_height = 1;
+ texture2.id = gx_no_bitmap_id;
+ texture2.shift = texture2.rep_shift = 0;
+ real_texture = &texture2;
+ }
+ else
+ { tcolors2[0] = tcolors2[1] = tcolors[0] & 1;
+ real_tcolors = tcolors2;
+ }
+ }
+ dev->width <<= log2_depth;
+ code = mem_mono_strip_copy_rop(dev, sdata,
+ (real_scolors == NULL ? sourcex << log2_depth : sourcex),
+ sraster, id, real_scolors, real_texture, real_tcolors,
+ x << log2_depth, y, width << log2_depth, height,
+ phase_x << log2_depth, phase_y, lop);
+ dev->width >>= log2_depth;
+ return code;
+}
+
+/* ---------------- RasterOp with 8-bit gray / 24-bit RGB ---------------- */
+
+int
+mem_gray8_rgb24_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)
+{ gs_rop3_t rop = (gs_rop3_t)(lop & lop_rop_mask);
+ gx_color_index const_source = gx_no_color_index;
+ gx_color_index const_texture = gx_no_color_index;
+ uint draster = mdev->raster;
+ int line_count;
+ byte *drow;
+ int depth = dev->color_info.depth;
+ int bpp = depth >> 3; /* bytes per pixel, 1 or 3 */
+ gx_color_index all_ones = ((gx_color_index)1 << depth) - 1;
+ gx_color_index strans =
+ (lop & lop_S_transparent ? all_ones : gx_no_color_index);
+ gx_color_index ttrans =
+ (lop & lop_T_transparent ? all_ones : gx_no_color_index);
+
+ /* Check for constant source. */
+ if ( scolors != 0 && scolors[0] == scolors[1] )
+ { /* Constant source */
+ const_source = scolors[0];
+ if ( const_source == 0 )
+ rop = (gs_rop3_t)rop3_know_S_0(rop);
+ else if ( const_source == all_ones )
+ rop = (gs_rop3_t)rop3_know_S_1(rop);
+ }
+ else if ( !rop3_uses_S(rop) )
+ const_source = 0; /* arbitrary */
+
+ /* Check for constant texture. */
+ if ( tcolors != 0 && tcolors[0] == tcolors[1] )
+ { /* Constant texture */
+ const_texture = tcolors[0];
+ if ( const_texture == 0 )
+ rop = (gs_rop3_t)rop3_know_T_0(rop);
+ else if ( const_texture == all_ones )
+ rop = (gs_rop3_t)rop3_know_T_1(rop);
+ }
+ else if ( !rop3_uses_T(rop) )
+ const_texture = 0; /* arbitrary */
+
+ /* Adjust coordinates to be in bounds. */
+ if ( const_source == gx_no_color_index )
+ { fit_copy(dev, sdata, sourcex, sraster, id,
+ x, y, width, height);
+ }
+ else
+ { fit_fill(dev, x, y, width, height);
+ }
+
+ /* Set up transfer parameters. */
+ line_count = height;
+ drow = scan_line_base(mdev, y) + x * bpp;
+
+ /*
+ * There are 18 cases depending on whether each of the source and
+ * texture is constant, 1-bit, or multi-bit, and on whether the
+ * depth is 8 or 24 bits. We divide first according to constant
+ * vs. non-constant, and then according to 1- vs. multi-bit, and
+ * finally according to pixel depth. This minimizes source code,
+ * but not necessarily time, since we do some of the divisions
+ * within 1 or 2 levels of loop.
+ */
+
+#define dbit(base, i) ((base)[(i) >> 3] & (0x80 >> ((i) & 7)))
+/* 8-bit */
+#define cbit8(base, i, colors)\
+ (dbit(base, i) ? (byte)colors[1] : (byte)colors[0])
+#define rop_body_8()\
+ if ( s_pixel == strans || /* So = 0, s_tr = 1 */\
+ t_pixel == ttrans /* Po = 0, p_tr = 1 */\
+ )\
+ continue;\
+ *dptr = (*rop_proc_tab[rop])(*dptr, s_pixel, t_pixel)
+/* 24-bit */
+#define get24(ptr)\
+ (((gx_color_index)(ptr)[0] << 16) | ((gx_color_index)(ptr)[1] << 8) | (ptr)[2])
+#define put24(ptr, pixel)\
+ (ptr)[0] = (byte)((pixel) >> 16),\
+ (ptr)[1] = (byte)((uint)(pixel) >> 8),\
+ (ptr)[2] = (byte)(pixel)
+#define cbit24(base, i, colors)\
+ (dbit(base, i) ? colors[1] : colors[0])
+#define rop_body_24()\
+ if ( s_pixel == strans || /* So = 0, s_tr = 1 */\
+ t_pixel == ttrans /* Po = 0, p_tr = 1 */\
+ )\
+ continue;\
+ { gx_color_index d_pixel = get24(dptr);\
+ d_pixel = (*rop_proc_tab[rop])(d_pixel, s_pixel, t_pixel);\
+ put24(dptr, d_pixel);\
+ }
+
+ if ( const_texture != gx_no_color_index ) /**** Constant texture ****/
+ {
+ if ( const_source != gx_no_color_index ) /**** Constant source & texture ****/
+ {
+ for ( ; line_count-- > 0; drow += draster )
+ { byte *dptr = drow;
+ int left = width;
+ if ( bpp == 1 ) /**** 8-bit destination ****/
+#define s_pixel (byte)const_source
+#define t_pixel (byte)const_texture
+ for ( ; left > 0; ++dptr, --left )
+ { rop_body_8();
+ }
+#undef s_pixel
+#undef t_pixel
+ else /**** 24-bit destination ****/
+#define s_pixel const_source
+#define t_pixel const_texture
+ for ( ; left > 0; dptr += 3, --left )
+ { rop_body_24();
+ }
+#undef s_pixel
+#undef t_pixel
+ }
+ }
+ else /**** Data source, const texture ****/
+ { const byte *srow = sdata;
+ for ( ; line_count-- > 0; drow += draster, srow += sraster )
+ { byte *dptr = drow;
+ int left = width;
+ if ( scolors ) /**** 1-bit source ****/
+ { int sx = sourcex;
+ if ( bpp == 1 ) /**** 8-bit destination ****/
+#define t_pixel (byte)const_texture
+ for ( ; left > 0; ++dptr, ++sx, --left )
+ { byte s_pixel = cbit8(srow, sx, scolors);
+ rop_body_8();
+ }
+#undef t_pixel
+ else /**** 24-bit destination ****/
+#define t_pixel const_texture
+ for ( ; left > 0; dptr += 3, ++sx, --left )
+ { bits32 s_pixel = cbit24(srow, sx, scolors);
+ rop_body_24();
+ }
+#undef t_pixel
+ }
+ else if ( bpp == 1) /**** 8-bit source & dest ****/
+ { const byte *sptr = srow + sourcex;
+#define t_pixel (byte)const_texture
+ for ( ; left > 0; ++dptr, ++sptr, --left )
+ { byte s_pixel = *sptr;
+ rop_body_8();
+ }
+#undef t_pixel
+ }
+ else /**** 24-bit source & dest ****/
+ { const byte *sptr = srow + sourcex * 3;
+#define t_pixel const_texture
+ for ( ; left > 0; dptr += 3, sptr += 3, --left )
+ { bits32 s_pixel = get24(sptr);
+ rop_body_24();
+ }
+#undef t_pixel
+ }
+ }
+ }
+ }
+ else if ( const_source != gx_no_color_index ) /**** Const source, data texture ****/
+ { uint traster = textures->raster;
+ int ty = y + phase_y;
+
+ for ( ; line_count-- > 0; drow += draster, ++ty )
+ { /* Loop over copies of the tile. */
+ int dx = x, w = width, nw;
+ byte *dptr = drow;
+ const byte *trow =
+ textures->data + (ty % textures->size.y) * traster;
+ int xoff = x_offset(phase_x, ty, textures);
+
+ for ( ; w > 0; dx += nw, w -= nw )
+ { int tx = (dx + xoff) % textures->rep_width;
+ int left = nw = min(w, textures->size.x - tx);
+ const byte *tptr = trow;
+
+ if ( tcolors ) /**** 1-bit texture ****/
+ { if ( bpp == 1 ) /**** 8-bit dest ****/
+#define s_pixel (byte)const_source
+ for ( ; left > 0; ++dptr, ++tx, --left )
+ { byte t_pixel = cbit8(tptr, tx, tcolors);
+ rop_body_8();
+ }
+#undef s_pixel
+ else /**** 24-bit dest ****/
+#define s_pixel const_source
+ for ( ; left > 0; dptr += 3, ++tx, --left )
+ { bits32 t_pixel = cbit24(tptr, tx, tcolors);
+ rop_body_24();
+ }
+#undef s_pixel
+ }
+ else if ( bpp == 1 ) /**** 8-bit T & D ****/
+ { tptr += tx;
+#define s_pixel (byte)const_source
+ for ( ; left > 0; ++dptr, ++tptr, --left )
+ { byte t_pixel = *tptr;
+ rop_body_8();
+ }
+#undef s_pixel
+ }
+ else /**** 24-bit T & D ****/
+ { tptr += tx * 3;
+#define s_pixel const_source
+ for ( ; left > 0; dptr += 3, tptr += 3, --left )
+ { bits32 t_pixel = get24(tptr);
+ rop_body_24();
+ }
+#undef s_pixel
+ }
+ }
+ }
+ }
+ else /**** Data source & texture ****/
+ {
+ uint traster = textures->raster;
+ int ty = y + phase_y;
+ const byte *srow = sdata;
+
+ /* Loop over scan lines. */
+ for ( ; line_count-- > 0; drow += draster, srow += sraster, ++ty )
+ { /* Loop over copies of the tile. */
+ int sx = sourcex;
+ int dx = x;
+ int w = width;
+ int nw;
+ byte *dptr = drow;
+ const byte *trow =
+ textures->data + (ty % textures->size.y) * traster;
+ int xoff = x_offset(phase_x, ty, textures);
+
+ for ( ; w > 0; dx += nw, w -= nw )
+ { /* Loop over individual pixels. */
+ int tx = (dx + xoff) % textures->rep_width;
+ int left = nw = min(w, textures->size.x - tx);
+ const byte *tptr = trow;
+
+ /*
+ * For maximum speed, we should split this loop
+ * into 7 cases depending on source & texture
+ * depth: (1,1), (1,8), (1,24), (8,1), (8,8),
+ * (24,1), (24,24). But since we expect these
+ * cases to be relatively uncommon, we just
+ * divide on the destination depth.
+ */
+ if ( bpp == 1 ) /**** 8-bit destination ****/
+ { const byte *sptr = srow + sx;
+ tptr += tx;
+ for ( ; left > 0; ++dptr, ++sptr, ++tptr, ++sx, ++tx, --left )
+ { byte s_pixel =
+ (scolors ? cbit8(srow, sx, scolors) : *sptr);
+ byte t_pixel =
+ (tcolors ? cbit8(tptr, tx, tcolors) : *tptr);
+ rop_body_8();
+ }
+ }
+ else /**** 24-bit destination ****/
+ { const byte *sptr = srow + sx * 3;
+ tptr += tx * 3;
+ for ( ; left > 0; dptr += 3, sptr += 3, tptr += 3, ++sx, ++tx, --left )
+ { bits32 s_pixel =
+ (scolors ? cbit24(srow, sx, scolors) :
+ get24(sptr));
+ bits32 t_pixel =
+ (tcolors ? cbit24(tptr, tx, tcolors) :
+ get24(tptr));
+ rop_body_24();
+ }
+ }
+ }
+ }
+ }
+#undef rop_body_8
+#undef rop_body_24
+#undef dbit
+#undef cbit8
+#undef cbit24
+ return 0;
+}
+
+/* ---------------- Default copy_rop implementations ---------------- */
+
+#undef mdev
+
+int
+gx_real_default_copy_rop(gx_device *dev,
+ const byte *sdata, int sourcex, uint sraster, gx_bitmap_id id,
+ const gx_color_index *scolors,
+ const gx_tile_bitmap *texture, const gx_color_index *tcolors,
+ int x, int y, int width, int height,
+ int phase_x, int phase_y, gs_logical_operation_t lop)
+{ const gx_strip_bitmap *textures;
+ gx_strip_bitmap tiles;
+
+ if ( texture == 0 )
+ textures = 0;
+ else
+ { *(gx_tile_bitmap *)&tiles = *texture;
+ tiles.rep_shift = tiles.shift = 0;
+ textures = &tiles;
+ }
+ return (*dev_proc(dev, strip_copy_rop))
+ (dev, sdata, sourcex, sraster, id, scolors, textures, tcolors,
+ x, y, width, height, phase_x, phase_y, lop);
+}
+
+int
+gx_real_default_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)
+{ /*
+ * The default implementation uses get_bits to read out the
+ * pixels, the memory device implementation to do the operation,
+ * and copy_color to write the pixels back.
+ */
+ gs_rop3_t rop = (gs_rop3_t)(lop & lop_rop_mask);
+ int depth = dev->color_info.depth;
+ const gx_device_memory *mdproto = gdev_mem_device_for_bits(depth);
+ gx_device_memory mdev;
+ uint draster = gx_device_raster(dev, true);
+ bool uses_d = rop3_uses_D(rop);
+ byte *row;
+ int code;
+ int py;
+
+#ifdef DEBUG
+ if ( gs_debug_c('b') )
+ trace_copy_rop("gx_default_strip_copy_rop",
+ dev, sdata, sourcex, sraster,
+ id, scolors, textures, tcolors,
+ x, y, width, height, phase_x, phase_y, lop);
+#endif
+ if ( mdproto == 0 )
+ return_error(gs_error_rangecheck);
+ gs_make_mem_device(&mdev, mdproto, 0, -1, dev);
+ mdev.width = width;
+ mdev.height = 1;
+ mdev.bitmap_memory = &gs_memory_default;
+ code = (*dev_proc(&mdev, open_device))((gx_device *)&mdev);
+ if ( code < 0 )
+ return code;
+ row = gs_malloc(1, draster, "copy_rop buffer");
+ if ( row == 0 )
+ { (*dev_proc(&mdev, close_device))((gx_device *)&mdev);
+ return_error(gs_error_VMerror);
+ }
+ for ( py = y; py < y + height; ++py )
+ { byte *data;
+
+ if ( uses_d )
+ { code = (*dev_proc(dev, get_bits))(dev, py, row, &data);
+ if ( code < 0 )
+ break;
+ code = (*dev_proc(&mdev, copy_color))((gx_device *)&mdev,
+ data, x, draster, gx_no_bitmap_id,
+ 0, 0, width, 1);
+ if ( code < 0 )
+ return code;
+ }
+ code = (*dev_proc(&mdev, strip_copy_rop))((gx_device *)&mdev,
+ sdata + (py - y) * sraster, sourcex, sraster,
+ gx_no_bitmap_id, scolors, textures, tcolors,
+ 0, 0, width, 1, phase_x + x, phase_y + py,
+ lop);
+ if ( code < 0 )
+ break;
+ code = (*dev_proc(&mdev, get_bits))((gx_device *)&mdev, 0, row, &data);
+ if ( code < 0 )
+ break;
+ code = (*dev_proc(dev, copy_color))(dev,
+ data, 0, draster, gx_no_bitmap_id,
+ x, py, width, 1);
+ if ( code < 0 )
+ break;
+ }
+ gs_free(row, 1, draster, "copy_rop buffer");
+ (*dev_proc(&mdev, close_device))((gx_device *)&mdev);
+ return code;
+}
+
+int
+gx_forward_copy_rop(gx_device *dev,
+ const byte *sdata, int sourcex, uint sraster, gx_bitmap_id id,
+ const gx_color_index *scolors,
+ const gx_tile_bitmap *texture, const gx_color_index *tcolors,
+ int x, int y, int width, int height,
+ int phase_x, int phase_y, gs_logical_operation_t lop)
+{ gx_device *tdev = ((gx_device_forward *)dev)->target;
+ dev_proc_copy_rop((*proc));
+
+ if ( tdev == 0 )
+ tdev = dev, proc = gx_default_copy_rop;
+ else
+ proc = dev_proc(tdev, copy_rop);
+ return (*proc)(tdev, sdata, sourcex, sraster, id, scolors,
+ texture, tcolors, x, y, width, height,
+ phase_x, phase_y, lop);
+}
+
+int
+gx_forward_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)
+{ gx_device *tdev = ((gx_device_forward *)dev)->target;
+ dev_proc_strip_copy_rop((*proc));
+
+ if ( tdev == 0 )
+ tdev = dev, proc = gx_default_strip_copy_rop;
+ else
+ proc = dev_proc(tdev, strip_copy_rop);
+ return (*proc)(tdev, sdata, sourcex, sraster, id, scolors,
+ textures, tcolors, x, y, width, height,
+ phase_x, phase_y, lop);
+}
+
+int
+gx_copy_rop_unaligned(gx_device *dev,
+ const byte *sdata, int sourcex, uint sraster, gx_bitmap_id id,
+ const gx_color_index *scolors,
+ const gx_tile_bitmap *texture, const gx_color_index *tcolors,
+ int x, int y, int width, int height,
+ int phase_x, int phase_y, gs_logical_operation_t lop)
+{ const gx_strip_bitmap *textures;
+ gx_strip_bitmap tiles;
+
+ if ( texture == 0 )
+ textures = 0;
+ else
+ { *(gx_tile_bitmap *)&tiles = *texture;
+ tiles.rep_shift = tiles.shift = 0;
+ textures = &tiles;
+ }
+ return gx_strip_copy_rop_unaligned
+ (dev, sdata, sourcex, sraster, id, scolors, textures, tcolors,
+ x, y, width, height, phase_x, phase_y, lop);
+}
+
+int
+gx_strip_copy_rop_unaligned(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)
+{ dev_proc_strip_copy_rop((*copy_rop)) = dev_proc(dev, strip_copy_rop);
+ int depth = (scolors == 0 ? dev->color_info.depth : 1);
+ int step = sraster & (align_bitmap_mod - 1);
+
+ /* Adjust the origin. */
+ if ( sdata != 0 )
+ { uint offset =
+ (uint)(sdata - (const byte *)0) & (align_bitmap_mod - 1);
+ /* See copy_color above re the following statement. */
+ if ( depth == 24 )
+ offset += (offset % 3) *
+ (align_bitmap_mod * (3 - (align_bitmap_mod % 3)));
+ sdata -= offset;
+ sourcex += (offset << 3) / depth;
+ }
+
+ /* Adjust the raster. */
+ if ( !step || sdata == 0 ||
+ (scolors != 0 && scolors[0] == scolors[1])
+ )
+ { /* No adjustment needed. */
+ return (*copy_rop)(dev, sdata, sourcex, sraster, id, scolors,
+ textures, tcolors, x, y, width, height,
+ phase_x, phase_y, lop);
+ }
+
+ /* Do the transfer one scan line at a time. */
+ { const byte *p = sdata;
+ int d = sourcex;
+ int dstep = (step << 3) / depth;
+ int code = 0;
+ int i;
+
+ for ( i = 0; i < height && code >= 0;
+ ++i, p += sraster - step, d += dstep
+ )
+ code = (*copy_rop)(dev, p, d, sraster, gx_no_bitmap_id, scolors,
+ textures, tcolors, x, y + i, width, 1,
+ phase_x, phase_y, lop);
+ return code;
+ }
+}
+
+/* ---------------- RasterOp texture device ---------------- */
+
+public_st_device_rop_texture();
+
+/* Device for clipping with a region. */
+private dev_proc_fill_rectangle(rop_texture_fill_rectangle);
+private dev_proc_copy_mono(rop_texture_copy_mono);
+private dev_proc_copy_color(rop_texture_copy_color);
+
+/* The device descriptor. */
+private const gx_device_rop_texture far_data gs_rop_texture_device =
+{ std_device_std_body(gx_device_rop_texture, 0, "rop source",
+ 0, 0, 1, 1),
+ { gx_default_open_device,
+ NULL, /* get_initial_matrix */
+ gx_default_sync_output,
+ gx_default_output_page,
+ gx_default_close_device,
+ NULL, /* map_rgb_color */
+ NULL, /* map_color_rgb */
+ rop_texture_fill_rectangle,
+ gx_default_tile_rectangle,
+ rop_texture_copy_mono,
+ rop_texture_copy_color,
+ gx_default_draw_line,
+ NULL, /* get_bits */
+ NULL, /* get_params */
+ NULL, /* put_params */
+ NULL, /* map_cmyk_color */
+ NULL, /* get_xfont_procs */
+ NULL, /* get_xfont_device */
+ NULL, /* map_rgb_alpha_color */
+ NULL, /* get_page_device */
+ gx_default_get_alpha_bits, /* (no alpha) */
+ gx_no_copy_alpha, /* shouldn't be called */
+ NULL, /* get_band */
+ gx_no_copy_rop /* shouldn't be called */
+ },
+ 0, /* target */
+ lop_default, /* log_op */
+ NULL /* texture */
+};
+#define rtdev ((gx_device_rop_texture *)dev)
+
+/* Initialize a RasterOp source device. */
+void
+gx_make_rop_texture_device(gx_device_rop_texture *dev, gx_device *target,
+ gs_logical_operation_t log_op, const gx_device_color *texture)
+{ *dev = gs_rop_texture_device;
+ gx_device_forward_fill_in_procs((gx_device_forward *)dev);
+ dev->color_info = target->color_info;
+ dev->target = target;
+ dev->log_op = log_op;
+ dev->texture = texture;
+}
+
+/* Fill a rectangle */
+private int
+rop_texture_fill_rectangle(gx_device *dev, int x, int y, int w, int h,
+ gx_color_index color)
+{ gx_rop_source_t source;
+
+ source.sdata = NULL;
+ source.sourcex = 0;
+ source.sraster = 0;
+ source.id = gx_no_bitmap_id;
+ source.scolors[0] = source.scolors[1] = color;
+ return gx_device_color_fill_rectangle(rtdev->texture,
+ x, y, w, h, rtdev->target,
+ rtdev->log_op, &source);
+}
+
+/* Copy a monochrome rectangle */
+private int
+rop_texture_copy_mono(gx_device *dev,
+ const byte *data, int sourcex, int raster, gx_bitmap_id id,
+ int x, int y, int w, int h,
+ gx_color_index color0, gx_color_index color1)
+{ gx_rop_source_t source;
+ gs_logical_operation_t lop = rtdev->log_op;
+
+ source.sdata = data;
+ source.sourcex = sourcex;
+ source.sraster = raster;
+ source.id = id;
+ source.scolors[0] = color0;
+ source.scolors[1] = color1;
+ /* Adjust the logical operation per transparent colors. */
+ if ( color0 == gx_no_color_index )
+ lop = rop3_use_D_when_S_0(lop);
+ else if ( color1 == gx_no_color_index )
+ lop = rop3_use_D_when_S_1(lop);
+ return gx_device_color_fill_rectangle(rtdev->texture,
+ x, y, w, h, rtdev->target,
+ lop, &source);
+}
+
+/* Copy a color rectangle */
+private int
+rop_texture_copy_color(gx_device *dev,
+ const byte *data, int sourcex, int raster, gx_bitmap_id id,
+ int x, int y, int w, int h)
+{ gx_rop_source_t source;
+
+ source.sdata = data;
+ source.sourcex = sourcex;
+ source.sraster = raster;
+ source.id = id;
+ source.scolors[0] = source.scolors[1] = gx_no_color_index;
+ return gx_device_color_fill_rectangle(rtdev->texture,
+ x, y, w, h, rtdev->target,
+ rtdev->log_op, &source);
+}
+
+/* ---------------- Internal routines ---------------- */
+
+/* Compute the effective RasterOp for the 1-bit case, */
+/* taking transparency into account. */
+private gs_rop3_t
+gs_transparent_rop(gs_rop3_t rop, bool source_transparent,
+ bool pattern_transparent)
+{ /*
+ * The algorithm for computing an effective RasterOp is presented,
+ * albeit obfuscated, in the H-P PCL5 technical documentation.
+ * One applies the original RasterOp to compute an intermediate
+ * result R, and then computes the final result as
+ * (R & M) | (D & ~M) where M depends on transparencies as follows:
+ * s_tr p_tr M
+ * 0 0 1
+ * 0 1 ~So | Po (? Po ?)
+ * 1 0 So
+ * 1 1 So & Po
+ * or equivalently
+ * So Po M
+ * 0 0 ~s_tr (? ~s_tr & ~p_tr ?)
+ * 0 1 ~s_tr
+ * 1 0 ~p_tr
+ * 1 1 1
+ * The s_tr = 0, p_tr = 1 case seems wrong, but it's clearly
+ * specified that way in the "PCL 5 Color Technical Reference
+ * Manual." So and Po are "source opaque" and "pattern opaque";
+ * in the uninverted 1-bit case with black = 0, these are
+ * equivalent to ~S and ~P.
+ */
+#define So rop3_not(rop3_S)
+#define Po rop3_not(rop3_T)
+ gs_rop3_t mask =
+ (gs_rop3_t)(source_transparent ?
+ (pattern_transparent ? So & Po : So) :
+ (pattern_transparent ? ~So | Po : rop3_1));
+ return (gs_rop3_t)((rop & mask) | (rop3_D & ~mask));
+}
diff --git a/pstoraster/gdevmrop.h b/pstoraster/gdevmrop.h
new file mode 100644
index 000000000..65699e51c
--- /dev/null
+++ b/pstoraster/gdevmrop.h
@@ -0,0 +1,65 @@
+/* Copyright (C) 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gdevmrop.h */
+/* Interfaces to RasterOp implementation */
+/* Requires gxdevmem.h, gsropt.h */
+
+/* Define the table of RasterOp implementation procedures. */
+extern const far_data rop_proc rop_proc_tab[256];
+
+/*
+ * PostScript colors normally act as the texture for RasterOp, with a null
+ * (all zeros) source. For images with CombineWithColor = true, we need
+ * a way to use the image data as the source. We implement this with a
+ * device that applies RasterOp with a specified texture to drawing
+ * operations, treating the drawing color as source rather than texture.
+ * The texture is a gx_device_color; it may be any type of PostScript color,
+ * even a Pattern.
+ */
+#ifndef gx_device_color_DEFINED
+# define gx_device_color_DEFINED
+typedef struct gx_device_color_s gx_device_color;
+#endif
+
+#ifndef gx_device_rop_texture_DEFINED
+# define gx_device_rop_texture_DEFINED
+typedef struct gx_device_rop_texture_s gx_device_rop_texture;
+#endif
+
+struct gx_device_rop_texture_s {
+ gx_device_forward_common;
+ gs_logical_operation_t log_op;
+ const gx_device_color *texture;
+};
+extern_st(st_device_rop_texture);
+#define public_st_device_rop_texture()\
+ gs_public_st_suffix_add1(st_device_rop_texture, gx_device_rop_texture,\
+ "gx_device_rop_texture", device_rop_texture_enum_ptrs, device_rop_texture_reloc_ptrs,\
+ st_device_forward, texture)
+
+/* Initialize a RasterOp source device. */
+void gx_make_rop_texture_device(P4(gx_device_rop_texture *rsdev,
+ gx_device *target,
+ gs_logical_operation_t lop,
+ const gx_device_color *texture));
diff --git a/pstoraster/gdevnfwd.c b/pstoraster/gdevnfwd.c
new file mode 100644
index 000000000..c65b6c401
--- /dev/null
+++ b/pstoraster/gdevnfwd.c
@@ -0,0 +1,561 @@
+/* Copyright (C) 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gdevnfwd.c */
+/* Null and forwarding device implementation */
+#include "gx.h"
+#include "gxdevice.h"
+
+/* ---------------- Forwarding procedures ---------------- */
+
+#define fdev ((gx_device_forward *)dev)
+
+/* Fill in NULL procedures in a forwarding device procedure record. */
+/* We don't fill in: open_device, close_device, or the lowest-level */
+/* drawing operations. */
+void
+gx_device_forward_fill_in_procs(register gx_device_forward *dev)
+{ gx_device_set_procs((gx_device *)dev);
+ /* NOT open_device */
+ fill_dev_proc(dev, get_initial_matrix, gx_forward_get_initial_matrix);
+ fill_dev_proc(dev, sync_output, gx_forward_sync_output);
+ fill_dev_proc(dev, output_page, gx_forward_output_page);
+ /* NOT close_device */
+ fill_dev_proc(dev, map_rgb_color, gx_forward_map_rgb_color);
+ fill_dev_proc(dev, map_color_rgb, gx_forward_map_color_rgb);
+ /* NOT fill_rectangle */
+ /* NOT tile_rectangle */
+ /* NOT copy_mono */
+ /* NOT copy_color */
+ /* NOT draw_line (OBSOLETE) */
+ fill_dev_proc(dev, get_bits, gx_forward_get_bits);
+ fill_dev_proc(dev, get_params, gx_forward_get_params);
+ fill_dev_proc(dev, put_params, gx_forward_put_params);
+ fill_dev_proc(dev, map_cmyk_color, gx_forward_map_cmyk_color);
+ fill_dev_proc(dev, get_xfont_procs, gx_forward_get_xfont_procs);
+ fill_dev_proc(dev, get_xfont_device, gx_forward_get_xfont_device);
+ fill_dev_proc(dev, map_rgb_alpha_color, gx_forward_map_rgb_alpha_color);
+ fill_dev_proc(dev, get_page_device, gx_forward_get_page_device);
+ fill_dev_proc(dev, get_alpha_bits, gx_forward_get_alpha_bits);
+ /* NOT copy_alpha */
+ fill_dev_proc(dev, get_band, gx_forward_get_band);
+ fill_dev_proc(dev, copy_rop, gx_forward_copy_rop_proc);
+ fill_dev_proc(dev, fill_path, gx_forward_fill_path);
+ fill_dev_proc(dev, stroke_path, gx_forward_stroke_path);
+ fill_dev_proc(dev, fill_mask, gx_forward_fill_mask);
+ fill_dev_proc(dev, fill_trapezoid, gx_forward_fill_trapezoid);
+ fill_dev_proc(dev, fill_parallelogram, gx_forward_fill_parallelogram);
+ fill_dev_proc(dev, fill_triangle, gx_forward_fill_triangle);
+ fill_dev_proc(dev, draw_thin_line, gx_forward_draw_thin_line);
+ fill_dev_proc(dev, begin_image, gx_forward_begin_image);
+ fill_dev_proc(dev, image_data, gx_forward_image_data);
+ fill_dev_proc(dev, end_image, gx_forward_end_image);
+ /* NOT strip_tile_rectangle */
+ fill_dev_proc(dev, strip_copy_rop, gx_forward_strip_copy_rop_proc);
+ gx_device_fill_in_procs((gx_device *)dev);
+}
+
+/* Forward the color mapping procedures from a device to its target. */
+void
+gx_device_forward_color_procs(gx_device_forward *dev)
+{ set_dev_proc(dev, map_rgb_color, gx_forward_map_rgb_color);
+ set_dev_proc(dev, map_color_rgb, gx_forward_map_color_rgb);
+ set_dev_proc(dev, map_cmyk_color, gx_forward_map_cmyk_color);
+ set_dev_proc(dev, map_rgb_alpha_color, gx_forward_map_rgb_alpha_color);
+}
+
+void
+gx_forward_get_initial_matrix(gx_device *dev, gs_matrix *pmat)
+{ gx_device *tdev = fdev->target;
+ if ( tdev == 0 )
+ gx_default_get_initial_matrix(dev, pmat);
+ else
+ (*dev_proc(tdev, get_initial_matrix))(tdev, pmat);
+}
+
+int
+gx_forward_sync_output(gx_device *dev)
+{ gx_device *tdev = fdev->target;
+ return (tdev == 0 ? gx_default_sync_output(dev) :
+ (*dev_proc(tdev, sync_output))(tdev));
+}
+
+int
+gx_forward_output_page(gx_device *dev, int num_copies, int flush)
+{ gx_device *tdev = fdev->target;
+ return (tdev == 0 ? gx_default_output_page(dev, num_copies, flush) :
+ (*dev_proc(tdev, output_page))(tdev, num_copies, flush));
+}
+
+gx_color_index
+gx_forward_map_rgb_color(gx_device *dev, gx_color_value r, gx_color_value g,
+ gx_color_value b)
+{ gx_device *tdev = fdev->target;
+ return (tdev == 0 ? gx_default_map_rgb_color(dev, r, g, b) :
+ (*dev_proc(tdev, map_rgb_color))(tdev, r, g, b));
+}
+
+int
+gx_forward_map_color_rgb(gx_device *dev, gx_color_index color,
+ gx_color_value prgb[3])
+{ gx_device *tdev = fdev->target;
+ return (tdev == 0 ? gx_default_map_color_rgb(dev, color, prgb) :
+ (*dev_proc(tdev, map_color_rgb))(tdev, color, prgb));
+}
+
+int
+gx_forward_tile_rectangle(gx_device *dev, const gx_tile_bitmap *tile,
+ int x, int y, int w, int h, gx_color_index color0, gx_color_index color1,
+ int px, int py)
+{ gx_device *tdev = fdev->target;
+ dev_proc_tile_rectangle((*proc));
+
+ if ( tdev == 0 )
+ tdev = dev, proc = gx_default_tile_rectangle;
+ else
+ proc = dev_proc(tdev, tile_rectangle);
+ return (*proc)(tdev, tile, x, y, w, h, color0, color1, px, py);
+}
+
+int
+gx_forward_get_bits(gx_device *dev, int y, byte *data, byte **actual_data)
+{ gx_device *tdev = fdev->target;
+ return (tdev == 0 ? gx_default_get_bits(dev, y, data, actual_data) :
+ (*dev_proc(tdev, get_bits))(tdev, y, data, actual_data));
+}
+
+int
+gx_forward_get_params(gx_device *dev, gs_param_list *plist)
+{ gx_device *tdev = fdev->target;
+ return (tdev == 0 ? gx_default_get_params(dev, plist) :
+ (*dev_proc(tdev, get_params))(tdev, plist));
+}
+
+int
+gx_forward_put_params(gx_device *dev, gs_param_list *plist)
+{ gx_device *tdev = fdev->target;
+ return (tdev == 0 ? gx_default_put_params(dev, plist) :
+ (*dev_proc(tdev, put_params))(tdev, plist));
+}
+
+gx_color_index
+gx_forward_map_cmyk_color(gx_device *dev, gx_color_value c, gx_color_value m,
+ gx_color_value y, gx_color_value k)
+{ gx_device *tdev = fdev->target;
+ return (tdev == 0 ? gx_default_map_cmyk_color(dev, c, m, y, k) :
+ (*dev_proc(tdev, map_cmyk_color))(tdev, c, m, y, k));
+}
+
+gx_xfont_procs *
+gx_forward_get_xfont_procs(gx_device *dev)
+{ gx_device *tdev = fdev->target;
+ return (tdev == 0 ? gx_default_get_xfont_procs(dev) :
+ (*dev_proc(tdev, get_xfont_procs))(tdev));
+}
+
+gx_device *
+gx_forward_get_xfont_device(gx_device *dev)
+{ gx_device *tdev = fdev->target;
+ return (tdev == 0 ? gx_default_get_xfont_device(dev) :
+ (*dev_proc(tdev, get_xfont_device))(tdev));
+}
+
+gx_color_index
+gx_forward_map_rgb_alpha_color(gx_device *dev, gx_color_value r,
+ gx_color_value g, gx_color_value b, gx_color_value alpha)
+{ gx_device *tdev = fdev->target;
+ return (tdev == 0 ?
+ gx_default_map_rgb_alpha_color(dev, r, g, b, alpha) :
+ (*dev_proc(tdev, map_rgb_alpha_color))(tdev, r, g, b, alpha));
+}
+
+gx_device *
+gx_forward_get_page_device(gx_device *dev)
+{ gx_device *tdev = fdev->target;
+ return (tdev == 0 ? gx_default_get_page_device(dev) :
+ (*dev_proc(tdev, get_page_device))(tdev));
+}
+
+int
+gx_forward_get_alpha_bits(gx_device *dev, graphics_object_type type)
+{ gx_device *tdev = fdev->target;
+ return (tdev == 0 ?
+ gx_default_get_alpha_bits(dev, type) :
+ (*dev_proc(tdev, get_alpha_bits))(tdev, type));
+}
+
+int
+gx_forward_get_band(gx_device *dev, int y, int *band_start)
+{ gx_device *tdev = fdev->target;
+ return (tdev == 0 ?
+ gx_default_get_band(dev, y, band_start) :
+ (*dev_proc(tdev, get_band))(tdev, y, band_start));
+}
+
+int
+gx_forward_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 *tdev = fdev->target;
+ dev_proc_fill_path((*proc));
+
+ if ( tdev == 0 )
+ tdev = dev, proc = gx_default_fill_path;
+ else
+ proc = dev_proc(tdev, fill_path);
+ return (*proc)(tdev, pis, ppath, params, pdcolor, pcpath);
+}
+
+int
+gx_forward_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 *tdev = fdev->target;
+ dev_proc_stroke_path((*proc));
+
+ if ( tdev == 0 )
+ tdev = dev, proc = gx_default_stroke_path;
+ else
+ proc = dev_proc(tdev, stroke_path);
+ return (*proc)(tdev, pis, ppath, params, pdcolor, pcpath);
+}
+
+int
+gx_forward_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 *tdev = fdev->target;
+ dev_proc_fill_mask((*proc));
+
+ if ( tdev == 0 )
+ tdev = dev, proc = gx_default_fill_mask;
+ else
+ proc = dev_proc(tdev, fill_mask);
+ return (*proc)(dev, data, dx, raster, id, x, y, w, h, pdcolor, depth,
+ lop, pcpath);
+}
+
+int
+gx_forward_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)
+{ gx_device *tdev = fdev->target;
+ dev_proc_fill_trapezoid((*proc));
+
+ if ( tdev == 0 )
+ tdev = dev, proc = gx_default_fill_trapezoid;
+ else
+ proc = dev_proc(tdev, fill_trapezoid);
+ return (*proc)(tdev, left, right, ybot, ytop, swap_axes, pdcolor, lop);
+}
+
+int
+gx_forward_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)
+{ gx_device *tdev = fdev->target;
+ dev_proc_fill_parallelogram((*proc));
+
+ if ( tdev == 0 )
+ tdev = dev, proc = gx_default_fill_parallelogram;
+ else
+ proc = dev_proc(tdev, fill_parallelogram);
+ return (*proc)(tdev, px, py, ax, ay, bx, by, pdcolor, lop);
+}
+
+int
+gx_forward_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)
+{ gx_device *tdev = fdev->target;
+ dev_proc_fill_triangle((*proc));
+
+ if ( tdev == 0 )
+ tdev = dev, proc = gx_default_fill_triangle;
+ else
+ proc = dev_proc(tdev, fill_triangle);
+ return (*proc)(tdev, px, py, ax, ay, bx, by, pdcolor, lop);
+}
+
+int
+gx_forward_draw_thin_line(gx_device *dev,
+ fixed fx0, fixed fy0, fixed fx1, fixed fy1,
+ const gx_drawing_color *pdcolor, gs_logical_operation_t lop)
+{ gx_device *tdev = fdev->target;
+ dev_proc_draw_thin_line((*proc));
+
+ if ( tdev == 0 )
+ tdev = dev, proc = gx_default_draw_thin_line;
+ else
+ proc = dev_proc(tdev, draw_thin_line);
+ return (*proc)(tdev, fx0, fy0, fx1, fy1, pdcolor, lop);
+}
+
+int
+gx_forward_begin_image(gx_device *dev,
+ const gs_imager_state *pis, const gs_image_t *pim,
+ gs_image_format_t format, gs_image_shape_t shape,
+ const gx_drawing_color *pdcolor, const gx_clip_path *pcpath,
+ gs_memory_t *memory, void **pinfo)
+{ gx_device *tdev = fdev->target;
+ dev_proc_begin_image((*proc));
+
+ if ( tdev == 0 )
+ tdev = dev, proc = gx_default_begin_image;
+ else
+ proc = dev_proc(tdev, begin_image);
+ return (*proc)(tdev, pis, pim, format, shape, pdcolor, pcpath,
+ memory, pinfo);
+}
+
+int
+gx_forward_image_data(gx_device *dev,
+ void *info, const byte **planes, uint raster,
+ int x, int y, int width, int height)
+{ gx_device *tdev = fdev->target;
+ dev_proc_image_data((*proc));
+
+ if ( tdev == 0 )
+ tdev = dev, proc = gx_default_image_data;
+ else
+ proc = dev_proc(tdev, image_data);
+ return (*proc)(tdev, info, planes, raster, x, y, width, height);
+}
+
+int
+gx_forward_end_image(gx_device *dev,
+ void *info, bool draw_last)
+{ gx_device *tdev = fdev->target;
+ dev_proc_end_image((*proc));
+
+ if ( tdev == 0 )
+ tdev = dev, proc = gx_default_end_image;
+ else
+ proc = dev_proc(tdev, end_image);
+ return (*proc)(tdev, info, draw_last);
+}
+
+int
+gx_forward_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 *tdev = fdev->target;
+ dev_proc_strip_tile_rectangle((*proc));
+
+ if ( tdev == 0 )
+ tdev = dev, proc = gx_default_strip_tile_rectangle;
+ else
+ proc = dev_proc(tdev, strip_tile_rectangle);
+ return (*proc)(tdev, tiles, x, y, w, h, color0, color1, px, py);
+}
+
+/* ---------------- The null device(s) ---------------- */
+
+private dev_proc_fill_rectangle(null_fill_rectangle);
+private dev_proc_copy_mono(null_copy_mono);
+private dev_proc_copy_color(null_copy_color);
+private dev_proc_put_params(null_put_params);
+private dev_proc_copy_alpha(null_copy_alpha);
+private dev_proc_copy_rop(null_copy_rop);
+private dev_proc_fill_path(null_fill_path);
+private dev_proc_stroke_path(null_stroke_path);
+private dev_proc_fill_trapezoid(null_fill_trapezoid);
+private dev_proc_fill_parallelogram(null_fill_parallelogram);
+private dev_proc_fill_triangle(null_fill_triangle);
+private dev_proc_draw_thin_line(null_draw_thin_line);
+private dev_proc_begin_image(null_begin_image);
+private dev_proc_image_data(null_image_data);
+private dev_proc_end_image(null_end_image);
+private dev_proc_strip_copy_rop(null_strip_copy_rop);
+
+#define null_procs(get_page_device) {\
+ gx_default_open_device,\
+ gx_forward_get_initial_matrix,\
+ gx_default_sync_output,\
+ gx_default_output_page,\
+ gx_default_close_device,\
+ gx_forward_map_rgb_color,\
+ gx_forward_map_color_rgb,\
+ null_fill_rectangle,\
+ gx_default_tile_rectangle,\
+ null_copy_mono,\
+ null_copy_color,\
+ gx_default_draw_line,\
+ gx_default_get_bits,\
+ gx_forward_get_params,\
+ null_put_params,\
+ gx_forward_map_cmyk_color,\
+ gx_forward_get_xfont_procs,\
+ gx_forward_get_xfont_device,\
+ gx_forward_map_rgb_alpha_color,\
+ get_page_device, /* differs */\
+ gx_forward_get_alpha_bits,\
+ null_copy_alpha,\
+ gx_forward_get_band,\
+ null_copy_rop,\
+ null_fill_path,\
+ null_stroke_path,\
+ gx_default_fill_mask,\
+ null_fill_trapezoid,\
+ null_fill_parallelogram,\
+ null_fill_triangle,\
+ null_draw_thin_line,\
+ null_begin_image,\
+ null_image_data,\
+ null_end_image,\
+ gx_default_strip_tile_rectangle,\
+ null_strip_copy_rop,\
+}
+
+gx_device_null far_data gs_null_device = {
+ std_device_std_body_open(gx_device_null, 0, "null",
+ 0, 0, 72, 72),
+ null_procs(gx_default_get_page_device /* not a page device */),
+ 0 /* target */
+};
+
+gx_device_null far_data gs_nullpage_device = {
+ std_device_std_body_open(gx_device_null, 0, "nullpage",
+ 0, 0, 72, 72),
+ null_procs(gx_page_device_get_page_device /* a page device */),
+ 0 /* target */
+};
+
+private int
+null_fill_rectangle(gx_device *dev, int x, int y, int w, int h,
+ gx_color_index color)
+{ return 0;
+}
+private int
+null_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;
+}
+private int
+null_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;
+}
+private int
+null_put_params(gx_device *dev, gs_param_list *plist)
+{ /* We must defeat attempts to reset the size; */
+ /* otherwise this is equivalent to gx_forward_put_params. */
+ gx_device *tdev = fdev->target;
+ int code;
+
+ if ( tdev != 0 )
+ return (*dev_proc(tdev, put_params))(tdev, plist);
+ code = gx_default_put_params(dev, plist);
+ if ( code < 0 )
+ return code;
+ dev->width = dev->height = 0;
+ return code;
+}
+private int
+null_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)
+{ return 0;
+}
+private int
+null_copy_rop(gx_device *dev,
+ const byte *sdata, int sourcex, uint sraster, gx_bitmap_id id,
+ const gx_color_index *scolors,
+ const gx_tile_bitmap *texture, const gx_color_index *tcolors,
+ int x, int y, int width, int height,
+ int phase_x, int phase_y, gs_logical_operation_t lop)
+{ return 0;
+}
+private int
+null_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)
+{ return 0;
+}
+private int
+null_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)
+{ return 0;
+}
+private int
+null_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)
+{ return 0;
+}
+private int
+null_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)
+{ return 0;
+}
+private int
+null_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)
+{ return 0;
+}
+private int
+null_draw_thin_line(gx_device *dev,
+ fixed fx0, fixed fy0, fixed fx1, fixed fy1,
+ const gx_drawing_color *pdcolor, gs_logical_operation_t lop)
+{ return 0;
+}
+private int
+null_begin_image(gx_device *dev,
+ const gs_imager_state *pis, const gs_image_t *pim,
+ gs_image_format_t format, gs_image_shape_t shape,
+ const gx_drawing_color *pdcolor, const gx_clip_path *pcpath,
+ gs_memory_t *memory, void **pinfo)
+{ *pinfo = 0;
+ return 0;
+}
+private int
+null_image_data(gx_device *dev,
+ void *info, const byte **planes, uint raster,
+ int x, int y, int width, int height)
+{ return 0;
+}
+private int
+null_end_image(gx_device *dev,
+ void *info, bool draw_last)
+{ return 0;
+}
+private int
+null_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)
+{ return 0;
+}
+
+#undef fdev
diff --git a/pstoraster/gdevpipe.c b/pstoraster/gdevpipe.c
new file mode 100644
index 000000000..ef6e7fa84
--- /dev/null
+++ b/pstoraster/gdevpipe.c
@@ -0,0 +1,74 @@
+/* Copyright (C) 1993, 1994 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gdevpipe.c */
+/* %pipe% IODevice */
+#include "errno_.h"
+#include "stdio_.h"
+#include "string_.h"
+#include "gserror.h"
+#include "gstypes.h"
+#include "gsmemory.h" /* for gxiodev.h */
+#include "stream.h"
+#include "gxiodev.h"
+
+/* popen isn't POSIX-standard, so we declare it here. */
+/* Because of inconsistent (and sometimes incorrect) header files, */
+/* we must omit the argument list. */
+extern FILE *popen( /* P2(const char *, const char *) */ );
+extern int pclose(P1(FILE *));
+
+/* The pipe IODevice */
+private iodev_proc_fopen(pipe_fopen);
+private iodev_proc_fclose(pipe_fclose);
+gx_io_device gs_iodev_pipe = {
+ "%pipe%", "FileSystem",
+ { iodev_no_init, iodev_no_open_device,
+ NULL /*iodev_os_open_file*/, pipe_fopen, pipe_fclose,
+ iodev_no_delete_file, iodev_no_rename_file, iodev_no_file_status,
+ iodev_no_enumerate_files, NULL, NULL,
+ iodev_no_get_params, iodev_no_put_params
+ }
+};
+
+/* The file device procedures */
+
+private int
+pipe_fopen(gx_io_device *iodev, const char *fname, const char *access,
+ FILE **pfile, char *rfname, uint rnamelen)
+{ /* The OSF/1 1.3 library doesn't include const in the */
+ /* prototype for popen.... */
+ errno = 0;
+ *pfile = popen((char *)fname, (char *)access);
+ if ( *pfile == NULL )
+ return_error(gs_fopen_errno_to_code(errno));
+ if ( rfname != NULL )
+ strcpy(rfname, fname);
+ return 0;
+}
+
+private int
+pipe_fclose(gx_io_device *iodev, FILE *file)
+{ pclose(file);
+ return 0;
+}
diff --git a/pstoraster/gdevprn.c b/pstoraster/gdevprn.c
new file mode 100644
index 000000000..98229dbc6
--- /dev/null
+++ b/pstoraster/gdevprn.c
@@ -0,0 +1,757 @@
+/*
+ Copyright 1993-1999 by Easy Software Products.
+ Copyright (C) 1990, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gdevprn.c */
+/* Generic printer driver support */
+#include "ctype_.h"
+#include "gdevprn.h"
+#include "gp.h"
+#include "gsparam.h"
+#include "gxclio.h"
+#include <stdlib.h>
+
+/* ---------------- Standard device procedures ---------------- */
+
+/* Macros for casting the pdev argument */
+#define ppdev ((gx_device_printer *)pdev)
+#define pmemdev ((gx_device_memory *)pdev)
+#define pcldev (&((gx_device_clist *)pdev)->common)
+
+/* Define the standard printer procedure vector. */
+gx_device_procs prn_std_procs =
+ prn_procs(gdev_prn_open, gdev_prn_output_page, gdev_prn_close);
+
+/* ------ Open/close ------ */
+
+/* Forward references */
+private int gdev_prn_alloc(P1(gx_device *));
+private int gdev_prn_free(P1(gx_device *));
+
+/* Open a generic printer device. */
+/* Specific devices may wish to extend this. */
+int
+gdev_prn_open(gx_device *pdev)
+{ int code;
+ ppdev->file = NULL;
+ code = gdev_prn_alloc(pdev);
+ if ( code < 0 )
+ return code;
+ if ( ppdev->OpenOutputFile )
+ code = gdev_prn_open_printer(pdev, 1);
+ return code;
+}
+
+/* Allocate buffer space and initialize the device. */
+/* We break this out as a separate procedure so that resetting */
+/* the page dimensions doesn't have to close and reopen the device. */
+private int
+gdev_prn_alloc(gx_device *pdev)
+{ ulong mem_space;
+ byte *base = 0;
+ void *left = 0;
+ int cache_size; /* Size of tile cache in bytes */
+ char *cache_env, /* Cache size environment variable */
+ cache_units[255]; /* Cache size units */
+
+
+ memset(ppdev->skip, 0, sizeof(ppdev->skip));
+ ppdev->orig_procs = pdev->std_procs;
+ ppdev->ccfile = ppdev->cbfile = NULL;
+
+ /*
+ * MRS - reset max_bitmap to the value of RIP_MAX_CACHE as necessary...
+ */
+
+ if ((cache_env = getenv("RIP_MAX_CACHE")) != NULL)
+ {
+ switch (sscanf(cache_env, "%d%s", &cache_size, cache_units))
+ {
+ case 0 :
+ cache_size = 32 * 1024 * 1024;
+ break;
+ case 1 :
+ cache_size *= 4 * 256 * 256;
+ break;
+ case 2 :
+ if (tolower(cache_units[0]) == 'g') /* Gigabytes */
+ cache_size *= 1024 * 1024 * 1024;
+ else if (tolower(cache_units[0]) == 'm') /* Megabytes */
+ cache_size *= 1024 * 1024;
+ else if (tolower(cache_units[0]) == 'k') /* Kilobytes */
+ cache_size *= 1024;
+ else if (tolower(cache_units[0]) == 't') /* Tiles */
+ cache_size *= 4 * 256 * 256;
+ break;
+ }
+ }
+ else
+ cache_size = 32 * 1024 * 1024;
+
+ ppdev->max_bitmap = cache_size;
+ ppdev->use_buffer_space = cache_size / 8;
+
+ mem_space = gdev_mem_bitmap_size(pmemdev);
+
+ if ( mem_space >= ppdev->max_bitmap ||
+ mem_space != (uint)mem_space || /* too big to allocate */
+ (base = (byte *)gs_malloc((uint)mem_space, 1, "printer buffer")) == 0 || /* can't allocate */
+ (PRN_MIN_MEMORY_LEFT != 0 &&
+ (left = gs_malloc(PRN_MIN_MEMORY_LEFT, 1, "printer memory left")) == 0) /* not enough left */
+ )
+ { /* Buffer the image in a command list. */
+ uint space;
+ int code, bcode = 0, ccode = 0;
+
+ /* Release the buffer if we allocated it. */
+ gs_free(base, (uint)mem_space, 1, "printer buffer(open)");
+ for ( space = ppdev->use_buffer_space; ; )
+ { base = (byte *)gs_malloc(space, 1,
+ "command list buffer");
+ if ( base != 0 ) break;
+ if ( (space >>= 1) < PRN_MIN_BUFFER_SPACE )
+ return_error(gs_error_VMerror); /* no hope */
+ }
+open_c: pcldev->data = base;
+ pcldev->data_size = space;
+ pcldev->target = pdev;
+ pcldev->make_buffer_device =
+ ppdev->printer_procs.make_buffer_device;
+ ppdev->buf = base;
+ ppdev->buffer_space = space;
+
+ /* Try opening the command list, to see if we allocated */
+ /* enough buffer space. */
+ code = (*gs_clist_device_procs.open_device)((gx_device *)pcldev);
+ if ( code < 0 )
+ { /* If there wasn't enough room, and we haven't */
+ /* already shrunk the buffer, try enlarging it. */
+ if ( code == gs_error_limitcheck &&
+ space >= ppdev->use_buffer_space
+ )
+ { gs_free(base, space, 1,
+ "command list buffer(retry open)");
+ space <<= 1;
+ base = (byte *)gs_malloc(space, 1,
+ "command list buffer(retry open)");
+ ppdev->buf = base;
+ if ( base != 0 )
+ goto open_c;
+ }
+ /* Fall through with code < 0 */
+ }
+ if ( code < 0 ||
+ (ccode = clist_open_scratch(ppdev->ccfname, &ppdev->ccfile, &gs_memory_default, true)) < 0 ||
+ (bcode = clist_open_scratch(ppdev->cbfname, &ppdev->cbfile, &gs_memory_default, false)) < 0
+ )
+ { /* Clean up before exiting */
+ eprintf3("Problem opening clist: code=%d, bcode=%d, ccode=%d\n",
+ code, bcode, ccode);
+ gdev_prn_free(pdev);
+ return (code < 0 ? code : ccode < 0 ? ccode : bcode);
+ }
+ pcldev->cfile = ppdev->ccfile;
+ pcldev->bfile = ppdev->cbfile;
+ pcldev->bfile_end_pos = 0;
+ ppdev->std_procs = gs_clist_device_procs;
+ }
+ else
+ { /* Render entirely in memory. */
+ int code;
+ /* Release the leftover memory. */
+ gs_free(left, PRN_MIN_MEMORY_LEFT, 1,
+ "printer memory left");
+ ppdev->buffer_space = 0;
+ code = (*ppdev->printer_procs.make_buffer_device)
+ (pmemdev, pdev, pdev->memory, false);
+ if ( code < 0 )
+ { gs_free(base, space, 1, "command list buffer");
+ return code;
+ }
+ pmemdev->base = base;
+ }
+
+ /* Synthesize the procedure vector. */
+ /* Rendering operations come from the memory or clist device, */
+ /* non-rendering come from the printer device. */
+#define copy_proc(p) set_dev_proc(ppdev, p, ppdev->orig_procs.p)
+ copy_proc(get_initial_matrix);
+ copy_proc(output_page);
+ copy_proc(close_device);
+ copy_proc(map_rgb_color);
+ copy_proc(map_color_rgb);
+ copy_proc(get_params);
+ copy_proc(put_params);
+ copy_proc(map_cmyk_color);
+ copy_proc(get_xfont_procs);
+ copy_proc(get_xfont_device);
+ copy_proc(map_rgb_alpha_color);
+ /* All printers are page devices, even if they didn't use the */
+ /* standard macros for generating their procedure vectors. */
+ set_dev_proc(ppdev, get_page_device, gx_page_device_get_page_device);
+ copy_proc(get_alpha_bits);
+#undef copy_proc
+ return (*dev_proc(pdev, open_device))(pdev);
+}
+
+/* Generic closing for the printer device. */
+/* Specific devices may wish to extend this. */
+int
+gdev_prn_close(gx_device *pdev)
+{ gdev_prn_free(pdev);
+ if ( ppdev->file != NULL )
+ { if ( ppdev->file != stdout )
+ gp_close_printer(ppdev->file, ppdev->fname);
+ ppdev->file = NULL;
+ }
+ return 0;
+}
+
+/* Free buffer space and related objects, the inverse of alloc. */
+/* Again, we break this out for resetting page dimensions. */
+private int
+gdev_prn_free(gx_device *pdev)
+{ if ( ppdev->ccfile != NULL )
+ { clist_fclose_and_unlink(ppdev->ccfile, ppdev->ccfname);
+ ppdev->ccfile = NULL;
+ }
+ if ( ppdev->cbfile != NULL )
+ { clist_fclose_and_unlink(ppdev->cbfile, ppdev->cbfname);
+ ppdev->cbfile = NULL;
+ }
+ if ( ppdev->buffer_space != 0 )
+ { /* Free the buffer */
+ gs_free(ppdev->buf, (uint)ppdev->buffer_space, 1,
+ "command list buffer(close)");
+ ppdev->buf = 0;
+ ppdev->buffer_space = 0;
+ }
+ else
+ { /* Free the memory device bitmap */
+ gs_free(pmemdev->base, (uint)gdev_mem_bitmap_size(pmemdev), 1,
+ "printer buffer(close)");
+ pmemdev->base = 0;
+ }
+ pdev->std_procs = ppdev->orig_procs;
+ return 0;
+}
+
+/* ------ Get/put parameters ------ */
+
+/* Get parameters. Printer devices add several more parameters */
+/* to the default set. */
+int
+gdev_prn_get_params(gx_device *pdev, gs_param_list *plist)
+{ int code = gx_default_get_params(pdev, plist);
+ gs_param_string ofns;
+
+ if ( code < 0 ) return code;
+
+ code = (ppdev->NumCopies_set ?
+ param_write_int(plist, "NumCopies", &ppdev->NumCopies) :
+ param_write_null(plist, "NumCopies"));
+ if ( code < 0 ) return code;
+
+ code = param_write_bool(plist, "OpenOutputFile", &ppdev->OpenOutputFile);
+ if ( code < 0 ) return code;
+
+ if ( ppdev->Duplex_set >= 0 )
+ { code =
+ (ppdev->Duplex_set ? param_write_bool(plist, "Duplex", &ppdev->Duplex) :
+ param_write_null(plist, "Duplex"));
+ if ( code < 0 ) return code;
+ }
+
+ code = param_write_long(plist, "BufferSpace", &ppdev->use_buffer_space);
+ if ( code < 0 ) return code;
+
+ code = param_write_long(plist, "MaxBitmap", &ppdev->max_bitmap);
+ if ( code < 0 ) return code;
+
+ ofns.data = (const byte *)ppdev->fname,
+ ofns.size = strlen(ppdev->fname),
+ ofns.persistent = false;
+ return param_write_string(plist, "OutputFile", &ofns);
+}
+
+/* Put parameters. */
+int
+gdev_prn_put_params(gx_device *pdev, gs_param_list *plist)
+{ int ecode = 0;
+ int code;
+ const char _ds *param_name;
+ bool is_open = pdev->is_open;
+ int nci = ppdev->NumCopies;
+ bool ncset = ppdev->NumCopies_set;
+ bool oof = ppdev->OpenOutputFile;
+ bool duplex;
+ int duplex_set = -1;
+ int width = pdev->width;
+ int height = pdev->height;
+ long buffer_space = ppdev->use_buffer_space;
+ long max_bitmap = ppdev->max_bitmap;
+ long bsl = buffer_space;
+ long mbl = max_bitmap;
+ gs_param_string ofs;
+ gs_param_dict mdict;
+
+ switch ( code = param_read_int(plist, (param_name = "NumCopies"), &nci) )
+ {
+ case 0:
+ if ( nci < 0 )
+ ecode = gs_error_rangecheck;
+ else
+ { ncset = true;
+ break;
+ }
+ goto nce;
+ default:
+ if ( (code = param_read_null(plist, param_name)) == 0 )
+ { ncset = false;
+ break;
+ }
+ ecode = code; /* can't be 1 */
+nce: param_signal_error(plist, param_name, ecode);
+ case 1:
+ break;
+ }
+
+ switch ( code = param_read_bool(plist, (param_name = "OpenOutputFile"), &oof) )
+ {
+ default:
+ ecode = code;
+ param_signal_error(plist, param_name, ecode);
+ case 0:
+ case 1:
+ break;
+ }
+
+ if ( ppdev->Duplex_set >= 0 ) /* i.e., Duplex is supported */
+ switch ( code = param_read_bool(plist, (param_name = "Duplex"),
+ &duplex) )
+ {
+ case 0:
+ duplex_set = 1;
+ break;
+ default:
+ if ( (code = param_read_null(plist, param_name)) == 0 )
+ { duplex_set = 0;
+ break;
+ }
+ ecode = code;
+ param_signal_error(plist, param_name, ecode);
+ case 1:
+ ;
+ }
+
+ switch ( code = param_read_long(plist, (param_name = "BufferSpace"), &bsl) )
+ {
+ case 0:
+ if ( bsl < 10000 )
+ ecode = gs_error_rangecheck;
+ else
+ break;
+ goto bse;
+ default:
+ ecode = code;
+bse: param_signal_error(plist, param_name, ecode);
+ case 1:
+ break;
+ }
+
+ switch ( code = param_read_long(plist, (param_name = "MaxBitmap"), &mbl) )
+ {
+ case 0:
+ if ( mbl < 10000 )
+ ecode = gs_error_rangecheck;
+ else
+ break;
+ goto mbe;
+ default:
+ ecode = code;
+mbe: param_signal_error(plist, param_name, ecode);
+ case 1:
+ break;
+ }
+
+ switch ( code = param_read_string(plist, (param_name = "OutputFile"), &ofs) )
+ {
+ case 0:
+ if ( ofs.size >= prn_fname_sizeof )
+ ecode = gs_error_limitcheck;
+ else
+ { /* Check the validity of any % formats. */
+ uint i;
+ bool pagenum = false;
+ for ( i = 0; i < ofs.size; ++i )
+ if ( ofs.data[i] == '%' )
+ { if ( i+1 < ofs.size && ofs.data[i+1] == '%' )
+ continue;
+ if ( pagenum ) /* more than one %, */
+ i = ofs.size - 1; /* force error */
+ pagenum = true;
+sw: if ( ++i == ofs.size )
+ { ecode = gs_error_rangecheck;
+ goto ofe;
+ }
+ switch ( ofs.data[i] )
+ {
+ case ' ': case '#': case '+': case '-':
+ case '0': case '1': case '2': case '3':
+ case '4': case '5': case '6': case '7':
+ case '8': case '9': case 'l':
+ goto sw;
+ case 'd': case 'i': case 'u': case 'o':
+ case 'x': case 'X':
+ continue;
+ default:
+ ecode = gs_error_rangecheck;
+ goto ofe;
+ }
+ }
+ break;
+ }
+ goto ofe;
+ default:
+ ecode = code;
+ofe: param_signal_error(plist, param_name, ecode);
+ case 1:
+ ofs.data = 0;
+ break;
+ }
+
+ /* Read InputAttributes and OutputAttributes just for the type */
+ /* check and to indicate that they aren't undefined. */
+#define read_media(pname)\
+ switch ( code = param_begin_read_dict(plist, (param_name = pname), &mdict, true) )\
+ {\
+ case 0:\
+ param_end_read_dict(plist, pname, &mdict);\
+ break;\
+ default:\
+ ecode = code;\
+ param_signal_error(plist, param_name, ecode);\
+ case 1:\
+ ;\
+ }
+
+ read_media("InputAttributes");
+ read_media("OutputAttributes");
+
+ if ( ecode < 0 )
+ return ecode;
+ /* Prevent gx_default_put_params from closing the printer. */
+ pdev->is_open = false;
+ code = gx_default_put_params(pdev, plist);
+ pdev->is_open = is_open;
+ if ( code < 0 )
+ return code;
+
+ ppdev->NumCopies = nci;
+ ppdev->NumCopies_set = ncset;
+ ppdev->OpenOutputFile = oof;
+ if ( duplex_set >= 0 )
+ { ppdev->Duplex = duplex;
+ ppdev->Duplex_set = duplex_set;
+ }
+ /* If necessary, free and reallocate the printer memory. */
+ if ( bsl != buffer_space || mbl != max_bitmap ||
+ pdev->width != width || pdev->height != height
+ )
+ { if ( is_open )
+ gdev_prn_free(pdev);
+ ppdev->use_buffer_space = bsl;
+ ppdev->max_bitmap = mbl;
+ if ( is_open )
+ { ecode = code = gdev_prn_alloc(pdev);
+ if ( code < 0 )
+ { /* Try to put things back as they were. */
+ ppdev->use_buffer_space = buffer_space;
+ ppdev->max_bitmap = max_bitmap;
+ gx_device_set_width_height(pdev,
+ width, height);
+ code = gdev_prn_alloc(pdev);
+ if ( code < 0 )
+ { /* Disaster! We can't get back. */
+ pdev->is_open = false;
+ return code;
+ }
+ return ecode;
+ }
+ }
+ }
+ if ( ofs.data != 0 &&
+ bytes_compare(ofs.data, ofs.size,
+ (const byte *)ppdev->fname, strlen(ppdev->fname))
+ )
+ { /* Close the file if it's open. */
+ if ( ppdev->file != NULL && ppdev->file != stdout )
+ gp_close_printer(ppdev->file, ppdev->fname);
+ ppdev->file = NULL;
+ memcpy(ppdev->fname, ofs.data, ofs.size);
+ ppdev->fname[ofs.size] = 0;
+ }
+
+ /* If the device is open and OpenOutputFile is true, */
+ /* open the OutputFile now. (If the device isn't open, */
+ /* this will happen when it is opened.) */
+ if ( pdev->is_open && oof )
+ { code = gdev_prn_open_printer(pdev, 1);
+ if ( code < 0 )
+ return code;
+ }
+
+ return 0;
+}
+
+/* Put InputAttributes and OutputAttributes. */
+int
+gdev_prn_input_page_size(int index, gs_param_dict *pdict,
+ floatp width_points, floatp height_points)
+{ input_media media;
+ media.PageSize[0] = width_points;
+ media.PageSize[1] = height_points;
+ media.MediaColor = 0;
+ media.MediaWeight = 0;
+ media.MediaType = 0;
+ return gdev_prn_input_media(index, pdict, &media);
+}
+private int
+finish_media(gs_param_list *mlist, gs_param_name key, const char *media_type)
+{ int code = 0;
+ if ( media_type != 0 )
+ { gs_param_string as;
+ param_string_from_string(as, media_type);
+ code = param_write_string(mlist, key, &as);
+ }
+ return code;
+}
+int
+gdev_prn_input_media(int index, gs_param_dict *pdict, const input_media *pim)
+{ char key[25];
+ gs_param_dict mdict;
+ int code;
+ gs_param_string as;
+
+ sprintf(key, "%d", index);
+ mdict.size = 4;
+ code = param_begin_write_dict(pdict->list, key, &mdict, false);
+ if ( code < 0 )
+ return code;
+ if ( pim->PageSize[0] != 0 && pim->PageSize[1] != 0 )
+ { gs_param_float_array psa;
+ psa.data = pim->PageSize, psa.size = 2, psa.persistent = false;
+ code = param_write_float_array(mdict.list, "PageSize",
+ &psa);
+ if ( code < 0 )
+ return code;
+ }
+ if ( pim->MediaColor != 0 )
+ { param_string_from_string(as, pim->MediaColor);
+ code = param_write_string(mdict.list, "MediaColor",
+ &as);
+ if ( code < 0 )
+ return code;
+ }
+ if ( pim->MediaWeight != 0 )
+ { /* We do the following silly thing in order to avoid */
+ /* having to work around the 'const' in the arg list. */
+ float weight = pim->MediaWeight;
+ code = param_write_float(mdict.list, "MediaWeight",
+ &weight);
+ if ( code < 0 )
+ return code;
+ }
+ code = finish_media(mdict.list, "MediaType", pim->MediaType);
+ if ( code < 0 )
+ return code;
+ return param_end_write_dict(pdict->list, key, &mdict);
+}
+int
+gdev_prn_output_media(int index, gs_param_dict *pdict, const output_media *pom)
+{ char key[25];
+ gs_param_dict mdict;
+ int code;
+
+ sprintf(key, "%d", index);
+ mdict.size = 4;
+ code = param_begin_write_dict(pdict->list, key, &mdict, false);
+ if ( code < 0 )
+ return code;
+ code = finish_media(mdict.list, "OutputType", pom->OutputType);
+ if ( code < 0 )
+ return code;
+ return param_end_write_dict(pdict->list, key, &mdict);
+}
+
+/* ------ Others ------ */
+
+/* Generic routine to send the page to the printer. */
+int
+gdev_prn_output_page(gx_device *pdev, int num_copies, int flush)
+{ int code, outcode, closecode, errcode;
+
+ if ( num_copies > 0 )
+ { code = gdev_prn_open_printer(pdev, 1);
+ if ( code < 0 )
+ return code;
+
+ /* Print the accumulated page description. */
+ outcode =
+ (*ppdev->printer_procs.print_page_copies)(ppdev,
+ ppdev->file,
+ num_copies);
+ if (ppdev->file != NULL)
+ errcode = (ferror(ppdev->file) ? gs_error_ioerror : 0);
+ else
+ errcode = 0;
+
+ closecode = gdev_prn_close_printer(pdev);
+ }
+ else
+ code = outcode = closecode = errcode = 0;
+
+ 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 ( errcode < 0 )
+ return_error(errcode);
+ if ( closecode < 0 )
+ return closecode;
+ return code;
+}
+
+/* Print multiple copies of a page by calling print_page multiple times. */
+int
+gx_default_print_page_copies(gx_device_printer *pdev, FILE *prn_stream,
+ int num_copies)
+{ int i = num_copies;
+ int code = 0;
+ while ( code >= 0 && i-- > 0 )
+ code = (*pdev->printer_procs.print_page)(pdev, prn_stream);
+ return code;
+}
+
+/* ---------------- Driver services ---------------- */
+
+/* Return the number of scan lines that should actually be passed */
+/* to the device. */
+int
+gdev_prn_print_scan_lines(gx_device *pdev)
+{ int height = pdev->height;
+ gs_matrix imat;
+ float yscale;
+ int top, bottom, offset, end;
+
+ (*dev_proc(pdev, get_initial_matrix))(pdev, &imat);
+ yscale = imat.yy * 72.0; /* Y dpi, may be negative */
+ top = (int)(dev_t_margin(pdev) * yscale);
+ bottom = (int)(dev_b_margin(pdev) * yscale);
+ offset = (int)(dev_y_offset(pdev) * yscale);
+ if ( yscale < 0 )
+ { /* Y=0 is top of page */
+ end = -offset + height + bottom;
+ }
+ else
+ { /* Y=0 is bottom of page */
+ end = offset + height - top;
+ }
+ return min(height, end);
+}
+
+/* Open the current page for printing. */
+int
+gdev_prn_open_printer(gx_device *pdev, int binary_mode)
+{ char *fname = ppdev->fname;
+ char pfname[prn_fname_sizeof + 20];
+ char *fchar = fname;
+ long count1 = ppdev->PageCount + 1;
+
+ while ( (fchar = strchr(fchar, '%')) != NULL )
+ { if ( *++fchar == '%' )
+ { ++fchar;
+ continue;
+ }
+ while ( !isalpha(*fchar) )
+ ++fchar;
+ sprintf(pfname, fname,
+ (*fchar == 'l' ? count1 : (int)count1));
+ fname = pfname;
+ break;
+ }
+ if ( ppdev->file == NULL )
+ { if ( !strcmp(fname, "-") )
+ ppdev->file = stdout;
+ else
+ { ppdev->file = gp_open_printer(fname, binary_mode);
+ if ( ppdev->file == NULL )
+ return_error(gs_error_invalidfileaccess);
+ }
+ ppdev->file_is_new = true;
+ }
+ else
+ ppdev->file_is_new = false;
+ return 0;
+}
+
+/* Copy a scan line from the buffer to the printer. */
+int
+gdev_prn_get_bits(gx_device_printer *pdev, int y, byte *str, byte **actual_data)
+{ int code = (*dev_proc(pdev, get_bits))((gx_device *)pdev, y, str, actual_data);
+ uint line_size = gdev_prn_raster(pdev);
+ int last_bits = -(pdev->width * pdev->color_info.depth) & 7;
+ if ( code < 0 ) return code;
+ if ( last_bits != 0 )
+ { byte *dest = (actual_data != 0 ? *actual_data : str);
+ dest[line_size - 1] &= 0xff << last_bits;
+ }
+ return 0;
+}
+/* Copy scan lines to a buffer. Return the number of scan lines, */
+/* or <0 if error. */
+int
+gdev_prn_copy_scan_lines(gx_device_printer *pdev, int y, byte *str, uint size)
+{ uint line_size = gdev_prn_raster(pdev);
+ int count = size / line_size;
+ int i;
+ byte *dest = str;
+ count = min(count, pdev->height - y);
+ for ( i = 0; i < count; i++, dest += line_size )
+ { int code = gdev_prn_get_bits(pdev, y + i, dest, NULL);
+ if ( code < 0 ) return code;
+ }
+ return count;
+}
+
+/* Close the current page. */
+int
+gdev_prn_close_printer(gx_device *pdev)
+{ if ( strchr(ppdev->fname, '%') ) /* file per page */
+ { gp_close_printer(ppdev->file, ppdev->fname);
+ ppdev->file = NULL;
+ }
+ return 0;
+}
diff --git a/pstoraster/gdevprn.h b/pstoraster/gdevprn.h
new file mode 100644
index 000000000..5206d2a2e
--- /dev/null
+++ b/pstoraster/gdevprn.h
@@ -0,0 +1,418 @@
+/*
+ Copyright 1993-1999 by Easy Software Products.
+ Copyright (C) 1989, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gdevprn.h */
+/* Common header file for memory-buffered printers */
+
+#ifndef gdevprn_INCLUDED
+# define gdevprn_INCLUDED
+
+#include "memory_.h"
+#include "string_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gsmatrix.h" /* for gxdevice.h */
+#include "gsutil.h" /* for memflip8x8 */
+#include "gxdevice.h"
+#include "gxdevmem.h"
+#include "gxclist.h"
+#include "gsparam.h"
+
+/* Define the page size parameters. */
+/* U.S. letter paper (8.5" x 11"). */
+#define DEFAULT_WIDTH_10THS_US_LETTER 85
+#define DEFAULT_HEIGHT_10THS_US_LETTER 110
+/* A4 paper (210mm x 297mm). The dimensions are off by a few mm.... */
+#define DEFAULT_WIDTH_10THS_A4 83
+#define DEFAULT_HEIGHT_10THS_A4 117
+/* Choose a default. A4 may be set in the makefile. */
+#ifdef A4
+# define DEFAULT_WIDTH_10THS DEFAULT_WIDTH_10THS_A4
+# define DEFAULT_HEIGHT_10THS DEFAULT_HEIGHT_10THS_A4
+#else
+# define DEFAULT_WIDTH_10THS DEFAULT_WIDTH_10THS_US_LETTER
+# define DEFAULT_HEIGHT_10THS DEFAULT_HEIGHT_10THS_US_LETTER
+#endif
+
+/*
+ * Define the parameters for the printer rendering method.
+ * If the entire bitmap fits in PRN_MAX_BITMAP, and there is at least
+ * PRN_MIN_MEMORY_LEFT memory left after allocating it, render in RAM,
+ * otherwise use a command list with a size of PRN_BUFFER_SPACE.
+ * (These are parameters that can be changed by a client program.)
+ */
+/* Define parameters for machines with little dinky RAMs.... */
+#define PRN_MAX_BITMAP_SMALL 32000
+#define PRN_BUFFER_SPACE_SMALL 25000
+#define PRN_MIN_MEMORY_LEFT_SMALL 32000
+/* Define parameters for machines with great big hulking RAMs.... */
+#define PRN_MAX_BITMAP_LARGE 10000000L
+#define PRN_BUFFER_SPACE_LARGE 1000000L
+#define PRN_MIN_MEMORY_LEFT_LARGE 500000L
+/* Define parameters valid on all machines. */
+#define PRN_MIN_BUFFER_SPACE 10000 /* give up if less than this */
+/* Now define conditional parameters. */
+#if arch_small_memory
+# define PRN_MAX_BITMAP PRN_MAX_BITMAP_SMALL
+# define PRN_BUFFER_SPACE PRN_BUFFER_SPACE_SMALL
+# define PRN_MIN_MEMORY_LEFT PRN_MIN_MEMORY_LEFT_SMALL
+#else
+/****** These should really be conditional on gs_debug_c('.') if
+ ****** DEBUG is defined, but they're used in static initializers,
+ ****** so we can't do it.
+ ******/
+# if 0 /****** # ifdef DEBUG ******/
+# define PRN_MAX_BITMAP\
+ (gs_debug_c('.') ? PRN_MAX_BITMAP_SMALL : PRN_MAX_BITMAP_LARGE)
+# define PRN_BUFFER_SPACE\
+ (gs_debug_c('.') ? PRN_BUFFER_SPACE_SMALL : PRN_BUFFER_SPACE_LARGE)
+# define PRN_MIN_MEMORY_LEFT\
+ (gs_debug_c('.') ? PRN_MIN_MEMORY_LEFT_SMALL : PRN_MIN_MEMORY_LEFT_LARGE)
+# else
+# define PRN_MAX_BITMAP PRN_MAX_BITMAP_LARGE
+# define PRN_BUFFER_SPACE PRN_BUFFER_SPACE_LARGE
+# define PRN_MIN_MEMORY_LEFT PRN_MIN_MEMORY_LEFT_LARGE
+# endif
+#endif
+
+/* Define the abstract type for a printer device. */
+typedef struct gx_device_printer_s gx_device_printer;
+
+/*
+ * Define the special procedures for band devices.
+ */
+typedef struct gx_printer_device_procs_s {
+
+ /*
+ * Print the page on the output file. Required only for devices
+ * where output_page is gdev_prn_output_page; ignored for other
+ * devices.
+ */
+
+#define dev_proc_print_page(proc)\
+ int proc(P2(gx_device_printer *, FILE *))
+ dev_proc_print_page((*print_page));
+
+ /* Print the page on the output file, with a given # of copies. */
+
+#define dev_proc_print_page_copies(proc)\
+ int proc(P3(gx_device_printer *, FILE *, int))
+ dev_proc_print_page_copies((*print_page_copies));
+
+ /* Initialize the memory device for a page or a band. */
+ /* (The macro definition is in gxdevice.h.) */
+
+ dev_proc_make_buffer_device((*make_buffer_device));
+
+} gx_printer_device_procs;
+
+/* ------ Printer device definition ------ */
+
+/* Structure for generic printer devices. */
+/* This must be preceded by gx_device_common. */
+/* Printer devices are actually a union of a memory device */
+/* and a clist device, plus some additional state. */
+#define prn_fname_sizeof 80
+#define gx_prn_device_common\
+ byte skip[max(sizeof(gx_device_memory), sizeof(gx_device_clist)) -\
+ sizeof(gx_device) + sizeof(double) /* padding */];\
+ gx_printer_device_procs printer_procs;\
+ /* ------ The following items must be set before ------ */\
+ /* ------ calling the device open routine. ------ */\
+ long max_bitmap; /* max size of non-buffered bitmap */\
+ long use_buffer_space; /* space to use for buffer */\
+ char fname[prn_fname_sizeof]; /* output file name */\
+ /* ------ End of preset items ------ */\
+ int NumCopies;\
+ bool NumCopies_set;\
+ bool OpenOutputFile;\
+ bool Duplex;\
+ int Duplex_set; /* -1 = not supported */\
+ bool file_is_new; /* true iff file just opened */\
+ FILE *file; /* output file */\
+ char ccfname[60]; /* clist file name */\
+ clist_file_ptr ccfile; /* command list scratch file */\
+ char cbfname[60]; /* clist block file name */\
+ clist_file_ptr cbfile; /* command list block scratch file */\
+ long buffer_space; /* amount of space for clist buffer, */\
+ /* 0 means not using clist */\
+ byte *buf; /* buffer for rendering */\
+ gx_device_procs orig_procs /* original (std_)procs */
+
+/* The device descriptor */
+struct gx_device_printer_s {
+ gx_device_common;
+ gx_prn_device_common;
+};
+
+/* Macro for casting gx_device argument */
+#define prn_dev ((gx_device_printer *)dev)
+
+/* Define a typedef for the sake of ansi2knr. */
+typedef dev_proc_print_page((*dev_proc_print_page_t));
+
+/* Standard device procedures for printers */
+dev_proc_open_device(gdev_prn_open);
+dev_proc_output_page(gdev_prn_output_page);
+dev_proc_close_device(gdev_prn_close);
+#define gdev_prn_map_rgb_color gx_default_b_w_map_rgb_color
+#define gdev_prn_map_color_rgb gx_default_b_w_map_color_rgb
+dev_proc_get_params(gdev_prn_get_params);
+dev_proc_put_params(gdev_prn_put_params);
+
+/* Macro for generating procedure table */
+#define prn_procs(p_open, p_output_page, p_close)\
+ prn_color_procs(p_open, p_output_page, p_close, gdev_prn_map_rgb_color, gdev_prn_map_color_rgb)
+#define prn_params_procs(p_open, p_output_page, p_close, p_get_params, p_put_params)\
+ prn_color_params_procs(p_open, p_output_page, p_close, gdev_prn_map_rgb_color, gdev_prn_map_color_rgb, p_get_params, p_put_params)
+#define prn_color_procs(p_open, p_output_page, p_close, p_map_rgb_color, p_map_color_rgb)\
+ prn_color_params_procs(p_open, p_output_page, p_close, p_map_rgb_color, p_map_color_rgb, gdev_prn_get_params, gdev_prn_put_params)
+/* See gdev_prn_open for explanation of the NULLs below. */
+#define prn_color_params_procs(p_open, p_output_page, p_close, p_map_rgb_color, p_map_color_rgb, p_get_params, p_put_params) {\
+ p_open,\
+ NULL, /* get_initial_matrix */\
+ NULL, /* sync_output */\
+ p_output_page,\
+ p_close,\
+ p_map_rgb_color,\
+ p_map_color_rgb,\
+ NULL, /* fill_rectangle */\
+ NULL, /* tile_rectangle */\
+ NULL, /* copy_mono */\
+ NULL, /* copy_color */\
+ NULL, /* draw_line */\
+ NULL, /* get_bits */\
+ p_get_params,\
+ p_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 */\
+}
+
+/* The standard printer device procedures */
+/* (using gdev_prn_open/output_page/close). */
+extern gx_device_procs prn_std_procs;
+
+/*
+ * Define macros for generating the device structure,
+ * analogous to the std_device_body macros in gxdevice.h
+ * Note that the macros are broken up so as to be usable for devices that
+ * add further initialized state to the printer device.
+ *
+ * The 'margin' values provided here specify the unimageable region
+ * around the edges of the page (in inches), and the left and top margins
+ * also specify the displacement of the device (0,0) point from the
+ * upper left corner. We should provide macros that allow specifying
+ * all 6 values independently, but we don't yet.
+ */
+#define prn_device_body_rest_(print_page)\
+ { 0 }, /* std_procs */\
+ { 0 }, /* skip */\
+ { print_page,\
+ gx_default_print_page_copies,\
+ gx_default_make_buffer_device\
+ },\
+ PRN_MAX_BITMAP,\
+ PRN_BUFFER_SPACE,\
+ { 0 }, /* fname */\
+ 1, 0/*false*/, /* NumCopies[_set] */\
+ 0/*false*/, /* OpenOutputFile */\
+ 0/*false*/, -1, /* Duplex[_set] */\
+ 0/*false*/, 0, { 0 }, 0, { 0 }, 0, 0, 0, { 0 } /* ... orig_procs */
+
+/* The Sun cc compiler won't allow \ within a macro argument list. */
+/* This accounts for the short parameter names here and below. */
+#define prn_device_margins_body(dtype, procs, dname, w10, h10, xdpi, ydpi, lo, to, lm, bm, rm, tm, ncomp, depth, mg, mc, dg, dc, print_page)\
+ std_device_full_body(dtype, &procs, dname,\
+ (int)((long)w10 * xdpi / 10),\
+ (int)((long)h10 * ydpi / 10),\
+ xdpi, ydpi,\
+ ncomp, depth, mg, mc, dg, dc,\
+ -(lo) * (xdpi), -(to) * (ydpi),\
+ (lm) * 72.0, (bm) * 72.0,\
+ (rm) * 72.0, (tm) * 72.0\
+ ),\
+ prn_device_body_rest_(print_page)
+
+#define prn_device_body(dtype, procs, dname, w10, h10, xdpi, ydpi, lm, bm, rm, tm, ncomp, depth, mg, mc, dg, dc, print_page)\
+ prn_device_margins_body(dtype, procs, dname, w10, h10, xdpi, ydpi,\
+ lm, tm, lm, bm, rm, tm, ncomp, depth, mg, mc, dg, dc, print_page)
+
+#define prn_device_body_copies(dtype, procs, dname, w10, h10, xdpi, ydpi, lm, bm, rm, tm, ncomp, depth, mg, mc, dg, dc, print_pages)\
+ std_device_full_body(dtype, &procs, dname,\
+ (int)((long)w10 * xdpi / 10),\
+ (int)((long)h10 * ydpi / 10),\
+ xdpi, ydpi,\
+ ncomp, depth, mg, mc, dg, dc,\
+ -(lm) * (xdpi), -(tm) * (ydpi),\
+ (lm) * 72.0, (bm) * 72.0,\
+ (rm) * 72.0, (tm) * 72.0\
+ ),\
+ { 0 }, /* std_procs */\
+ { 0 }, /* skip */\
+ { NULL,\
+ print_pages,\
+ gx_default_make_buffer_device\
+ },\
+ PRN_MAX_BITMAP,\
+ PRN_BUFFER_SPACE,\
+ { 0 }, /* fname */\
+ 1, 0/*false*/, /* NumCopies[_set] */\
+ 0/*false*/, /* OpenOutputFile */\
+ 0/*false*/, -1, /* Duplex[_set] */\
+ 0/*false*/, 0, { 0 }, 0, { 0 }, 0, 0, 0, { 0 } /* ... orig_procs */
+
+#define prn_device_std_margins_body(dtype, procs, dname, w10, h10, xdpi, ydpi, lo, to, lm, bm, rm, tm, color_bits, print_page)\
+ std_device_std_color_full_body(dtype, &procs, dname,\
+ (int)((long)w10 * xdpi / 10),\
+ (int)((long)h10 * ydpi / 10),\
+ xdpi, ydpi, color_bits,\
+ -(lo) * (xdpi), -(to) * (ydpi),\
+ (lm) * 72.0, (bm) * 72.0,\
+ (rm) * 72.0, (tm) * 72.0\
+ ),\
+ prn_device_body_rest_(print_page)
+
+#define prn_device_std_body(dtype, procs, dname, w10, h10, xdpi, ydpi, lm, bm, rm, tm, color_bits, print_page)\
+ prn_device_std_margins_body(dtype, procs, dname, w10, h10, xdpi, ydpi,\
+ lm, tm, lm, bm, rm, tm, color_bits, print_page)
+
+#define prn_device_margins(procs, dname, w10, h10, xdpi, ydpi, lo, to, lm, bm, rm, tm, color_bits, print_page)\
+{ prn_device_std_margins_body(gx_device_printer, procs, dname,\
+ w10, h10, xdpi, ydpi, lo, to, lm, bm, rm, tm, color_bits, print_page)\
+}
+
+#define prn_device(procs, dname, w10, h10, xdpi, ydpi, lm, bm, rm, tm, color_bits, print_page)\
+ prn_device_margins(procs, dname, w10, h10, xdpi, ydpi,\
+ lm, tm, lm, bm, rm, tm, color_bits, print_page)\
+
+/* ------ Utilities ------ */
+/* These are defined in gdevprn.c. */
+
+int gdev_prn_open_printer(P2(gx_device *dev, int binary_mode));
+#define gdev_prn_file_is_new(pdev) ((pdev)->file_is_new)
+#define gdev_prn_raster(pdev) gx_device_raster((gx_device *)(pdev), 0)
+int gdev_prn_get_bits(P4(gx_device_printer *, int, byte *, byte **));
+int gdev_prn_copy_scan_lines(P4(gx_device_printer *, int, byte *, uint));
+int gdev_prn_close_printer(P1(gx_device *));
+
+/* Define the InputAttributes and OutputAttributes of a device. */
+/* The device get_params procedure would call these. */
+
+typedef struct input_media_s {
+ float PageSize[2];
+ const char *MediaColor;
+ float MediaWeight;
+ const char *MediaType;
+} input_media;
+#define gdev_prn_begin_input_media(plist, pdict, count)\
+ ((pdict)->size = (count),\
+ param_begin_write_dict(plist, "InputAttributes", pdict, true))
+int gdev_prn_input_page_size(P4(int index, gs_param_dict *pdict,
+ floatp width_points, floatp height_points));
+int gdev_prn_input_media(P3(int index, gs_param_dict *pdict,
+ const input_media *pim));
+#define gdev_prn_end_input_media(plist, pdict)\
+ param_end_write_dict(plist, "InputAttributes", pdict)
+
+typedef struct output_media_s {
+ const char *OutputType;
+} output_media;
+#define gdev_prn_begin_output_media(plist, pdict, count)\
+ ((pdict)->size = (count),\
+ param_begin_write_dict(plist, "OutputAttributes", pdict, true))
+int gdev_prn_output_media(P3(int index, gs_param_dict *pdict,
+ const output_media *pom));
+#define gdev_prn_end_output_media(plist, pdict)\
+ param_end_write_dict(plist, "OutputAttributes", pdict)
+
+/* The default print_page_copies procedure just calls print_page */
+/* the given number of times. */
+dev_proc_print_page_copies(gx_default_print_page_copies);
+
+/* Define the number of scan lines that should actually be passed */
+/* to the device. */
+int gdev_prn_print_scan_lines(P1(gx_device *));
+
+/* BACKWARD COMPATIBILITY */
+#define dev_print_scan_lines(dev)\
+ gdev_prn_print_scan_lines((gx_device *)(dev))
+#define gdev_mem_bytes_per_scan_line(dev)\
+ gdev_prn_raster((gx_device_printer *)(dev))
+#define gdev_prn_transpose_8x8(inp,ils,outp,ols)\
+ memflip8x8(inp,ils,outp,ols)
+
+/* ------ Printer device types ------ */
+/**************** THE FOLLOWING CODE IS NOT USED YET. ****************/
+
+#if 0 /**************** VMS linker gets upset ****************/
+extern_st(st_prn_device);
+#endif
+int gdev_prn_initialize(P3(gx_device *, const char _ds *, dev_proc_print_page((*))));
+void gdev_prn_init_color(P4(gx_device *, int, dev_proc_map_rgb_color((*)), dev_proc_map_color_rgb((*))));
+
+#define prn_device_type(dtname, initproc, pageproc)\
+private dev_proc_print_page(pageproc);\
+device_type(dtname, st_prn_device, initproc)
+
+/****** FOLLOWING SHOULD CHECK __PROTOTYPES__ ******/
+#define prn_device_type_mono(dtname, dname, initproc, pageproc)\
+private dev_proc_print_page(pageproc);\
+private int \
+initproc(gx_device *dev)\
+{ return gdev_prn_initialize(dev, dname, pageproc);\
+}\
+device_type(dtname, st_prn_device, initproc)
+
+/****** DITTO ******/
+#define prn_device_type_color(dtname, dname, depth, initproc, pageproc, rcproc, crproc)\
+private dev_proc_print_page(pageproc);\
+private int \
+initproc(gx_device *dev)\
+{ int code = gdev_prn_initialize(dev, dname, pageproc);\
+ gdev_prn_init_color(dev, depth, rcproc, crproc);\
+ return code;\
+}\
+device_type(dtname, st_prn_device, initproc)
+
+#endif /* gdevprn_INCLUDED */
diff --git a/pstoraster/genarch.c b/pstoraster/genarch.c
new file mode 100644
index 000000000..855fcdbf3
--- /dev/null
+++ b/pstoraster/genarch.c
@@ -0,0 +1,131 @@
+/* Copyright (C) 1989, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* genarch.c */
+/* Generate a header file (arch.h) with parameters */
+/* reflecting the machine architecture and compiler characteristics. */
+
+#include "stdpre.h"
+#include <stdio.h>
+#include "iref.h"
+
+/* We should write the result on stdout, but the original Turbo C 'make' */
+/* can't handle output redirection (sigh). */
+
+main(int argc, char *argv[])
+{ char *fname = argv[1];
+ long one = 1;
+#define ffs_strlen 16
+ char *ffs = "ffffffffffffffff";
+ struct { char c; short s; } ss;
+ struct { char c; int i; } si;
+ struct { char c; long l; } sl;
+ struct { char c; char *p; } sp;
+ struct { char c; float f; } sf;
+ struct { char c; double d; } sd;
+ struct { char c; ref r; } sr;
+ static int log2s[17] = {0,0,1,0,2,0,0,0,3,0,0,0,0,0,0,0,4};
+ long lm1 = -1;
+ long lr1 = lm1 >> 1, lr2 = lm1 >> 2;
+ unsigned long um1 = ~(unsigned long)0;
+ int im1 = -1;
+ int ir1 = im1 >> 1, ir2 = im1 >> 2;
+ union { long l; char *p; } pl0, pl1;
+ int ars;
+ int lwidth = size_of(long) * 8;
+ union { float f; int i; long l; } f0, f1, fm1;
+ FILE *f = fopen(fname, "w");
+ if ( f == NULL )
+ { fprintf(stderr, "genarch.c: can't open %s for writing\n", fname);
+ return exit_FAILED;
+ }
+ fprintf(f, "/* Parameters derived from machine and compiler architecture */\n");
+#define S(str) fprintf(f, "\n\t%s\n\n", str)
+
+S("/* ---------------- Scalar alignments ---------------- */");
+
+#define offset(s, e) (int)((char *)&s.e - (char *)&s)
+ fprintf(f, "#define arch_align_short_mod %d\n", offset(ss, s));
+ fprintf(f, "#define arch_align_int_mod %d\n", offset(si, i));
+ fprintf(f, "#define arch_align_long_mod %d\n", offset(sl, l));
+ fprintf(f, "#define arch_align_ptr_mod %d\n", offset(sp, p));
+ fprintf(f, "#define arch_align_float_mod %d\n", offset(sf, f));
+ fprintf(f, "#define arch_align_double_mod %d\n", offset(sd, d));
+ fprintf(f, "#define arch_align_ref_mod %d\n", offset(sr, r));
+#undef offset
+
+S("/* ---------------- Scalar sizes ---------------- */");
+
+ fprintf(f, "#define arch_log2_sizeof_short %d\n", log2s[size_of(short)]);
+ fprintf(f, "#define arch_log2_sizeof_int %d\n", log2s[size_of(int)]);
+ fprintf(f, "#define arch_log2_sizeof_long %d\n", log2s[size_of(long)]);
+ fprintf(f, "#define arch_sizeof_ds_ptr %d\n", size_of(char _ds *));
+ fprintf(f, "#define arch_sizeof_ptr %d\n", size_of(char *));
+ fprintf(f, "#define arch_sizeof_float %d\n", size_of(float));
+ fprintf(f, "#define arch_sizeof_double %d\n", size_of(double));
+ fprintf(f, "#define arch_sizeof_ref %d\n", size_of(ref));
+
+S("/* ---------------- Unsigned max values ---------------- */");
+
+#define pmax(str, typ, tstr, l)\
+ fprintf(f, "#define arch_max_%s ((%s)0x%s%s + (%s)0)\n",\
+ str, tstr, ffs + ffs_strlen - size_of(typ) * 2, l, tstr)
+ pmax("uchar", unsigned char, "unsigned char", "");
+ pmax("ushort", unsigned short, "unsigned short", "");
+ pmax("uint", unsigned int, "unsigned int", "");
+ pmax("ulong", unsigned long, "unsigned long", "L");
+#undef pmax
+
+S("/* ---------------- Miscellaneous ---------------- */");
+
+ fprintf(f, "#define arch_is_big_endian %d\n", 1 - *(char *)&one);
+ pl0.l = 0;
+ pl1.l = -1;
+ fprintf(f, "#define arch_ptrs_are_signed %d\n",
+ (pl1.p < pl0.p));
+ f0.f = 0.0, f1.f = 1.0, fm1.f = -1.0;
+ /* We have to test the size dynamically here, */
+ /* because the preprocessor can't evaluate sizeof. */
+ fprintf(f, "#define arch_floats_are_IEEE %d\n",
+ ((size_of(float) == size_of(int) ?
+ f0.i == 0 && f1.i == (int)0x3f800000 && fm1.i == (int)0xbf800000 :
+ f0.l == 0 && f1.l == 0x3f800000L && fm1.l == 0xbf800000L)
+ ? 1 : 0));
+ /* There are three cases for arithmetic right shift: */
+ /* always correct, correct except for right-shifting a long by 1 */
+ /* (a bug in some versions of the Turbo C compiler), and */
+ /* never correct. */
+ ars = (lr2 != -1 || ir1 != -1 || ir2 != -1 ? 0 :
+ lr1 != -1 ? 1 : /* Turbo C problem */
+ 2);
+ fprintf(f, "#define arch_arith_rshift %d\n", ars);
+ /* Some machines can't handle a variable shift by */
+ /* the full width of a long. */
+ fprintf(f, "#define arch_can_shift_full_long %d\n",
+ um1 >> lwidth == 0);
+
+/* ---------------- Done. ---------------- */
+
+ fclose(f);
+ return exit_OK;
+}
diff --git a/pstoraster/ghost.h b/pstoraster/ghost.h
new file mode 100644
index 000000000..fbb6db087
--- /dev/null
+++ b/pstoraster/ghost.h
@@ -0,0 +1,27 @@
+/* Copyright (C) 1989, 1992 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* ghost.h */
+/* Common definitions for interpreter */
+#include "gx.h"
+#include "iref.h"
diff --git a/pstoraster/gp.h b/pstoraster/gp.h
new file mode 100644
index 000000000..05e9d0fcc
--- /dev/null
+++ b/pstoraster/gp.h
@@ -0,0 +1,201 @@
+/* Copyright (C) 1991, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gp.h */
+/* Interface to platform-specific routines */
+/* Requires gsmemory.h, gstypes.h */
+
+/*
+ * This file defines the interface to ***ALL*** platform-specific routines.
+ * The routines are implemented in a gp_*.c file specific to each platform.
+ * We try very hard to keep this list short!
+ */
+
+/* ------ Initialization/termination ------ */
+
+/*
+ * This routine is called early in the initialization.
+ * It should do as little as possible. In particular, it should not
+ * do things like open display connections: that is the responsibility
+ * of the display device driver.
+ */
+void gp_init(P0());
+
+/*
+ * This routine is called just before the program exits (normally or
+ * abnormally). It too should do as little as possible.
+ */
+void gp_exit(P2(int exit_status, int code));
+
+/*
+ * Exit the program. Normally this just calls the `exit' library procedure,
+ * but it does something different on a few platforms.
+ */
+void gp_do_exit(P1(int exit_status));
+
+/* ------ Miscellaneous ------ */
+
+/*
+ * Get the string corresponding to an OS error number.
+ * If no string is available, return NULL. The caller may assume
+ * the string is allocated statically and permanently.
+ */
+const char *gp_strerror(P1(int));
+
+/* ------ Date and time ------ */
+
+/*
+ * Read the current time (in seconds since an implementation-defined epoch)
+ * into ptm[0], and fraction (in nanoseconds) into ptm[1].
+ */
+void gp_get_realtime(P1(long ptm[2]));
+
+/*
+ * Read the current user CPU time (in seconds) into ptm[0],
+ * and fraction (in nanoseconds) into ptm[1].
+ */
+void gp_get_usertime(P1(long ptm[2]));
+
+/* ------ Screen management ------ */
+
+/*
+ * The following routines are only relevant in a single-window environment
+ * such as a PC; on platforms with window systems, the 'make current'
+ * routines do nothing.
+ */
+
+#ifndef gx_device_DEFINED
+# define gx_device_DEFINED
+typedef struct gx_device_s gx_device;
+#endif
+
+/* Initialize the console. */
+void gp_init_console(P0());
+
+/* Write a string to the console. */
+void gp_console_puts(P2(const char *, uint));
+
+/* Make the console current on the screen. */
+int gp_make_console_current(P1(gx_device *));
+
+/* Make the graphics current on the screen. */
+int gp_make_graphics_current(P1(gx_device *));
+
+/*
+ * The following are only relevant for X Windows.
+ */
+
+/* Get the environment variable that specifies the display to use. */
+const char *gp_getenv_display(P0());
+
+/* ------ Printer accessing ------ */
+
+/*
+ * Open a connection to a printer. A null file name means use the
+ * standard printer connected to the machine, if any.
+ * If possible, support "|command" for opening an output pipe.
+ * Return NULL if the connection could not be opened.
+ */
+FILE *gp_open_printer(P2(char *fname, int binary_mode));
+
+/* Close the connection to the printer. */
+void gp_close_printer(P2(FILE *pfile, const char *fname));
+
+/* ------ File naming and accessing ------ */
+
+/* Define the character used for separating file names in a list. */
+extern const char gp_file_name_list_separator;
+
+/* Define the default scratch file name prefix. */
+extern const char gp_scratch_file_name_prefix[];
+
+/* Define the name of the null output file. */
+extern const char gp_null_file_name[];
+
+/* Define the name that designates the current directory. */
+extern const char gp_current_directory_name[];
+
+/* Define the string to be concatenated with the file mode */
+/* for opening files without end-of-line conversion. */
+/* This is always either "" or "b". */
+extern const char gp_fmode_binary_suffix[];
+/* Define the file modes for binary reading or writing. */
+/* (This is just a convenience: they are "r" or "w" + the suffix.) */
+extern const char gp_fmode_rb[];
+extern const char gp_fmode_wb[];
+
+/* Create and open a scratch file with a given name prefix. */
+/* Write the actual file name at fname. */
+FILE *gp_open_scratch_file(P3(const char *prefix, char *fname,
+ const char *mode));
+
+/* Open a file with the given name, as a stream of uninterpreted bytes. */
+FILE *gp_fopen(P2(const char *fname, const char *mode));
+
+/* Answer whether a file name contains a directory/device specification, */
+/* i.e. is absolute (not directory- or device-relative). */
+bool gp_file_name_is_absolute(P2(const char *fname, uint len));
+
+/* Answer the string to be used for combining a directory/device prefix */
+/* with a base file name. The file name is known to not be absolute. */
+const char *gp_file_name_concat_string(P4(const char *prefix, uint plen,
+ const char *fname, uint len));
+
+/* ------ File enumeration ------ */
+
+#ifndef file_enum_DEFINED /* also defined in iodev.h */
+# define file_enum_DEFINED
+struct file_enum_s; /* opaque to client, defined by implementor */
+typedef struct file_enum_s file_enum;
+#endif
+
+/*
+ * Begin an enumeration. pat is a C string that may contain *s or ?s.
+ * The implementor should copy the string to a safe place.
+ * If the operating system doesn't support correct, arbitrarily placed
+ * *s and ?s, the implementation should modify the string so that it
+ * will return a conservative superset of the request, and then use
+ * the string_match procedure to select the desired subset. E.g., if the
+ * OS doesn't implement ? (single-character wild card), any consecutive
+ * string of ?s should be interpreted as *. Note that \ can appear in
+ * the pattern also, as a quoting character.
+ */
+file_enum *gp_enumerate_files_init(P3(const char *pat, uint patlen,
+ gs_memory_t *memory));
+
+/*
+ * Return the next file name in the enumeration. The client passes in
+ * a scratch string and a max length. If the name of the next file fits,
+ * the procedure returns the length. If it doesn't fit, the procedure
+ * returns max length +1. If there are no more files, the procedure
+ * returns -1.
+ */
+uint gp_enumerate_files_next(P3(file_enum *pfen, char *ptr, uint maxlen));
+
+/*
+ * Clean up a file enumeration. This is only called to abandon
+ * an enumeration partway through: ...next should do it if there are
+ * no more files to enumerate. This should deallocate the file_enum
+ * structure and any subsidiary structures, strings, buffers, etc.
+ */
+void gp_enumerate_files_close(P1(file_enum *pfen));
diff --git a/pstoraster/gp_nofb.c b/pstoraster/gp_nofb.c
new file mode 100644
index 000000000..76e681633
--- /dev/null
+++ b/pstoraster/gp_nofb.c
@@ -0,0 +1,54 @@
+/* Copyright (C) 1993 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gp_nofb.c */
+/* Dummy routines for Ghostscript platforms with no frame buffer management */
+#include "gx.h"
+#include "gp.h"
+#include "gxdevice.h"
+
+/* ------ Screen management ------ */
+
+/* Initialize the console. */
+void
+gp_init_console(void)
+{
+}
+
+/* Write a string to the console. */
+void
+gp_console_puts(const char *str, uint size)
+{ fwrite(str, 1, size, stdout);
+}
+
+/* Make the console current on the screen. */
+int
+gp_make_console_current(gx_device *dev)
+{ return 0;
+}
+
+/* Make the graphics current on the screen. */
+int
+gp_make_graphics_current(gx_device *dev)
+{ return 0;
+}
diff --git a/pstoraster/gp_unifn.c b/pstoraster/gp_unifn.c
new file mode 100644
index 000000000..10d55a583
--- /dev/null
+++ b/pstoraster/gp_unifn.c
@@ -0,0 +1,58 @@
+/* Copyright (C) 1994, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gp_unifn.c */
+/* Unix-like file name syntax platform routines for Ghostscript */
+#include "gx.h"
+#include "gp.h"
+
+/* Define the character used for separating file names in a list. */
+const char gp_file_name_list_separator = ':';
+
+/* Define the string to be concatenated with the file mode */
+/* for opening files without end-of-line conversion. */
+const char gp_fmode_binary_suffix[] = "";
+/* Define the file modes for binary reading or writing. */
+const char gp_fmode_rb[] = "r";
+const char gp_fmode_wb[] = "w";
+
+/* Answer whether a file name contains a directory/device specification, */
+/* i.e. is absolute (not directory- or device-relative). */
+bool
+gp_file_name_is_absolute(const char *fname, unsigned len)
+{ /* A file name is absolute if it starts with a 0 or more .s */
+ /* followed by a /. */
+ while ( len && *fname == '.' )
+ ++fname, --len;
+ return (len && *fname == '/');
+}
+
+/* Answer the string to be used for combining a directory/device prefix */
+/* with a base file name. The file name is known to not be absolute. */
+const char *
+gp_file_name_concat_string(const char *prefix, unsigned plen,
+ const char *fname, unsigned len)
+{ if ( plen > 0 && prefix[plen - 1] == '/' )
+ return "";
+ return "/";
+}
diff --git a/pstoraster/gp_unifs.c b/pstoraster/gp_unifs.c
new file mode 100644
index 000000000..b2251519f
--- /dev/null
+++ b/pstoraster/gp_unifs.c
@@ -0,0 +1,424 @@
+/* Copyright (C) 1993, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gp_unifs.c */
+/* "Unix-like" file system platform routines for Ghostscript */
+#include "memory_.h"
+#include "string_.h"
+#include "gx.h"
+#include "gp.h"
+#include "gsstruct.h"
+#include "gsutil.h" /* for string_match */
+#include "stat_.h"
+#include "dirent_.h"
+#include <sys/param.h> /* for MAXPATHLEN */
+
+/* Some systems (Interactive for example) don't define MAXPATHLEN,
+ * so we define it here. (This probably should be done via a Config-Script.)
+ */
+
+#ifndef MAXPATHLEN
+# define MAXPATHLEN 1024
+#endif
+
+/* Library routines not declared in a standard header */
+extern char *getenv(P1(const char *));
+extern char *mktemp(P1(char *));
+
+/* ------ File naming and accessing ------ */
+
+/* Define the default scratch file name prefix. */
+const char gp_scratch_file_name_prefix[] = "gs_";
+
+/* Define the name of the null output file. */
+const char gp_null_file_name[] = "/dev/null";
+
+/* Define the name that designates the current directory. */
+const char gp_current_directory_name[] = ".";
+
+/* Create and open a scratch file with a given name prefix. */
+/* Write the actual file name at fname. */
+FILE *
+gp_open_scratch_file(const char *prefix, char *fname, const char *mode)
+{ char *temp;
+ if ( (temp = getenv("TMPDIR")) == NULL )
+ strcpy(fname, "/var/tmp/");
+ else
+ { strcpy(fname, temp);
+ if ( strlen(fname) != 0 && fname[strlen(fname) - 1] != '/' )
+ strcat(fname, "/");
+ }
+ strcat(fname, prefix);
+ /* Prevent trailing X's in path from being converted by mktemp. */
+ if ( *fname != 0 && fname[strlen(fname) - 1] == 'X' )
+ strcat(fname, "-");
+ strcat(fname, "XXXXXX");
+ mktemp(fname);
+ return fopen(fname, mode);
+}
+
+/* Open a file with the given name, as a stream of uninterpreted bytes. */
+FILE *
+gp_fopen(const char *fname, const char *mode)
+{ return fopen(fname, mode);
+}
+
+/* ------ File enumeration ------ */
+
+/* Thanks to Fritz Elfert (Fritz_Elfert@wue.maus.de) for */
+/* the original version of the following code, and Richard Mlynarik */
+/* (mly@adoc.xerox.com) for an improved version. */
+
+typedef struct dirstack_s dirstack;
+struct dirstack_s {
+ dirstack *next;
+ DIR *entry;
+};
+gs_private_st_ptrs1(st_dirstack, dirstack, "dirstack",
+ dirstack_enum_ptrs, dirstack_reloc_ptrs, next);
+
+struct file_enum_s {
+ DIR *dirp; /* pointer to current open directory */
+ char *pattern; /* original pattern */
+ char *work; /* current path */
+ int worklen; /* strlen (work) */
+ dirstack *dstack; /* directory stack */
+ int patlen;
+ int pathead; /* how much of pattern to consider
+ * when listing files in current directory */
+ bool first_time;
+ gs_memory_t *memory;
+};
+gs_private_st_ptrs3(st_file_enum, struct file_enum_s, "file_enum",
+ file_enum_enum_ptrs, file_enum_reloc_ptrs, pattern, work, dstack);
+
+/* Private procedures */
+
+/* Do a wild-card match. */
+#ifdef DEBUG
+private bool
+wmatch(const byte *str, uint len, const byte *pstr, uint plen,
+ const string_match_params *psmp)
+{ bool match = string_match(str, len, pstr, plen, psmp);
+ if ( gs_debug_c('e') )
+ { dputs("[e]string_match(\"");
+ fwrite(str, 1, len, dstderr);
+ dputs("\", \"");
+ fwrite(pstr, 1, plen, dstderr);
+ dprintf1("\") = %s\n", (match ? "TRUE" : "false"));
+ }
+ return match;
+}
+#define string_match wmatch
+#endif
+
+/* Search a string backward for a character. */
+/* (This substitutes for strrchr, which some systems don't provide.) */
+private char *
+rchr(char *str, char ch, int len)
+{ register char *p = str + len;
+ while ( p > str )
+ if ( *--p == ch ) return p;
+ return 0;
+}
+
+/* Pop a directory from the enumeration stack. */
+private bool
+popdir(file_enum *pfen)
+{ dirstack *d = pfen->dstack;
+ if ( d == 0 )
+ return false;
+ pfen->dirp = d->entry;
+ pfen->dstack = d->next;
+ gs_free_object(pfen->memory, d, "gp_enumerate_files(popdir)");
+ return true;
+}
+
+/* Initialize an enumeration. */
+file_enum *
+gp_enumerate_files_init(const char *pat, uint patlen, gs_memory_t *mem)
+{ file_enum *pfen;
+ char *p;
+ char *work;
+
+ /* Reject attempts to enumerate paths longer than the */
+ /* system-dependent limit. */
+ if ( patlen > MAXPATHLEN )
+ return 0;
+
+ /* Reject attempts to enumerate with a pattern containing zeroes. */
+ { const char *p1;
+ for (p1 = pat; p1 < pat + patlen; p1++)
+ if (*p1 == 0) return 0;
+ }
+ /* >>> Should crunch strings of repeated "/"'s in pat to a single "/"
+ * >>> to match stupid unix filesystem "conventions" */
+
+ pfen = gs_alloc_struct(mem, file_enum, &st_file_enum,
+ "gp_enumerate_files");
+ if (pfen == 0)
+ return 0;
+
+ /* pattern and work could be allocated as strings, */
+ /* but it's simpler for GC and freeing to allocate them as bytes. */
+
+ pfen->pattern =
+ (char *)gs_alloc_bytes(mem, patlen + 1,
+ "gp_enumerate_files(pattern)");
+ if (pfen->pattern == 0)
+ return 0;
+ memcpy(pfen->pattern, pat, patlen);
+ pfen->pattern[patlen] = 0;
+
+ work = (char *)gs_alloc_bytes(mem, MAXPATHLEN+1,
+ "gp_enumerate_files(work)");
+ if (work == 0)
+ return 0;
+ pfen->work = work;
+
+ p = work;
+ memcpy(p, pat, patlen);
+ p += patlen;
+ *p = 0;
+
+ /* Remove directory specifications beyond the first wild card. */
+ /* Some systems don't have strpbrk, so we code it open. */
+ p = pfen->work;
+ while ( !(*p == '*' || *p == '?' || *p == 0) ) p++;
+ while ( !(*p == '/' || *p == 0) ) p++;
+ if ( *p == '/' )
+ *p = 0;
+ /* Substring for first wildcard match */
+ pfen->pathead = p - work;
+
+ /* Select the next higher directory-level. */
+ p = rchr(work, '/', p - work);
+ if (!p)
+ { /* No directory specification */
+ work[0] = 0;
+ pfen->worklen = 0;
+ }
+ else
+ { if (p == work)
+ { /* Root directory -- don't turn "/" into "" */
+ p++;
+ }
+ *p = 0;
+ pfen->worklen = p - work;
+ }
+
+ pfen->memory = mem;
+ pfen->dstack = 0;
+ pfen->first_time = true;
+ pfen->patlen = patlen;
+ return pfen;
+}
+
+/* Enumerate the next file. */
+uint
+gp_enumerate_files_next(file_enum *pfen, char *ptr, uint maxlen)
+{ dir_entry *de;
+ char *work = pfen->work;
+ int worklen = pfen->worklen;
+ char *pattern = pfen->pattern;
+ int pathead = pfen->pathead;
+ int len;
+ struct stat stbuf;
+
+ if ( pfen->first_time )
+ { pfen->dirp = ((worklen == 0) ? opendir(".") : opendir(work));
+ if_debug1('e', "[e]file_enum:First-Open '%s'\n", work);
+ pfen->first_time = false;
+ if (pfen->dirp == 0) /* first opendir failed */
+ { gp_enumerate_files_close(pfen);
+ return ~(uint)0;
+ }
+ }
+
+ top: de = readdir(pfen->dirp);
+ if (de == 0)
+ { /* No more entries in this directory */
+ char *p;
+
+ if_debug0('e', "[e]file_enum:Closedir\n");
+ closedir(pfen->dirp);
+ /* Back working directory and matching pattern up one level */
+ p = rchr(work,'/', worklen);
+ if (p != 0)
+ { if (p == work) p++;
+ *p = 0;
+ worklen = p - work;
+ }
+ else
+ worklen = 0;
+ p = rchr(pattern,'/', pathead);
+ if (p != 0)
+ pathead = p - pattern;
+ else
+ pathead = 0;
+
+ if (popdir(pfen))
+ { /* Back up the directory tree. */
+ if_debug1('e', "[e]file_enum:Dir popped '%s'\n", work);
+ goto top;
+ }
+ else
+ { if_debug0('e', "[e]file_enum:Dirstack empty\n");
+ gp_enumerate_files_close(pfen);
+ return ~(uint)0;
+ }
+ }
+
+ /* Skip . and .. */
+ len = strlen(de->d_name);
+ if (len <= 2 && (!strcmp(de->d_name,".") || !strcmp(de->d_name,"..") ))
+ goto top;
+ if (len + worklen + 1 > MAXPATHLEN)
+ /* Should be an error, I suppose */
+ goto top;
+ if (worklen == 0)
+ { /* "Current" directory (evil un*x kludge) */
+ memcpy(work, de->d_name, len + 1);
+ }
+ else if (worklen == 1 && work[0] == '/')
+ { /* Root directory */
+ memcpy(work + 1, de->d_name, len + 1);
+ len = len + 1;
+ }
+ else
+ { work[worklen] = '/';
+ memcpy(work + worklen + 1, de->d_name, len + 1);
+ len = worklen + 1 + len;
+ }
+
+ /* Test for a match at this directory level */
+ if (!string_match((byte *)work, len, (byte *)pattern, pathead, NULL))
+ goto top;
+
+ /* Perhaps descend into subdirectories */
+ if (pathead < pfen->patlen)
+ { DIR *dp;
+
+ if (((stat(work,&stbuf) >= 0)
+ ? !stat_is_dir(stbuf)
+ /* Couldn't stat it.
+ * Well, perhaps it's a directory and
+ * we'll be able to list it anyway.
+ * If it isn't or we can't, no harm done. */
+ : 0))
+ goto top;
+
+ if (pfen->patlen == pathead + 1)
+ { /* Listing "foo/?/" -- return this entry */
+ /* if it's a directory. */
+ if (!stat_is_dir (stbuf))
+ { /* Do directoryp test the hard way */
+ dp = opendir(work);
+ if (!dp) goto top;
+ closedir(dp);
+ }
+ work[len++] = '/';
+ goto winner;
+ }
+
+ /* >>> Should optimise the case in which the next level */
+ /* >>> of directory has no wildcards. */
+ dp = opendir(work);
+#ifdef DEBUG
+ { char save_end = pattern[pathead];
+ pattern[pathead] = 0;
+ if_debug2('e', "[e]file_enum:fname='%s', p='%s'\n",
+ work, pattern);
+ pattern[pathead] = save_end;
+ }
+#endif /* DEBUG */
+ if (!dp)
+ /* Can't list this one */
+ goto top;
+ else
+ { /* Advance to the next directory-delimiter */
+ /* in pattern */
+ char *p;
+ dirstack *d;
+ for (p = pattern + pathead + 1; ; p++)
+ { if (*p == 0)
+ { /* No more subdirectories to match */
+ pathead = pfen->patlen;
+ break;
+ }
+ else if (*p == '/')
+ { pathead = p - pattern;
+ break;
+ }
+ }
+
+ /* Push a directory onto the enumeration stack. */
+ d = gs_alloc_struct(pfen->memory, dirstack,
+ &st_dirstack,
+ "gp_enumerate_files(pushdir)");
+ if ( d != 0 )
+ {
+ d->next = pfen->dstack;
+ d->entry = pfen->dirp;
+ pfen->dstack = d;
+ }
+ else
+ DO_NOTHING; /* >>> e_VMerror!!! */
+
+ if_debug1('e', "[e]file_enum:Dir pushed '%s'\n",
+ work);
+ worklen = len;
+ pfen->dirp = dp;
+ goto top;
+ }
+ }
+
+ winner:
+ /* We have a winner! */
+ pfen->worklen = worklen;
+ pfen->pathead = pathead;
+ memcpy(ptr, work, len);
+ return len;
+}
+
+/* Clean up the file enumeration. */
+void
+gp_enumerate_files_close(file_enum *pfen)
+{ gs_memory_t *mem = pfen->memory;
+
+ if_debug0('e', "[e]file_enum:Cleanup\n");
+ while (popdir(pfen)) /* clear directory stack */
+ DO_NOTHING;
+ gs_free_object(mem, (byte *)pfen->work,
+ "gp_enumerate_close(work)");
+ gs_free_object(mem, (byte *)pfen->pattern,
+ "gp_enumerate_files_close(pattern)");
+ gs_free_object(mem, pfen, "gp_enumerate_files_close");
+}
+
+/* Test-cases:
+ (../?*r*?/?*.ps) {==} 100 string filenameforall
+ (../?*r*?/?*.ps*) {==} 100 string filenameforall
+ (../?*r*?/) {==} 100 string filenameforall
+ (/t*?/?*.ps) {==} 100 string filenameforall
+*/
diff --git a/pstoraster/gp_unix.c b/pstoraster/gp_unix.c
new file mode 100644
index 000000000..d81553c72
--- /dev/null
+++ b/pstoraster/gp_unix.c
@@ -0,0 +1,174 @@
+/* Copyright (C) 1989, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gp_unix.c */
+/* Unix-specific routines for Ghostscript */
+#include "string_.h"
+#include "gx.h"
+#include "gsexit.h"
+#include "gp.h"
+#include "time_.h"
+
+#if defined(SYSV) || defined(SVR4) || defined(CLK_TCK)
+#include <sys/times.h>
+#endif
+
+/*
+ * Because of inconsistent (and sometimes incorrect) header files,
+ * we must omit the argument list for popen.
+ */
+extern FILE *popen( /* P2(const char *, const char *) */ );
+extern int pclose(P1(FILE *));
+/*
+ * This is the only place in Ghostscript that calls 'exit'. Including
+ * <stdlib.h> is overkill, but that's where it's declared on ANSI systems.
+ * We don't have any way of detecting whether we have a standard library
+ * (some GNU compilers perversely define __STDC__ but don't provide
+ * an ANSI-compliant library), so we check __PROTOTYPES__ and
+ * hope for the best. We pick up getenv at the same time.
+ */
+#ifdef __PROTOTYPES__
+# include <stdlib.h> /* for exit and getenv */
+#else
+extern void exit(P1(int));
+extern char *getenv(P1(const char *));
+#endif
+
+/* Do platform-dependent initialization. */
+void
+gp_init(void)
+{
+}
+
+/* Do platform-dependent cleanup. */
+void
+gp_exit(int exit_status, int code)
+{
+}
+
+/* Exit the program. */
+void
+gp_do_exit(int exit_status)
+{ exit(exit_status);
+}
+
+/* ------ Miscellaneous ------ */
+
+/* Get the string corresponding to an OS error number. */
+/* Unix systems support this so inconsistently that we don't attempt */
+/* to figure out whether it's available. */
+const char *
+gp_strerror(int errnum)
+{ return NULL;
+}
+
+/* ------ Date and time ------ */
+
+/* Read the current time (in seconds since Jan. 1, 1970) */
+/* and fraction (in nanoseconds). */
+void
+gp_get_realtime(long *pdt)
+{ struct timeval tp;
+
+#if gettimeofday_no_timezone /* older versions of SVR4 */
+ { if ( gettimeofday(&tp) == -1 )
+ { lprintf("Ghostscript: gettimeofday failed!\n");
+ gs_exit(1);
+ }
+ }
+#else /* All other systems */
+ { struct timezone tzp;
+ if ( gettimeofday(&tp, &tzp) == -1 )
+ { lprintf("Ghostscript: gettimeofday failed!\n");
+ gs_exit(1);
+ }
+ }
+#endif
+
+ /* tp.tv_sec is #secs since Jan 1, 1970 */
+ pdt[0] = tp.tv_sec;
+
+ /* Some Unix systems (e.g., Interactive 3.2 r3.0) return garbage */
+ /* in tp.tv_usec. Try to filter out the worst of it here. */
+ pdt[1] = tp.tv_usec >= 0 && tp.tv_usec < 1000000 ? tp.tv_usec*1000 : 0;
+
+#ifdef DEBUG_CLOCK
+ printf("tp.tv_sec = %d tp.tv_usec = %d pdt[0] = %ld pdt[1] = %ld\n",
+ tp.tv_sec, tp.tv_usec, pdt[0], pdt[1]);
+#endif
+}
+
+/* Read the current user CPU time (in seconds) */
+/* and fraction (in nanoseconds). */
+void
+gp_get_usertime(long *pdt)
+{
+#if use_times_for_usertime
+ struct tms tms;
+ long ticks;
+
+ static long ticks_per_sec;
+ if ( !ticks_per_sec ) /* not initialized yet */
+ ticks_per_sec = CLK_TCK;
+
+ times(&tms);
+ ticks = tms.tms_utime + tms.tms_stime + tms.tms_cutime + tms.tms_cstime;
+ pdt[0] = ticks / ticks_per_sec;
+ pdt[1] = (ticks % ticks_per_sec) * (1000000000 / ticks_per_sec);
+#else
+ gp_get_realtime(pdt); /* Use an approximation on other hosts. */
+#endif
+}
+
+/* ------ Screen management ------ */
+
+/* Get the environment variable that specifies the display to use. */
+const char *
+gp_getenv_display(void)
+{ return getenv("DISPLAY");
+}
+
+/* ------ Printer accessing ------ */
+
+/* Open a connection to a printer. A null file name means use the */
+/* standard printer connected to the machine, if any. */
+/* "|command" opens an output pipe. */
+/* Return NULL if the connection could not be opened. */
+FILE *
+gp_open_printer(char *fname, int binary_mode)
+{ return
+ (strlen(fname) == 0 ?
+ gp_open_scratch_file(gp_scratch_file_name_prefix, fname, "w") :
+ fname[0] == '|' ?
+ popen(fname + 1, "w") :
+ fopen(fname, "w"));
+}
+
+/* Close the connection to the printer. */
+void
+gp_close_printer(FILE *pfile, const char *fname)
+{ if ( fname[0] == '|' )
+ pclose(pfile);
+ else
+ fclose(pfile);
+}
diff --git a/pstoraster/gpcheck.h b/pstoraster/gpcheck.h
new file mode 100644
index 000000000..c9308931c
--- /dev/null
+++ b/pstoraster/gpcheck.h
@@ -0,0 +1,58 @@
+/* Copyright (C) 1992, 1994 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gpcheck.h */
+/* Interrupt check interface */
+
+/*
+ * On some platforms, the interpreter must check periodically for user-
+ * initiated actions. (Eventually, this may be extended to all platforms,
+ * to handle multi-tasking through the 'context' facility.) Routines that
+ * run for a long time must periodically call gp_check_interrupts(), and
+ * if it returns true, must clean up whatever they are doing and return an
+ * e_interrupted (or gs_error_interrupted) exceptional condition.
+ * The return_if_interrupt macro provides a convenient way to do this.
+ *
+ * On platforms that require an interrupt check, the makefile defines
+ * a symbol CHECK_INTERRUPTS. Currently this is only the Microsoft
+ * Windows platform.
+ */
+
+#ifdef CHECK_INTERRUPTS
+int gp_check_interrupts(P0());
+int gs_return_check_interrupt(P1(int code));
+# define process_interrupts() discard(gp_check_interrupts())
+# define return_if_interrupt()\
+ { int icode_ = gp_check_interrupts();\
+ if ( icode_ )\
+ return gs_note_error((icode_ > 0 ? gs_error_interrupt : icode_));\
+ }
+# define return_check_interrupt(code)\
+ return gs_return_check_interrupt(code)
+#else
+# define gp_check_interrupts() 0
+# define process_interrupts() DO_NOTHING
+# define return_if_interrupt() DO_NOTHING
+# define return_check_interrupt(code)\
+ return (code)
+#endif
diff --git a/pstoraster/gs_btokn.ps b/pstoraster/gs_btokn.ps
new file mode 100644
index 000000000..1a81a6c86
--- /dev/null
+++ b/pstoraster/gs_btokn.ps
@@ -0,0 +1,287 @@
+% Copyright (C) 1994, 1996 Aladdin Enterprises. All rights reserved.
+%
+% This file is part of GNU Ghostscript.
+%
+% GNU Ghostscript is distributed in the hope that it will be useful, but
+% WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+% anyone for the consequences of using it or for whether it serves any
+% particular purpose or works at all, unless he says so in writing. Refer to
+% the GNU General Public License for full details.
+%
+% Everyone is granted permission to copy, modify and redistribute GNU
+% Ghostscript, but only under the conditions described in the GNU General
+% Public License. A copy of this license is supposed to have been given to
+% you along with GNU Ghostscript so you can know your rights and
+% responsibilities. It should be in a file named COPYING. Among other
+% things, the copyright notice and this notice must be preserved on all
+% copies.
+%
+% Aladdin Enterprises is not affiliated with the Free Software Foundation or
+% the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+% does not depend on any other GNU software.
+
+% Initialization file for binary tokens.
+% When this is run, systemdict is still writable,
+% but everything defined here goes into level2dict.
+
+% Define whether or not to allow writing dictionaries.
+/WRITEDICTS true def
+
+languagelevel 1 .setlanguagelevel
+level2dict begin
+
+% Initialization for the system name table.
+
+mark
+% 0
+ /abs /add /aload /anchorsearch /and
+ /arc /arcn /arct /arcto /array
+ /ashow /astore /awidthshow /begin /bind
+ /bitshift /ceiling /charpath /clear /cleartomark
+% 20
+ /clip /clippath /closepath /concat /concatmatrix
+ /copy /count /counttomark /currentcmykcolor /currentdash
+ /currentdict /currentfile /currentfont /currentgray /currentgstate
+ /currenthsbcolor /currentlinecap /currentlinejoin /currentlinewidth /currentmatrix
+% 40
+ /currentpoint /currentrgbcolor /currentshared /curveto /cvi
+ /cvlit /cvn /cvr /cvrs /cvs
+ /cvx /def /defineusername /dict /div
+ /dtransform /dup /end /eoclip /eofill
+% 60
+ /eoviewclip /eq /exch /exec /exit
+ /file /fill /findfont /flattenpath /floor
+ /flush /flushfile /for /forall /ge
+ /get /getinterval /grestore /gsave /gstate
+% 80
+ /gt /identmatrix /idiv /idtransform /if
+ /ifelse /image /imagemask /index /ineofill
+ /infill /initviewclip /inueofill /inufill /invertmatrix
+ /itransform /known /le /length /lineto
+% 100
+ /load /loop /lt /makefont /matrix
+ /maxlength /mod /moveto /mul /ne
+ /neg /newpath /not /null /or
+ /pathbbox /pathforall /pop /print /printobject
+% 120
+ /put /putinterval /rcurveto /read /readhexstring
+ /readline /readstring /rectclip /rectfill /rectstroke
+ /rectviewclip /repeat /restore /rlineto /rmoveto
+ /roll /rotate /round /save /scale
+% 140
+ /scalefont /search /selectfont /setbbox /setcachedevice
+ /setcachedevice2 /setcharwidth /setcmykcolor /setdash /setfont
+ /setgray /setgstate /sethsbcolor /setlinecap /setlinejoin
+ /setlinewidth /setmatrix /setrgbcolor /setshared /shareddict
+% 160
+ /show /showpage /stop /stopped /store
+ /string /stringwidth /stroke /strokepath /sub
+ /systemdict /token /transform /translate /truncate
+ /type /uappend /ucache /ueofill /ufill
+% 180
+ /undef /upath /userdict /ustroke /viewclip
+ /viewclippath /where /widthshow /write /writehexstring
+ /writeobject /writestring /wtranslation /xor /xshow
+ /xyshow /yshow /FontDirectory /SharedFontDirectory /Courier
+% 200
+ /Courier-Bold /Courier-BoldOblique /Courier-Oblique /Helvetica /Helvetica-Bold
+ /Helvetica-BoldOblique /Helvetica-Oblique /Symbol /Times-Bold /Times-BoldItalic
+ /Times-Italic /Times-Roman /execuserobject /currentcolor /currentcolorspace
+ /currentglobal /execform /filter /findresource /globaldict
+% 220
+ /makepattern /setcolor /setcolorspace /setglobal /setpagedevice
+ /setpattern
+% pad to 256
+ counttomark 256 exch sub { 0 } repeat
+% 256
+ /= /== /ISOLatin1Encoding /StandardEncoding
+% 260
+ ([) cvn (]) cvn /atan /banddevice /bytesavailable
+ /cachestatus /closefile /colorimage /condition /copypage
+ /cos /countdictstack /countexecstack /cshow /currentblackgeneration
+ /currentcacheparams /currentcolorscreen /currentcolortransfer /currentcontext /currentflat
+% 280
+ /currenthalftone /currenthalftonephase /currentmiterlimit /currentobjectformat /currentpacking
+ /currentscreen /currentstrokeadjust /currenttransfer /currentundercolorremoval /defaultmatrix
+ /definefont /deletefile /detach /deviceinfo /dictstack
+ /echo /erasepage /errordict /execstack /executeonly
+% 300
+ /exp /false /filenameforall /fileposition /fork
+ /framedevice /grestoreall /handleerror /initclip /initgraphics
+ /initmatrix /instroke /inustroke /join /kshow
+ /ln /lock /log /mark /monitor
+% 320
+ /noaccess /notify /nulldevice /packedarray /quit
+ /rand /rcheck /readonly /realtime /renamefile
+ /renderbands /resetfile /reversepath /rootfont /rrand
+ /run /scheck /setblackgeneration /setcachelimit /setcacheparams
+% 340
+ /setcolorscreen /setcolortransfer /setfileposition /setflat /sethalftone
+ /sethalftonephase /setmiterlimit /setobjectformat /setpacking /setscreen
+ /setstrokeadjust /settransfer /setucacheparams /setundercolorremoval /sin
+ /sqrt /srand /stack /status /statusdict
+% 360
+ /true /ucachestatus /undefinefont /usertime /ustrokepath
+ /version /vmreclaim /vmstatus /wait /wcheck
+ /xcheck /yield /defineuserobject /undefineuserobject /UserObjects
+ /cleardictstack
+% 376
+ /A /B /C /D /E /F /G /H /I /J /K /L /M
+ /N /O /P /Q /R /S /T /U /V /W /X /Y /Z
+ /a /b /c /d /e /f /g /h /i /j /k /l /m
+ /n /o /p /q /r /s /t /u /v /w /x /y /z
+% 428
+ /setvmthreshold (<<) cvn
+ (>>) cvn /currentcolorrendering /currentdevparams /currentoverprint /currentpagedevice
+ /currentsystemparams /currentuserparams /defineresource /findencoding /gcheck
+% 440
+ /glyphshow /languagelevel /product /pstack /resourceforall
+ /resourcestatus /revision /serialnumber /setcolorrendering /setdevparams
+ /setoverprint /setsystemparams /setuserparams /startjob /undefineresource
+ /GlobalFontDirectory /ASCII85Decode /ASCII85Encode /ASCIIHexDecode /ASCIIHexEncode
+% 460
+ /CCITTFaxDecode /CCITTFaxEncode /DCTDecode /DCTEncode /LZWDecode
+ /LZWEncode /NullEncode /RunLengthDecode /RunLengthEncode /SubFileDecode
+ /CIEBasedA /CIEBasedABC /DeviceCMYK /DeviceGray /DeviceRGB
+ /Indexed /Pattern /Separation
+% 478 -- end
+.packtomark
+
+% Install the system and user name tables.
+% The user name table is read-only for ordinary programs,
+% since it doesn't obey save/restore and must be managed specially.
+
+dup /SystemNames exch def
+100 array readonly dup /UserNames exch def
+.installnames
+
+% Define printobject and writeobject.
+% These are mostly implemented in PostScript, so that we don't have to
+% worry about interrupts or callbacks when writing to the output file.
+
+% Define procedures for accumulating the space required to represent
+% an object in binary form.
+/cntdict mark % <#refs> <#chars> <obj> -proc- <#refs> <#chars>
+ /integertype /pop load
+ /realtype 1 index
+ /marktype 1 index
+ /nulltype 1 index
+ /booleantype 1 index
+ /nametype { length add } bind
+ /stringtype 1 index
+ /arraytype null
+ WRITEDICTS { /dicttype null } if
+.dicttomark def
+cntdict /arraytype
+ { dup dup length 5 -1 roll add 4 2 roll
+ { dup type //cntdict exch get exec } forall
+ } bind put
+WRITEDICTS
+ { cntdict /dicttype
+ { dup dup length 2 mul 5 -1 roll add 4 2 roll
+ { 4 1 roll dup type //cntdict exch get exec
+ 3 -1 roll dup type //cntdict exch get exec
+ } forall
+ } bind put
+ } if
+
+/w2dict mark
+ /nametype { 2 copy .writecvs pop } bind
+ /stringtype 1 index
+.dicttomark def
+
+/.writeobjects % <file> <tag> <array> .writeobjects -
+ {
+ mark exch
+
+ % Count the space required for refs and strings.
+ dup length 0 3 -1 roll
+ % Stack: <file> <tag> -mark- <#refs> <#chars> <array>
+
+ dup 4 1 roll
+ { dup type //cntdict exch get exec
+ } forall
+
+ % Write the header.
+ % Stack: <file> <tag> -mark- <array1> ... <array|dictN> <#refs> <#chars>
+ counttomark 3 add -2 roll 4 1 roll
+ % Stack: -mark- <array1> ... <array|dictN> <tag> <#refs> <#chars> <file>
+ dup counttomark 1 sub index length
+ 4 index 3 bitshift 4 index add
+ (xxxxxxxx) .bosheader writestring
+
+ % Write the objects per se.
+ 3 1 roll pop
+ counttomark 1 sub index length 3 bitshift exch
+ 3 bitshift
+ % Stack: -mark- <array1> ... <array|dictN> <tag> <file> <ref#> <char#>
+
+ counttomark 4 sub
+ { counttomark -1 roll dup 6 1 roll
+ dup type /dicttype eq % can't be first object
+ { { 5 1 roll (xxxxxxxx) .bosobject
+ 3 index exch writestring
+ 4 -1 roll (xxxxxxxx) .bosobject
+ 3 index exch writestring
+ } forall
+ }
+ { { (xxxxxxxx) .bosobject
+ dup 1 6 index put
+ 3 index exch writestring
+ 4 -1 roll pop 0 4 1 roll % clear tag
+ } forall
+ }
+ ifelse
+ }
+ repeat
+
+ % Write the strings and names.
+ pop pop exch pop
+ % Stack: -mark- <array1> ... <array|dictN> <file>
+
+ counttomark 1 sub
+ { counttomark -1 roll
+ { % The counting pass ensured that the keys and values
+ % of any dictionary must be writable objects.
+ % Hence, we are processing a dictionary iff
+ % the next-to-top stack element is not a file.
+ 1 index type /filetype ne
+ { exch 2 index exch dup type //w2dict exch .knownget
+ { exec } { pop } ifelse pop
+ }
+ if
+ dup type //w2dict exch .knownget { exec } { pop } ifelse
+ } forall
+ }
+ repeat
+
+ % Clean up.
+ % Stack: -mark- <file>
+
+ pop pop
+
+ } odef
+currentdict /cntdict .undef
+currentdict /w2dict .undef
+
+/printobject { (%stderr) (w) file 3 1 roll writeobject } odef
+/writeobject { exch 1 array astore .writeobjects } odef
+
+% Implement binary error message output.
+ /.printerror
+ { $error /binary get .languagelevel 2 eq and
+ { currentobjectformat 0 ne
+ { [ /Error $error /errorname get $error /command get false
+ ] 250 printobject
+ }
+ //.printerror
+ ifelse
+ }
+ //.printerror
+ ifelse
+ } bind def
+
+% End of level2dict
+
+end
+.setlanguagelevel
diff --git a/pstoraster/gs_ccfnt.ps b/pstoraster/gs_ccfnt.ps
new file mode 100644
index 000000000..faec930f9
--- /dev/null
+++ b/pstoraster/gs_ccfnt.ps
@@ -0,0 +1,98 @@
+% Copyright (C) 1994, 1996 Aladdin Enterprises. All rights reserved.
+%
+% This file is part of GNU Ghostscript.
+%
+% GNU Ghostscript is distributed in the hope that it will be useful, but
+% WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+% anyone for the consequences of using it or for whether it serves any
+% particular purpose or works at all, unless he says so in writing. Refer to
+% the GNU General Public License for full details.
+%
+% Everyone is granted permission to copy, modify and redistribute GNU
+% Ghostscript, but only under the conditions described in the GNU General
+% Public License. A copy of this license is supposed to have been given to
+% you along with GNU Ghostscript so you can know your rights and
+% responsibilities. It should be in a file named COPYING. Among other
+% things, the copyright notice and this notice must be preserved on all
+% copies.
+%
+% Aladdin Enterprises is not affiliated with the Free Software Foundation or
+% the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+% does not depend on any other GNU software.
+
+% Find and register all the precompiled font operators in systemdict.
+
+/registerfont % <fontname> <fontdict> registerfont <font>
+ { DEBUG { (Registering ) print 1 index = } if
+ dup begin
+ Encoding type /nametype eq
+ { Encoding .findencoding /Encoding exch def
+ }
+ if
+ dup /PrefEnc known
+ { PrefEnc type /nametype eq
+ { PrefEnc .findencoding /PrefEnc exch def
+ }
+ if
+ }
+ if
+ dup /FDepVector known
+ { /FDepVector [ FDepVector
+ { FontDirectory 1 index .knownget
+ { exch pop }
+ { ccfonts 1 index .knownget
+ { registerfont
+ }
+ { Fontmap 1 index known
+ { findfont }
+ { pop NullFont }
+ ifelse
+ }
+ ifelse
+ }
+ ifelse
+ }
+ forall ] readonly def
+ }
+ if
+ end
+ % Use the value of definefont appropriate at run-time, not bind-time
+ /definefont load exec
+ } bind odef
+% Bind recursive call (bind descends into oparrays: feature!)
+/registerfont dup load bind def
+
+/.loadinitialfonts {
+ //.loadinitialfonts exec
+ /ccfonts mark
+ 0 1 null .getccfont 1 sub { .getccfont dup /FontName get exch } for
+ .dicttomark def
+ ccfonts
+ { FontDirectory 2 index known { pop pop } { registerfont pop } ifelse }
+ forall
+ currentdict /ccfonts .undef
+} bind def
+
+currentdict /registerfont .undef
+
+
+% If we're in a Level 2 system but running in Level 1 mode,
+% register the fonts explicitly as resources.
+% This is a bit of a hack, but doing better is too much work.
+
+/level2dict where
+ { pop /findresource where
+ { % Level 2 system, Level 2 mode
+ pop
+ }
+ { % Level 2 system, Level 1 mode
+ /Font /Category level2dict /findresource get exec begin
+ FontDirectory
+ { dup .gcheck { Instances } { LocalInstances } ifelse
+ 3 1 roll [exch 0 -1] .growput
+ }
+ forall end
+ }
+ ifelse
+ }
+if
diff --git a/pstoraster/gs_cidfn.ps b/pstoraster/gs_cidfn.ps
new file mode 100644
index 000000000..395986462
--- /dev/null
+++ b/pstoraster/gs_cidfn.ps
@@ -0,0 +1,127 @@
+% Copyright (C) 1995 Aladdin Enterprises. All rights reserved.
+%
+% This file is part of GNU Ghostscript.
+%
+% GNU Ghostscript is distributed in the hope that it will be useful, but
+% WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+% anyone for the consequences of using it or for whether it serves any
+% particular purpose or works at all, unless he says so in writing. Refer to
+% the GNU General Public License for full details.
+%
+% Everyone is granted permission to copy, modify and redistribute GNU
+% Ghostscript, but only under the conditions described in the GNU General
+% Public License. A copy of this license is supposed to have been given to
+% you along with GNU Ghostscript so you can know your rights and
+% responsibilities. It should be in a file named COPYING. Among other
+% things, the copyright notice and this notice must be preserved on all
+% copies.
+%
+% Aladdin Enterprises is not affiliated with the Free Software Foundation or
+% the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+% does not depend on any other GNU software.
+
+% ProcSet for implementing CID-keyed fonts.
+% When this is run, systemdict is still writable.
+
+%**************** This file is not ready for use:
+% - It doesn't include the actual character mapper (BuildGlyph).
+% - It loads the entire font into RAM.
+% - It has never been tested on a real font.
+
+/.setlanguagelevel where { pop 2 .setlanguagelevel } if
+.currentglobal true .setglobal
+
+/GS_CIDInit_ProcSet 30 dict dup begin
+
+% ---------------- CIDFont operators ---------------- %
+
+/StartData % <cidfontdict> <(Binary)|(Hex)> <datalength>
+ % StartData -
+ { 2 index begin % CID font dict
+ 20 dict begin % temporary dict
+ /datalength exch def
+ (Hex) eq /hex exch def
+ /cidfont exch def
+ /startdata currentfile fileposition def
+
+ % Read the character data into an array of strings.
+ % There's no particular reason to prefer long strings over short,
+ % so we just create a separate string for each character.
+
+ /charstrings CIDCount array def
+ /fontindices CIDCount FDBytes 1 eq { string } { array } ifelse def
+ 0 1 CIDCount 1 sub
+ { /cid exch def
+ currentfile FDBytes GDBytes add cid mul startdata add setfileposition
+ fontindices cid FDBytes .readint put
+ charstrings cid
+ /pos GDBytes .readint def
+ FDBytes .readint pop % skip FD of next entry
+ GDBytes .readint pos sub
+ dup 0 eq
+ { pop null }
+ { currentfile pos setfileposition string readstring pop }
+ ifelse put
+ }
+ for
+
+ % Process each font in the FDArray.
+ % For Type 3 fonts, just do a definefont with an empty Encoding.
+ % For Type 1 fonts, read the Subrs (don't bother to check for
+ % duplicates), and set CharStrings to the character data array.
+ % We don't support embedded Type 0 fonts, but it isn't clear
+ % whether they're allowed anyway.
+
+ cidfont /FDepVector [ FDArray
+ { dup /FontType get 1 eq
+ { dup /CharStrings charstrings put
+ /Private get
+ dup /SubrCount known
+ { begin /Subrs [ % Private
+ 0 1 SubrCount 1 sub
+ { SDBytes mul SubrMapOffset add startdata add
+ currentfile exch setfileposition
+ /pos SDBytes .readint def
+ SDBytes .readint pos sub
+ currentfile pos setfileposition string readstring pop
+ }
+ ] readonly def end % Private
+ }
+ if pop
+ }
+ if
+ dup /Encoding [] put
+ dup /FontName get exch definefont
+ }
+ forall ] readonly def
+
+ % Install the rest of the data in the font.
+
+ cidfont /CharStrings charstrings readonly put
+ cidfont /FontIndices fontindices readonly put
+ FontName cidfont /CIDFont defineresource pop
+
+ % Wrap up.
+
+ end % temporary dict
+ end % CID font dict
+ end % resource dict
+ } bind def
+
+/.readint % <nbytes> .readint <int>
+ { 0 exch { 8 bitshift currentfile read pop add } repeat
+ } bind def
+
+% ---------------- Resource category definition ---------------- %
+
+end readonly def
+
+/defineresource where
+ { pop
+ /CIDFont /Generic /Category findresource dup length dict .copydict
+ /Category defineresource pop
+ /CIDInit GS_CIDInit_ProcSet /ProcSet defineresource pop
+ }
+if
+
+.setglobal
diff --git a/pstoraster/gs_cmap.ps b/pstoraster/gs_cmap.ps
new file mode 100644
index 000000000..8b699811e
--- /dev/null
+++ b/pstoraster/gs_cmap.ps
@@ -0,0 +1,235 @@
+% Copyright (C) 1995 Aladdin Enterprises. All rights reserved.
+%
+% This file is part of GNU Ghostscript.
+%
+% GNU Ghostscript is distributed in the hope that it will be useful, but
+% WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+% anyone for the consequences of using it or for whether it serves any
+% particular purpose or works at all, unless he says so in writing. Refer to
+% the GNU General Public License for full details.
+%
+% Everyone is granted permission to copy, modify and redistribute GNU
+% Ghostscript, but only under the conditions described in the GNU General
+% Public License. A copy of this license is supposed to have been given to
+% you along with GNU Ghostscript so you can know your rights and
+% responsibilities. It should be in a file named COPYING. Among other
+% things, the copyright notice and this notice must be preserved on all
+% copies.
+%
+% Aladdin Enterprises is not affiliated with the Free Software Foundation or
+% the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+% does not depend on any other GNU software.
+
+% ProcSet for implementing CMap resource.
+% When this is run, systemdict is still writable.
+
+%**************** This file is not ready for use:
+% - Rearranged fonts are not implemented.
+% - It has never been tested on a real font.
+
+/.setlanguagelevel where { pop 2 .setlanguagelevel } if
+.currentglobal true .setglobal
+
+/GS_CMapInit_ProcSet 30 dict dup begin
+
+% ---------------- CMap operators ---------------- %
+
+% We create the following structures for character code mapping.
+% Map - a multi-level array indexed by the successive bytes of
+% the character code. All of the arrays are read-only.
+% NotdefMap - the same.
+
+% ------ Font-level operators ------ %
+
+% composefont doesn't appear in CMap files -- it's documented in
+% the "PostScript Language Reference Manual Supplement".
+/composefont % <name> <cmap|cmapname> <fonts> composefont <font>
+ { exch dup /dicttype ne { /CMap findresource } if
+ 10 dict
+ dup /FontType 0 put
+ dup /FMapType 9 put
+ dup /CMap 5 -1 roll put
+ dup /Encoding [ 0 1 6 index length 1 sub { } for ] put
+ dup /FDepVector 4 -1 roll put
+ /Font defineresource
+ } bind def
+
+/begincmap % - begincmap -
+ { /Map 256 array def
+ /NotdefMap 256 array def
+ } bind def
+/endcmap % - endcmap -
+ { /Map Map .endmap def
+ /NotdefMap NotdefMap .endmap def
+ } bind def
+
+/begincodespacerange % <count> begincodespacerange -
+ { pop mark
+ } bind def
+/endcodespacerange % <code_lo> <code_hi> ... endcodespacerange -
+ { counttomark 2 idiv
+ { 2 copy Map .addcodespacerange NotdefMap .addcodespacerange
+ } repeat pop
+ } bind def
+
+/.addcodespacerange % <code_lo> <code_hi> <map> .addcodespacerange -
+ { 2 index length 1 eq
+ { 2 { 3 -1 roll 0 get } repeat 1 exch
+ { 2 copy 0 put pop } for pop
+ }
+ { 2 index 0 get 1 3 index 0 get
+ 6 -2 roll
+ 2 { 1 1 index length 1 sub getinterval 6 1 roll } repeat
+ % Stack: lo hi map lo0 1 hi0
+ { 2 copy get null eq { 2 copy 256 array put } if
+ 4 copy get .addcodespacerange pop
+ }
+ for pop pop pop
+ }
+ ifelse
+ } bind def
+/.endmap % <map> .endmap <map>
+ { dup type /arraytype eq { dup { .endmap exch } forall astore readonly } if
+ } bind def
+
+/usecmap % <CMap_name> usecmap -
+ { /CMap findresource
+ dup length dict .copydict
+ currentdict end exch .copydict begin
+ } bind def
+
+% ------ Rearranged font operators ------ %
+
+/beginrearrangedfont % <font_name> <font*> beginrearrangedfont -
+ { (NOT IMPLEMENTED YET.\n) print flush
+ } bind def
+/endrearrangedfont % - endrearrangedfont -
+ { (NOT IMPLEMENTED YET.\n) print flush
+ } bind def
+
+/usefont % <fontID> usefont -
+ { (NOT IMPLEMENTED YET.\n) print flush
+ } bind def
+
+/beginusematrix % <fontID> beginusematrix -
+ { (NOT IMPLEMENTED YET.\n) print flush
+ } bind def
+/endusematrix % <matrix> endusematrix -
+ { (NOT IMPLEMENTED YET.\n) print flush
+ } bind def
+
+% ------ Character name/code selector operators ------ %
+
+/beginbfchar % <count> beginbfchar -
+ { pop mark
+ } bind def
+/endbfchar % <code> <to_code|charname> ... endbfchar
+ { Map .endmapchar /Map exch store pop
+ } bind def
+
+/beginbfrange % <count> beginbfrange -
+ { pop mark
+ } bind def
+/endbfrange % <code_lo> <code_hi> <to_code|(charname*)> ...
+ % endbfrange -
+ { Map counttomark 3 idiv { .addbfrange } repeat /Map exch store pop
+ } bind def
+
+/.addbfrange % <code_lo> <code_hi> <to_code|(charname*)> <map>
+ % .addbfrange <map>
+ { 1 index type /stringtype eq
+ { { dup length string copy dup dup length 1 sub 2 copy get 1 add put }
+ exch .addmaprange
+ }
+ { 2 dict begin exch /codes 1 index def 0 get exch
+ { codes dup length 1 sub 1 exch getinterval /codes 1 index def
+ dup length 0 gt { 0 get } if
+ }
+ exch .addmaprange end
+ }
+ ifelse exch pop
+ } bind def
+
+% ------ CID selector operators ------ %
+
+/begincidchar % <count> begincidchar -
+ { pop mark
+ } bind def
+/endcidchar % <code> <cid> ... endcidchar -
+ { Map .endmapchar /Map exch store pop
+ } bind def
+
+/begincidrange % <count> begincidrange -
+ { pop mark
+ } bind def
+/endcidrange % <code_lo> <code_hi> <cid_base> ... endcidrange -
+ { Map counttomark 3 idiv { { 1 add } exch .addmaprange exch pop } repeat
+ /Map exch store pop
+ } bind def
+
+/.endmapchar % <code> <value> ... <map> .endmapchar -
+ { counttomark 2 idiv
+ { 2 index 3 1 roll { } exch .addmaprange exch pop
+ } repeat exch pop
+ } bind def
+
+/.addmaprange % <code_lo> <code_hi> <value_base> <next_proc> <map>
+ % .addcidrange <value_next> <map>
+ { % We may be updating a (partly) read-only map from another CMap.
+ % If so, implement copy-on-write.
+ dup wcheck not { dup length array copy } if
+ 4 index length 1 eq
+ { 2 { 5 -1 roll 0 get } repeat 1 exch
+ { % Stack: value proc map code
+ 2 copy 5 index put pop
+ 3 -1 roll 2 index exec 3 1 roll
+ } for
+ }
+ { 4 index 0 get 1 5 index 0 get
+ 8 -2 roll
+ 2 { 1 1 index length 1 sub getinterval 8 1 roll } repeat
+ % Stack: lo hi next proc map lo0 1 hi0
+ { 6 copy get .addmaprange
+ % Stack: lo hi oldnext proc map i next submap
+ exch 6 1 roll 5 -1 roll pop
+ % Stack: lo hi next proc map i submap
+ 3 copy put pop pop
+ }
+ for 5 -2 roll pop pop
+ }
+ ifelse exch pop
+ } bind def
+
+% ------ notdef operators ------ %
+
+/beginnotdefchar % <count> beginnotdefchar -
+ { pop mark
+ } bind def
+/endnotdefchar % <code> <cid> ... endnotdefchar -
+ { counttomark 2 idiv { 1 index exch .addnotdefrange } repeat pop
+ } bind def
+
+/beginnotdefrange % <count> beginnotdefrange -
+ { pop mark
+ } bind def
+/endnotdefrange % <code_lo> <code_hi> <cid> ... endnotdefrange -
+ { counttomark 3 idiv { .addnotdefrange } repeat pop
+ } bind def
+
+/.addnotdefrange % <code_lo> <code_hi> <cid_base> .addnotdefrange -
+ { { } NotdefMap .addmaprange /NotdefMap exch store pop
+ } bind def
+
+% ---------------- Resource category definition ---------------- %
+
+end readonly def
+
+/defineresource where
+ { pop
+ /CMap /Generic /Category findresource dup length dict .copydict
+ /Category defineresource pop
+ /CMapInit GS_CMapInit_ProcSet /ProcSet defineresource pop
+ }
+if
+
+.setglobal
diff --git a/pstoraster/gs_cmdl.ps b/pstoraster/gs_cmdl.ps
new file mode 100644
index 000000000..ad6dfbcc0
--- /dev/null
+++ b/pstoraster/gs_cmdl.ps
@@ -0,0 +1,186 @@
+% Copyright (C) 1994, 1996 Aladdin Enterprises. All rights reserved.
+%
+% This file is part of GNU Ghostscript.
+%
+% GNU Ghostscript is distributed in the hope that it will be useful, but
+% WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+% anyone for the consequences of using it or for whether it serves any
+% particular purpose or works at all, unless he says so in writing. Refer to
+% the GNU General Public License for full details.
+%
+% Everyone is granted permission to copy, modify and redistribute GNU
+% Ghostscript, but only under the conditions described in the GNU General
+% Public License. A copy of this license is supposed to have been given to
+% you along with GNU Ghostscript so you can know your rights and
+% responsibilities. It should be in a file named COPYING. Among other
+% things, the copyright notice and this notice must be preserved on all
+% copies.
+%
+% Aladdin Enterprises is not affiliated with the Free Software Foundation or
+% the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+% does not depend on any other GNU software.
+
+% Parse and execute the command line.
+% C code handles the following switches: -h/-? -I -M -v
+
+/cmddict 50 dict def
+cmddict begin
+
+% ---------------- Utility procedures ---------------- %
+
+% Get the next argument from the parsed argument list.
+/nextarg % - nextarg <arg> true
+ % - nextarg false
+ { argv length 0 eq
+ { false }
+ { argv dup 0 get exch dup length 1 sub 1 exch getinterval /argv exch def }
+ ifelse
+ } bind def
+
+% Run a file, under job control if implemented.
+/runjob % <file> runjob -
+ { end % cmddict
+ /startjob where { pop false () startjob pop }
+ run
+ //cmddict begin
+ } bind def
+/runfilejob % <filename> runfilejob -
+ { findlibfile { exch pop } { (r) file } runjob
+ } bind def
+
+% Expand arguments. Free variables: expand@.
+/expandarg % <string> expandarg <args...>
+ { dup () eq
+ { pop
+ }
+ { dup dup (--) eq exch (-+) eq or
+ { pop /expand@ false def
+ }
+ { expand@ { (@) anchorsearch } { false } ifelse
+ { pop findlibfile
+ { exch pop }
+ { (r) file } % let the error happen
+ expandargfile
+ }
+ if
+ }
+ ifelse
+ }
+ } bind def
+/expandargfile % <file> expandargfile <args...>
+ { [ exch cvlit
+ { token not { exit } if
+ dup type /stringtype ne { =string cvs dup length string copy } if
+ expandarg
+ }
+ /exec cvx
+ ] cvx loop
+ } bind def
+
+% ---------------- Recognized switches ---------------- %
+
+% Switches with arguments are defined as <x>;
+% switches without arguments are defined as -<x>.
+
+% Switches without arguments
+/--
+ { nextarg not
+ { (-- and -+ require a file name.\n) print flush }
+ { //systemdict /ARGUMENTS argv put /argv [] def runjob }
+ ifelse
+ } bind def
+/-+ /-- load def
+/-@ /-- load def
+/-A { (@) Z } bind def
+/-c
+ { { argv length 0 eq { exit } if
+ argv 0 get (-) anchorsearch { pop pop exit } if
+ pop nextarg token
+ { exch pop % Probably should check for empty.
+ end exec //cmddict begin
+ }
+ if
+ }
+ loop
+ } bind def
+/-e { (#) Z } bind def
+/-E /-e load def
+/-f { } def
+/-q { //systemdict /QUIET true put } bind def
+
+% Switches with arguments
+/d
+ { (=) search not { (#) search not { () exch dup } if } if
+ exch pop cvn dup where
+ { pop (Redefining ) print print ( is not allowed.\n) print flush pop }
+ { exch token
+ { exch pop } % Probably should check for empty.
+ { true }
+ ifelse
+ //systemdict 3 1 roll put
+ }
+ ifelse
+ } bind def
+/D /d load def
+/f { dup length 0 ne { runfilejob } if } bind def
+/g
+ { (x) search { cvi pop exch cvi } { cvi dup } ifelse
+ //systemdict begin /DEVICEHEIGHT exch def /DEVICEWIDTH exch def end
+ } bind def
+/r
+ { (x) search { cvr pop exch cvr } { cvr dup } ifelse
+ //systemdict begin /DEVICEYRESOLUTION exch def /DEVICEXRESOLUTION exch def end
+ } bind def
+/s
+ { (=) search not { (#) search not { () exch dup } if } if
+ exch pop cvn dup where { pop dup load } { () } ifelse
+ type /stringtype ne
+ { (Redefining ) print print ( is not allowed.\n) print flush pop }
+ { exch //systemdict 3 1 roll put }
+ ifelse
+ } bind def
+/S /s load def
+/Z { true .setdebug } bind def
+
+% ---------------- Main program ---------------- %
+
+% We process the command line in two passes. In the first pass,
+% we read and expand any @-files as necessary. The second pass
+% does the real work.
+
+/cmdstart
+ { //cmddict begin
+ /expand@ true def
+ [
+ % Process the GS_OPTIONS environment variable.
+ (GS_OPTIONS) getenv { 0 () /SubFileDecode filter expandargfile } if
+ % Process the actual command line.
+ .getargv { expandarg } forall
+ ] readonly /argv exch def
+ % Now interpret the commands.
+ { nextarg not { exit } if
+ dup 0 get (-) 0 get eq
+ { dup length 1 eq
+ { pop (%stdin) (r) file runjob
+ }
+ { dup length 2 gt
+ { dup dup length 2 sub 2 exch getinterval exch 1 1 getinterval }
+ if currentdict .knownget
+ { exec
+ }
+ { (Ignoring unknown switch ) print
+ dup length 1 eq { (-) print print } if print
+ (\n) print flush
+ }
+ ifelse
+ }
+ ifelse
+ }
+ { runfilejob
+ }
+ ifelse
+ }
+ loop end
+ } bind def
+
+end % cmddict
diff --git a/pstoraster/gs_dbt_e.ps b/pstoraster/gs_dbt_e.ps
new file mode 100644
index 000000000..70c153762
--- /dev/null
+++ b/pstoraster/gs_dbt_e.ps
@@ -0,0 +1,65 @@
+% Copyright (C) 1993, 1994 Aladdin Enterprises. All rights reserved.
+%
+% This file is part of GNU Ghostscript.
+%
+% GNU Ghostscript is distributed in the hope that it will be useful, but
+% WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+% anyone for the consequences of using it or for whether it serves any
+% particular purpose or works at all, unless he says so in writing. Refer to
+% the GNU General Public License for full details.
+%
+% Everyone is granted permission to copy, modify and redistribute GNU
+% Ghostscript, but only under the conditions described in the GNU General
+% Public License. A copy of this license is supposed to have been given to
+% you along with GNU Ghostscript so you can know your rights and
+% responsibilities. It should be in a file named COPYING. Among other
+% things, the copyright notice and this notice must be preserved on all
+% copies.
+%
+% Aladdin Enterprises is not affiliated with the Free Software Foundation or
+% the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+% does not depend on any other GNU software.
+
+% Define the Dingbats encoding vector.
+/currentglobal where
+ { pop currentglobal { setglobal } true setglobal }
+ { { } }
+ifelse
+/DingbatsEncoding
+% \000
+ StandardEncoding 0 32 getinterval aload pop % /.notdef
+% \040
+ /space /a1 /a2 /a202 /a3 /a4 /a5 /a119
+ /a118 /a117 /a11 /a12 /a13 /a14 /a15 /a16
+ /a105 /a17 /a18 /a19 /a20 /a21 /a22 /a23
+ /a24 /a25 /a26 /a27 /a28 /a6 /a7 /a8
+% \100
+ /a9 /a10 /a29 /a30 /a31 /a32 /a33 /a34
+ /a35 /a36 /a37 /a38 /a39 /a40 /a41 /a42
+ /a43 /a44 /a45 /a46 /a47 /a48 /a49 /a50
+ /a51 /a52 /a53 /a54 /a55 /a56 /a57 /a58
+% \140
+ /a59 /a60 /a61 /a62 /a63 /a64 /a65 /a66
+ /a67 /a68 /a69 /a70 /a71 /a72 /a73 /a74
+ /a203 /a75 /a204 /a76 /a77 /a78 /a79 /a81
+ /a82 /a83 /a84 /a97 /a98 /a99 /a100 /.notdef
+% \200
+ StandardEncoding 0 32 getinterval aload pop % /.notdef
+% \240
+ /.notdef /a101 /a102 /a103 /a104 /a106 /a107 /a108
+ /a112 /a111 /a110 /a109 /a120 /a121 /a122 /a123
+ /a124 /a125 /a126 /a127 /a128 /a129 /a130 /a131
+ /a132 /a133 /a134 /a135 /a136 /a137 /a138 /a139
+% \300
+ /a140 /a141 /a142 /a143 /a144 /a145 /a146 /a147
+ /a148 /a149 /a150 /a151 /a152 /a153 /a154 /a155
+ /a156 /a157 /a158 /a159 /a160 /a161 /a163 /a164
+ /a196 /a165 /a192 /a166 /a167 /a168 /a169 /a170
+% \340
+ /a171 /a172 /a173 /a162 /a174 /a175 /a176 /a177
+ /a178 /a179 /a193 /a180 /a199 /a181 /a200 /a182
+ /.notdef /a201 /a183 /a184 /a197 /a185 /a194 /a198
+ /a186 /a195 /a187 /a188 /a189 /a190 /a191 /.notdef
+256 packedarray .defineencoding
+3 DingbatsEncoding .registerencoding
+exec
diff --git a/pstoraster/gs_diskf.ps b/pstoraster/gs_diskf.ps
new file mode 100644
index 000000000..4d55fd6f1
--- /dev/null
+++ b/pstoraster/gs_diskf.ps
@@ -0,0 +1,230 @@
+% Copyright (C) 1996 Aladdin Enterprises. All rights reserved.
+%
+% This file is part of GNU Ghostscript.
+%
+% GNU Ghostscript is distributed in the hope that it will be useful, but
+% WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+% anyone for the consequences of using it or for whether it serves any
+% particular purpose or works at all, unless he says so in writing. Refer to
+% the GNU General Public License for full details.
+%
+% Everyone is granted permission to copy, modify and redistribute GNU
+% Ghostscript, but only under the conditions described in the GNU General
+% Public License. A copy of this license is supposed to have been given to
+% you along with GNU Ghostscript so you can know your rights and
+% responsibilities. It should be in a file named COPYING. Among other
+% things, the copyright notice and this notice must be preserved on all
+% copies.
+%
+% Aladdin Enterprises is not affiliated with the Free Software Foundation or
+% the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+% does not depend on any other GNU software.
+
+% Support for converting Type 1 fonts without eexec encryption to
+% Type 4 fonts that load individual character outlines on demand.
+
+% If DISKFONTS is true, we load individual CharStrings as they are needed.
+% (This is intended primarily for machines with very small memories.)
+% Initially, the character definition is the file position of the definition;
+% this gets replaced with the actual CharString.
+% Note that if we are loading characters lazily, CharStrings is writable.
+
+% _Cstring must be long enough to hold the longest CharString for
+% a character defined using seac. This is lenIV + 4 * 5 (for the operands
+% of sbw, assuming div is not used) + 2 (for sbw) + 3 * 5 (for the operands
+% of seac other than the character codes) + 2 * 2 (for the character codes)
+% + 2 (for seac), i.e., lenIV + 43.
+
+/_Cstring 60 string def
+
+% When we initially load the font, we call
+% <index|charname> <length> <readproc> cskip_C
+% to skip over each character definition and return the file position instead.
+% This substitutes for the procedure
+% <index|charname> <length> string currentfile exch read[hex]string pop
+% [encrypt]
+% What we actually store in the CharString is fileposition * 1000 + length,
+% negated if the string is stored in binary form.
+
+/cskip_C
+ { exch dup 1000 ge 3 index type /nametype ne or
+ { % This is a Subrs string, or the string is so long we can't represent
+ % its length. Load it now.
+ exch exec
+ }
+ { % Record the position and length, and skip the string.
+ dup currentfile fileposition 1000 mul add
+ 2 index 3 get /readstring cvx eq { neg } if
+ 3 1 roll
+ dup _Cstring length idiv
+ { currentfile _Cstring 3 index 3 get exec pop pop
+ } repeat
+ _Cstring length mod _Cstring exch 0 exch getinterval
+ currentfile exch 3 -1 roll 3 get exec pop pop
+ }
+ ifelse
+ } bind def
+
+% Load a CharString from the file. The font is the top entry
+% on the dictionary stack.
+/load_C % <charname> <fileposandlength> load_C -
+ { dup abs 1000 idiv FontFile exch setfileposition
+ CharStrings 3 1 roll
+ .currentglobal CharStrings .gcheck .setglobal exch
+ dup 0 lt
+ { neg 1000 mod string FontFile exch readstring }
+ { 1000 mod string FontFile exch readhexstring }
+ ifelse pop
+ exch .setglobal
+% If the CharStrings aren't encrypted on the file, encrypt now.
+ Private /-| get 0 get
+ dup type /nametype ne
+ { dup length 5 sub 5 exch getinterval exec }
+ { pop }
+ ifelse dup 4 1 roll put
+% If the character is defined with seac, load its components now.
+ mark exch seac_C
+ counttomark
+ { StandardEncoding exch get dup CharStrings exch get
+ dup type /integertype eq { load_C } { pop pop } ifelse
+ } repeat
+ pop % the mark
+ } bind def
+
+/seac_C % <charstring> seac_C <achar> <bchar> ..or nothing..
+ { dup length _Cstring length le
+ { 4330 exch _Cstring .type1decrypt exch pop
+ dup dup length 2 sub 2 getinterval <0c06> eq % seac
+ { dup length
+ Private /lenIV known { Private /lenIV get } { 4 } ifelse
+ exch 1 index sub getinterval
+% Parse the string just enough to extract the seac information.
+% We assume that the only possible operators are hsbw, sbw, and seac,
+% and that there are no 5-byte numbers.
+ mark 0 3 -1 roll
+ { exch
+ { { dup 32 lt
+ { pop 0 }
+ { dup 247 lt
+ { 139 sub 0 }
+ { dup 251 lt
+ { 247 sub 256 mul 108 add 1 1 }
+ { 251 sub -256 mul -108 add -1 1 }
+ ifelse
+ }
+ ifelse
+ }
+ ifelse
+ } % 0
+ { mul add 0 } % 1
+ }
+ exch get exec
+ }
+ forall pop
+ counttomark 1 add 2 roll cleartomark % pop all but achar bchar
+ }
+ { pop % not seac
+ }
+ ifelse
+ }
+ { pop % punt
+ }
+ ifelse
+ } bind def
+
+% Define replacement procedures for loading fonts.
+% If DISKFONTS is true and the body of the font is not encrypted with eexec:
+% - Prevent the CharStrings from being made read-only.
+% - Substitute a different CharString-reading procedure.
+% (eexec disables this because the implicit 'systemdict begin' hides
+% the redefinitions that make the scheme work.)
+% We assume that:
+% - The magic procedures (-|, -!, |-, and |) are defined with
+% executeonly or readonly;
+% - The contents of the reading procedures are as defined in bdftops.ps;
+% - The font includes the code
+% <font> /CharStrings <CharStrings> readonly put
+/.loadfontdict 6 dict def mark
+ /begin % push this dict after systemdict
+ { dup begin
+ //systemdict eq { //.loadfontdict begin } if
+ } bind
+ /end % match begin
+ { currentdict end
+ //.loadfontdict eq currentdict //systemdict eq and { end } if
+ } bind
+ /dict % leave room for FontFile, BuildChar, BuildGlyph
+ { 3 add dict
+ } bind
+ /executeonly % for reading procedures
+ { readonly
+ }
+ /noaccess % for Subrs strings and Private dictionary
+ { readonly
+ }
+ /readonly % for procedures and CharStrings dictionary
+ { % We want to take the following non-standard actions here:
+ % - If the operand is the CharStrings dictionary, do nothing;
+ % - If the operand is a number (a file position replacing the
+ % actual CharString), do nothing;
+ % - If the operand is either of the reading procedures (-| or -!),
+ % substitute a different one.
+ dup type /dicttype eq % CharStrings or Private
+ count 2 gt and
+ { 1 index /CharStrings ne { readonly } if }
+ { dup type /arraytype eq % procedure or data array
+ { dup length 5 ge 1 index xcheck and
+ { dup 0 get /string eq
+ 1 index 1 get /currentfile eq and
+ 1 index 2 get /exch eq and
+ 1 index 3 get dup /readstring eq exch /readhexstring eq or and
+ 1 index 4 get /pop eq and
+ { /cskip_C cvx 2 packedarray cvx
+ }
+ { readonly
+ }
+ ifelse
+ }
+ { readonly
+ }
+ ifelse
+ }
+ { dup type /stringtype eq % must be a Subr string
+ { readonly }
+ if
+ }
+ ifelse
+ }
+ ifelse
+ } bind
+ /definefont % to insert BuildChar/Glyph and change FontType
+ { dup /FontType get 1 eq
+ { dup /FontType 4 put
+ dup /BuildChar /build_C load put
+ dup /BuildGlyph /build_C load put
+ }
+ if definefont
+ } bind
+counttomark 2 idiv { .loadfontdict 3 1 roll put } repeat pop
+.loadfontdict readonly pop
+
+% Define the BuildChar and BuildGlyph procedures for modified fonts.
+% A single procedure serves for both.
+/build_C % <font> <code|name> build_C -
+ { 1 index begin
+ dup dup type /integertype eq { Encoding exch get } if
+ % Stack: font code|name name
+ dup CharStrings exch .knownget not
+ { 2 copy eq { exch pop /.notdef exch } if
+ QUIET not
+ { (Substituting .notdef for ) print = flush }
+ { pop }
+ ifelse
+ /.notdef CharStrings /.notdef get
+ } if
+ % Stack: font code|name name charstring
+ dup type /integertype eq
+ { load_C end build_C }
+ { end .type1execchar }
+ ifelse
+ } bind def
diff --git a/pstoraster/gs_dps1.ps b/pstoraster/gs_dps1.ps
new file mode 100644
index 000000000..ebf5e9888
--- /dev/null
+++ b/pstoraster/gs_dps1.ps
@@ -0,0 +1,307 @@
+% Copyright (C) 1990, 1996 Aladdin Enterprises. All rights reserved.
+%
+% This file is part of GNU Ghostscript.
+%
+% GNU Ghostscript is distributed in the hope that it will be useful, but
+% WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+% anyone for the consequences of using it or for whether it serves any
+% particular purpose or works at all, unless he says so in writing. Refer to
+% the GNU General Public License for full details.
+%
+% Everyone is granted permission to copy, modify and redistribute GNU
+% Ghostscript, but only under the conditions described in the GNU General
+% Public License. A copy of this license is supposed to have been given to
+% you along with GNU Ghostscript so you can know your rights and
+% responsibilities. It should be in a file named COPYING. Among other
+% things, the copyright notice and this notice must be preserved on all
+% copies.
+%
+% Aladdin Enterprises is not affiliated with the Free Software Foundation or
+% the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+% does not depend on any other GNU software.
+
+% Initialization file for analogs of Display PostScript functions
+% that are also included in Level 2.
+% When this is run, systemdict is still writable,
+% but (almost) everything defined here goes into level2dict.
+
+level2dict begin
+
+% ------ Virtual memory ------ %
+
+/currentshared /.currentglobal load def
+/scheck /.gcheck load def
+%****** FOLLOWING IS WRONG ******
+/shareddict currentdict /globaldict .knownget not { 20 dict } if def
+
+% Global and LocalFontDirectory must remain in systemdict
+% even if we temporarily exit Level 2 mode.
+
+end % level2dict
+systemdict begin
+
+/SharedFontDirectory FontDirectory .gcheck
+ { .currentglobal false .setglobal
+ /LocalFontDirectory FontDirectory dup maxlength dict copy def
+ .setglobal FontDirectory
+ }
+ { /LocalFontDirectory FontDirectory def
+ 50 dict
+ }
+ifelse def
+
+end % systemdict
+level2dict begin
+
+% setshared must rebind FontDirectory to the appropriate one of
+% Local or SharedFontDirectory.
+
+/.setglobal
+ { .setglobal
+ //systemdict /FontDirectory .currentglobal
+ { //SharedFontDirectory }
+ { //systemdict /LocalFontDirectory get } % can't embed ref to local VM
+ ifelse .forceput
+ } .bind odef % must bind .forceput and .setglobal
+ % even if NOBIND in effect
+/setshared /.setglobal load def
+.currentglobal setshared
+
+% ------ Fonts ------ %
+
+/selectfont
+ { exch findfont exch
+ dup type /arraytype eq { makefont } { scalefont } ifelse
+ setfont
+ } odef
+% Undefinefont has to take local/global VM into account.
+/undefinefont
+ { FontDirectory 1 index .undef
+ .currentglobal
+ { % Current mode is global; delete from local directory too.
+ //systemdict /LocalFontDirectory .knownget
+ { exch .undef }
+ { pop }
+ ifelse
+ }
+ { % Current mode is local; if there was a shadowed global
+ % definition, copy it into the local directory.
+ //systemdict /SharedFontDirectory .knownget
+ { 1 index .knownget
+ { FontDirectory 3 1 roll put }
+ { pop }
+ ifelse
+ }
+ if
+ }
+ ifelse
+ } odef
+
+% If we load a font into global VM within an inner save, the restore
+% will delete it from FontDirectory but not from SharedFontDirectory.
+% We have to handle this by making restore copy missing entries from
+% SharedFontDirectory to FontDirectory. Since this could slow down restore
+% considerably, we define a new operator .dictcopynew for this purpose.
+% Furthermore, if FAKEFONTS is in effect, we want global real fonts to
+% override fake local ones. We handle this by brute force.
+/restore
+ { //restore
+ //systemdict /LocalFontDirectory get
+ FAKEFONTS
+ { mark
+ % We want to delete a fake font from the local directory
+ % iff the global directory now has no definition for it,
+ % or a non-fake definition.
+ 1 index dup
+ { % Stack: lfd mark lfd key ... lfd key value
+ length 1 gt
+ { % This is a real local definition; don't do anything.
+ pop
+ }
+ { % This is a fake local definition, check for global.
+ //SharedFontDirectory 1 index .knownget
+ { % A global definition exists, check for fake.
+ length 1 eq { pop } { 1 index } ifelse
+ }
+ { % No global definition, delete the local one.
+ 1 index
+ }
+ ifelse
+ }
+ ifelse
+ } forall
+ pop counttomark 2 idiv { .undef } repeat pop
+ }
+ if
+ //SharedFontDirectory exch .dictcopynew pop
+ } bind odef
+
+% ------ Halftones ------ %
+
+/.makestackdict
+ { { counttomark -1 roll } forall .dicttomark
+ } bind def
+/currenthalftone
+ { mark .currenthalftone
+ { { exch pop } % halftone
+ { /HalftoneType 1 % screen
+ { /Frequency /Angle /SpotFunction }
+ .makestackdict
+ }
+ { /HalftoneType 2 % colorscreen
+ { /RedFrequency /RedAngle /RedSpotFunction
+ /GreenFrequency /GreenAngle /GreenSpotFunction
+ /BlueFrequency /BlueAngle /BlueSpotFunction
+ /GrayFrequency /GrayAngle /GraySpotFunction
+ }
+ .makestackdict
+ }
+ }
+ exch get exec
+ } odef
+% Define sethalftone so it converts all other types to type 5.
+/.sethalftoneRGBV % <dict> <type> <keys> <keysRGBV>
+ { 4 -1 roll exch { 1 index exch get exch } forall 15 1 roll
+ 14 -2 roll mark 15 1 roll { /Gray /Blue /Green /Red }
+ { % stack: v0 v1 v2 type keys comp
+ mark
+ 2 index 0 get 8 -1 roll
+ 4 index 1 get 9 -1 roll
+ 6 index 2 get 10 -1 roll
+ % stack: type keys comp mark k0 v0 k1 v1 k2 v2
+ /HalftoneType 10 index .dicttomark
+ counttomark 2 roll
+ }
+ forall pop pop
+ /Default 1 index .dicttomark .sethalftone5
+ } bind def
+/sethalftone
+ { dup /HalftoneType get 1 sub
+ { { mark /Default 2 index .dicttomark .sethalftone5 }
+ { 1 { /Frequency /Angle /SpotFunction }
+ { /RedFrequency /RedAngle /RedSpotFunction
+ /GreenFrequency /GreenAngle /GreenSpotFunction
+ /BlueFrequency /BlueAngle /BlueSpotFunction
+ /GrayFrequency /GrayAngle /GraySpotFunction
+ } .sethalftoneRGBV
+ }
+ { mark /Default 2 index .dicttomark .sethalftone5 }
+ { 3 { /Width /Height /Thresholds }
+ { /RedWidth /RedHeight /RedThresholds
+ /GreenWidth /GreenHeight /GreenThresholds
+ /BlueWidth /BlueHeight /BlueThresholds
+ /GrayWidth /GrayHeight /GrayThresholds
+ } .sethalftoneRGBV
+ }
+ { dup .sethalftone5 }
+ } exch get exec
+ } odef
+% Redefine setscreen and setcolorscreen to recognize halftone dictionaries,
+% and to insert the Frequency and Angle into Type 1 halftones, per
+% Adobe TN 5085.
+/.fixsethalftonescreen
+ { dup /HalftoneType get 1 eq
+ { dup wcheck not { dup length .copydict } if
+ dup /Frequency 4 index put
+ dup /Angle 3 index put
+ }
+ if
+ } bind def
+/setscreen
+ { dup type /dicttype eq
+ { .fixsethalftonescreen sethalftone pop pop }
+ { //setscreen }
+ ifelse
+ } odef
+/setcolorscreen
+ { dup type /dicttype eq
+ { .fixsethalftonescreen sethalftone 11 { pop } repeat }
+ { //setcolorscreen }
+ ifelse
+ } odef
+% Redefine currentscreen and currentcolorscreen to extract the Frequency
+% and Angle from Type 1 halftones, per Adobe TN 5085.
+/.fixcurrenthalftonescreen % <dict> .fix... <freq> <angle> <proc>
+ { dup /HalftoneType get 1 eq
+ { dup /Frequency get 1 index /Angle get }
+ { 60 0 }
+ ifelse 3 2 roll
+ } bind def
+/currentscreen
+ { .currenthalftone
+ { { .fixcurrenthalftonescreen } % halftone
+ { } % screen
+ { 12 3 roll 9 { pop } repeat % colorscreen
+ dup type /dicttype eq { .fixcurrenthalftonescreen } if
+ }
+ }
+ exch get exec
+ } odef
+/currentcolorscreen
+ { .currenthalftone
+ { { .fixcurrenthalftonescreen 3 copy 6 copy } % halftone
+ { 3 copy 6 copy } % screen
+ { } % colorscreen
+ }
+ exch get exec
+ } odef
+
+% ------ User objects ------ %
+
+/.localarray where
+ { pop }
+ { /.localarray
+ { currentglobal false setglobal
+ exch array exch setglobal
+ } bind def
+ }
+ifelse
+/defineuserobject
+ { userdict /.UserObjects known
+ { 1 index userdict /.UserObjects get length ge
+ { 1 index 1 add .localarray userdict /.UserObjects get
+ 1 index copy pop
+ userdict /.UserObjects 3 -1 roll put
+ }
+ if
+ }
+ { userdict /.UserObjects 3 index 1 add .localarray put
+ }
+ ifelse
+ userdict /.UserObjects get 3 1 roll put
+ } odef
+/execuserobject
+ { userdict /.UserObjects get exch get exec
+ } odef
+/undefineuserobject
+ { userdict /.UserObjects get exch null put
+ } odef
+
+% ------ User paths ------ %
+
+% We define upath carefully so it won't leave garbage on the stack
+% if an error occurs.
+/upath
+ { [
+ { 1 index {/ucache cvx} if true .pathbbox /setbbox cvx
+ {/moveto cvx} {/lineto cvx} {/curveto cvx} {/closepath cvx}
+ pathforall ]
+ }
+ .internalstopped
+ { cleartomark /upath load $error /errorname get signalerror
+ }
+ if cvx exch pop
+ } odef
+
+% Dummy definitions for cache control operators
+
+/ucachestatus
+ { mark 0 0 0 0 0 } odef
+/setucacheparams
+ { cleartomark } odef
+
+% ------ Miscellaneous ------ %
+
+/undef /.undef load def
+
+end % level2dict
diff --git a/pstoraster/gs_fform.ps b/pstoraster/gs_fform.ps
new file mode 100644
index 000000000..13d6547d2
--- /dev/null
+++ b/pstoraster/gs_fform.ps
@@ -0,0 +1,115 @@
+% Copyright (C) 1995, 1996 Aladdin Enterprises. All rights reserved.
+%
+% This file is part of GNU Ghostscript.
+%
+% GNU Ghostscript is distributed in the hope that it will be useful, but
+% WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+% anyone for the consequences of using it or for whether it serves any
+% particular purpose or works at all, unless he says so in writing. Refer to
+% the GNU General Public License for full details.
+%
+% Everyone is granted permission to copy, modify and redistribute GNU
+% Ghostscript, but only under the conditions described in the GNU General
+% Public License. A copy of this license is supposed to have been given to
+% you along with GNU Ghostscript so you can know your rights and
+% responsibilities. It should be in a file named COPYING. Among other
+% things, the copyright notice and this notice must be preserved on all
+% copies.
+%
+% Aladdin Enterprises is not affiliated with the Free Software Foundation or
+% the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+% does not depend on any other GNU software.
+
+% gs_fform.ps
+% Monochrome Form caching implemented in PostScript.
+
+% This implementation is pretty unreasonable:
+% - It doesn't remember transparent pixels.
+% - It reduces everything to black and white.
+% - It doesn't handle halftone or Pattern phasing.
+% However, it's good enough to produce some useful output.
+
+% In order to prevent restore from clearing the cache, we explicitly
+% push the cache entries on the stack before a restore and reinstall them.
+/formcachedict 20 dict def
+currentglobal true setglobal
+/restore
+ { mark formcachedict { } forall
+ counttomark 1 add index { restore } .internalstopped
+ { cleartomark restore }
+ { counttomark 2 idiv { formcachedict 3 1 roll put } repeat pop pop }
+ ifelse
+ } bind odef
+
+/.form.buffer 65000 string def
+/execform
+ { dup /Implementation known not
+ { dup /FormType get 1 ne { /rangecheck signalerror } if
+ formcachedict 1 index .knownget not
+ { currentglobal true setglobal
+ % Stack: form global
+ 10 dict
+ dup /ImageType 1 put
+ dup /ImageMatrix [0 0 0 0 0 0] put
+ dup /DataSource
+ { % Stack: y (impl is on dict stack)
+ Height 1 index sub
+ //.form.buffer length Width 7 add 8 idiv idiv .min
+ 1 index add exch
+ Device exch //.form.buffer copyscanlines
+ }
+ put
+ dup /BitsPerComponent 1 put
+ dup /Decode [0 1] put
+ dup /Device null put
+ % Stack: form global impl
+ formcachedict 3 index 2 index put
+ exch setglobal
+ }
+ if 1 index /Implementation 3 -1 roll put
+ }
+ if
+ gsave dup /Matrix get concat
+ dup /Implementation get
+ % Check whether we can use the cached value.
+ % Stack: form implementation
+ dup /ImageMatrix get matrix currentmatrix
+ true 0 1 3
+ { % Stack: form impl cachemat curmat true index
+ 3 index 1 index get exch 3 index exch get ne { pop false exit } if
+ }
+ for % Stack: form impl cachemat curmat valid
+ exch pop exch pop not
+ { % Cache is invalid. Execute the Form and save the bits.
+ gsave begin
+ currentglobal exch true setglobal
+ ImageMatrix currentmatrix pop
+ dup /BBox get aload pop
+ exch 3 index sub exch 2 index sub rectclip
+ % Make the cache device.
+ clippath gsave matrix setmatrix pathbbox grestore
+ % We now have the bounding box in device space.
+ 2 { 4 -1 roll floor cvi } repeat
+ 2 { 4 -1 roll ceiling cvi } repeat
+ 2 index sub /Height exch def
+ 2 index sub /Width exch def
+ ImageMatrix aload pop
+ exch 7 index sub exch 6 index sub
+ 6 array astore
+ 3 1 roll pop pop
+ dup ImageMatrix copy pop
+ Width Height <00 ff> makeimagedevice
+ /Device 1 index def
+ nulldevice setdevice initgraphics
+ exch setglobal
+ dup dup /PaintProc get exec
+ nulldevice grestore currentdict end
+ }
+ if
+ % Now paint the bits.
+ % Stack: form implementation
+ /DeviceGray setcolorspace dup begin 0 exch image end pop
+ pop grestore
+ } odef
+
+setglobal
diff --git a/pstoraster/gs_fonts.ps b/pstoraster/gs_fonts.ps
new file mode 100644
index 000000000..ddcd93cdb
--- /dev/null
+++ b/pstoraster/gs_fonts.ps
@@ -0,0 +1,797 @@
+% Copyright (C) 1990, 1995, 1996 Aladdin Enterprises. All rights reserved.
+%
+% This file is part of GNU Ghostscript.
+%
+% GNU Ghostscript is distributed in the hope that it will be useful, but
+% WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+% anyone for the consequences of using it or for whether it serves any
+% particular purpose or works at all, unless he says so in writing. Refer to
+% the GNU General Public License for full details.
+%
+% Everyone is granted permission to copy, modify and redistribute GNU
+% Ghostscript, but only under the conditions described in the GNU General
+% Public License. A copy of this license is supposed to have been given to
+% you along with GNU Ghostscript so you can know your rights and
+% responsibilities. It should be in a file named COPYING. Among other
+% things, the copyright notice and this notice must be preserved on all
+% copies.
+%
+% Aladdin Enterprises is not affiliated with the Free Software Foundation or
+% the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+% does not depend on any other GNU software.
+
+% Font initialization and management code.
+
+% Define the default font.
+/defaultfontname /Courier def
+
+% Define the name of the font map file.
+/defaultfontmap (Fontmap) def
+
+% ------ End of editable parameters ------ %
+
+% If DISKFONTS is true, we load individual CharStrings as they are needed.
+% (This is intended primarily for machines with very small memories.)
+% In this case, we define another dictionary, parallel to FontDirectory,
+% that retains an open file for every font loaded.
+/FontFileDirectory 10 dict def
+
+% Split up a search path into individual directories or files.
+/.pathlist % <path> .pathlist <dir1|file1> ...
+ { { dup length 0 eq { pop exit } if
+ .filenamelistseparator search not { exit } if
+ exch pop exch
+ }
+ loop
+ } bind def
+
+% Load a font name -> font file name map.
+userdict /Fontmap FontDirectory maxlength dict put
+/.loadFontmap % <file> .loadFontmap -
+ { % We would like to simply execute .definefontmap as we read,
+ % but we have to maintain backward compatibility with an older
+ % specification that makes later entries override earlier.
+ 50 dict exch
+ { dup token not { closefile exit } if
+ % stack: <file> fontname
+ % This is a hack to get around the absurd habit of MS-DOS editors
+ % of adding an EOF character at the end of the file.
+ dup (\032) eq { pop closefile exit } if
+ 1 index token not
+ { (Fontmap entry for ) print dup =only
+ ( has no associated file or alias name! Giving up.\n) print flush
+ {.loadFontmap} 0 get 1 .quit
+ } if
+ dup type dup /stringtype eq exch /nametype eq or not
+ { (Fontmap entry for ) print 1 index =only
+ ( has an invalid file or alias name! Giving up.\n) print flush
+ {.loadFontmap} 0 get 1 .quit
+ } if
+ % stack: dict file fontname filename|aliasname
+ % Read and pop tokens until a semicolon.
+ { 2 index token not
+ { (Fontmap entry for ) print 1 index =only
+ ( ends prematurely! Giving up.\n) print flush
+ {.loadFontmap} 0 get 1 .quit
+ } if
+ dup /; eq { pop 3 index 3 1 roll .growput exit } if
+ pop
+ } loop
+ } loop
+ { .definefontmap } forall
+ } bind def
+% Add an entry in Fontmap. We redefine this if the Level 2
+% resource machinery is loaded.
+/.definefontmap % <fontname> <file|alias> .definefontmap -
+ { % Since Fontmap is global, make sure the values are storable.
+ .currentglobal 3 1 roll true .setglobal
+ dup type /stringtype eq
+ { dup .gcheck not { dup length string copy } if
+ }
+ if
+ Fontmap 3 -1 roll 2 copy .knownget
+ { % Add an element to the end of the existing value,
+ % unless it's the same as the current last element.
+ mark exch aload pop counttomark 4 add -1 roll
+ 2 copy eq { cleartomark pop pop } { ] readonly .growput } ifelse
+ }
+ { % Make a new entry.
+ mark 4 -1 roll ] readonly .growput
+ }
+ ifelse .setglobal
+ } bind def
+
+% Parse a font file just enough to find the FontName or FontType.
+/.findfontvalue % <file> <key> .findfontvalue <value> true
+ % <file> <key> .findfontvalue false
+ % Closes the file in either case.
+ { exch dup read not { -1 } if
+ 2 copy unread 16#80 eq
+ { dup (xxxxxx) readstring pop pop } % skip .PFB header
+ if
+ % Stack: key file
+ { dup token not { false exit } if % end of file
+ dup /eexec eq { pop false exit } if % reached eexec section
+ dup /Subrs eq { pop false exit } if % Subrs without eexec
+ dup /CharStrings eq { pop false exit } if % CharStrings without eexec
+ dup 3 index eq
+ { xcheck not { dup token exit } if } % found key
+ { pop }
+ ifelse
+ } loop
+ % Stack: key file value true (or)
+ % Stack: key file false
+ dup { 4 } { 3 } ifelse -2 roll closefile pop
+ } bind def
+/.findfontname
+ { /FontName .findfontvalue
+ } bind def
+
+% If there is no FONTPATH, try to get one from the environment.
+NOFONTPATH { /FONTPATH () def } if
+/FONTPATH where
+ { pop }
+ { /FONTPATH (GS_FONTPATH) getenv not { () } if def }
+ifelse
+FONTPATH length 0 eq { (%END FONTPATH) .skipeof } if
+/FONTPATH [ FONTPATH .pathlist ] def
+
+% Scan directories looking for plausible fonts. "Plausible" means that
+% the file begins with %!PS-AdobeFont or %!FontType1, or with \200\001
+% followed by four arbitrary bytes and then either of these strings.
+% To speed up the search, we skip any file whose name appears in
+% the Fontmap (with any extension and upper/lower case variation) already,
+% and any file whose extension definitely indicates it is not a font.
+%
+% NOTE: The current implementation of this procedure is somewhat Unix/DOS-
+% specific. It assumes that '/' and '\' are directory separators, and that
+% the part of a file name following the last '.' is the extension.
+%
+/.lowerstring % <string> .lowerstring <lowerstring>
+ { 0 1 2 index length 1 sub
+ { 2 copy get dup 65 ge exch 90 le and
+ { 2 copy 2 copy get 32 add put }
+ if pop
+ }
+ for
+ } bind def
+/.splitfilename % <dir.../base.extn> .basename <base> <extn>
+ { { (/) search { true } { (\\) search } ifelse
+ { pop pop }
+ { exit }
+ ifelse
+ }
+ loop
+ dup { (.) search { pop pop } { exit } ifelse } loop
+ 2 copy eq
+ { pop () }
+ { exch dup length 2 index length 1 add sub 0 exch getinterval exch }
+ ifelse
+% Following is debugging code.
+% (*** Split => ) print 2 copy exch ==only ( ) print ==only
+% ( ***\n) print flush
+ } bind def
+/.scanfontdict 1 dict def % establish a binding
+/.scanfontbegin
+ { % Construct the table of all file names already in Fontmap.
+ currentglobal true setglobal
+ .scanfontdict dup maxlength Fontmap length 2 add .max .setmaxlength
+ Fontmap
+ { exch pop
+ { dup type /stringtype eq
+ { .splitfilename pop =string copy .lowerstring cvn
+ .scanfontdict exch true put
+ }
+ { pop
+ }
+ ifelse
+ }
+ forall
+ }
+ forall
+ setglobal
+ } bind def
+/.scanfontskip mark
+ % Strings are converted to names anyway, so....
+ /afm true
+ /bat true
+ /c true
+ /cmd true
+ /com true
+ /dll true
+ /doc true
+ /drv true
+ /exe true
+ /fon true
+ /fot true
+ /h true
+ /o true
+ /obj true
+ /pfm true
+ /txt true
+.dicttomark def
+/.scan1fontstring 128 string def
+/.scanfontheaders [(%!PS-AdobeFont*) (%!FontType1*)] def
+0 .scanfontheaders { length max } forall 6 add % extra for PFB header
+/.scan1fontfirst exch string def
+/.scanfontdir % <dirname> .scanfontdir -
+ { currentglobal exch true setglobal
+ QUIET not { (Scanning ) print dup print ( for fonts...) print flush } if
+ (*) 2 copy .filenamedirseparator
+ dup (\\) eq { pop (\\\\) } if % double \ for pattern match
+ exch concatstrings concatstrings
+ 0 0 0 4 -1 roll % found scanned files
+ { % stack: <fontcount> <scancount> <filecount> <filename>
+ exch 1 add exch % increment filecount
+ dup .splitfilename .lowerstring
+ % stack: <fontcount> <scancount> <filecount+1> <filename>
+ % <BASE> <ext>
+ .scanfontskip exch known exch .scanfontdict exch known or
+ { pop
+ % stack: <fontcount> <scancount> <filecount+1>
+ }
+ { 3 -1 roll 1 add 3 1 roll
+ % stack: <fontcount> <scancount+1> <filecount+1> <filename>
+ dup (r) { file } .internalstopped
+ { pop pop null ()
+ % stack: <fontcount> <scancount+1> <filecount+1> <filename>
+ % null ()
+ }
+ {
+ % On some platforms, the file operator will open directories,
+ % but an error will occur if we try to read from one.
+ % Handle this possibility here.
+ dup .scan1fontfirst { readstring } .internalstopped
+ { pop pop () }
+ { pop }
+ ifelse
+ % stack: <fontcount> <scancount+1> <filecount+1>
+ % <filename> <file> <header>
+ }
+ ifelse
+ % Check for PFB file header.
+ dup (\200\001????*) .stringmatch
+ { dup length 6 sub 6 exch getinterval }
+ if
+ % Check for font file headers.
+ false .scanfontheaders
+ { 2 index exch .stringmatch or
+ }
+ forall exch pop
+ { % stack: <fontcount> <scancount+1> <filecount+1> <filename>
+ % <file>
+ dup 0 setfileposition .findfontname
+ { dup Fontmap exch known
+ { pop pop
+ }
+ { exch copystring exch
+ DEBUG { ( ) print dup =only } if
+ 1 index .definefontmap
+ .splitfilename pop true .scanfontdict 3 1 roll .growput
+ % Increment fontcount.
+ 3 -1 roll 1 add 3 1 roll
+ }
+ ifelse
+ }
+ { pop
+ }
+ ifelse
+ }
+ % .findfontname will have done a closefile in the above case.
+ { dup null eq { pop } { closefile } ifelse pop
+ }
+ ifelse
+ }
+ ifelse
+ }
+ .scan1fontstring filenameforall
+ QUIET
+ { pop pop pop }
+ { ( ) print =only ( files, ) print =only ( scanned, ) print
+ =only ( new fonts.\n) print flush
+ }
+ ifelse
+ setglobal
+ } bind def
+
+%END FONTPATH
+
+% Define definefont. This is a procedure built on a set of operators
+% that do all the error checking and key insertion.
+mark
+ /.buildfont0 where { pop 0 /.buildfont0 cvx } if
+ /.buildfont1 where { pop 1 /.buildfont1 cvx } if
+ /.buildfont3 where { pop 3 /.buildfont3 cvx } if
+ /.buildfont4 where { pop 4 /.buildfont4 cvx } if
+ /.buildfont42 where { pop 42 /.buildfont42 cvx } if
+.dicttomark /buildfontdict exch def
+/.growfontdict
+ { % Grow the font dictionary, if necessary, to ensure room for an
+ % added entry, making sure there is at least one slot left for FID.
+ dup maxlength 1 index length sub 2 lt
+ { dup dup wcheck
+ { .growdict }
+ { .growdictlength dict .copydict }
+ ifelse
+ }
+ { dup wcheck not { dup maxlength dict .copydict } if
+ }
+ ifelse
+ } bind def
+/definefont
+ { 1 dict begin count /d exch def % save stack depth in case of error
+ { % Check for disabled platform fonts.
+ NOPLATFONTS
+ { % Make sure we leave room for FID.
+ .growfontdict dup /ExactSize 0 put
+ }
+ { % Hack: if the Encoding looks like it might be the
+ % Symbol or Dingbats encoding, load those now (for the
+ % benefit of platform font matching) just in case
+ % the font didn't actually reference them.
+ dup /Encoding get length 65 ge
+ { dup /Encoding get 64 get
+ dup /congruent eq { SymbolEncoding pop } if
+ /a9 eq { DingbatsEncoding pop } if
+ }
+ if
+ }
+ ifelse
+ dup /FontType get //buildfontdict exch get exec
+ DISKFONTS
+ { FontFileDirectory 2 index known
+ { dup /FontFile FontFileDirectory 4 index get .growput
+ }
+ if
+ }
+ if
+ readonly
+ }
+ stopped
+ { count d sub { pop } repeat end /invalidfont signalerror
+ }
+ { end % stack: name fontdict
+ % If the current allocation mode is global, also enter
+ % the font in LocalFontDirectory.
+ .currentglobal
+ { //systemdict /LocalFontDirectory .knownget
+ { 2 index 2 index .growput }
+ if
+ }
+ if
+ dup FontDirectory 4 -2 roll .growput
+ }
+ ifelse
+ } odef
+
+% Define a procedure for defining aliased fonts.
+% We can't just copy the font (or even use the same font unchanged),
+% because a significant number of PostScript files assume that
+% the FontName of a font is the same as the font resource name or
+% the key in [Shared]FontDirectory; on the other hand, some Adobe files
+% rely on the FontName of a substituted font *not* being the same as
+% the requested resource name. We address this issue heuristically:
+% we substitute the new name iff the font name doesn't have MM in it.
+/.aliasfont % <name> <font> .aliasfont <newFont>
+ { .currentglobal 3 1 roll dup .gcheck .setglobal
+ dup length 2 add dict
+ dup 3 -1 roll { 1 index /FID eq { pop pop } { put dup } ifelse } forall
+ % Stack: global fontname newfont newfont.
+ % We might be defining a global font whose FontName
+ % is a local string. This is weird, but legal,
+ % and doesn't cause problems anywhere else.
+ % To avoid any possible problems, do a cvn.
+ 2 index =string cvs (MM) search
+ { pop pop pop pop
+ }
+ { /FontName exch dup type /stringtype eq { cvn } if put
+ }
+ ifelse
+ //systemdict /definefont get exec % Don't bind, since Level 2
+ % redefines definefont
+ exch .setglobal
+ } odef % so findfont will bind it
+
+% Define .loadfontfile for loading a font. If we recognize Type 1 and/or
+% TrueType fonts, gs_type1.ps and/or gs_ttf.ps will redefine this.
+/.loadfontfile { cvx exec } bind def
+/.loadfont
+ { % Some buggy fonts leave extra junk on the stack,
+ % so we have to make a closure that records the stack depth
+ % in a fail-safe way.
+ /.loadfontfile cvx count 1 sub 2 packedarray cvx exec
+ count exch sub { pop } repeat
+ } bind def
+
+% Find an alternate font to substitute for an unknown one.
+% We go to some trouble to parse the font name and extract
+% properties from it. Later entries take priority over earlier.
+/.substitutefaces [
+ % Guess at suitable substitutions for random unknown fonts.
+ [(Grot) /Times]
+ [(Roman) /Times]
+ [(Book) /NewCenturySchlbk]
+ % If the family name appears in the font name,
+ % use a font from that family.
+ [(Arial) /Helvetica]
+ [(Avant) /AvantGarde]
+ [(Bookman) /Bookman]
+ [(Century) /NewCenturySchlbk]
+ [(Cour) /Courier]
+ [(Geneva) /Helvetica]
+ [(Helv) /Helvetica]
+ [(NewYork) /Times]
+ [(Pala) /Palatino]
+ [(Sans) /Helvetica]
+ [(Schlbk) /NewCenturySchlbk]
+ [(Serif) /Times]
+ [(Swiss) /Helvetica]
+ [(Times) /Times]
+ % Substitute for Adobe Multiple Master fonts.
+ [(Myriad) /Times]
+ [(Minion) /Helvetica]
+ % Condensed or narrow fonts map to the only narrow family we have.
+ [(Cond) /Helvetica-Narrow]
+ [(Narrow) /Helvetica-Narrow]
+ % If the font wants to be monospace, use Courier.
+ [(Monospace) /Courier]
+ [(Typewriter) /Courier]
+] readonly def
+/.substituteproperties [
+ [(It) 1] [(Oblique) 1]
+ [(Bd) 2] [(Bold) 2] [(bold) 2] [(Demi) 2] [(Heavy) 2] [(Sb) 2]
+] readonly def
+/.substitutefamilies mark
+ /AvantGarde
+ {/AvantGarde-Book /AvantGarde-BookOblique
+ /AvantGarde-Demi /AvantGarde-DemiOblique}
+ /Bookman
+ {/Bookman-Demi /Bookman-DemiItalic /Bookman-Light /Bookman-LightItalic}
+ /Courier
+ {/Courier /Courier-Oblique /Courier-Bold /Courier-BoldOblique}
+ /Helvetica
+ {/Helvetica /Helvetica-Oblique /Helvetica-Bold /Helvetica-BoldOblique}
+ /Helvetica-Narrow
+ {/Helvetica-Narrow /Helvetica-Narrow-Oblique
+ /Helvetica-Narrow-Bold /Helvetica-Narrow-BoldOblique}
+ /NewCenturySchlbk
+ {/NewCenturySchlbk-Roman /NewCenturySchlbk-Italic
+ /NewCenturySchlbk-Bold /NewCenturySchlbk-BoldItalic}
+ /Palatino
+ {/Palatino-Roman /Palatino-Italic /Palatino-Bold /Palatino-BoldItalic}
+ /Times
+ {/Times-Roman /Times-Italic /Times-Bold /Times-BoldItalic}
+.dicttomark readonly def
+/.substitutefont % <fontname> .substitutefont <altname>
+ { % Look for properties and/or a face name in the font name.
+ % If we find any, use Helvetica as the base font;
+ % otherwise, use the default font.
+ % Note that the "substituted" font name may be the same as
+ % the requested one; the caller must check this.
+ dup length string cvs
+ {defaultfontname /Helvetica-Oblique /Helvetica-Bold /Helvetica-BoldOblique}
+ exch 0 exch % stack: fontname facelist properties fontname
+ % Look for a face name.
+ .substitutefaces
+ { 2 copy 0 get search
+ { pop pop pop 1 get .substitutefamilies exch get
+ 4 -1 roll pop 3 1 roll
+ }
+ { pop pop
+ }
+ ifelse
+ }
+ forall
+ .substituteproperties
+ { 2 copy 0 get search
+ { pop pop pop 1 get 3 -1 roll or exch }
+ { pop pop }
+ ifelse
+ }
+ forall pop get
+ % If SUBSTFONT is defined, use it.
+ /SUBSTFONT where
+ { pop pop /SUBSTFONT load cvn }
+ { exec }
+ ifelse
+ % Only accept fonts known in the Fontmap.
+ Fontmap 1 index known not { pop defaultfontname } if
+ } bind def
+
+% If requested, make (and recognize) fake entries in FontDirectory for fonts
+% present in Fontmap but not actually loaded. Thanks to Ray Johnston for
+% the idea behind this code.
+FAKEFONTS not { (%END FAKEFONTS) .skipeof } if
+
+% We use the presence or absence of the FontMatrix key to indicate whether
+% a font is real or fake.
+
+/definefont % <name> <font> definefont <font>
+ { dup /FontMatrix known not { /FontName get findfont } if
+ //definefont
+ } bind odef
+
+/scalefont % <font> <scale> scalefont <font>
+ { exch dup /FontMatrix known not { /FontName get findfont } if
+ exch //scalefont
+ } bind odef
+
+/makefont % <font> <matrix> makefont <font>
+ { exch dup /FontMatrix known not { /FontName get findfont } if
+ exch //makefont
+ } bind def
+
+/setfont % <font> setfont -
+ { dup /FontMatrix known not { /FontName get findfont } if
+ //setfont
+ } bind odef
+
+%END FAKEFONTS
+
+% Define findfont so it tries to load a font if it's not found.
+% The Red Book requires that findfont be a procedure, not an operator.
+/findfont
+ { mark exch
+ { .dofindfont
+ } stopped
+ { counttomark 1 sub { pop } repeat exch pop stop
+ }
+ { % Define any needed aliases.
+ counttomark 1 sub { .aliasfont } repeat
+ exch pop
+ }
+ ifelse
+ } bind def
+% Check whether the font name we are about to look for is already on the list
+% of aliases we're accumulating; if so, cause an error.
+/.checkalias % -mark- <alias1> ... <name> .checkalias <<same>>
+ { counttomark 1 sub -1 1
+ { index 1 index eq
+ { pop QUIET not
+ { (Unable to substitute for font.\n) print flush
+ } if
+ /findfont cvx /invalidfont signalerror
+ }
+ if
+ }
+ for
+ } bind def
+% Get a (non-fake) font if present in a FontDirectory.
+/.fontknownget % <fontdir> <fontname> .fontknownget <font> true
+ % <fontdir> <fontname> .fontknownget false
+ { .knownget
+ { FAKEFONTS
+ { dup /FontMatrix known { true } { pop false } ifelse }
+ { true }
+ ifelse
+ }
+ { false
+ }
+ ifelse
+ } bind def
+% Do the work of findfont, including substitution, defaulting, and
+% scanning of FONTPATH.
+/.dofindfont % <fontname> .dofindfont <font>
+ { { .tryfindfont { exit } if
+ % We didn't find the font. If we haven't scanned
+ % all the directories in FONTPATH, scan the next one now,
+ % and look for the font again.
+ null 0 1 FONTPATH length 1 sub
+ { FONTPATH 1 index get null ne { exch pop exit } if pop
+ }
+ for dup null ne
+ { dup 0 eq { .scanfontbegin } if
+ FONTPATH 1 index get .scanfontdir
+ FONTPATH exch null put
+ % Start over with an empty alias list.
+ counttomark 1 sub { pop } repeat
+ .dofindfont exit
+ }
+ if pop
+ % No luck, substitute for the font.
+ dup defaultfontname eq
+ { QUIET not
+ { (Unable to load default font ) print
+ dup =only (! Giving up.\n) print flush
+ }
+ if /findfont cvx /invalidfont signalerror
+ }
+ if dup .substitutefont
+ 2 copy eq { pop defaultfontname } if
+ .checkalias
+ QUIET not
+ { (Substituting font ) print dup =only ( for ) print
+ 1 index =only (.\n) print flush
+ }
+ if
+ }
+ loop
+ } bind def
+% Try to find a font using only the present contents of Fontmap.
+/.tryfindfont % <fontname> .tryfindfont <font> true
+ % <fontname> .tryfindfont false
+ { FontDirectory 1 index .fontknownget
+ { % Already loaded
+ exch pop true
+ }
+ { dup Fontmap exch .knownget not
+ { % Unknown font name
+ false
+ }
+
+ { % Try each element of the Fontmap in turn.
+ false exch % (in case we exhaust the list)
+ { exch pop
+ dup type /nametype eq
+ { % Font alias
+ .checkalias .tryfindfont exit
+ }
+ { dup dup type dup /arraytype eq exch /packedarraytype eq or exch xcheck and
+ { % Font with a procedural definition
+ exec % The procedure will load the font.
+ % Check to make sure this really happened.
+ FontDirectory 1 index .knownget
+ { exch pop true exit }
+ if
+ }
+ { % Font file name
+ .loadfontloop { true exit } if
+ }
+ ifelse
+ }
+ ifelse false
+ }
+ forall
+ }
+ ifelse
+ }
+ ifelse
+ } bind def
+% Attempt to load a font from a file.
+/.loadfontloop % <filename> .loadfontloop <font> true
+ % <filename> .loadfontloop false
+ { % See above regarding the use of 'loop'.
+
+ {
+ % Can we open the file?
+ findlibfile not
+ { QUIET not
+ { (Can't find \(or can't open\) font file ) print dup print
+ (.\n) print flush
+ }
+ if pop false exit
+ }
+ if
+
+ % Stack: fontname fontfilename fontfile
+ DISKFONTS
+ { .currentglobal true .setglobal
+ 2 index (r) file
+ FontFileDirectory exch 5 index exch .growput
+ .setglobal
+ }
+ if
+ QUIET not
+ { (Loading ) print 2 index =only
+ ( font from ) print 1 index print (... ) print flush
+ }
+ if
+ % If LOCALFONTS isn't set, load the font into local or global
+ % VM according to FontType; if LOCALFONTS is set, load the font
+ % into the current VM, which is what Adobe printers (but not
+ % DPS or CPSI) do.
+ LOCALFONTS { false } { /setglobal where } ifelse
+ { pop /FontType .findfontvalue { 1 eq } { false } ifelse
+ % .setglobal, like setglobal, aliases FontDirectory to
+ % GlobalFontDirectory if appropriate. However, we mustn't
+ % allow the current version of .setglobal to be bound in,
+ % because it's different depending on language level.
+ .currentglobal exch /.setglobal load exec
+ % Remove the fake definition, if any.
+ FontDirectory 3 index .undef
+ 1 index (r) file .loadfont FontDirectory exch
+ /.setglobal load exec
+ }
+ { .loadfont FontDirectory
+ }
+ ifelse
+ % Stack: fontname fontfilename fontdirectory
+ QUIET not
+ { //systemdict /level2dict known
+ { .currentglobal false .setglobal vmstatus
+ true .setglobal vmstatus 3 -1 roll pop
+ 6 -1 roll .setglobal 5
+ }
+ { vmstatus 3
+ }
+ ifelse { =only ( ) print } repeat
+ (done.\n) print flush
+ } if
+
+ % Check to make sure the font was actually loaded.
+ dup 3 index .fontknownget
+ { 4 1 roll pop pop pop true exit } if
+
+ % Maybe the file had a different FontName.
+ % See if we can get a FontName from the file, and if so,
+ % whether a font by that name exists now.
+ exch (r) file .findfontname
+ { 2 copy .fontknownget
+ { % Yes. Stack: origfontname fontdirectory filefontname fontdict
+ 3 -1 roll pop exch
+ QUIET
+ { pop
+ }
+ { (Using ) print =only
+ ( font for ) print 1 index =only
+ (.\n) print flush
+ }
+ ifelse true exit
+ }
+ if pop
+ }
+ if pop
+
+ % The font definitely did not load correctly.
+ QUIET not
+ { (Loading ) print dup =only
+ ( font failed.\n) print flush
+ } if
+ false exit
+
+ } loop % end of loop
+
+ } bind def
+
+% Define a procedure to load all known fonts.
+% This isn't likely to be very useful.
+/loadallfonts
+ { Fontmap { pop findfont pop } forall
+ } bind def
+
+% If requested, load all the fonts defined in the Fontmap into FontDirectory
+% as "fake" fonts i.e., font dicts with only FontName defined.
+% We must ensure that this happens in both global and local directories.
+/.definefakefonts
+ {
+ }
+ { (gs_fonts FAKEFONTS) VMDEBUG
+ 2
+ { .currentglobal not .setglobal
+ Fontmap
+ { pop dup type /stringtype eq { cvn } if
+ FontDirectory 1 index known not
+ { 1 dict dup /FontName 3 index put
+ FontDirectory 3 1 roll put
+ }
+ if
+ }
+ forall
+ }
+ repeat
+ }
+FAKEFONTS { exch } if pop def % don't bind, .current/setglobal get redefined
+
+% Install initial fonts from Fontmap.
+/.loadinitialfonts
+ { NOFONTMAP not
+ { /FONTMAP where
+ { pop [ FONTMAP .pathlist ]
+ { dup VMDEBUG findlibfile
+ { exch pop .loadFontmap }
+ { /undefinedfilename signalerror }
+ ifelse
+ }
+ }
+ { LIBPATH
+ { defaultfontmap 2 copy .filenamedirseparator
+ exch concatstrings concatstrings dup VMDEBUG
+ (r) { file } .internalstopped
+ { pop pop } { .loadFontmap } ifelse
+ }
+ }
+ ifelse forall
+ }
+ if
+ .definefakefonts
+ } def % don't bind, .current/setglobal get redefined
diff --git a/pstoraster/gs_init.ps b/pstoraster/gs_init.ps
new file mode 100644
index 000000000..452e9f679
--- /dev/null
+++ b/pstoraster/gs_init.ps
@@ -0,0 +1,1271 @@
+% Copyright (C) 1989, 1996 Aladdin Enterprises. All rights reserved.
+%
+% This file is part of GNU Ghostscript.
+%
+% GNU Ghostscript is distributed in the hope that it will be useful, but
+% WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+% anyone for the consequences of using it or for whether it serves any
+% particular purpose or works at all, unless he says so in writing. Refer to
+% the GNU General Public License for full details.
+%
+% Everyone is granted permission to copy, modify and redistribute GNU
+% Ghostscript, but only under the conditions described in the GNU General
+% Public License. A copy of this license is supposed to have been given to
+% you along with GNU Ghostscript so you can know your rights and
+% responsibilities. It should be in a file named COPYING. Among other
+% things, the copyright notice and this notice must be preserved on all
+% copies.
+%
+% Aladdin Enterprises is not affiliated with the Free Software Foundation or
+% the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+% does not depend on any other GNU software.
+
+% Initialization file for the interpreter.
+% When this is run, systemdict is still writable.
+
+% Comment lines of the form
+% %% Replace <n> <file(s)>
+% indicate places where the next <n> lines should be replaced by
+% the contents of <file(s)>, when creating a single merged init file.
+
+% The interpreter can call out to PostScript code. All procedures
+% called in this way, and no other procedures defined in these
+% initialization files, have names that begin with %, e.g.,
+% (%Type1BuildChar) cvn.
+
+% Check the interpreter revision. NOTE: the interpreter code requires
+% that the first non-comment token in this file be an integer.
+40000
+dup revision ne
+ { (pstoraster: Interpreter revision \() print revision 10 string cvs print
+ (\) does not match gs_init.ps revision \() print 10 string cvs print
+ (\).\n) print flush null 1 .quit
+ }
+if pop
+
+% Acquire userdict, and set its length if necessary.
+/userdict where
+ { pop userdict maxlength 0 eq }
+ { true }
+ifelse
+ { % userdict wasn't already set up by iinit.c.
+ /userdict
+ currentdict dup 200 .setmaxlength % userdict
+ systemdict begin def % can't use 'put', userdict is local
+ }
+ { systemdict begin
+ }
+ifelse
+
+% Define dummy local/global operators if needed.
+systemdict /.setglobal known
+ { true .setglobal
+ }
+ { /.setglobal { pop } bind def
+ /.currentglobal { false } bind def
+ /.gcheck { pop false } bind def
+ }
+ifelse
+
+% Define .languagelevel if needed.
+systemdict /.languagelevel known not { /.languagelevel 1 def } if
+
+% Optionally choose a default paper size other than U.S. letter.
+% (a4) /PAPERSIZE where { pop pop } { /PAPERSIZE exch def } ifelse
+
+% Turn on array packing for the rest of initialization.
+true setpacking
+
+% Define the old MS-DOS EOF character as a no-op.
+% This is a hack to get around the absurd habit of MS-DOS editors
+% of adding an EOF character at the end of the file.
+<1a> cvn { } def
+
+% Acquire the debugging flags.
+currentdict /DEBUG known /DEBUG exch def
+ /VMDEBUG
+ DEBUG {{print mark
+ systemdict /level2dict known
+ { .currentglobal dup false .setglobal vmstatus
+ true .setglobal vmstatus 3 -1 roll pop
+ 6 -2 roll pop .setglobal
+ }
+ { vmstatus 3 -1 roll pop
+ }
+ ifelse usertime 16#fffff and counttomark
+ { ( ) print ( ) cvs print }
+ repeat pop
+ ( ) print systemdict length ( ) cvs print
+ ( ) print countdictstack ( ) cvs print
+ ( <) print count ( ) cvs print (>\n) print flush
+ }}
+ {{pop
+ }}
+ ifelse
+ def
+
+currentdict /DELAYBIND known /DELAYBIND exch def
+currentdict /DISKFONTS known /DISKFONTS exch def
+currentdict /ESTACKPRINT known /ESTACKPRINT exch def
+currentdict /FAKEFONTS known /FAKEFONTS exch def
+currentdict /FIXEDMEDIA known /FIXEDMEDIA exch def
+currentdict /FIXEDRESOLUTION known /FIXEDRESOLUTION exch def
+currentdict /LOCALFONTS known /LOCALFONTS exch def
+currentdict /NOBIND known /NOBIND exch def
+/.bind /bind load def
+NOBIND { /bind { } def } if
+currentdict /NOCACHE known /NOCACHE exch def
+currentdict /NOCIE known /NOCIE exch def
+currentdict /NODISPLAY known not /DISPLAYING exch def
+currentdict /NOFONTMAP known /NOFONTMAP exch def
+currentdict /NOFONTPATH known /NOFONTPATH exch def
+currentdict /NOGC known /NOGC exch def
+currentdict /NOPAUSE known /NOPAUSE exch def
+currentdict /NOPLATFONTS known /NOPLATFONTS exch def
+currentdict /NOPROMPT known /NOPROMPT exch def
+% The default value of ORIENT1 is true, not false.
+currentdict /ORIENT1 known not { /ORIENT1 true def } if
+currentdict /OSTACKPRINT known /OSTACKPRINT exch def
+currentdict /OUTPUTFILE known % obsolete
+ { /OutputFile /OUTPUTFILE load def
+ currentdict /OUTPUTFILE .undef
+ } if
+currentdict /QUIET known /QUIET exch def
+currentdict /SAFER known /SAFER exch def
+currentdict /SHORTERRORS known /SHORTERRORS exch def
+currentdict /WRITESYSTEMDICT known /WRITESYSTEMDICT exch def
+
+% Acquire environment variables.
+currentdict /DEVICE known not
+ { (GS_DEVICE) getenv { /DEVICE exch def } if } if
+
+(START) VMDEBUG
+
+% Open the standard files, so they will be open at the outermost save level.
+(%stdin) (r) file pop
+(%stdout) (w) file pop
+(%stderr) (w) file pop
+
+% Define a procedure for skipping over an unneeded section of code.
+% This avoids allocating space for the skipped procedures.
+% We can't use readline, because that imposes a line length limit.
+/.skipeof % <string> .skipeof -
+ { currentfile exch 1 exch .subfiledecode flushfile
+ } bind def
+
+% If we're delaying binding, remember everything that needs to be bound later.
+DELAYBIND NOBIND not and
+ { .currentglobal false .setglobal
+ userdict /.delaybind 1500 array put
+ .setglobal
+ userdict /.delaycount 0 put
+ % When we've done the delayed bind, we want to stop saving.
+ % Detect this by the disappearance of .delaybind.
+ /bind
+ { userdict /.delaybind .knownget
+ { .delaycount 2 index put
+ userdict /.delaycount .delaycount 1 add put
+ }
+ { .bind
+ }
+ ifelse
+ } bind def
+ } if
+
+% Define procedures to assist users who don't read the documentation.
+userdict begin
+/help
+ { (Enter PostScript commands. '(filename) run' runs a file, 'quit' exits.\n)
+ print flush
+ } bind def
+/? /help load def
+end
+
+% Define =string, which is used by some PostScript programs even though
+% it isn't documented anywhere.
+% Put it in userdict so that each context can have its own copy.
+userdict /=string 256 string put
+
+% Print the greeting.
+
+/printgreeting
+ { mark
+ product (Ghostscript) search
+ { pop pop pop
+ (This software comes with NO WARRANTY: see the file COPYING for details.\n)
+ }
+ { pop
+ }
+ ifelse
+ (\n) copyright
+ (\)\n) revisiondate 100 mod (-)
+ revisiondate 100 idiv 100 mod (-)
+ revisiondate 10000 idiv ( \()
+ revision 10 mod
+ revision 10 idiv 10 mod (.)
+ revision 100 idiv ( )
+ product
+ counttomark
+ { (%stderr) (w) file exch false .writecvp
+ } repeat pop
+ } bind def
+
+QUIET not { printgreeting flush } if
+
+% Define a special version of def for making operator procedures.
+/odef % <name> <proc> odef -
+ { 1 index exch .makeoperator def
+ } .bind def
+
+%**************** BACKWARD COMPATIBILITY
+/getdeviceprops
+ { null .getdeviceparams
+ } bind odef
+/.putdeviceprops
+ { null true counttomark 1 add 3 roll .putdeviceparams
+ dup type /booleantype ne
+ { dup mark eq { /unknown /rangecheck } if
+ counttomark 4 add 1 roll cleartomark pop pop pop
+ /.putdeviceprops load exch signalerror
+ }
+ if
+ } bind odef
+/.devicenamedict 1 dict dup /OutputDevice dup put def
+/.devicename
+ { //.devicenamedict .getdeviceparams exch pop exch pop
+ } bind odef
+/max { .max } bind def
+/min { .min } bind def
+/.currentfilladjust { .currentfilladjust2 pop } bind odef
+/.setfilladjust { dup .setfilladjust2 } bind odef
+/.writecvs { false .writecvp } bind odef
+
+% Define predefined procedures substituting for operators,
+% in alphabetical order.
+
+userdict /#copies 1 put
+% Adobe implementations don't accept /[ or /], so we don't either.
+([) cvn
+ /mark load def
+(]) cvn
+ {counttomark array astore exch pop} odef
+/abs {dup 0 lt {neg} if} odef
+% .beginpage is an operator in Level 2.
+/.beginpage { } odef
+/copypage
+ { 1 .endpage
+ { .currentnumcopies false .outputpage
+ (>>copypage, press <return> to continue<<\n) .confirm
+ }
+ if .beginpage
+ } odef
+% .currentnumcopies is redefined in Level 2.
+/.currentnumcopies { #copies } odef
+/setcolorscreen where { pop % not in all Level 1 configurations
+ /currentcolorscreen
+ { .currenthalftone
+ { { 60 exch 0 exch 3 copy 6 copy } % halftone - not possible
+ { 3 copy 6 copy } % screen
+ { } % colorscreen
+ }
+ exch get exec
+ } odef
+} if
+/currentscreen
+ { .currenthalftone
+ { { 60 exch 0 exch } % halftone - not possible
+ { } % screen
+ { 12 3 roll 9 { pop } repeat } % colorscreen
+ }
+ exch get exec
+ } odef
+/.echo /echo load def
+userdict /.echo.mode true put
+/echo {dup /.echo.mode exch store .echo} odef
+/eexec
+ { 55665 //filterdict /eexecDecode get exec
+ cvx //systemdict begin stopped
+ % Only pop systemdict if it is still the top element,
+ % because this is apparently what Adobe interpreters do.
+ currentdict //systemdict eq { end } if
+ { stop } if
+ } odef
+% .endpage is an operator in Level 2.
+/.endpage { 2 ne } odef
+% erasepage mustn't use gsave/grestore, because we call it before
+% the graphics state stack has been fully initialized.
+/erasepage
+ { /currentcolor where
+ { pop currentcolor currentcolorspace { setcolorspace setcolor } }
+ { /currentcmykcolor where
+ { pop currentcmykcolor { setcmykcolor } }
+ { currentrgbcolor { setrgbcolor } }
+ ifelse
+ }
+ ifelse 1 setgray .fillpage exec
+ } odef
+/executive
+ { { prompt
+ { (%statementedit) (r) file } stopped
+ { pop pop $error /errorname get /undefinedfilename eq
+ { .clearerror exit } if % EOF
+ handleerror null % ioerror??
+ }
+ if
+ cvx execute
+ } loop
+ } odef
+/filter
+ { //filterdict 1 index .knownget
+ { exch pop exec }
+ { /filter load /undefined signalerror }
+ ifelse
+ } odef
+/handleerror
+ { errordict /handleerror get exec } bind def
+/identmatrix [1.0 0.0 0.0 1.0 0.0 0.0] readonly def
+/identmatrix
+ { //identmatrix exch copy } odef
+/initgraphics
+ { initmatrix newpath initclip
+ 1 setlinewidth 0 setlinecap 0 setlinejoin
+ [] 0 setdash 0 setgray 10 setmiterlimit
+ } odef
+/languagelevel 1 def % gs_lev2.ps may change this
+/makeimagedevice { false makewordimagedevice } odef
+/matrix { 6 array identmatrix } odef
+/pathbbox { false .pathbbox } odef
+/prompt { flush flushpage
+ (GS) print
+ count 0 ne { (<) print count =only } if
+ (>) print flush
+ } bind def
+/pstack { 0 1 count 3 sub { index == } for } bind def
+/putdeviceprops
+ { .putdeviceprops { erasepage } if } odef
+/quit { /quit load 0 .quit } odef
+/run { dup type /filetype ne { (r) file } if
+ % We must close the file when execution terminates,
+ % regardless of the state of the stack,
+ % and then propagate an error, if any.
+ cvx .runexec
+ } odef
+/setdevice
+ { .setdevice { erasepage } if } odef
+/showpage
+ { 0 .endpage
+ { .currentnumcopies true .outputpage
+ (>>showpage, press <return> to continue<<\n) .confirm
+ erasepage
+ }
+ if initgraphics .beginpage
+ } odef
+% Code output by Adobe Illustrator relies on the fact that
+% `stack' is a procedure, not an operator!!!
+/stack { 0 1 count 3 sub { index = } for } bind def
+/start { executive } def
+/stop { true .stop } odef
+% Internal uses of stopped that aren't going to do a stop if an error occurs
+% should use .internalstopped to avoid setting newerror et al.
+/stopped { false .stopped } odef
+/.internalstopped { null .stopped null ne } bind def
+/store { 1 index where { 3 1 roll put } { def } ifelse } odef
+% When running in Level 1 mode, this interpreter is supposed to be
+% compatible with PostScript "version" 54.0 (I think).
+/version (54.0) def
+
+% internaldict is defined in systemdict, but is allocated in local VM.
+systemdict /internaldict .knownget not { 0 } if type /operatortype ne
+ { .currentglobal false .setglobal
+ //systemdict /internaldict known not { /internaldict 5 dict def } if
+ /internaldict
+ [ /dup load 1183615869 /eq load
+ [ /pop load internaldict ] cvx
+ [ /internaldict /cvx load /invalidaccess /signalerror cvx ] cvx
+ /ifelse load
+ ] cvx bind odef
+ .setglobal
+ } if
+
+% Define some additional built-in procedures (beyond the ones defined by
+% the PostScript Language Reference Manual).
+% Warning: these are not guaranteed to stay the same from one release
+% to the next!
+/concatstrings
+ { exch dup length 2 index length add string % str2 str1 new
+ dup dup 4 2 roll copy % str2 new new new1
+ length 4 -1 roll putinterval
+ } bind def
+/copyarray
+ { dup length array copy } bind def
+% Copy a dictionary per the Level 2 spec even in Level 1.
+/.copydict % <fromdict> <todict> .copydict <todict>
+ { dup 3 -1 roll { put dup } forall pop } bind def
+/copystring
+ { dup length string copy } bind def
+/finddevice
+ { //systemdict /devicedict get exch get
+ dup 1 get null eq
+ { % This is the first request for this type of device.
+ % Create a default instance now.
+ % Stack: [proto null]
+ .currentglobal true .setglobal exch
+ dup dup 0 get copydevice 1 exch put
+ exch .setglobal
+ }
+ if 1 get
+ } bind def
+/.growdictlength % get size for growing a dictionary
+ { length 3 mul 2 idiv 1 add
+ } bind def
+/.growdict % grow a dictionary
+ { dup .growdictlength .setmaxlength
+ } bind def
+/.growput % put, grow the dictionary if needed
+ { 2 index length 3 index maxlength eq
+ { 3 copy pop known not { 2 index .growdict } if
+ } if
+ put
+ } bind def
+/.packtomark
+ { counttomark packedarray exch pop } bind def
+/ppstack
+ { 0 1 count 3 sub { index === } for } bind def
+/runlibfile
+ { findlibfile
+ { exch pop run }
+ { /undefinedfilename signalerror }
+ ifelse
+ } bind def
+/selectdevice
+ { finddevice setdevice .setdefaultscreen } bind def
+/signalerror % <object> <errorname> signalerror -
+ { errordict exch get exec } bind def
+
+% Define the =[only] procedures. Also define =print,
+% which is used by some PostScript programs even though
+% it isn't documented anywhere.
+/write=only
+ { { .writecvs } .internalstopped
+ { pop (--nostringval--) writestring
+ }
+ if
+ } bind def
+/write=
+ { 1 index exch write=only (\n) writestring
+ } bind def
+/=only { (%stderr) (w) file exch write=only } bind def
+/= { =only (\n) print } bind def
+/=print /=only load def
+% Temporarily define == as = for the sake of runlibfile0.
+/== /= load def
+
+% Define procedures for getting and setting the current device resolution.
+
+/gsgetdeviceprop % <device> <propname> gsgetdeviceprop <value>
+ { 2 copy mark exch null .dicttomark .getdeviceparams
+ dup mark eq % if true, not found
+ { pop dup /undefined signalerror }
+ { 5 1 roll pop pop pop pop }
+ ifelse
+ } bind def
+/gscurrentresolution % - gscurrentresolution <[xres yres]>
+ { currentdevice /HWResolution gsgetdeviceprop
+ } bind def
+/gssetresolution % <[xres yres]> gssetresolution -
+ { 2 array astore mark exch /HWResolution exch
+ currentdevice copydevice putdeviceprops setdevice
+ } bind def
+
+% Define auxiliary procedures needed for the above.
+/shellarguments % -> shell_arguments true (or) false
+ { /ARGUMENTS where
+ { /ARGUMENTS get dup type /arraytype eq
+ { aload pop /ARGUMENTS null store true }
+ { pop false }
+ ifelse }
+ { false } ifelse
+ } bind def
+/.confirm
+ { DISPLAYING NOPAUSE not and
+ { % Print a message (unless NOPROMPT is true)
+ % and wait for the user to type something.
+ % If the user just types a newline, flush it.
+ NOPROMPT { pop } { print flush } ifelse
+ .echo.mode false echo
+ (%stdin) (r) file dup read
+ { dup (\n) 0 get eq { pop pop } { unread } ifelse }
+ { pop }
+ ifelse echo
+ }
+ { pop
+ }
+ ifelse
+ } bind def
+
+% Define the procedure used by .runfile, .runstdin and .runstring
+% for executing user input.
+% This is called with a procedure or executable file on the operand stack.
+/execute
+ { stopped
+ $error /newerror get and
+ { handleerror flush
+ } if
+ } odef
+% Define an execute analogue of runlibfile0.
+/execute0
+ { stopped
+ $error /newerror get and
+ { handleerror flush /execute0 cvx 1 .quit
+ } if
+ } bind def
+% Define the procedure that the C code uses for running files
+% named on the command line.
+/.runfile { { runlibfile } execute } def
+% Define the procedure that the C code uses for running piped input.
+/.runstdin { (%stdin) (r) file cvx execute0 } bind def
+% Define the procedure that the C code uses for running commands
+% given on the command line with -c.
+/.runstring { cvx execute } def
+
+% Define a special version of runlibfile that aborts on errors.
+/runlibfile0
+ { cvlit dup /.currentfilename exch def
+ { findlibfile not { stop } if }
+ stopped
+ { (Can't find \(or open\) initialization file ) print
+ .currentfilename == flush /runlibfile0 cvx 1 .quit
+ } if
+ exch pop cvx stopped
+ { (While reading ) print .currentfilename print (:\n) print flush
+ handleerror /runlibfile0 1 .quit
+ } if
+ } bind def
+% Temporarily substitute it for the real runlibfile.
+/.runlibfile /runlibfile load def
+/runlibfile /runlibfile0 load def
+
+% Create the error handling machinery.
+% Define the standard error handlers.
+% The interpreter has created the ErrorNames array.
+/.unstoppederrorhandler % <command> <errorname> .unstoppederrorhandler -
+ { % This is the handler that gets used for recursive errors,
+ % or errors outside the scope of a 'stopped'.
+ 2 copy SHORTERRORS
+ { (%%[ Error: ) print =only flush
+ (; OffendingCommand: ) print =only ( ]%%\n) print
+ }
+ { (Unrecoverable error: ) print =only flush
+ ( in ) print = flush
+ count 2 gt
+ { (Operand stack:\n ) print
+ 2 1 count 3 sub { ( ) print index =only flush } for
+ (\n) print flush
+ } if
+ }
+ ifelse
+ -1 0 1 //ErrorNames length 1 sub
+ { dup //ErrorNames exch get 3 index eq
+ { not exch pop exit } { pop } ifelse
+ }
+ for exch pop .quit
+ } bind def
+/.errorhandler % <command> <errorname> .errorhandler -
+ { % Detect an internal 'stopped'.
+ .instopped { null eq { pop pop stop } if } if
+ $error /.inerror get .instopped { pop } { pop true } ifelse
+ { .unstoppederrorhandler
+ } if % detect error recursion
+ $error /globalmode .currentglobal false .setglobal put
+ $error /.inerror true put
+ $error /newerror true put
+ $error exch /errorname exch put
+ $error exch /command exch put
+ $error /recordstacks get $error /errorname get /VMerror ne and
+ { % Attempt to store the stack contents atomically.
+ count array astore dup $error /ostack 4 -1 roll
+ countexecstack array execstack $error /estack 3 -1 roll
+ countdictstack array dictstack $error /dstack 3 -1 roll
+ put put put aload pop
+ }
+ { $error /dstack .undef
+ $error /estack .undef
+ $error /ostack .undef
+ }
+ ifelse
+ $error /position currentfile status
+ { currentfile { fileposition } .internalstopped { pop null } if
+ }
+ { % If this was a scanner error, the file is no longer current,
+ % but the command holds the file, which may still be open.
+ $error /command get dup type /filetype eq
+ { { fileposition } .internalstopped { pop null } if }
+ { pop null }
+ ifelse
+ }
+ ifelse put
+ % During initialization, we don't reset the allocation
+ % mode on errors.
+ $error /globalmode get $error /.nosetlocal get and .setglobal
+ $error /.inerror false put
+ stop
+ } bind def
+% Define the standard handleerror. We break out the printing procedure
+% (.printerror) so that it can be extended for binary output
+% if the Level 2 facilities are present.
+ /.printerror
+ { $error begin
+ /command load errorname SHORTERRORS
+ { (%%[ Error: ) print =only flush
+ (; OffendingCommand: ) print =only
+ currentdict /errorinfo .knownget
+ { (;\nErrorInfo:) print
+ dup type /arraytype eq
+ { { ( ) print =only } forall }
+ { ( ) print =only }
+ ifelse
+ } if
+ ( ]%%\n) print flush
+ }
+ { (Error: ) print ==only flush
+ ( in ) print ==only flush
+ currentdict /errorinfo .knownget
+ { (\nAdditional information: ) print ==only flush
+ } if
+ .printerror_long
+ }
+ ifelse
+ .clearerror
+ end
+ flush
+ } bind def
+ /.printerror_long % long error printout,
+ % $error is on the dict stack
+ { % Push the (anonymous) stack printing procedure.
+ % <heading> <==flag> <override-name> <stackname> proc
+ {
+ currentdict exch .knownget % stackname defined in $error?
+ {
+ 4 1 roll % stack: <stack> <head> <==flag> <over>
+ errordict exch .knownget % overridename defined?
+ {
+ exch pop exch pop exec % call override with <stack>
+ }
+ {
+ exch print exch % print heading. stack <==flag> <stack>
+ 1 index not { (\n) print } if
+ { 1 index { (\n ) } { ( ) } ifelse print
+ dup type /dicttype eq
+ {
+ (--dict:) print
+ dup rcheck
+ { dup length =only (/) print maxlength =only }
+ { pop }
+ ifelse
+ (--) print
+ }
+ {
+ dup type /stringtype eq 2 index or
+ { ===only } { =only } ifelse
+ } ifelse
+ } forall
+ pop
+ }
+ ifelse % overridden
+ }
+ { pop pop pop
+ }
+ ifelse % stack known
+ }
+
+ (\nOperand stack:) OSTACKPRINT /.printostack /ostack 4 index exec
+ (\nExecution stack:) ESTACKPRINT /.printestack /estack 4 index exec
+ (\nBacktrace:) true /.printbacktrace /backtrace 4 index exec
+ (\nDictionary stack:) false /.printdstack /dstack 4 index exec
+ (\n) print
+ pop % printing procedure
+
+ errorname /VMerror eq
+ { (VM status:) print mark vmstatus
+ counttomark { ( ) print counttomark -1 roll dup =only } repeat
+ cleartomark (\n) print
+ } if
+
+ .languagelevel 2 ge
+ { (Current allocation mode is ) print
+ globalmode { (global\n) } { (local\n) } ifelse print
+ } if
+
+ .oserrno dup 0 ne
+ { (Last OS error: ) print
+ errorname /VMerror ne
+ { dup .oserrorstring { = pop } { = } ifelse }
+ { = }
+ ifelse
+ }
+ { pop
+ }
+ ifelse
+
+ position null ne
+ { (Current file position is ) print position = }
+ if
+
+ } bind def
+% Define a procedure for clearing the error indication.
+/.clearerror
+ { $error /newerror false put
+ $error /errorinfo .undef
+ 0 .setoserrno
+ } bind def
+
+% Define $error. This must be in local VM.
+.currentglobal false .setglobal
+/$error 40 dict def % newerror, errorname, command, errorinfo,
+ % ostack, estack, dstack, recordstacks,
+ % binary, globalmode,
+ % .inerror, .nosetlocal, position,
+ % plus extra space for badly designed error handers.
+$error begin
+ /newerror false def
+ /recordstacks true def
+ /binary false def
+ /globalmode .currentglobal def
+ /.inerror false def
+ /.nosetlocal true def
+ /position null def
+end
+% Define errordict similarly. It has one entry per error name,
+% plus handleerror.
+/errordict ErrorNames length 1 add dict def
+.setglobal % contents of errordict are global
+errordict begin
+ ErrorNames
+ { mark 1 index systemdict /.errorhandler get /exec load .packtomark cvx def
+ } forall
+% The handlers for interrupt and timeout are special; there is no
+% 'current object', so they push their own name.
+ { /interrupt /timeout }
+ { mark 1 index dup systemdict /.errorhandler get /exec load .packtomark cvx def
+ } forall
+/handleerror
+ { //systemdict /.printerror get exec
+ } bind def
+end
+
+% Define the [write]==[only] procedures.
+/.dict 26 dict dup
+begin def
+ /.cvp {1 index exch .writecvs} bind def
+ /.nop {exch pop .p} bind def
+ /.p {1 index exch writestring} bind def
+ /.p1 {2 index exch writestring} bind def
+ /.p2 {3 index exch writestring} bind def
+ /.print
+ { dup type .dict exch .knownget
+ { dup type /stringtype eq { .nop } { exec } ifelse }
+ { (-) .p1 type .cvp (-) .p }
+ ifelse
+ } bind def
+ /.pstring
+ { { dup dup 32 lt exch 127 ge or
+ { (\\) .p1 2 copy -6 bitshift 48 add write
+ 2 copy -3 bitshift 7 and 48 add write
+ 7 and 48 add
+ }
+ { dup dup -2 and 40 eq exch 92 eq or {(\\) .p1} if
+ }
+ ifelse 1 index exch write
+ }
+ forall
+ } bind def
+ /booleantype /.cvp load def
+ /conditiontype (-condition-) def
+ /devicetype (-device-) def
+ /dicttype (-dict-) def
+ /filetype (-file-) def
+ /fonttype (-fontID-) def
+ /gstatetype (-gstate-) def
+ /integertype /.cvp load def
+ /locktype (-lock-) def
+ /marktype (-mark-) def
+ /nulltype (null) def
+ /realtype {1 index exch true .writecvp} bind def
+ /savetype (-save-) def
+ /nametype
+ {dup xcheck not {(/) .p1} if
+ 1 index exch .writecvs} bind def
+ /arraytype
+ {dup rcheck
+ {() exch dup xcheck
+ {({) .p2
+ {exch .p1
+ 1 index exch .print pop ( )} forall
+ (})}
+ {([) .p2
+ {exch .p1
+ 1 index exch .print pop ( )} forall
+ (])}
+ ifelse exch pop .p}
+ {(-array-) .nop}
+ ifelse} bind def
+ /operatortype
+ {(--) .p1 .cvp (--) .p} bind def
+ /packedarraytype
+ { dup rcheck
+ { arraytype }
+ { (-packedarray-) .nop }
+ ifelse
+ } bind def
+ /stringtype
+ { dup rcheck
+ { (\() .p1 dup length 200 le
+ { .pstring }
+ { 0 200 getinterval .pstring (...) .p }
+ ifelse (\)) .p
+ }
+ { (-string-) .nop
+ }
+ ifelse
+ } bind def
+{//.dict begin .print pop end}
+ bind
+end
+
+/write==only exch def
+/write== {1 index exch write==only (\n) writestring} bind def
+/==only { (%stderr) (w) file exch write==only } bind def
+/== {==only (\n) print} bind def
+
+% Define [write]===[only], an extension that prints dictionaries
+% in readable form and doesn't truncate strings.
+/.dict /write==only load 0 get dup length dict .copydict dup
+begin def
+ /dicttype
+ { dup rcheck
+ { (<< ) .p1
+ { 2 index 3 -1 roll .print pop ( ) .p1
+ 1 index exch .print pop ( ) .p
+ }
+ forall (>>) .p
+ }
+ { (-dict-) .nop
+ }
+ ifelse
+ } bind def
+ /stringtype
+ { dup rcheck
+ { (\() .p1 .pstring (\)) .p }
+ { (-string-) .nop }
+ ifelse
+ } bind def
+
+{//.dict begin .print pop end}
+ bind
+end
+
+/write===only exch def
+/write=== {1 index exch write===only (\n) writestring} bind def
+/===only { (%stderr) (w) file exch write===only } bind def
+/=== { ===only (\n) print } bind def
+
+(END PROCS) VMDEBUG
+
+% Define the font directory.
+/FontDirectory false .setglobal 100 dict true .setglobal def
+
+% Define the encoding dictionary.
+/EncodingDirectory 10 dict def % enough for Level 2 + PDF standard encodings
+
+% Define .findencoding. (This is redefined in Level 2.)
+/.findencoding
+ { //EncodingDirectory exch get exec
+ } bind def
+/.defineencoding
+ { //EncodingDirectory 3 1 roll put
+ } bind def
+% If we've got the composite font extensions, define findencoding.
+/rootfont where { pop /findencoding { .findencoding } odef } if
+
+% Load StandardEncoding.
+%% Replace 1 (gs_std_e.ps)
+(gs_std_e.ps) dup runlibfile VMDEBUG
+
+% Load ISOLatin1Encoding.
+%% Replace 1 (gs_iso_e.ps)
+(gs_iso_e.ps) dup runlibfile VMDEBUG
+
+% Define stubs for the Symbol and Dingbats encodings.
+% Note that the first element of the procedure must be the file name,
+% since gs_lev2.ps extracts it to set up the Encoding resource category.
+
+ /SymbolEncoding { /SymbolEncoding .findencoding } bind def
+%% Replace 3 (gs_sym_e.ps)
+ EncodingDirectory /SymbolEncoding
+ { (gs_sym_e.ps) //systemdict begin runlibfile SymbolEncoding end }
+ bind put
+
+ /DingbatsEncoding { /DingbatsEncoding .findencoding } bind def
+%% Replace 3 (gs_dbt_e.ps)
+ EncodingDirectory /DingbatsEncoding
+ { (gs_dbt_e.ps) //systemdict begin runlibfile DingbatsEncoding end }
+ bind put
+
+(END FONTDIR/ENCS) VMDEBUG
+
+% Construct a dictionary of all available devices.
+% These are (read-only) device prototypes that can't be
+% installed or have their parameters changed. For this reason,
+% the value in the dictionary is actually a 2-element writable array,
+% to allow us to create a default instance of the prototype on demand.
+
+ % Loop until the .getdevice gets a rangecheck.
+errordict /rangecheck 2 copy get
+errordict /rangecheck { pop stop } put % pop the command
+ 0 { {dup .getdevice exch 1 add} loop} null .stopped pop
+ 1 add dict /devicedict 1 index def
+ begin % 2nd copy of count is on stack
+ { dup .devicename exch
+ dup wcheck { dup } { null } ifelse 2 array astore def
+ } repeat
+ end
+put % errordict /rangecheck
+.clearerror
+/devicenames devicedict { pop } forall devicedict length packedarray def
+
+% Determine the default device.
+/defaultdevice DISPLAYING
+ { systemdict /DEVICE .knownget
+ { devicedict 1 index known not
+ { (Unknown device: ) print =
+ flush /defaultdevice cvx 1 .quit
+ }
+ if
+ }
+ { 0 .getdevice .devicename
+ }
+ ifelse
+ }
+ { /nullpage
+ }
+ifelse
+/.defaultdevicename 1 index def
+finddevice % make a copy
+def
+devicedict /Default devicedict .defaultdevicename get put
+
+(END DEVS) VMDEBUG
+
+% Define statusdict, for the benefit of programs
+% that think they are running on a LaserWriter or similar printer.
+%% Replace 1 (gs_statd.ps)
+(gs_statd.ps) runlibfile
+
+(END STATD) VMDEBUG
+
+% Load the standard font environment.
+%% Replace 1 (gs_fonts.ps)
+(gs_fonts.ps) runlibfile
+
+(END GS_FONTS) VMDEBUG
+
+% Load the initialization files for optional features.
+%% Replace 4 INITFILES
+systemdict /INITFILES known
+ { INITFILES { dup runlibfile VMDEBUG } forall
+ }
+if
+
+% If Level 2 functionality is implemented, enable it now.
+/.setlanguagelevel where
+ { pop 2 .setlanguagelevel
+ } if
+
+(END INITFILES) VMDEBUG
+
+% Create a null font. This is the initial font.
+8 dict dup begin
+ /FontMatrix [ 1 0 0 1 0 0 ] readonly def
+ /FontType 3 def
+ /FontName () def
+ /Encoding StandardEncoding def
+ /FontBBox { 0 0 0 0 } readonly def % executable is bogus, but customary ...
+ /BuildChar { pop pop 0 0 setcharwidth } bind def
+ /PaintType 0 def % shouldn't be needed!
+end
+/NullFont exch definefont setfont
+
+% Define NullFont as the font.
+/NullFont currentfont def
+
+% Load initial fonts from FONTPATH directories, Fontmap file,
+% and/or .getccfont as appropriate.
+.loadinitialfonts
+
+% Remove NullFont from FontDirectory, so it can't be accessed by mistake.
+FontDirectory /NullFont .undef
+
+(END FONTS) VMDEBUG
+
+% Restore the real definition of runlibfile.
+/runlibfile /.runlibfile load def
+currentdict /.runlibfile .undef
+
+% Bind all the operators defined as procedures.
+/.bindoperators % binds operators in currentdict
+ { % Temporarily disable the typecheck error.
+ errordict /typecheck 2 copy get
+ errordict /typecheck { pop } put % pop the command
+ currentdict
+ { dup type /operatortype eq
+ { % This might be a real operator, so bind might cause a typecheck,
+ % but we've made the error a no-op temporarily.
+ .bind % do a real bind even if NOBIND is set
+ }
+ if pop pop
+ } forall
+ put
+ } def
+NOBIND DELAYBIND or not { .bindoperators } if
+
+% Establish a default environment.
+
+defaultdevice
+DISPLAYING not { setdevice (%END DISPLAYING) .skipeof } if
+systemdict /DEVICEWIDTH known
+systemdict /DEVICEHEIGHT known or
+systemdict /DEVICEWIDTHPOINTS known or
+systemdict /DEVICEHEIGHTPOINTS known or
+systemdict /DEVICEXRESOLUTION known or
+systemdict /DEVICEYRESOLUTION known or
+systemdict /PAPERSIZE known or
+not { (%END DEVICE) .skipeof } if
+% Let DEVICE{WIDTH,HEIGHT}[POINTS] override PAPERSIZE.
+systemdict /PAPERSIZE known
+systemdict /DEVICEWIDTH known not and
+systemdict /DEVICEHEIGHT known not and
+systemdict /DEVICEWIDTHPOINTS known not and
+systemdict /DEVICEHEIGHTPOINTS known not and
+ { % Convert the paper size to device dimensions.
+ true statusdict /.pagetypenames get
+ { PAPERSIZE eq
+ { PAPERSIZE load
+ dup 0 get /DEVICEWIDTHPOINTS exch def
+ 1 get /DEVICEHEIGHTPOINTS exch def
+ pop false exit
+ }
+ if
+ }
+ forall
+ { (Unknown paper size: ) print PAPERSIZE ==only (.\n) print
+ }
+ if
+ }
+if
+% Adjust the device parameters per the command line.
+% It is possible to specify resolution, pixel size, and page size;
+% since any two of these determine the third, conflicts are possible.
+% We simply pass them to .setdeviceparams and let it sort things out.
+ mark /HWResolution null /HWSize null /PageSize null .dicttomark
+ .getdeviceparams .dicttomark begin
+ mark
+ % Check for resolution.
+ /DEVICEXRESOLUTION where dup
+ { exch pop HWResolution 0 DEVICEXRESOLUTION put }
+ if
+ /DEVICEYRESOLUTION where dup
+ { exch pop HWResolution 1 DEVICEYRESOLUTION put }
+ if
+ or { /HWResolution HWResolution } if
+ % Check for device sizes specified in pixels.
+ /DEVICEWIDTH where dup
+ { exch pop HWSize 0 DEVICEWIDTH put }
+ if
+ /DEVICEHEIGHT where dup
+ { exch pop HWSize 1 DEVICEHEIGHT put }
+ if
+ or { /HWSize HWSize } if
+ % Check for device sizes specified in points.
+ /DEVICEWIDTHPOINTS where dup
+ { exch pop PageSize 0 DEVICEWIDTHPOINTS put }
+ if
+ /DEVICEHEIGHTPOINTS where dup
+ { exch pop PageSize 1 DEVICEHEIGHTPOINTS put }
+ if
+ or { /PageSize PageSize } if
+ % Check whether any parameters were set.
+ dup mark eq { pop } { defaultdevice putdeviceprops } ifelse
+ end
+%END DEVICE
+% Set any device properties defined on the command line.
+% If BufferSpace is defined but not MaxBitmap, set MaxBitmap to BufferSpace.
+systemdict /BufferSpace known
+systemdict /MaxBitmap known not and
+ { systemdict /MaxBitmap BufferSpace put
+ } if
+dup getdeviceprops
+counttomark 2 idiv
+ { systemdict 2 index known
+ { pop dup load counttomark 2 roll }
+ { pop pop }
+ ifelse
+ } repeat
+counttomark dup 0 ne
+ { 2 add -1 roll putdeviceprops }
+ { pop pop }
+ifelse
+setdevice % does an erasepage
+% If the media size is fixed, update the current page device dictionary.
+FIXEDMEDIA
+dup { pop systemdict /.currentpagedevice known } if
+dup { pop .currentpagedevice exch pop } if
+not { (%END MEDIA) .skipeof } if
+currentpagedevice dup length dict .copydict
+dup /InputAttributes
+2 copy get dup length dict .copydict
+ % Stack: <pagedevice> <pagedevice> /InputAttributes <inputattrs>
+dup length dict .copydict dup
+0 2 copy get dup length dict .copydict
+ % Stack: <pagedevice> <pagedevice> /InputAttributes
+ % <inputattrs> <inputattrs> 0 <attrs0>
+dup /PageSize 7 index /PageSize get
+put % PageSize in 0
+put % 0 in InputAttributes
+put % InputAttributes in pagedevice
+.setpagedevice
+%END MEDIA
+%END DISPLAYING
+
+(END DEVICE) VMDEBUG
+
+% Establish a default upper limit in the character cache,
+% namely, enough room for a 18-point character at the resolution
+% of the default device, or for a character consuming 1% of the
+% maximum cache size, whichever is larger.
+mark
+ % Compute limit based on character size.
+ 18 dup dtransform
+ exch abs cvi 31 add 32 idiv 4 mul % X raster
+ exch abs cvi mul % Y
+ % Compute limit based on allocated space.
+ cachestatus pop pop pop pop pop exch pop 0.01 mul cvi
+ .max dup 10 idiv exch
+setcacheparams
+% Conditionally disable the character cache.
+NOCACHE { 0 setcachelimit } if
+
+(END CONFIG) VMDEBUG
+
+% Set the default screen based on the device resolution.
+/.setdefaultscreen
+{
+ <<
+ /HalftoneType 3
+ /Width 16
+ /Height 16
+ /Thresholds
+ < 00 80 20 A0 08 88 28 A8 02 82 22 A2 0A 8A 2A AA
+ C0 40 E0 60 C8 48 E8 68 C2 42 E2 62 CA 4A EA 6A
+ 30 B0 10 90 38 B8 18 98 32 B2 12 92 3A BA 1A 9A
+ F0 70 D0 50 F8 78 D8 58 F2 72 D2 52 FA 7A DA 5A
+ 0C 8C 2C AC 04 84 24 A4 0E 8E 2E AE 06 86 26 A6
+ CC 4C EC 6C C4 44 E4 64 CE 4E EE 6E C6 46 E6 66
+ 3C BC 1C 9C 34 B4 14 94 3E BE 1E 9E 36 B6 16 96
+ FC 7C DC 5C F4 74 D4 54 FE 7E DE 5E F6 76 D6 56
+ 03 83 23 A3 0B 8B 2B AB 01 81 21 A1 09 89 29 A9
+ C3 43 E3 63 CB 4B EB 6B C1 41 E1 61 C9 49 E9 69
+ 33 B3 13 93 3B BB 1B 9B 31 B1 11 91 39 B9 19 99
+ F3 73 D3 53 FB 7B DB 5B F1 71 D1 51 F9 79 D9 59
+ 0F 8F 2F AF 07 87 27 A7 0D 8D 2D AD 05 85 25 A5
+ CF 4F EF 6F C7 47 E7 67 CD 4D ED 6D C5 45 E5 65
+ 3F BF 1F 9F 37 B7 17 97 3D BD 1D 9D 35 B5 15 95
+ FF 7F DF 5F F7 77 D7 57 FD 7D DD 5D F5 75 D5 55 >
+ >> sethalftone
+} bind def
+.setdefaultscreen
+% Set a null transfer function...
+{} bind settransfer
+initgraphics
+% The interpreter relies on there being at least 2 entries
+% on the graphics stack. Establish the second one now.
+gsave
+
+% Define some control sequences as no-ops.
+% This is a hack to get around problems
+% in some common PostScript-generating applications.
+% Note that <04> and <1a> are self-delimiting characters, like [.
+<04> cvn { } def % Apple job separator
+%<0404> cvn { } def % two of the same
+<1b> cvn { } def % MS Windows LaserJet 4 prologue
+%<041b> cvn { } def % MS Windows LaserJet 4 epilogue
+(\001M) cvn % TBCP initiator
+ { currentfile /TBCPDecode filter cvx exec
+ } bind def
+/@PJL % H-P job control
+ { currentfile //=string readline { pop } if
+ } bind def
+
+% If we want a "safer" system, disable some obvious ways to cause havoc.
+SAFER not { (%END SAFER) .skipeof } if
+/file
+ { dup (r) eq 2 index (%pipe*) .stringmatch not and
+ { file }
+ { /invalidfileaccess signalerror }
+ ifelse
+ } .bind odef
+/renamefile { /invalidfileaccess signalerror } odef
+/deletefile { /invalidfileaccess signalerror } odef
+/putdeviceprops
+ { counttomark
+ dup 2 mod 0 eq { pop /rangecheck signalerror } if
+ 3 2 3 2 roll
+ { dup index /OutputFile eq
+ { -2 roll
+ dup () ne { /putdeviceprops load /invalidfileaccess signalerror } if
+ 3 -1 roll
+ }
+ { pop
+ }
+ ifelse
+ } for
+ putdeviceprops
+ } .bind odef
+
+%END SAFER
+
+% If we delayed binding, make it possible to do it later.
+/.bindnow
+ { //systemdict begin .bindoperators end
+ % Temporarily disable the typecheck error.
+ errordict /typecheck 2 copy get
+ errordict /typecheck { pop } put % pop the command
+ 0 1 .delaycount 1 sub { .delaybind exch get .bind pop } for
+ userdict /.delaybind .undef % reclaim the space
+ userdict /.delaycount .undef
+ put
+ } .bind def
+
+% Turn off array packing, since some PostScript code assumes that
+% procedures are writable.
+false setpacking
+
+% Close up systemdict.
+currentdict /.forceput .undef % remove temptation
+currentdict /filterdict .undef % bound in where needed
+end
+WRITESYSTEMDICT not { systemdict readonly pop } if
+
+(END INIT) VMDEBUG
+
+% Establish local VM as the default.
+false /setglobal where { pop setglobal } { .setglobal } ifelse
+$error /.nosetlocal false put
+
+% Clean up VM, and enable GC.
+/vmreclaim where
+ { pop NOGC not { 2 vmreclaim 0 vmreclaim } if
+ } if
+
+(END GC) VMDEBUG
+
+% The interpreter will run the initial procedure (start).
diff --git a/pstoraster/gs_iso_e.ps b/pstoraster/gs_iso_e.ps
new file mode 100644
index 000000000..9be8b8aab
--- /dev/null
+++ b/pstoraster/gs_iso_e.ps
@@ -0,0 +1,72 @@
+% Copyright (C) 1993, 1994 Aladdin Enterprises. All rights reserved.
+%
+% This file is part of GNU Ghostscript.
+%
+% GNU Ghostscript is distributed in the hope that it will be useful, but
+% WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+% anyone for the consequences of using it or for whether it serves any
+% particular purpose or works at all, unless he says so in writing. Refer to
+% the GNU General Public License for full details.
+%
+% Everyone is granted permission to copy, modify and redistribute GNU
+% Ghostscript, but only under the conditions described in the GNU General
+% Public License. A copy of this license is supposed to have been given to
+% you along with GNU Ghostscript so you can know your rights and
+% responsibilities. It should be in a file named COPYING. Among other
+% things, the copyright notice and this notice must be preserved on all
+% copies.
+%
+% Aladdin Enterprises is not affiliated with the Free Software Foundation or
+% the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+% does not depend on any other GNU software.
+
+% Define the ISO Latin-1 encoding vector.
+% The first half is the same as the standard encoding,
+% except for minus instead of hyphen at code 055.
+/ISOLatin1Encoding
+StandardEncoding 0 45 getinterval aload pop
+ /minus
+StandardEncoding 46 82 getinterval aload pop
+% NOTE: the following are missing in the Adobe documentation,
+% but appear in the displayed table:
+% macron at 0225, dieresis at 0230, cedilla at 0233, space at 0240.
+% This is an error in the Red Book, corrected in Adobe TN 5085.
+% \20x
+ /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
+ /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
+ /dotlessi /grave /acute /circumflex /tilde /macron /breve /dotaccent
+ /dieresis /.notdef /ring /cedilla /.notdef /hungarumlaut /ogonek /caron
+% \24x
+ /space /exclamdown /cent /sterling
+ /currency /yen /brokenbar /section
+ /dieresis /copyright /ordfeminine /guillemotleft
+ /logicalnot /minus /registered /macron
+ /degree /plusminus /twosuperior /threesuperior
+ /acute /mu /paragraph /periodcentered
+ /cedilla /onesuperior /ordmasculine /guillemotright
+ /onequarter /onehalf /threequarters /questiondown
+% \30x
+ /Agrave /Aacute /Acircumflex /Atilde
+ /Adieresis /Aring /AE /Ccedilla
+ /Egrave /Eacute /Ecircumflex /Edieresis
+ /Igrave /Iacute /Icircumflex /Idieresis
+ /Eth /Ntilde /Ograve /Oacute
+ /Ocircumflex /Otilde /Odieresis /multiply
+ /Oslash /Ugrave /Uacute /Ucircumflex
+ /Udieresis /Yacute /Thorn /germandbls
+% \34x
+ /agrave /aacute /acircumflex /atilde
+ /adieresis /aring /ae /ccedilla
+ /egrave /eacute /ecircumflex /edieresis
+ /igrave /iacute /icircumflex /idieresis
+ /eth /ntilde /ograve /oacute
+ /ocircumflex /otilde /odieresis /divide
+ /oslash /ugrave /uacute /ucircumflex
+ /udieresis /yacute /thorn /ydieresis
+% Make an array on large systems, a packed array on small ones.
+256
+vmstatus exch pop exch pop
+100000 ge { array astore readonly } { packedarray } ifelse
+def
+1 ISOLatin1Encoding .registerencoding
+/ISOLatin1Encoding ISOLatin1Encoding .defineencoding
diff --git a/pstoraster/gs_kanji.ps b/pstoraster/gs_kanji.ps
new file mode 100644
index 000000000..c45dddfb9
--- /dev/null
+++ b/pstoraster/gs_kanji.ps
@@ -0,0 +1,164 @@
+% Copyright (C) 1994, 1995, 1996 Aladdin Enterprises. All rights reserved.
+%
+% This file is part of GNU Ghostscript.
+%
+% GNU Ghostscript is distributed in the hope that it will be useful, but
+% WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+% anyone for the consequences of using it or for whether it serves any
+% particular purpose or works at all, unless he says so in writing. Refer to
+% the GNU General Public License for full details.
+%
+% Everyone is granted permission to copy, modify and redistribute GNU
+% Ghostscript, but only under the conditions described in the GNU General
+% Public License. A copy of this license is supposed to have been given to
+% you along with GNU Ghostscript so you can know your rights and
+% responsibilities. It should be in a file named COPYING. Among other
+% things, the copyright notice and this notice must be preserved on all
+% copies.
+%
+% Aladdin Enterprises is not affiliated with the Free Software Foundation or
+% the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+% does not depend on any other GNU software.
+
+% Scaffolding for Kanji fonts. This is based on the Wadalab free font
+% from the University of Tokyo; it may not be appropriate for other
+% Kanji fonts.
+
+/currentglobal where
+ { pop currentglobal { setglobal } true setglobal }
+ { { } }
+ifelse
+
+% Define the encoding for the root font.
+
+/KanjiEncoding
+% \x00
+ 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0
+% \x20
+ 0 1 2 3 4 5 6 7
+ 8 0 0 0 0 0 0 0
+ 9 10 11 12 13 14 15 16
+ 17 18 19 20 21 22 23 24
+% \x40
+ 25 26 27 28 29 30 31 32
+ 33 34 35 36 37 38 39 40
+ 41 42 43 44 45 46 47 48
+ 49 50 51 52 53 54 55 56
+% \x60
+ 57 58 59 60 61 62 63 64
+ 65 66 67 68 69 70 71 72
+ 73 74 75 76 77 0 0 0
+ 0 0 0 0 0 0 0 0
+% \x80
+ 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0
+% \xA0
+ 0 1 2 3 4 5 6 7
+ 8 0 0 0 0 0 0 0
+ 9 10 11 12 13 14 15 16
+ 17 18 19 20 21 22 23 24
+% \xC0
+ 25 26 27 28 29 30 31 32
+ 33 34 35 36 37 38 39 40
+ 41 42 43 44 45 46 47 48
+ 49 50 51 52 53 54 55 56
+% \xE0
+ 57 58 59 60 61 62 63 64
+ 65 66 67 68 69 70 71 72
+ 73 74 75 76 77 0 0 0
+ 0 0 0 0 0 0 0 0
+256 packedarray def
+
+% Define a stub for the base font encoding.
+
+ /KanjiSubEncoding { /KanjiSubEncoding .findencoding } bind def
+%% Replace 3 (gs_ksb_e.ps)
+ EncodingDirectory /KanjiSubEncoding
+ { (gs_ksb_e.ps) //systemdict begin runlibfile KanjiSubEncoding end }
+ bind put
+
+% Support procedures and data.
+
+/T1FontInfo 8 dict begin
+ /version (001.001) readonly def
+ /FullName (KanjiBase) readonly def
+ /FamilyName (KanjiBase) readonly def
+ /Weight (Medium) readonly def
+ /ItalicAngle 0 def
+ /isFixedPitch false def
+ /UnderlinePosition 0 def
+ /UnderlineThickness 0 def
+currentdict end readonly def
+
+/T1NF % <fontname> T1NF <font>
+{
+20 dict begin
+ /FontName exch def
+ /FontType 1 def
+ /FontInfo T1FontInfo def
+ /FontMatrix [.001 0 0 .001 0 0] def
+ /FontBBox [0 0 1000 1000] def
+ /Encoding KanjiSubEncoding def
+ /CharStrings 150 dict def
+ /PaintType 0 def
+ /Private 2 dict def
+ Private begin
+ /BlueValues [] def
+ /password 5839 def
+ end
+FontName currentdict end definefont
+} def
+
+/T0NF % <fontname> T0NF <font>
+{
+20 dict begin
+ /FontName exch def
+ /FDepVector exch def
+ /FontType 0 def
+ /FontMatrix [1 0 0 1 0 0] def
+ /FMapType 2 def
+ /Encoding KanjiEncoding def
+FontName currentdict end definefont
+} def
+
+% Define the composite font and all the base fonts.
+
+/CompNF % <fontname> CompNF <font>
+{
+/newname1 exch def
+newname1 dup length string cvs /str exch def
+str length /len exch def
+/fdepvector 78 array def
+/j 1 def
+16#21 1 16#74 {
+/i exch def
+KanjiEncoding i get 0 gt {
+len 4 add string /newstr exch def
+newstr 0 str putinterval
+newstr len (.r) putinterval
+newstr len 2 add i 16 2 string cvrs putinterval
+newstr cvn /newlit exch def
+newlit T1NF /newfont exch def
+fdepvector j newfont put
+/j j 1 add def
+} if
+} for
+fdepvector 0 fdepvector 1 get put
+/j 0 def
+fdepvector newname1 T0NF
+} def
+
+% Define an individual character in a composite font.
+/CompD % <charstring> <(HL)> CompD -
+ { currentfont /Encoding get 1 index 0 get get % FDepVector index
+ currentfont /FDepVector get exch get % base font
+ dup /Encoding get 3 -1 roll 1 get get % base font character name
+ exch /CharStrings get exch 3 -1 roll put
+ } bind def
+
+exec
diff --git a/pstoraster/gs_ksb_e.ps b/pstoraster/gs_ksb_e.ps
new file mode 100644
index 000000000..765a05e3b
--- /dev/null
+++ b/pstoraster/gs_ksb_e.ps
@@ -0,0 +1,70 @@
+% Copyright (C) 1994 Aladdin Enterprises. All rights reserved.
+%
+% This file is part of GNU Ghostscript.
+%
+% GNU Ghostscript is distributed in the hope that it will be useful, but
+% WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+% anyone for the consequences of using it or for whether it serves any
+% particular purpose or works at all, unless he says so in writing. Refer to
+% the GNU General Public License for full details.
+%
+% Everyone is granted permission to copy, modify and redistribute GNU
+% Ghostscript, but only under the conditions described in the GNU General
+% Public License. A copy of this license is supposed to have been given to
+% you along with GNU Ghostscript so you can know your rights and
+% responsibilities. It should be in a file named COPYING. Among other
+% things, the copyright notice and this notice must be preserved on all
+% copies.
+%
+% Aladdin Enterprises is not affiliated with the Free Software Foundation or
+% the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+% does not depend on any other GNU software.
+
+% Define the KanjiSub encoding vector.
+/currentglobal where
+ { pop currentglobal { setglobal } true setglobal }
+ { { } }
+ifelse
+/KanjiSubEncoding
+%\x00
+ /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
+ /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
+ /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
+ /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
+%\x20
+ /.notdef /c21 /c22 /c23 /c24 /c25 /c26 /c27
+ /c28 /c29 /c2A /c2B /c2C /c2D /c2E /c2F
+ /c30 /c31 /c32 /c33 /c34 /c35 /c36 /c37
+ /c38 /c39 /c3A /c3B /c3C /c3D /c3E /c3F
+%\x40
+ /c40 /c41 /c42 /c43 /c44 /c45 /c46 /c47
+ /c48 /c49 /c4A /c4B /c4C /c4D /c4E /c4F
+ /c50 /c51 /c52 /c53 /c54 /c55 /c56 /c57
+ /c58 /c59 /c5A /c5B /c5C /c5D /c5E /c5F
+%\x60
+ /c60 /c61 /c62 /c63 /c64 /c65 /c66 /c67
+ /c68 /c69 /c6A /c6B /c6C /c6D /c6E /c6F
+ /c70 /c71 /c72 /c73 /c74 /c75 /c76 /c77
+ /c78 /c79 /c7A /c7B /c7C /c7D /c7E /.notdef
+%\x80
+ /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
+ /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
+ /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
+ /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
+%\xA0
+ /.notdef /c21 /c22 /c23 /c24 /c25 /c26 /c27
+ /c28 /c29 /c2A /c2B /c2C /c2D /c2E /c2F
+ /c30 /c31 /c32 /c33 /c34 /c35 /c36 /c37
+ /c38 /c39 /c3A /c3B /c3C /c3D /c3E /c3F
+%\xC0
+ /c40 /c41 /c42 /c43 /c44 /c45 /c46 /c47
+ /c48 /c49 /c4A /c4B /c4C /c4D /c4E /c4F
+ /c50 /c51 /c52 /c53 /c54 /c55 /c56 /c57
+ /c58 /c59 /c5A /c5B /c5C /c5D /c5E /c5F
+%\xE0
+ /c60 /c61 /c62 /c63 /c64 /c65 /c66 /c67
+ /c68 /c69 /c6A /c6B /c6C /c6D /c6E /c6F
+ /c70 /c71 /c72 /c73 /c74 /c75 /c76 /c77
+ /c78 /c79 /c7A /c7B /c7C /c7D /c7E /.notdef
+256 packedarray .defineencoding
+exec
diff --git a/pstoraster/gs_l2img.ps b/pstoraster/gs_l2img.ps
new file mode 100644
index 000000000..2a24c3cb0
--- /dev/null
+++ b/pstoraster/gs_l2img.ps
@@ -0,0 +1,191 @@
+% Copyright (C) 1995, 1996 Aladdin Enterprises. All rights reserved.
+%
+% This file is part of GNU Ghostscript.
+%
+% GNU Ghostscript is distributed in the hope that it will be useful, but
+% WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+% anyone for the consequences of using it or for whether it serves any
+% particular purpose or works at all, unless he says so in writing. Refer to
+% the GNU General Public License for full details.
+%
+% Everyone is granted permission to copy, modify and redistribute GNU
+% Ghostscript, but only under the conditions described in the GNU General
+% Public License. A copy of this license is supposed to have been given to
+% you along with GNU Ghostscript so you can know your rights and
+% responsibilities. It should be in a file named COPYING. Among other
+% things, the copyright notice and this notice must be preserved on all
+% copies.
+%
+% Aladdin Enterprises is not affiliated with the Free Software Foundation or
+% the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+% does not depend on any other GNU software.
+
+% Emulate the Level 2 dictionary-based image operator in Level 1,
+% except for Interpolate (ignored) and MultipleDataSources = true;
+% also, we require that the data source be either a procedure of a
+% particular form or a stream, not a string or a general procedure.
+
+% pdf2ps copies the portion of this file from %BEGIN to %END if Level 1
+% compatible output is requested.
+
+/currentglobal where
+ { pop currentglobal { setglobal } true setglobal }
+ { { } }
+ifelse
+
+/packedarray where
+ { pop }
+ { /packedarray { array astore readonly } bind def }
+ifelse
+
+%BEGIN
+
+11 dict /.csncompdict 1 index def begin
+ /DeviceGray { 1 /setgray load } bind def
+ /DeviceRGB { 3 /setrgbcolor load } bind def
+ /DeviceCMYK { 4 /setcmykcolor load } bind def
+ /Indexed
+ { dup 1 index 1 get //.csncompdict exch get exec
+ % Stack: [/Indexed base hival map] ncomp basesetcolor
+ 3 -1 roll 3 get mark 3 1 roll
+ % Stack: ncomp -mark- basesetcolor map
+ dup type /stringtype eq
+ { { -
+ { exch round cvi get 255 div
+ }
+ -
+ { exch round cvi 3 mul 2 copy 2 copy get 255 div
+ 3 1 roll 1 add get 255 div
+ 4 2 roll 2 add get 255 div
+ }
+ { exch round cvi 4 mul 2 copy 2 copy 2 copy get 255 div
+ 3 1 roll 1 add get 255 div
+ 4 2 roll 2 add get 255 div
+ 5 3 roll 3 add get 255 div
+ }
+ }
+ 4 index get aload pop counttomark -1 roll
+ }
+ { /exec load 3 -1 roll
+ % Stack: -mark- mapproc --exec-- basesetcolor
+ }
+ ifelse .packtomark cvx
+ exch pop 1 exch
+ } bind def
+ /Separation
+ { dup 2 index //.csncompdict exch get exec
+ % Stack: [/Separation name alt xform] ncomp altsetcolor
+ 3 -1 roll 3 get /exec load 3 -1 roll 3 array astore readonly cvx
+ exch pop 1 exch
+ } bind def
+ % Substitute device spaces for CIE spaces.
+ /CIEBasedA /DeviceGray load def
+ /CIEBasedABC /DeviceRGB load def
+ /CIEBasedDEF /DeviceRGB load def
+ /CIEBasedDEFG /DeviceCMYK load def
+end
+
+/.packtomark { counttomark packedarray exch pop } bind def
+
+/.csinextbits % - .csinextbits <bits>
+ % Uses b, nnb, i, row, mask, BitsPerComponent;
+ % sets b, nnb, i.
+ { /nnb nnb BitsPerComponent add
+ { dup 0 le { exit } if
+ /b b 8 bitshift row i get add def
+ /i i 1 add def 8 sub
+ }
+ loop def
+ b nnb bitshift mask and
+ } bind def
+
+% Note that the ColorSpace key must be present in the image dictionary.
+/.colorspaceimage % <imagedict> .colorspaceimage -
+ { save exch
+ dup length 15 add dict begin { cvlit def } forall
+ ColorSpace dup dup type /nametype ne { 0 get } if
+ .csncompdict exch get exec
+ /setpixelcolor exch def /ncomp exch def pop
+ /row ncomp BitsPerComponent mul Width mul 7 add 8 idiv string def
+ /mask 1 BitsPerComponent bitshift 1 sub def
+ /nextbits BitsPerComponent 8 eq
+ { { row i get /i i 1 add def } }
+ { /.csinextbits load }
+ ifelse def
+ /nextpixel mark 0 2 ncomp 1 sub 2 mul
+ { /nextbits cvx exch
+ Decode exch 2 getinterval
+ dup aload pop exch sub
+ dup mask eq { pop } { mask div /mul load 3 -1 roll } ifelse
+ 0 get dup 0 eq { pop } { /sub load 3 -1 roll } ifelse
+ }
+ for
+ /setpixelcolor load dup type /operatortype ne { /exec load } if
+ .packtomark cvx def
+ /readrow
+ /DataSource load dup type
+ dup /arraytype eq exch /packedarraytype eq or
+ { % Must be { <file> <string> ... }
+ aload length 1 add array /pop load exch astore
+ dup 1 row put cvx
+ }
+ { pop
+ % Adobe requires readstring to signal an error if given
+ % an empty string. Work around this nonsense here.
+ row length 0 eq
+ { { } }
+ { { DataSource row readstring pop pop } }
+ ifelse
+ }
+ ifelse def
+ ImageMatrix matrix invertmatrix concat
+ /imat matrix def
+ 0 1 Height 1 sub
+ { imat 5 3 -1 roll neg put
+%(.) print flush
+ readrow
+ /b 0 def /nnb 0 def /i 0 def
+ 0 1 Width 1 sub
+ { imat 4 3 -1 roll neg put nextpixel
+ 1 1 true imat {<80>} imagemask
+ }
+ for
+ }
+ for
+ end restore
+ } bind def
+
+%END
+exec
+currentfile closefile
+
+% Patch for testing.
+/.cincompdict 3 dict begin
+ 1 { {0 1} {/DeviceGray} } def
+ 3 { {0 1 0 1 0 1} {/DeviceRGB} } def
+ 4 { {0 1 0 1 0 1 0 1} {/DeviceCMYK} } def
+currentdict end def
+/.imagekeys [
+ /Decode /DataSource /ImageMatrix /BitsPerComponent /Height /Width
+] def
+/colorimage % <width> <height> <bits/comp> <matrix>
+ % <datasrc> false <ncomp> colorimage -
+ { 1 index { /colorimage load /rangecheck signalerror } if exch pop
+ //.cincompdict exch get exec
+ 7 dict begin /ColorSpace exch cvlit def
+ .imagekeys { exch cvlit def } forall
+ currentdict end .colorspaceimage
+ } bind odef
+/image
+ { dup type /dicttype ne
+ { 7 dict begin /ColorSpace /DeviceGray def [0 1]
+ .imagekeys { exch cvlit def } forall
+ currentdict end
+ }
+ { dup length 1 add dict .copydict dup /ColorSpace currentcolorspace put
+ }
+ ifelse
+ .colorspaceimage
+ } bind odef
+
+exec
diff --git a/pstoraster/gs_lev2.ps b/pstoraster/gs_lev2.ps
new file mode 100644
index 000000000..125872f1f
--- /dev/null
+++ b/pstoraster/gs_lev2.ps
@@ -0,0 +1,332 @@
+% Copyright (C) 1990, 1996 Aladdin Enterprises. All rights reserved.
+%
+% This file is part of GNU Ghostscript.
+%
+% GNU Ghostscript is distributed in the hope that it will be useful, but
+% WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+% anyone for the consequences of using it or for whether it serves any
+% particular purpose or works at all, unless he says so in writing. Refer to
+% the GNU General Public License for full details.
+%
+% Everyone is granted permission to copy, modify and redistribute GNU
+% Ghostscript, but only under the conditions described in the GNU General
+% Public License. A copy of this license is supposed to have been given to
+% you along with GNU Ghostscript so you can know your rights and
+% responsibilities. It should be in a file named COPYING. Among other
+% things, the copyright notice and this notice must be preserved on all
+% copies.
+%
+% Aladdin Enterprises is not affiliated with the Free Software Foundation or
+% the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+% does not depend on any other GNU software.
+
+% Initialization file for Level 2 functions.
+% When this is run, systemdict is still writable,
+% but (almost) everything defined here goes into level2dict.
+
+level2dict begin
+
+% ------ Miscellaneous ------ %
+
+(<<) cvn /mark load def
+(>>) cvn /.dicttomark load def
+/currentsystemparams { mark .currentsystemparams .dicttomark } odef
+/currentuserparams { mark .currentuserparams .dicttomark } odef
+/deviceinfo { currentdevice getdeviceprops .dicttomark readonly } odef
+/languagelevel 2 def
+% When running in Level 2 mode, this interpreter is supposed to be
+% compatible with PostScript version 2017 (I think).
+/version (2017) def
+
+% If binary tokens are supported by this interpreter,
+% set an appropriate default binary object format.
+/setobjectformat where
+ { pop
+ currentsystemparams dup
+ /RealFormat get (IEEE) eq { 1 } { 3 } ifelse
+ exch /ByteOrder get { 1 add } if
+ setobjectformat
+ } if
+
+% ------ Virtual memory ------ %
+
+/currentglobal /currentshared load def
+/gcheck /scheck load def
+/setglobal /setshared load def
+% We can make the global dictionaries very small, because they auto-expand.
+/globaldict currentdict /shareddict .knownget not { 4 dict } if def
+/GlobalFontDirectory SharedFontDirectory def
+
+% ------ IODevices ------ %
+
+/.getdevparams where
+ { pop /currentdevparams { .getdevparams .dicttomark } odef
+ } if
+/.putdevparams where
+ { pop /setdevparams { mark exch { } forall counttomark 2 add -1 roll .putdevparams } odef
+ } if
+
+% ------ Job control ------ %
+
+serverdict begin
+
+% We could protect the job information better, but we aren't attempting
+% (currently) to protect ourselves against maliciousness.
+
+/.jobsave null def % top-level save object
+/.jobsavelevel 0 def % save depth of job (0 if .jobsave is null,
+ % 1 otherwise)
+/.adminjob true def % status of current unencapsulated job
+
+/exitserver
+ { true exch startjob not { /exitserver /invalidaccess signalerror } if
+ } bind def
+
+end % serverdict
+
+%**************** The definition of startjob is not complete yet, since
+% it doesn't clear the exec stack, doesn't reset stdin/stdout,
+% doesn't run the job under its own control, and doesn't reset
+% other aspects of the interpreter.
+/startjob
+ { vmstatus pop pop serverdict /.jobsavelevel get eq
+ 1 index .checkpassword 0 gt and
+ { .checkpassword count 2 roll count 2 sub { pop } repeat
+ cleardictstack
+ serverdict /.jobsave get dup null eq { pop } { restore } ifelse
+ exch
+ { % unencapsulated job
+ serverdict /.jobsave null put
+ serverdict /.jobsavelevel 0 put
+ serverdict /.adminjob 3 -1 roll 1 gt put
+ }
+ { % encapsulated job
+ serverdict /.jobsave save put
+ serverdict /.jobsavelevel 1 put
+ userdict /quit /stop load put
+ pop
+ }
+ ifelse true
+ }
+ { pop pop false
+ }
+ ifelse
+ } odef
+
+systemdict begin
+/quit
+ { //systemdict /serverdict get /.jobsave get null eq
+ { //quit }
+ { //systemdict /quit get /invalidaccess signalerror }
+ ifelse
+ } bind odef
+end
+
+% ------ Compatibility ------ %
+
+% In Level 2 mode, the following replace the definitions that gs_statd.ps
+% installs in statusdict and serverdict.
+% Note that statusdict must be allocated in local VM.
+% We don't bother with many of these yet, and the ones defined in terms
+% of currentsystemparams are cavalier about allocating a dictionary
+% in order to retrieve a single element from it....
+
+/.dict1 { exch mark 3 1 roll .dicttomark } bind def
+
+currentglobal false setglobal 25 dict exch setglobal begin
+currentsystemparams
+
+/buildtime 1 index /BuildTime get def
+/byteorder 1 index /ByteOrder get def
+/checkpassword { .checkpassword 0 gt } bind def
+/defaulttimeouts
+ { currentsystemparams dup
+ /JobTimeout .knownget not { 0 } if
+ exch /WaitTimeout .knownget not { 0 } if
+ currentpagedevice /ManualFeedTimeout .knownget not { 0 } if
+ } bind def
+dup /DoStartPage known
+ { /dostartpage { currentsystemparams /DoStartPage get } bind def
+ /setdostartpage { /DoStartPage .dict1 setsystemparams } bind def
+ } if
+dup /StartupMode known
+ { /dosysstart { currentsystemparams /StartupMode get 0 ne } bind def
+ /setdosysstart { { 1 } { 0 } ifelse /StartupMode .dict1 setsystemparams } bind def
+ } if
+%****** Setting jobname is supposed to set userparams.JobName, too.
+/jobname { currentuserparams /JobName get } bind def
+/jobtimeout { currentuserparams /JobTimeout get } bind def
+%manualfeed
+%manualfeedtimeout
+/margins
+ { currentpagedevice /Margins .knownget { exch } { [0 0] } ifelse
+ } bind def
+%pagecount
+%pagestackorder
+/printername
+ { currentsystemparams /PrinterName .knownget not { () } if exch copy
+ } bind def
+%/ramsize { currentsystemparams /RamSize get } bind def
+/realformat 1 index /RealFormat get def
+
+/.setpagedevice where
+ { pop
+ /setdefaulttimeouts
+ { exch mark /ManualFeedTimeout 3 -1 roll
+ /Policies mark /ManualFeedTimeout 1 .dicttomark
+ .dicttomark setpagedevice
+ /WaitTimeout exch mark /JobTimeout 5 2 roll .dicttomark setsystemparams
+ } bind def
+ /setmargins
+ { exch 2 array astore /Margins .dict1 setpagedevice
+ } bind def
+ }
+if
+%setpagestackorder
+dup /PrinterName known
+ { /setprintername { /PrinterName .dict1 setsystemparams } bind def
+ } if
+currentuserparams /WaitTimeout known
+ { /waittimeout { currentuserparams /WaitTimeout get } bind def
+ } if
+
+/.setpagedevice where
+ { pop
+ /pagemargin
+ { currentpagedevice /PageOffset .knownget { 0 get } { 0 } ifelse
+ } bind def
+ /pageparams
+ { currentpagedevice
+ dup /Orientation .knownget { 1 and ORIENT1 { 1 xor } if } { 0 } ifelse exch
+ dup /PageSize get aload pop 3 index 0 ne { exch } if 3 2 roll
+ /PageOffset .knownget { 0 get } { 0 } ifelse 4 -1 roll
+ } bind def
+ /.setpagesize { 2 array astore /PageSize .dict1 setpagedevice } bind def
+ /setduplexmode { /Duplex .dict1 setpagedevice } bind def
+ /setpagemargin { 0 2 array astore /PageOffset .dict1 setpagedevice } bind def
+ /setpageparams
+ { mark /PageSize 6 -2 roll
+ 4 index 1 and ORIENT1 { 1 } { 0 } ifelse ne { exch } if 2 array astore
+ /Orientation 5 -1 roll ORIENT1 { 1 xor } if
+ /PageOffset counttomark 2 add -1 roll 0 2 array astore
+ .dicttomark setpagedevice
+ } bind def
+ /setresolution
+ { dup 2 array astore /HWResolution .dict1 setpagedevice
+ } bind def
+ }
+if
+
+pop % currentsystemparams
+
+% Flag the current dictionary so it will be swapped when we
+% change language levels. (See zmisc2.c for more information.)
+/statusdict currentdict def
+
+currentdict end
+/statusdict exch def
+
+% ------ Color spaces ------ %
+
+% Define the setcolorspace procedures.
+/colorspacedict mark
+ /DeviceGray { pop 0 setgray } bind
+ /DeviceRGB { pop 0 0 0 setrgbcolor } bind
+ /setcmykcolor where
+ { pop /DeviceCMYK { pop 0 0 0 1 setcmykcolor } bind
+ } if
+ /.setcieaspace where
+ { pop /CIEBasedA { NOCIE { pop 0 setgray } { 1 get .setcieaspace } ifelse } bind
+ } if
+ /.setcieabcspace where
+ { pop /CIEBasedABC { NOCIE { pop 0 0 0 setrgbcolor } { 1 get .setcieabcspace } ifelse } bind
+ } if
+ /.setciedefspace where
+ { pop /CIEBasedDEF { NOCIE { pop 0 0 0 setrgbcolor } { 1 get .setciedefspace } ifelse } bind
+ } if
+ /.setciedefgspace where
+ { pop /CIEBasedDEFG { NOCIE { pop 0 0 0 1 setcmykcolor } { 1 get .setciedefgspace } ifelse } bind
+ } if
+ /.setseparationspace where
+ { pop /Separation { dup 2 get setcolorspace .setseparationspace } bind
+ } if
+ /.setindexedspace where
+ { pop /Indexed { dup 1 get setcolorspace .setindexedspace } bind
+ } if
+ /.setpatternspace where
+ { pop /Pattern
+ { dup length 1 gt { dup 1 get setcolorspace } if
+ .setpatternspace
+ } bind
+ } if
+.dicttomark def
+
+/.devcs [/DeviceGray /DeviceRGB /DeviceCMYK] readonly def
+/currentcolorspace
+ { .currentcolorspace dup type /integertype eq
+ { //.devcs exch 1 getinterval
+ } if
+ } odef
+currentdict /.devcs .undef
+
+/setcolorspace
+ { dup type /nametype eq { 1 array astore } if
+ dup //colorspacedict 1 index 0 get get exec
+ .setcolorspace
+ } odef
+
+% Initialize the CIE rendering dictionary if necessary.
+% The most common CIE files seem to assume the "calibrated RGB color space"
+% described on p. 189 of the PostScript Language Reference Manual,
+% 2nd Edition; we simply invert this transformation back to RGB.
+/setcolorrendering where
+ { pop mark
+ /ColorRenderingType 1
+% We must make RangePQR and RangeLMN large enough so that values computed by
+% the assumed encoding MatrixLMN don't get clamped.
+ /RangePQR [0 0.9505 0 1 0 1.0890]
+ /TransformPQR [ { } dup dup ]
+ /RangeLMN [0 0.9505 0 1 0 1.0890]
+ /MatrixABC
+ [ 3.24063 -0.96893 0.05571
+ -1.53721 1.87576 -0.20402
+ -0.49863 0.04152 1.05700
+ ]
+ /EncodeABC [{0 max 0.45 exp} bind dup dup]
+ /WhitePoint [0.9505 1 1.0890]
+ .dicttomark setcolorrendering
+ } if
+
+% ------ Painting ------ %
+
+% A straightforward definition of execform that doesn't actually
+% do any caching.
+/execform
+ { dup /Implementation known not
+ { dup /FormType get 1 ne { /rangecheck signalerror } if
+ dup /Implementation null put readonly
+ } if
+ gsave dup /Matrix get concat
+ dup /BBox get aload pop
+ exch 3 index sub exch 2 index sub rectclip
+ dup /PaintProc get exec
+ grestore
+ } odef
+
+/makepattern
+ { currentglobal
+ { false setglobal .buildpattern true setglobal }
+ { .buildpattern }
+ ifelse
+ exch dup length 1 add dict .copydict
+ dup /Implementation 4 -1 roll put
+ readonly
+ } odef
+
+/setpattern
+ { currentcolorspace 0 get /Pattern ne
+ { [ /Pattern currentcolorspace ] setcolorspace } if
+ setcolor
+ } odef
+
+end % level2dict
diff --git a/pstoraster/gs_mex_e.ps b/pstoraster/gs_mex_e.ps
new file mode 100644
index 000000000..fb4ba6936
--- /dev/null
+++ b/pstoraster/gs_mex_e.ps
@@ -0,0 +1,70 @@
+% Copyright (C) 1994 Aladdin Enterprises. All rights reserved.
+%
+% This file is part of GNU Ghostscript.
+%
+% GNU Ghostscript is distributed in the hope that it will be useful, but
+% WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+% anyone for the consequences of using it or for whether it serves any
+% particular purpose or works at all, unless he says so in writing. Refer to
+% the GNU General Public License for full details.
+%
+% Everyone is granted permission to copy, modify and redistribute GNU
+% Ghostscript, but only under the conditions described in the GNU General
+% Public License. A copy of this license is supposed to have been given to
+% you along with GNU Ghostscript so you can know your rights and
+% responsibilities. It should be in a file named COPYING. Among other
+% things, the copyright notice and this notice must be preserved on all
+% copies.
+%
+% Aladdin Enterprises is not affiliated with the Free Software Foundation or
+% the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+% does not depend on any other GNU software.
+
+% Define the MacExpert encoding vector.
+/currentglobal where
+ { pop currentglobal { setglobal } true setglobal }
+ { { } }
+ifelse
+/MacExpertEncoding
+% \00x
+ /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
+ /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
+ /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
+ /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
+% \04x
+ /space /exclamsmall /Hungarumlautsmall /centoldstyle /dollaroldstyle /dollarsuperior /ampersandsmall /Acutesmall
+ /parenleftsuperior /parenrightsuperior /twodotenleader /onedotenleader /comma /hyphen /period /fraction
+ /zerooldstyle /oneoldstyle /twooldstyle /threeoldstyle /fouroldstyle /fiveoldstyle /sixoldstyle /sevenoldstyle
+ /eightoldstyle /nineoldstyle /colon /semicolon /.notdef /threequartersemdash /.notdef /questionsmall
+% \10x
+ /.notdef /.notdef /.notdef /.notdef /Ethsmall /.notdef /.notdef /onequarter
+ /onehalf /threequarters /oneeighth /threeeighths /fiveeighths /seveneighths /onethird /twothirds
+ /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /ff /fi
+ /fl /ffi /ffl /parenleftinferior /.notdef /parenrightinferior /Circumflexsmall /hypheninferior
+% \14x
+ /Gravesmall /Asmall /Bsmall /Csmall /Dsmall /Esmall /Fsmall /Gsmall
+ /Hsmall /Ismall /Jsmall /Ksmall /Lsmall /Msmall /Nsmall /Osmall
+ /Psmall /Qsmall /Rsmall /Ssmall /Tsmall /Usmall /Vsmall /Wsmall
+ /Xsmall /Ysmall /Zsmall /colonmonetary /onefitted /rupiah /Tildesmall /.notdef
+% \20x
+ /.notdef /asuperior /centsuperior /.notdef /.notdef /.notdef /.notdef /Aacutesmall
+ /Agravesmall /Acircumflexsmall /Adieresissmall /Atildesmall /Aringsmall /Ccedillasmall /Eacutesmall /Egravesmall
+ /Ecircumflexsmall /Edieresissmall /Iacutesmall /Igravesmall /Icircumflexsmall /Idieresissmall /Ntildesmall /Oacutesmall
+ /Ogravesmall /Ocircumflexsmall /Odieresissmall /Otildesmall /Uacutesmall /Ugravesmall /Ucircumflexsmall /Udieresissmall
+% \24x
+ /.notdef /eightsuperior /fourinferior /threeinferior /sixinferior /eightinferior /seveninferior /Scaronsmall
+ /.notdef /centinferior /twoinferior /.notdef /Dieresissmall /.notdef /Caronsmall /osuperior
+ /fiveinferior /.notdef /commainferior /periodinferior /Yacutesmall /.notdef /dollarinferior /.notdef
+ /.notdef /Thornsmall /.notdef /nineinferior /zeroinferior /Zcaronsmall /AEsmall /Oslashsmall
+% \30x
+ /questiondownsmall /oneinferior /Lslashsmall /.notdef /.notdef /.notdef /.notdef /.notdef
+ /.notdef /Cedillasmall /.notdef /.notdef /.notdef /.notdef /.notdef /OEsmall
+ /figuredash /hyphensuperior /.notdef /.notdef /.notdef /.notdef /exclamdownsmall /.notdef
+ /Ydieresissmall /.notdef /onesuperior /twosuperior /threesuperior /foursuperior /fivesuperior /sixsuperior
+% \34x
+ /sevensuperior /ninesuperior /zerosuperior /.notdef /esuperior /rsuperior /tsuperior /.notdef
+ /.notdef /isuperior /ssuperior /dsuperior /.notdef /.notdef /.notdef /.notdef
+ /.notdef /lsuperior /Ogoneksmall /Brevesmall /Macronsmall /bsuperior /nsuperior /msuperior
+ /commasuperior /periodsuperior /Dotaccentsmall /Ringsmall /.notdef /.notdef /.notdef /.notdef
+256 packedarray .defineencoding
+exec
diff --git a/pstoraster/gs_mro_e.ps b/pstoraster/gs_mro_e.ps
new file mode 100644
index 000000000..f77cc7461
--- /dev/null
+++ b/pstoraster/gs_mro_e.ps
@@ -0,0 +1,63 @@
+% Copyright (C) 1994 Aladdin Enterprises. All rights reserved.
+%
+% This file is part of GNU Ghostscript.
+%
+% GNU Ghostscript is distributed in the hope that it will be useful, but
+% WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+% anyone for the consequences of using it or for whether it serves any
+% particular purpose or works at all, unless he says so in writing. Refer to
+% the GNU General Public License for full details.
+%
+% Everyone is granted permission to copy, modify and redistribute GNU
+% Ghostscript, but only under the conditions described in the GNU General
+% Public License. A copy of this license is supposed to have been given to
+% you along with GNU Ghostscript so you can know your rights and
+% responsibilities. It should be in a file named COPYING. Among other
+% things, the copyright notice and this notice must be preserved on all
+% copies.
+%
+% Aladdin Enterprises is not affiliated with the Free Software Foundation or
+% the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+% does not depend on any other GNU software.
+
+% Define the MacRoman encoding vector.
+/currentglobal where
+ { pop currentglobal { setglobal } true setglobal }
+ { { } }
+ifelse
+/MacRomanEncoding
+StandardEncoding 0 39 getinterval aload pop
+ /quotesingle
+StandardEncoding 40 56 getinterval aload pop
+ /grave
+StandardEncoding 97 31 getinterval aload pop
+% \20x
+ /Adieresis /Aring /Ccedilla /Eacute /Ntilde /Odieresis /Udieresis /aacute
+ /agrave /acircumflex /adieresis /atilde /aring /ccedilla /eacute /egrave
+ /ecircumflex /edieresis /iacute /igrave
+ /icircumflex /idieresis /ntilde /oacute
+ /ograve /ocircumflex /odieresis /otilde
+ /uacute /ugrave /ucircumflex /udieresis
+% \24x
+ /dagger /degree /cent /sterling /section /bullet /paragraph /germandbls
+ /registered /copyright /trademark /acute /dieresis /.notdef /AE /Oslash
+ /.notdef /plusminus /.notdef /.notdef /yen /mu /.notdef /.notdef
+ /.notdef /.notdef /.notdef /ordfeminine /ordmasculine /.notdef /ae /oslash
+% \30x
+ /questiondown /exclamdown /logicalnot /.notdef
+ /florin /.notdef /.notdef /guillemotleft
+ /guillemotright /ellipsis /space /Agrave /Atilde /Otilde /OE /oe
+ /endash /emdash /quotedblleft /quotedblright
+ /quoteleft /quoteright /divide /.notdef
+ /ydieresis /Ydieresis /fraction /currency
+ /guilsingleft /guilsingright /fi /fl
+% \34x
+ /daggerdbl /periodcentered /quotesinglbase /quotedblbase
+ /perthousand /Acircumflex /Ecircumflex /Aacute
+ /Edieresis /Egrave /Iacute /Icircumflex
+ /Idieresis /Igrave /Oacute /Ocircumflex
+ /.notdef /Ograve /Uacute /Ucircumflex
+ /Ugrave /dotlessi /circumflex /tilde
+ /macron /breve /dotaccent /ring /cedilla /hungarumlaut /ogonek /caron
+256 packedarray .defineencoding
+exec
diff --git a/pstoraster/gs_pdf.ps b/pstoraster/gs_pdf.ps
new file mode 100644
index 000000000..2c4ce8df4
--- /dev/null
+++ b/pstoraster/gs_pdf.ps
@@ -0,0 +1,575 @@
+% Copyright (C) 1994, 1996 Aladdin Enterprises. All rights reserved.
+%
+% This file is part of GNU Ghostscript.
+%
+% GNU Ghostscript is distributed in the hope that it will be useful, but
+% WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+% anyone for the consequences of using it or for whether it serves any
+% particular purpose or works at all, unless he says so in writing. Refer to
+% the GNU General Public License for full details.
+%
+% Everyone is granted permission to copy, modify and redistribute GNU
+% Ghostscript, but only under the conditions described in the GNU General
+% Public License. A copy of this license is supposed to have been given to
+% you along with GNU Ghostscript so you can know your rights and
+% responsibilities. It should be in a file named COPYING. Among other
+% things, the copyright notice and this notice must be preserved on all
+% copies.
+%
+% Aladdin Enterprises is not affiliated with the Free Software Foundation or
+% the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+% does not depend on any other GNU software.
+
+% gs_pdf.ps
+% ProcSet for PostScript files created by the PDF to PostScript converter.
+% This ProcSet requires only a Level 1 interpreter.
+
+% pdf2ps copies this file from %BEGIN to the end.
+
+%BEGIN
+mark % patches
+/currentglobal { false }
+/setglobal { pop }
+/packedarray { array astore readonly }
+/setcmykcolor
+ { 1 exch sub
+ 4 -1 roll 1 exch sub 1 index mul
+ 4 -1 roll 1 exch sub 2 index mul
+ 4 -2 roll exch 1 exch sub mul
+ setrgbcolor
+ }
+/.copydict
+ { dup 3 -1 roll { put dup } forall pop }
+/.dicttomark
+ { counttomark 2 idiv dup dict begin { def } repeat pop currentdict end }
+/.knownget
+ { 2 copy known { get true } { pop pop false } ifelse }
+counttomark 2 idiv
+ { 1 index where { pop pop pop } { bind executeonly def } ifelse
+ } repeat pop
+
+currentglobal true setglobal
+
+% Define pdfmark. Don't allow it to be bound in.
+% Also don't define it in systemdict, because this leads some Adobe code
+% to think this interpreter is a distiller.
+% (If this interpreter really is a distiller, don't do this.)
+systemdict /pdfmark known not
+ { userdict /pdfmark { cleartomark } bind put } if
+
+% This ProcSet is designed so that it can be used either to execute PDF
+% (the default) or to convert PDF to PostScript. See ! and ~ below.
+
+userdict /GS_PDF_ProcSet 119 dict dup begin
+
+% ---------------- Abbreviations ---------------- %
+
+/bdef { bind def } bind def
+
+% ---------------- Operator execution ---------------- %
+
+% We record "operator" names in a dictionary with their argument counts,
+% so that they can easily be redefined later to write PostScript in
+% addition to (or instead of) being executed.
+
+/numargsdict 100 dict def
+/! % <procname> <proc> <numargs> ! -
+ { //numargsdict 3 index 3 -1 roll put def
+ } bdef
+/~ % <procname> <opname> <numargs> ~ -
+ { exch cvx 1 packedarray cvx exch !
+ } bdef
+
+% ---------------- Graphics state stack ---------------- %
+
+% PDF adds a number of parameters to the graphics state.
+% We implement this by pushing and popping a dictionary
+% each time we do a PDF gsave or grestore.
+% The keys in this dictionary are as follows:
+% self % identifies the dictionary as one of ours
+% Show
+% TextOrigin % origin of current line, in text space
+% TextSaveMatrix % matrix at time of BT
+% (The following correspond directly to PDF state parameters.)
+% FillColor
+% FillColorSpace
+% StrokeColor
+% StrokeColorSpace
+% TextSpacing
+% TextHScaling
+% Leading
+% TextFont
+% TextMatrix
+% TextRise
+% TextRenderingMode
+% WordSpacing
+
+/nodict 1 dict def
+nodict /self { //nodict } executeonly put
+nodict readonly pop
+
+/beginpage
+ { //nodict 20 dict .copydict begin graphicsbeginpage textbeginpage
+ } bdef
+/endpage
+ { showpage end
+ } bdef
+
+/graphicsbeginpage { initgraphics 0 g 0 G } bdef
+
+/gput % <value> <key> gput -
+ { exch currentdict //nodict eq { /self dup load end 5 dict begin def } if
+ % If we're in a Level 1 system, we need to grow the
+ % dictionary explicitly.
+ currentdict length currentdict maxlength ge %eq
+ { currentdict dup length 3 mul 2 idiv 1 add dict .copydict end begin
+ }
+ if def
+ } bdef
+
+/q_
+ { gsave //nodict begin
+ } bdef
+/q /q_ load 0 !
+% Some PDF files have excess Q operators!
+/Q_
+ { currentdict /self .knownget { exec //nodict eq { end grestore } if } if
+ } bdef
+/Q /Q_ load 0 !
+
+% ---------------- Graphics state parameters ---------------- %
+
+/d /setdash 2 ~
+/i /setflat 1 ~
+/j /setlinejoin 1 ~
+/J /setlinecap 1 ~
+/M /setmiterlimit 1 ~
+/w /setlinewidth 1 ~
+
+% ---------------- Color setting ---------------- %
+
+/fcput % <color> <colorspace> fcput -
+ { /FillColorSpace gput /FillColor gput
+ } bdef
+/scput % <color> <colorspace> scput -
+ { /StrokeColorSpace gput /StrokeColor gput
+ } bdef
+
+/csdevgray [/DeviceGray] readonly def
+/csdevrgb [/DeviceRGB] readonly def
+/csdevcmyk [/DeviceCMYK] readonly def
+
+/CSdict 11 dict dup begin
+ /DeviceGray { 0 exch } bdef
+ /DeviceRGB { [0 0 0] cvx exch } bdef
+ /DeviceCMYK { [0 0 0 1] cvx exch } bdef
+ /Indexed
+ { dup 1 get csset exch pop
+ dup 2 index 1 get eq
+ { pop }
+ { exch 4 array copy dup 1 4 -1 roll put }
+ ifelse 0 exch
+ } bdef
+ /setcolorrendering where
+ { pop
+ /CalGray
+ { 1 get dup /Gamma .knownget
+ { dup length 1 add dict .copydict
+ dup /DecodeA 4 -1 roll /exp load 2 packedarray cvx put
+ }
+ if /CIEBasedA exch 2 array astore 0 exch
+ } bdef
+ /CalRGB
+ { 1 get dup /Gamma known 1 index /Matrix known or
+ { dup length 2 add dict .copydict
+ dup /Matrix .knownget { 1 index /MatrixABC 3 -1 roll put } if
+ dup /Gamma .knownget
+ { [ exch { /exp load 2 packedarray cvx } forall
+ ] 1 index /DecodeABC 3 -1 roll put
+ }
+ if
+ }
+ if /CIEBasedABC exch 2 array astore [0 0 0] cvx exch
+ } bdef
+ /CalCMYK { pop //csdevcmyk csset } bdef % not supported yet
+ }
+ { /CalGray { pop //csdevgray csset } bdef
+ /CalRGB { pop //csdevrgb csset } bdef
+ /CalCMYK { pop //csdevcmyk csset } bdef
+ }
+ ifelse
+end def
+/csset % <cspace> csset <color> <cspace>
+ { dup dup type /nametype ne { 0 get } if //CSdict exch get exec
+ } bdef
+
+/g { //csdevgray fcput } 1 !
+/G { //csdevgray scput } 1 !
+/rg { 3 array astore cvx //csdevrgb fcput } 3 !
+/RG { 3 array astore cvx //csdevrgb scput } 3 !
+/k { 4 array astore cvx //csdevcmyk fcput } 4 !
+/K { 4 array astore cvx //csdevcmyk scput } 4 !
+/cs { csset fcput } 1 !
+/CS { csset scput } 1 !
+% We have to break up sc according to the number of operands.
+/sc1 { /FillColor gput } 1 !
+/SC1 { /StrokeColor gput } 1 !
+/sc3 { /FillColor load astore pop } 3 !
+/SC3 { /StrokeColor load astore pop } 3 !
+/sc4 { /FillColor load astore pop } 4 !
+/SC4 { /StrokeColor load astore pop } 4 !
+
+% ---------------- Color installation ---------------- %
+
+% Establish a given color (and color space) as current.
+/setfillcolor { FillColor FillColorSpace setgcolor } def
+/setstrokecolor { StrokeColor StrokeColorSpace setgcolor } def
+/CIdict mark % only used for Level 1
+ /DeviceGray 1 /DeviceRGB 3 /DeviceCMYK 4
+ /CIEBaseA 1 /CIEBaseABC 3 /CIEBasedDEF 3 /CIEBaseDEFG 4
+.dicttomark def
+/Cdict 11 dict dup begin % <color...> <colorspace> -proc- -
+ /DeviceGray { pop setgray } bdef
+ /DeviceRGB { pop setrgbcolor } bdef
+ /DeviceCMYK { pop setcmykcolor } bdef
+ /CIEBasedA
+ { dup currentcolorspace eq { pop } { setcolorspace } ifelse setcolor } bdef
+ /CIEBasedABC /CIEBasedA load def
+ /CIEBasedDEF /CIEBasedA load def
+ /CIEBasedDEFG /CIEBasedA load def
+ /Indexed /setcolorspace where
+ { pop /CIEBasedA load }
+ { /setindexedcolor cvx }
+ ifelse def
+end def
+/setindexedcolor % <index> [/Indexed base hival proc|str]
+ % setindexedcolor - (only used for Level 1)
+ { mark 3 -1 roll
+ 2 index 3 get % Stack: cspace -mark- index proc|str
+ dup type /stringtype eq
+ { //CIdict 4 index 1 get 0 get get % # of components
+ dup 4 -1 roll mul exch getinterval { 255 div } forall
+ }
+ { exec
+ }
+ ifelse
+ counttomark 2 add -2 roll pop
+ 1 get setgcolor
+ } bdef
+/setgcolor % (null | <color...>) <colorspace> setgcolor -
+ { 1 index null eq
+ { pop pop }
+ { dup 0 get //Cdict exch get exec }
+ ifelse
+ } bdef
+/fsexec % <fillop|strokeop> fsexec -
+ { % Preserve the current point, if any.
+ { currentpoint } stopped
+ { $error /newerror false put cvx exec }
+ { 3 -1 roll cvx exec moveto }
+ ifelse
+ } bdef
+
+% ---------------- Transformations ---------------- %
+
+/cmmatrix matrix def
+/cm { //cmmatrix astore concat } 6 !
+
+% ---------------- Path creation ---------------- %
+
+/m /moveto 2 ~
+/l /lineto 2 ~
+/c /curveto 6 ~
+/h /closepath 0 ~
+/v { currentpoint 6 2 roll curveto } 4 !
+/y { 2 copy curveto } 4 !
+/re
+ { 4 2 roll moveto exch dup 0 rlineto 0 3 -1 roll rlineto neg 0 rlineto
+ closepath
+ } 4 !
+
+% ---------------- Path painting and clipping ---------------- %
+
+/S_ { setstrokecolor /stroke fsexec } bdef
+/S { S_ } 0 !
+/f { setfillcolor /fill fsexec } 0 !
+/f* { setfillcolor /eofill fsexec } 0 !
+/n_ { newpath } bdef % don't allow n_ to get bound in
+/n { n_ } 0 !
+/s { closepath S_ } 0 !
+/B_ { gsave setfillcolor fill grestore S_ } bdef
+/B /B_ load 0 !
+/b { closepath B_ } 0 !
+/B*_ { gsave setfillcolor eofill grestore S_ } bdef
+/B* /B*_ load 0 !
+/b* { closepath B*_ } 0 !
+
+% Clipping:
+
+/Wdict 4 dict dup begin
+/S_ { gsave setstrokecolor stroke grestore n_ } bdef
+/f { gsave setfillcolor fill grestore n_ } 0 !
+/f* { gsave setfillcolor eofill grestore n_ } 0 !
+/n_ { end clip newpath } bdef
+end readonly def
+/W { //Wdict begin } 0 !
+/W*dict 4 dict dup begin
+/S_ { gsave setstrokecolor stroke grestore n_ } bdef
+/f { gsave setfillcolor fill grestore n_ } 0 !
+/f* { gsave setfillcolor eofill grestore n_ } 0 !
+/n_ { end eoclip newpath } bdef
+end readonly def
+/W* { //W*dict begin } 0 !
+
+% ---------------- Images ---------------- %
+
+% We mustn't bind these now, since they reference Level 2 operators.
+/Is % <imagedict> Is <imagedict> <datasource>
+ { dup /DataSource get string /readstring cvx /currentfile cvx
+ % Stack: imagedict string -readstring- -currentfile-
+ 3 index /FilterProc .knownget
+ { dup dup 0 get /ASCIIHexDecode eq exch length 2 eq and
+ { pop exch pop /readhexstring cvx exch }
+ { exch exec exch exec }
+ ifelse
+ }
+ if 3 1 roll /pop cvx 4 packedarray cvx
+ } bdef
+/EI { } def % placeholder, only needed when writing PostScript
+% Note that ID* take a dictionary, not separate values;
+% ColorSpace must be a name if it has no parameters;
+% DataSource is the size of the row buffer in bytes;
+% FilterProc is an optional procedure for constructing the decoding filter;
+% and ImageMask is required, not optional.
+/csimage
+ { /setcolorspace where
+ { pop dup /ColorSpace get csset setcolorspace pop image }
+ { .colorspaceimage }
+ ifelse
+ } def % don't bind, because of Level 2
+/ID % <imagedict> ID -
+ { Is dup 3 -1 roll dup /ImageMask get
+ { setfillcolor dup /Interpolate .knownget not { false } if
+ { dup /DataSource 4 -1 roll put /imagemask cvx exec
+ }
+ { { /Width /Height /Decode /ImageMatrix }
+ { 1 index exch get exch }
+ forall pop exch 0 get 0 ne exch
+ 5 -1 roll imagemask
+ }
+ ifelse
+ }
+ { dup /ColorSpace get /DeviceGray eq
+ 1 index /BitsPerComponent get 8 le and
+ 1 index /Decode get dup 1 get 1 eq exch 0 get 0 eq and and
+ 1 index /Interpolate .knownget not { false } if not and
+ { { /Width /Height /BitsPerComponent /ImageMatrix }
+ { 1 index exch get exch }
+ forall pop 5 -1 roll image
+ }
+ { dup /DataSource 4 -1 roll put csimage
+ }
+ ifelse
+ }
+ ifelse
+ % If we were reading with readhexstring,
+ % skip the terminating > now.
+ % Stack: datasource
+ dup type /filetype ne % array or packedarray
+ { dup 2 get /readhexstring eq
+ { { dup 0 get exec read pop (>) 0 get eq { exit } if } loop
+ }
+ if pop
+ }
+ { pop
+ }
+ ifelse EI
+ } 1 !
+% IDx handles general images.
+/IDx % <imagedict> IDx -
+ { Is 1 index /DataSource 3 -1 roll put
+ csimage EI
+ } 1 !
+
+% ---------------- Text control ---------------- %
+
+/textbeginpage
+ { /TextSpacing 0 def % 0 Tc
+ /TextLeading 0 def % 0 TL
+ /TextRenderingMode 0 def % 0 Tr
+ /TextRise 0 def % 0 Ts
+ /WordSpacing 0 def % 0 Tw
+ /TextHScaling 1.0 def % 100 Tz
+ /TextFont null def
+ /Show { showfirst } def
+ } bdef
+
+% Contrary to the statement in the PDF manual, BT and ET *can* be nested,
+% if the CharProc for a Type 3 font does a BT/ET itself.
+% Since we always call the CharProc inside a q_/Q_, we simply ensure that
+% the text state is saved and restored like the rest of the extended
+% graphics state.
+
+/settextmatrix
+ { TextMatrix concat
+ TextHScaling 1 ne { TextHScaling 1 scale } if
+ TextRise 0 ne { 0 TextRise translate } if
+ } bdef
+/settextstate { TextSaveMatrix setmatrix settextmatrix } bdef
+
+/BT
+ { currentdict /TextMatrix .knownget
+ { identmatrix pop }
+ { matrix /TextMatrix gput }
+ ifelse
+ currentdict /TextOrigin .knownget
+ { dup 0 0 put 1 0 put }
+ { [0 0] cvx /TextOrigin gput }
+ ifelse
+ { showfirst } /Show gput
+ currentdict /TextSaveMatrix .knownget not
+ { matrix dup /TextSaveMatrix gput }
+ if currentmatrix pop settextmatrix 0 0 moveto
+ TextFont dup null eq { pop } { setfont } ifelse
+ } bind 0 !
+/ET
+ { TextSaveMatrix setmatrix
+ } bind 0 !
+/Tc_ { /TextSpacing gput { showfirst } /Show gput } bdef
+/Tc { Tc_ } 1 !
+/TL { /TextLeading gput } bind 1 !
+/Tr { /TextRenderingMode gput { showfirst } /Show gput } bind 1 !
+/Ts { /TextRise gput settextstate } bind 1 !
+/Tw_ { /WordSpacing gput { showfirst } /Show gput } bdef
+/Tw { Tw_ } 1 !
+/Tz { 100 div /TextHScaling gput settextstate } bind 1 !
+
+/Tf % <font> <scale> Tf -
+ { dup 1 eq { pop } { scalefont } ifelse
+ dup setfont /TextFont gput
+ } 2 !
+
+% Copy a font, removing its FID. If changed is true, also remove
+% the UniqueID and XUID, if any. If the original dictionary doesn't have
+% the keys being removed, don't copy it.
+/.copyfontdict % <font> <changed> .copyfontdict <dict>
+ { 1 index /FID known
+ 1 index { 2 index /UniqueID known or 2 index /XUID known or } if
+ { % We add 1 to the length just in case the original
+ % didn't have a FID.
+ exch dup length 1 add dict exch
+ { % Stack: changed newfont key value
+ 1 index /FID eq 4 index
+ { 2 index /UniqueID eq or 2 index /XUID eq or }
+ if not { 3 copy put } if pop pop
+ }
+ forall exch
+ }
+ if pop
+ } bdef
+
+% Insert a new Encoding or Metrics into a font if necessary.
+% Return a possibly updated font, and a flag to indicate whether
+% the font was actually copied.
+/.updatefont % <font> <Encoding|null> <Metrics|null> .updatefont
+ % <font'> <copied>
+ { 2 index 4 1 roll
+ dup null ne
+ { 3 -1 roll true .copyfontdict dup /Metrics 4 -1 roll put exch }
+ { pop }
+ ifelse
+ dup null ne 1 index 3 index /Encoding get ne and
+ { exch false .copyfontdict dup /Encoding 4 -1 roll put }
+ { pop }
+ ifelse exch 1 index ne
+ } bdef
+
+% ---------------- Text positioning ---------------- %
+
+/Td_
+ { TextOrigin exch 4 -1 roll add 3 1 roll add
+ 2 copy /TextOrigin load astore pop moveto
+ } bdef
+/Td { Td_ } 2 !
+/TD { dup neg /TextLeading gput Td_ } 2 !
+/T*_ { 0 TextLeading neg Td_ } bdef
+/T* { T*_ } 0 !
+/Tm
+ { TextMatrix astore pop settextstate
+ 0 0 /TextOrigin load astore pop
+ 0 0 moveto
+ } 6 !
+
+% ---------------- Text painting ---------------- %
+
+/textrenderingprocs [ % (0 is handled specially)
+ { tf } { tS } { tB } { tn }
+ % We don't know what the clipping modes mean....
+ 4 copy
+] readonly def
+/setshowstate
+ { WordSpacing 0 eq TextSpacing 0 eq and
+ { TextRenderingMode 0 eq
+ { { setfillcolor show } }
+ { { false charpath textrenderingprocs TextRenderingMode get exec } }
+ ifelse
+ }
+ { TextRenderingMode 0 eq
+ { WordSpacing 0 eq
+ { { setfillcolor TextSpacing exch 0 exch ashow } }
+ { TextSpacing 0 eq
+ { { setfillcolor WordSpacing exch 0 exch 32 exch widthshow } }
+ { { setfillcolor WordSpacing exch TextSpacing exch 0 32 4 2 roll 0 exch awidthshow } }
+ ifelse
+ }
+ ifelse
+ }
+ { { WordSpacing TextSpacing 2 index
+ % Implement the combination of t3 and false charpath.
+ % Stack: xword xchar string
+ 0 1 2 index length 1 sub
+ { 2 copy 1 getinterval false charpath
+ % Stack: xword xchar string i
+ 4 copy get 32 eq { add } { exch pop } ifelse 0 rmoveto
+ pop
+ }
+ for pop pop pop pop
+ textrenderingprocs TextRenderingMode get exec
+ }
+ }
+ ifelse
+ }
+ ifelse /Show gput
+ } bdef
+/showfirst { setshowstate Show } def
+
+/Tj { Show } 1 !
+/' { T*_ Show } 1 !
+/" { exch Tc_ exch Tw_ T*_ Show } 3 !
+% TJ expects a mark followed by arguments, not an array.
+/TJ
+ { counttomark -1 1
+ { -1 roll dup type /stringtype eq
+ { Show }
+ { neg 1000 div 0 rmoveto }
+ ifelse
+ }
+ for pop
+% Adobe implementations don't accept /[, so we don't either.
+ } ([) cvn !
+
+/tf { setfillcolor currentpoint fill moveto } bdef
+/tn { currentpoint newpath moveto } bdef
+% For stroking characters, temporarily restore the graphics CTM so that
+% the line width will be transformed properly.
+/Tmatrix matrix def
+/tS
+ { setstrokecolor
+ currentpoint //Tmatrix currentmatrix TextSaveMatrix setmatrix stroke
+ setmatrix moveto
+ } bdef
+/tB { gsave tf grestore tS } bdef
+
+end readonly put % GS_PDF_ProcSet
+
+setglobal
diff --git a/pstoraster/gs_pdf_e.ps b/pstoraster/gs_pdf_e.ps
new file mode 100644
index 000000000..ca1e934d2
--- /dev/null
+++ b/pstoraster/gs_pdf_e.ps
@@ -0,0 +1,48 @@
+% Copyright (C) 1994 Aladdin Enterprises. All rights reserved.
+%
+% This file is part of GNU Ghostscript.
+%
+% GNU Ghostscript is distributed in the hope that it will be useful, but
+% WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+% anyone for the consequences of using it or for whether it serves any
+% particular purpose or works at all, unless he says so in writing. Refer to
+% the GNU General Public License for full details.
+%
+% Everyone is granted permission to copy, modify and redistribute GNU
+% Ghostscript, but only under the conditions described in the GNU General
+% Public License. A copy of this license is supposed to have been given to
+% you along with GNU Ghostscript so you can know your rights and
+% responsibilities. It should be in a file named COPYING. Among other
+% things, the copyright notice and this notice must be preserved on all
+% copies.
+%
+% Aladdin Enterprises is not affiliated with the Free Software Foundation or
+% the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+% does not depend on any other GNU software.
+
+% Define the PDFDoc encoding vector.
+/currentglobal where
+ { pop currentglobal { setglobal } true setglobal }
+ { { } }
+ifelse
+/PDFDocEncoding
+ISOLatin1Encoding 0 24 getinterval aload pop
+ /breve /caron /circumflex /dotaccent /hungarumlaut /ogonek /ring /tilde
+ISOLatin1Encoding 32 7 getinterval aload pop
+ /quotesingle
+ISOLatin1Encoding 40 56 getinterval aload pop
+ /grave
+ISOLatin1Encoding 97 31 getinterval aload pop
+% \20x
+ /bullet /dagger /daggerdbl /ellipsis /emdash /endash /florin /fraction
+ /guilsinglleft /guilsinglright /minus /perthousand
+ /quotedblbase /quotedblleft /quotedblright /quoteleft
+ /quoteright /quotesinglbase /trademark /fi /fl /Lslash /OE /Scaron
+ /Ydieresis /Zcaron /dotlessi /lslash /oe /scaron /zcaron /.notdef
+% \24x
+ /.notdef
+ISOLatin1Encoding 161 12 getinterval aload pop
+ /.notdef
+ISOLatin1Encoding 174 82 getinterval aload pop
+256 packedarray .defineencoding
+exec
diff --git a/pstoraster/gs_pdfwr.ps b/pstoraster/gs_pdfwr.ps
new file mode 100644
index 000000000..6b982bdec
--- /dev/null
+++ b/pstoraster/gs_pdfwr.ps
@@ -0,0 +1,288 @@
+% Copyright (C) 1996 Aladdin Enterprises. All rights reserved.
+%
+% This file is part of GNU Ghostscript.
+%
+% GNU Ghostscript is distributed in the hope that it will be useful, but
+% WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+% anyone for the consequences of using it or for whether it serves any
+% particular purpose or works at all, unless he says so in writing. Refer to
+% the GNU General Public License for full details.
+%
+% Everyone is granted permission to copy, modify and redistribute GNU
+% Ghostscript, but only under the conditions described in the GNU General
+% Public License. A copy of this license is supposed to have been given to
+% you along with GNU Ghostscript so you can know your rights and
+% responsibilities. It should be in a file named COPYING. Among other
+% things, the copyright notice and this notice must be preserved on all
+% copies.
+%
+% Aladdin Enterprises is not affiliated with the Free Software Foundation or
+% the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+% does not depend on any other GNU software.
+
+% gs_pdfwr.ps
+% PDF writer additions to systemdict.
+
+% This file should be included iff the pdfwrite "device" is included
+% in the executable.
+
+% Redefine pdfmark to pass the data to the driver.
+/.pdf===dict mark
+ /arraytype
+ { dup xcheck { ({) (}) } { ([) (]) } ifelse
+ % Stack: file obj left right
+ 4 1 roll 2 index exch writestring () exch
+ { exch 2 index exch writestring
+ 1 index exch pdfmark===only ( )
+ }
+ forall pop exch writestring
+ } bind
+ /packedarraytype 1 index
+ /dicttype
+ { 1 index (<<\n) writestring
+ { 2 index 3 -1 roll pdfmark===only 1 index ( ) writestring
+ 1 index exch pdfmark===only dup (\n) writestring
+ }
+ forall (>>) writestring
+ } bind
+.dicttomark readonly def
+/pdfmark===only % <file> <obj> pdfmark===only -
+ { .pdf===dict 1 index type .knownget { exec } { write==only } ifelse
+ } bind def
+/.pdfcvs % <obj> .pdfcvs <string>
+ { % We can't handle long values yet.
+ =string /NullEncode filter dup 2 index pdfmark===only
+ dup (\n\000) writestring closefile pop
+ =string (\n\000) search
+ { dup length string copy exch pop exch pop }
+ { % The converted representation didn't fit. Punt.
+ pop (???)
+ }
+ ifelse
+ } bind def
+/.pdfputparams % <paramarray> <paramname> .pdfputparams <result...>
+ { currentdevice null false mark 6 -2 roll exch
+ % Don't allow the page device to get cleared....
+ {.putdeviceparams} 0 get .currentpagedevice pop {.setpagedevice} 0 get
+ 3 array astore cvx exec
+ } bind def
+/pdfmark
+ { ] 1 2 2 index length 1 sub { 2 copy 2 copy get .pdfcvs put pop } for
+ /pdfmark .pdfputparams
+ type /booleantype ne { cleartomark pop pop } if pop
+ } odef
+userdict /pdfmark .undef
+
+% Define setdistillerparams / currentdistillerparams.
+% Distiller parameters are currently treated as device parameters.
+/.distillerparamkeys mark
+ % General parameters
+ /CoreDistVersion { }
+ /DoThumbnails { }
+ /LZWEncodePages { }
+ /ASCII85EncodePages { }
+ % Color sampled image parameters
+ /DownsampleColorImages { }
+ /ColorImageResolution { }
+ /EncodeColorImages { }
+ /ColorImageFilter { }
+ /ColorImageDict { }
+ /ColorImageDepth { }
+ /AntiAliasColorImages { }
+ /ConvertCMYKImagesToRGB { }
+ % Grayscale sampled image parameters
+ /DownsampleGrayImages { }
+ /GrayImageResolution { }
+ /EncodeGrayImages { }
+ /GrayImageFilter { }
+ /GrayImageDict { }
+ /GrayImageDepth { }
+ /AntiAliasGrayImages { }
+ % Monochrome sampled image parameters
+ /DownsampleMonoImages { }
+ /MonoImageResolution { }
+ /EncodeMonoImages { }
+ /MonoImageFilter { }
+ /MonoImageDict { }
+ /MonoImageDepth { }
+ /AntiAliasMonoImages { }
+ % Font embedding parameters
+ /AlwaysEmbed
+ { dup length 0 gt
+ { dup 0 get false eq
+ { dup length 1 sub 1 exch getinterval exch pop /~AlwaysEmbed exch
+ } if
+ } if
+ }
+ /NeverEmbed
+ { dup length 0 gt
+ { dup 0 get false eq
+ { dup length 1 sub 1 exch getinterval exch pop /~NeverEmbed exch
+ } if
+ } if
+ }
+ /EmbedAllFonts { }
+ /SubsetFonts { }
+ /MaxSubsetPct { }
+.dicttomark readonly def
+/.distillerdevice
+ { currentdevice .devicename /pdfwrite eq
+ { currentdevice }
+ { /pdfwrite finddevice }
+ ifelse
+ } bind def
+/setdistillerparams % <dict> setdistillerparams -
+ { .distillerdevice null false mark 5 -1 roll
+ { //.distillerparamkeys 2 index .knownget { exec } { pop pop } ifelse }
+ forall .putdeviceparams
+ type /booleantype eq { pop } { cleartomark pop pop pop } ifelse
+ } odef
+/currentdistillerparams % - currentdistillerparams <dict>
+ { .distillerdevice //.distillerparamkeys .getdeviceparams .dicttomark
+ } odef
+
+% Patch the 'show' operators to pass the data to the device.
+% We use a pseudo-parameter named /show whose value is a dictionary:
+% /String (str)
+% /Values [cx cy char ax ay px py]
+% /Matrix [xx xy yx yy tx ty]
+% /FontName /fontname
+% /Color [r g b]
+% /Encoding [e0 .. e255]
+% /BaseEncoding [e0 ... e255]
+% THIS IS A BIG HACK.
+/.findorigfont % <font> .findorigfont <origfont>
+ { % Check for a known font with this name and
+ % the same UniqueID.
+ dup /UniqueID .knownget
+ { 1 index /FontName .knownget
+ { % Stack: font uniqueid fontname
+ FontDirectory exch .knownget
+ { dup /UniqueID .knownget
+ { % Stack: font uniqueid knownfont knownid
+ 3 -1 roll eq { true } { pop false } ifelse
+ }
+ { pop pop false
+ }
+ ifelse
+ }
+ { pop false
+ }
+ ifelse
+ }
+ { pop false
+ }
+ ifelse
+ }
+ { false
+ }
+ ifelse
+ % Stack: font knownfont -true- | font -false-
+ { exch pop
+ }
+ { { dup /OrigFont .knownget not { exit } if exch pop } loop
+ }
+ ifelse
+ } .bind def
+/.pdfdoshow % <string> <cxd> <cyd> <char> <axd> <ayd> .pdfdoshow
+ % <bool>
+ { mark /String 8 2 roll
+ currentpoint transform 7 array astore /Values exch
+ % Concatenate the "quotient" of the current FontMatrix
+ % and the FontMatrix of the original font.
+ % Be sure to include any translation.
+ /Matrix
+ currentfont .findorigfont /FontMatrix get matrix invertmatrix
+ currentfont /FontMatrix get 1 index concatmatrix
+ matrix currentmatrix dup 4 0 put dup 5 0 put dup concatmatrix
+ /FontName currentfont /FontName get
+ /Color [ currentrgbcolor ]
+ /Encoding currentfont /Encoding .knownget not { [] } if
+ % Make a reasonable guess at the base encoding.
+ /BaseEncoding StandardEncoding
+ .dicttomark /show .pdfputparams
+ dup type /booleantype eq
+ { pop pop true }
+ { dup /undefined eq
+ { cleartomark pop pop pop false }
+ { dup mark eq { /unknown /rangecheck } if
+ counttomark 4 add 1 roll cleartomark pop pop pop
+ /.pdfshow cvx exch signalerror
+ }
+ ifelse
+ }
+ ifelse
+ } .bind def
+/.pdfexecshow % <proc> .pdfexecshow -
+ { matrix currentmatrix gsave nulldevice setmatrix
+ exec currentpoint grestore moveto
+ } .bind def
+% Create a 1-element cache for currentdevice .devicename /pdfwrite eq.
+userdict begin
+ /.pdfwritedevice null def
+ /.pdfwriteenabled false def % place-holder
+end
+/.pdfwrite? % - .pdfwrite? <bool>
+ { currentdevice .pdfwritedevice eq
+ { .pdfwriteenabled
+ }
+ { currentdevice .devicename /pdfwrite eq
+ userdict /.pdfwriteenabled 2 index put
+ userdict /.pdfwritedevice currentdevice put
+ }
+ ifelse currentfont /FontType get 1 eq and
+ } .bind def
+/.pdfshow % <string> <cxd> <cyd> <char> <axd> <ayd> <showproc>
+ % .pdfdoshow -
+ { 7 1 roll .pdfwrite?
+ { .pdfdoshow }
+ { 6 { pop } repeat false }
+ ifelse
+ { .pdfexecshow }
+ { exec }
+ ifelse
+ } .bind def
+/show
+ { dup 0 0 32 0 0 { show } .pdfshow
+ } .bind odef
+/ashow
+ { dup 0 0 32 6 index 6 index dtransform { ashow } .pdfshow
+ } .bind odef
+/widthshow
+ { 4 copy 4 -2 roll dtransform 4 -1 roll 0 0 { widthshow } .pdfshow
+ } .bind odef
+/awidthshow
+ { 6 copy 6 -2 roll dtransform 6 -3 roll dtransform { awidthshow } .pdfshow
+ } .bind odef
+/glyphshow
+ { .pdfwrite?
+ { currentfont /Encoding .knownget not { {} } if
+ 0 1 2 index length 1 sub
+ { % Stack: glyph encoding index
+ 2 copy get 3 index eq { exch pop exch pop null exit } if pop
+ }
+ for null eq
+ { (X) dup 0 4 -1 roll put show }
+ { glyphshow }
+ ifelse
+ }
+ { glyphshow
+ }
+ ifelse
+ } .bind odef
+% The remaining operators aren't implemented correctly.
+/kshow
+ { .pdfwrite?
+ { { (X) dup 0 4 -1 roll put show dup exec } forall pop }
+ { kshow }
+ ifelse
+ } .bind odef
+/xshow
+ { .pdfwrite? { pop show } { xshow } ifelse
+ } .bind odef
+/yshow
+ { .pdfwrite? { pop show } { yshow } ifelse
+ } .bind odef
+/xyshow
+ { .pdfwrite? { pop show } { xyshow } ifelse
+ } .bind odef
diff --git a/pstoraster/gs_pfile.ps b/pstoraster/gs_pfile.ps
new file mode 100644
index 000000000..5d49a6448
--- /dev/null
+++ b/pstoraster/gs_pfile.ps
@@ -0,0 +1,184 @@
+% Copyright (C) 1994, 1995 Aladdin Enterprises. All rights reserved.
+%
+% This file is part of GNU Ghostscript.
+%
+% GNU Ghostscript is distributed in the hope that it will be useful, but
+% WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+% anyone for the consequences of using it or for whether it serves any
+% particular purpose or works at all, unless he says so in writing. Refer to
+% the GNU General Public License for full details.
+%
+% Everyone is granted permission to copy, modify and redistribute GNU
+% Ghostscript, but only under the conditions described in the GNU General
+% Public License. A copy of this license is supposed to have been given to
+% you along with GNU Ghostscript so you can know your rights and
+% responsibilities. It should be in a file named COPYING. Among other
+% things, the copyright notice and this notice must be preserved on all
+% copies.
+%
+% Aladdin Enterprises is not affiliated with the Free Software Foundation or
+% the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+% does not depend on any other GNU software.
+
+% Runtime support for minimum-space fonts and packed files.
+
+% ****** NOTE: This file must be kept consistent with
+% ****** packfile.ps and wrfont.ps.
+
+% ---------------- Packed file support ---------------- %
+
+% A packed file is the concatenation of several file groups, each of which
+% is the result of compressing several files concatenated together.
+% The packed file begins with a procedure that creates an appropriate
+% decoding filter for each file group, as follows:
+% <group-subfile-filter> -proc- <group-decode-filter>
+% Thus, accessing an individual file requires 4 parameters:
+% the starting address and length of the outer compressed file,
+% and the starting address and length of the inner file.
+/.packedfilefilter % <file> <ostart> <olength> <istart> <ilength>
+ % .packedfilefilter <filter>
+ { 4 index systemdict begin token pop end 6 1 roll
+ % Stack: fproc file ostart olength istart ilength
+ 4 index 5 -1 roll setfileposition
+ % Stack: fproc file olength istart ilength
+ 4 -2 roll () /SubFileDecode filter
+ % Stack: fproc istart ilength ofilter
+ 4 -1 roll exec
+ % Filters don't support setfileposition, so we must skip data
+ % by reading it into a buffer. We rely on the fact that
+ % save/restore don't affect file positions.
+ % Stack: istart ilength dfilter
+ save exch 1000 string
+ % Stack: istart ilength save dfilter scratch
+ 4 index 1 index length idiv { 2 copy readstring pop pop } repeat
+ 2 copy 0 8 -1 roll 2 index length mod getinterval readstring pop pop pop
+ % Stack: ilength save dfilter
+ exch restore exch () /SubFileDecode filter
+ } bind def
+
+% Run a packed library file.
+/.runpackedlibfile % <filename> <ostart> <olength> <istart> <ilength>
+ % .runpackedlibfile
+ { 5 -1 roll findlibfile
+ { exch pop dup 6 2 roll .packedfilefilter
+ currentobjectformat exch 1 setobjectformat run
+ setobjectformat closefile
+ }
+ { 5 1 roll /findlibfile load /undefinedfilename signalerror
+ }
+ ifelse
+ } bind def
+
+% ---------------- Compacted font support ---------------- %
+
+% Compacted fonts written by wrfont.ps depend on the existence and
+% specifications of the procedures and data in this section.
+
+/.compactfontdefault mark
+ /PaintType 0
+ /FontMatrix [0.001 0 0 0.001 0 0] readonly
+ /FontType 1
+ /Encoding StandardEncoding
+.dicttomark readonly def
+
+/.checkexistingfont % <fontname> <uid> <privatesize> <fontsize>
+ % .checkexistingfont
+ % {} (<font> on d-stack)
+ % <fontname> <uid> <privatesize> <fontsize>
+ % .checkexistingfont
+ % -save- --restore-- (<font> on d-stack)
+ { FontDirectory 4 index .knownget
+ { dup /UniqueID .knownget
+ { 4 index eq exch /FontType get 1 eq and }
+ { pop false }
+ ifelse
+ }
+ { false
+ }
+ ifelse
+ { save /restore load 6 2 roll }
+ { {} 5 1 roll }
+ ifelse
+ dict //.compactfontdefault exch .copydict begin
+ dict /Private exch def
+ Private begin
+ /MinFeature {16 16} def
+ /Password 5839 def
+ /UniqueID 1 index def
+ end
+ /UniqueID exch def
+ /FontName exch def
+ } bind def
+
+/.knownEncodings [
+ ISOLatin1Encoding
+ StandardEncoding
+ SymbolEncoding
+] readonly def
+
+/.readCharStrings % <count> <encrypt> .readCharStrings <dict>
+ { exch dup dict dup 3 -1 roll
+ { currentfile token pop dup type /integertype eq
+ { dup -8 bitshift //.knownEncodings exch get exch 255 and get } if
+ currentfile token pop dup type /nametype eq
+ { 2 index exch get
+ }
+ { % Stack: encrypt dict dict key value
+ 4 index { 4330 exch dup .type1encrypt exch pop } if
+ readonly
+ }
+ ifelse put dup
+ }
+ repeat pop exch pop
+ } bind def
+
+% ---------------- Synthetic font support ---------------- %
+
+% Create a new font by modifying an existing one. paramdict contains
+% entries with the same keys as the ones found in a Type 1 font;
+% it should also contain enough empty entries to allow adding the
+% corresponding non-overridden entries from the original font dictionary,
+% including FID. If paramdict includes a FontInfo entry, this will
+% also override the original font's FontInfo, entry by entry;
+% again, it must contain enough empty entries.
+
+% Note that this procedure does not perform a definefont.
+
+/.makemodifiedfont % <fontdict> <paramdict> .makemodifiedfont <fontdict'>
+ { exch
+ { % Stack: destdict key value
+ 1 index /FID ne
+ { 2 index 2 index known
+ { % Skip fontdict entry supplied in paramdict, but
+ % handle FontInfo specially.
+ 1 index /FontInfo eq
+ { 2 index 2 index get % new FontInfo
+ 1 index % old FontInfo
+ { % Stack: destdict key value destinfo key value
+ 2 index 2 index known
+ { pop pop }
+ { 2 index 3 1 roll put }
+ ifelse
+ }
+ forall pop
+ }
+ if
+ }
+ { % No override, copy the fontdict entry.
+ 2 index 3 1 roll put
+ dup dup % to match pop pop below
+ }
+ ifelse
+ }
+ if
+ pop pop
+ } forall
+ } bind def
+
+% Make a modified font and define it. Note that unlike definefont,
+% this does not leave the font on the operand stack.
+
+/.definemodifiedfont % <fontdict> <paramdict> .definemodifiedfont -
+ { .makemodifiedfont
+ dup /FontName get exch definefont pop
+ } bind def
diff --git a/pstoraster/gs_res.ps b/pstoraster/gs_res.ps
new file mode 100644
index 000000000..6b955e901
--- /dev/null
+++ b/pstoraster/gs_res.ps
@@ -0,0 +1,555 @@
+% Copyright (C) 1994, 1996 Aladdin Enterprises. All rights reserved.
+%
+% This file is part of GNU Ghostscript.
+%
+% GNU Ghostscript is distributed in the hope that it will be useful, but
+% WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+% anyone for the consequences of using it or for whether it serves any
+% particular purpose or works at all, unless he says so in writing. Refer to
+% the GNU General Public License for full details.
+%
+% Everyone is granted permission to copy, modify and redistribute GNU
+% Ghostscript, but only under the conditions described in the GNU General
+% Public License. A copy of this license is supposed to have been given to
+% you along with GNU Ghostscript so you can know your rights and
+% responsibilities. It should be in a file named COPYING. Among other
+% things, the copyright notice and this notice must be preserved on all
+% copies.
+%
+% Aladdin Enterprises is not affiliated with the Free Software Foundation or
+% the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+% does not depend on any other GNU software.
+
+% Initialization file for Level 2 resource machinery.
+% When this is run, systemdict is still writable,
+% but everything defined here goes into level2dict.
+
+level2dict begin
+
+(BEGIN RESOURCES) VMDEBUG
+
+% We keep track of (global) instances with another entry in the resource
+% dictionary, an Instances dictionary. For categories with implicit
+% instances, the values in Instances are the same as the keys;
+% for other categories, the values are [instance status size].
+
+% Note that the dictionary that defines a resource category is stored
+% in global memory. The PostScript manual says that each category must
+% manage global and local instances separately. However, objects in
+% global memory other than systemdict can't reference objects in local memory.
+% This means that the resource category dictionary, which would otherwise be
+% the obvious place to keep track of the instances, can't be used to keep
+% track of local instances. Instead, we define a dictionary in local VM
+% called localinstancedict, in which the key is the category name and
+% the value is the analogue of Instances for local instances.
+
+% We don't currently implement automatic resource unloading.
+% When and if we do, it should be hooked to the garbage collector.
+% However, Ed Taft of Adobe says their interpreters don't implement this
+% either, so we aren't going to worry about it for a while.
+
+currentglobal false setglobal systemdict begin
+ /localinstancedict 5 dict def
+end true setglobal
+/.emptydict 0 dict readonly def
+setglobal
+
+% Resource category dictionaries have the following keys (those marked with
+% * are optional):
+% Standard, defined in the Red Book:
+% Category (name)
+% *InstanceType (name)
+% DefineResource
+% UndefineResource
+% FindResource
+% ResourceStatus
+% ResourceForAll
+% *ResourceFileName
+% Additional, specific to our implementation:
+% Instances (dictionary)
+% .LocalInstances
+% - .LocalInstances <dict>
+% .GetInstance
+% <key> .GetInstance <instance> -true-
+% <key> .GetInstance -false-
+% .CheckResource
+% <value> .CheckResource <ok>
+% .DoLoadResource
+% <key> .DoLoadResource - (may give an error)
+% .LoadResource
+% <key> .LoadResource - (may give an error)
+% .ResourceFile
+% <key> .ResourceFile <file> -true-
+% <key> .ResourceFile <key> -false-
+% All the above procedures expect that the top dictionary on the d-stack
+% is the resource dictionary.
+
+% Define enough of the Category category so we can define other categories.
+% The dictionary we're about to create will become the Category
+% category definition dictionary.
+
+12 dict begin
+
+ % Standard entries
+
+/Category /Category def
+/InstanceType /dicttype def
+
+/DefineResource
+ { dup .CheckResource
+ { dup /Category 3 index cvlit .growput readonly
+ dup [ exch 0 -1 ] exch
+ Instances 4 2 roll put
+ }
+ { /typecheck signalerror
+ }
+ ifelse
+ } bind def
+/FindResource % (redefined below)
+ { Instances exch get 0 get
+ } bind def
+
+ % Additional entries
+
+/Instances 25 dict def
+Instances /Category [currentdict 0 -1] put
+
+/.LocalInstances 0 dict def
+/.GetInstance
+ { Instances exch .knownget
+ } bind def
+/.CheckResource
+ { dup gcheck currentglobal and
+ { /DefineResource /FindResource /ResourceForAll /ResourceStatus
+ /UndefineResource }
+ { 2 index exch known and }
+ forall exch pop
+ } bind def
+
+Instances end begin % for the base case of findresource
+
+(END CATEGORY) VMDEBUG
+
+% Define the resource operators. I don't see how we can possibly restore
+% the stacks after an error, since the procedure may have popped and
+% pushed elements arbitrarily....
+
+mark
+/defineresource % <key> <instance> <category> defineresource <instance>
+ { /Category findresource dup begin
+ /InstanceType known
+ { dup type InstanceType ne
+ { dup type /packedarraytype eq InstanceType /arraytype eq and
+ not { /typecheck signalerror } if
+ } if
+ } if
+ /DefineResource load stopped end { stop } if
+ }
+/findresource % <key> <category> findresource <instance>
+ { dup /Category eq
+ { pop //Category 0 get } { /Category findresource } ifelse
+ begin
+ /FindResource load stopped end { stop } if
+ }
+/resourceforall % <template> <proc> <scratch> <category> resourceforall -
+ { /Category findresource begin
+ /ResourceForAll load stopped end { stop } if
+ }
+/resourcestatus % <key> <category> resourcestatus <status> <size> true
+ % <key> <category> resourcestatus false
+ { /Category findresource begin
+ /ResourceStatus load stopped end { stop } if
+ }
+/undefineresource % <key> <category> undefineresource -
+ { /Category findresource begin
+ /UndefineResource load stopped end { stop } if
+ }
+end % Instances of Category
+counttomark 2 idiv { bind odef } repeat pop
+
+% Define the Generic category.
+
+/Generic mark
+
+ % Standard entries
+
+% We're still running in Level 1 mode, so dictionaries won't expand.
+% Leave room for the /Category entry.
+/Category null
+
+/DefineResource
+ { dup .CheckResource
+ { dup [ exch 0 -1 ] exch
+ currentglobal
+ { false setglobal 2 index UndefineResource % remove local def if any
+ true setglobal
+ Instances dup //.emptydict eq
+ { pop 3 dict /Instances 1 index def
+ }
+ if
+ }
+ { .LocalInstances dup //.emptydict eq
+ { pop 3 dict localinstancedict Category 2 index put
+ }
+ if
+ }
+ ifelse
+ 4 2 roll .growput
+ }
+ { /typecheck signalerror
+ }
+ ifelse
+ } bind
+/UndefineResource
+ { { dup 2 index .knownget
+ { dup 1 get 1 ge
+ { dup 0 null put 1 2 put pop pop }
+ { pop exch .undef }
+ ifelse
+ }
+ { pop pop
+ }
+ ifelse
+ }
+ currentglobal
+ { 2 copy Instances exch exec
+ }
+ if .LocalInstances exch exec
+ } bind
+/FindResource
+ { dup ResourceStatus
+ { pop 1 gt % not in VM
+ { .DoLoadResource
+ }
+ if
+ .GetInstance pop % can't fail
+ 0 get
+ }
+ { /undefinedresource signalerror
+ }
+ ifelse
+ } bind
+/ResourceStatus
+ { dup .GetInstance
+ { exch pop dup 1 get exch 2 get true }
+ { .ResourceFile
+ { closefile 2 -1 true }
+ { pop false }
+ ifelse
+ }
+ ifelse
+ } bind
+/ResourceForAll
+ { % **************** Doesn't present instance groups in
+ % **************** the correct order yet.
+ % We construct a new procedure so we don't have to use
+ % static resources to hold the iteration state.
+ 3 packedarray % template, proc, scratch
+ { exch pop % stack contains: key, {template, proc, scratch}
+ 2 copy 0 get .stringmatch
+ { 1 index type dup /stringtype eq exch /nametype eq or
+ { 2 copy 2 get cvs
+ exch 1 get 3 -1 roll pop
+ }
+ { 1 get
+ }
+ ifelse exec
+ }
+ { pop pop
+ }
+ ifelse
+ } /exec cvx 3 packedarray cvx
+ % We must pop the resource dictionary off the dict stack
+ % when doing the actual iteration, and restore it afterwards.
+ currentglobal .LocalInstances length 0 eq or not
+ { % We must do local instances, and do them first.
+ /forall cvx 1 index currentdict 3 packedarray cvx
+ .LocalInstances 3 1 roll end exec begin
+ }
+ if
+ Instances exch
+ /forall cvx currentdict 2 packedarray cvx
+ end exec begin
+ } bind
+
+ % Additional entries
+
+% Unfortunately, we can't create the real Instances dictionary now,
+% because if someone copies the Generic category (which pp. 95-96 of the
+% 2nd Edition Red Book says is legitimate), they'll wind up sharing
+% the Instances. Instead, we have to create Instances on demand,
+% just like the entry in localinstancedict.
+% We also have to prevent anyone from creating instances of Generic itself.
+/Instances //.emptydict
+
+/.LocalInstances
+ { localinstancedict Category .knownget not { //.emptydict } if
+ } bind
+/.GetInstance
+ { currentglobal
+ { Instances exch .knownget }
+ { .LocalInstances 1 index .knownget
+ { exch pop true }
+ { Instances exch .knownget }
+ ifelse
+ }
+ ifelse
+ } bind
+/.CheckResource
+ { pop true
+ } bind
+/.DoLoadResource
+ { dup vmstatus pop exch pop exch
+ .LoadResource
+ vmstatus pop exch pop exch sub
+ 1 index .GetInstance not
+ { pop /undefinedresource signalerror } % didn't load
+ if
+ dup 1 1 put
+ 2 3 -1 roll put
+ } bind
+/.LoadResource
+ { dup .ResourceFile
+ { exch pop currentglobal
+ { run }
+ { true setglobal { run } stopped false setglobal { stop } if }
+ ifelse
+ }
+ { /undefinedresource signalerror
+ }
+ ifelse
+ } bind
+/.ResourceFile
+ { currentdict /ResourceFileName known
+ { mark 1 index 100 string { ResourceFileName }
+ .internalstopped
+ { cleartomark false }
+ { exch pop findlibfile
+ { exch pop exch pop true }
+ { false }
+ ifelse
+ }
+ ifelse
+ }
+ { false }
+ ifelse
+ } bind
+
+.dicttomark
+/Category defineresource pop
+
+% Fill in the rest of the Category category.
+/Category /Category findresource dup
+/Generic /Category findresource begin
+ { /FindResource /ResourceForAll /ResourceStatus /UndefineResource /.ResourceFile }
+ { dup load put dup } forall
+pop readonly pop end
+
+(END GENERIC) VMDEBUG
+
+% Define the fixed categories.
+
+mark
+ % Things other than types
+ /ColorSpaceFamily
+ mark colorspacedict { pop } forall .packtomark
+ /Emulator
+ mark EMULATORS { cvn } forall .packtomark
+ /Filter
+ mark filterdict { pop } forall .packtomark
+ /IODevice
+ % Loop until the .getiodevice gets a rangecheck.
+ errordict /rangecheck 2 copy get
+ errordict /rangecheck { pop stop } put % pop the command
+ mark 0 { {dup .getiodevice exch 1 add} loop} .internalstopped
+ pop pop pop .packtomark
+ 4 1 roll put
+ .clearerror
+ % Types
+ /setcolorrendering where
+ { pop /ColorRenderingType
+ {1}
+ } if
+buildfontdict 0 known
+ { /FMapType
+ {2 3 4 5 6 7 8}
+ } if
+ /FontType
+ [ buildfontdict { pop } forall ]
+ /FormType
+ {1}
+ /HalftoneType
+ {1 2 3 4 5}
+ /ImageType
+ {1}
+ /PatternType
+ {1} % should check for Pattern color space
+counttomark 2 idiv
+ { mark
+
+ % Standard entries
+
+ /DefineResource
+ { /invalidaccess signalerror } bind
+ /UndefineResource
+ { /invalidaccess signalerror } bind
+ /FindResource
+ { Instances exch get } bind
+ /ResourceStatus
+ { Instances exch known { 0 0 true } { false } ifelse } bind
+ /ResourceForAll
+ /Generic /Category findresource /ResourceForAll get
+
+ % Additional entries
+
+ counttomark 2 add -1 roll
+ dup length dict dup begin exch { dup def } forall end readonly
+ /Instances exch
+ /.LocalInstances % used by ResourceForAll
+ 0 dict def
+
+ .dicttomark /Category defineresource pop
+ } repeat pop
+
+(END FIXED) VMDEBUG
+
+% Define the other built-in categories.
+
+/.definecategory % <name> -mark- <key1> ... <valuen> .definecategory -
+ { counttomark 2 idiv 2 add % Instances, Category
+ /Generic /Category findresource dup maxlength 3 -1 roll add
+ dict .copydict begin
+ counttomark 2 idiv { def } repeat pop % pop the mark
+ currentdict end /Category defineresource pop
+ } bind def
+
+/ColorRendering mark /InstanceType /dicttype .definecategory
+/ColorSpace mark /InstanceType /arraytype .definecategory
+/Form mark /InstanceType /dicttype .definecategory
+/Halftone mark /InstanceType /dicttype .definecategory
+/Pattern mark /InstanceType /dicttype .definecategory
+/ProcSet mark /InstanceType /dicttype .definecategory
+
+(END MISC) VMDEBUG
+
+% Define the Encoding category.
+
+/Encoding mark
+
+/InstanceType /arraytype
+
+% Handle already-registered encodings, including lazily loaded encodings
+% that aren't loaded yet.
+
+/Instances mark
+ EncodingDirectory
+ { dup length 256 eq { [ exch 0 -1 ] } { pop [null 2 -1] } ifelse
+ } forall
+.dicttomark
+
+/.ResourceFileDict mark
+ EncodingDirectory
+ { dup length 256 eq { pop pop } { 0 get } ifelse
+ } forall
+.dicttomark
+
+/ResourceFileName
+ { exch dup .ResourceFileDict exch .knownget
+ { exch pop exch copy }
+ { exch pop /undefinedresource signalerror }
+ ifelse
+ } bind
+
+.definecategory % Encoding
+
+/.findencoding { /Encoding findresource } bind def
+/findencoding /.findencoding load odef
+/.defineencoding
+ { 2 copy /Encoding defineresource pop
+ //EncodingDirectory 3 1 roll put
+ } bind def
+
+(END ENCODING) VMDEBUG
+
+% Define the Font category.
+
+/Font mark
+
+/InstanceType /dicttype
+
+/DefineResource
+ { 2 copy //definefont exch pop
+ /Generic /Category findresource /DefineResource get exec
+ } bind
+/UndefineResource
+ { dup //undefinefont
+ /Generic /Category findresource /UndefineResource get exec
+ } bind
+/FindResource
+ { dup ResourceStatus
+ { pop 1 gt { .DoLoadResource } if }
+ { .DoLoadResource }
+ ifelse
+ .GetInstance pop 0 get
+ } bind
+
+/.LoadResource
+ { //findfont exec pop
+ } bind
+
+/Instances FontDirectory length 2 mul dict
+
+.definecategory % Font
+
+% Redefine font "operators".
+/.definefontmap
+ { /Font /Category findresource /Instances get
+ dup 3 index known
+ { pop
+ }
+ { 2 index
+ % Make sure we create the array in global VM.
+ .currentglobal true .setglobal
+ [null 2 -1] exch .setglobal
+ .growput
+ }
+ ifelse
+ //.definefontmap exec
+ } bind def
+
+% Make sure the old definitions are still in systemdict so that
+% they will get bound properly.
+systemdict begin
+ /.origdefinefont /definefont load def
+ /.origundefinefont /undefinefont load def
+ /.origfindfont /findfont load def
+end
+/definefont
+ { /Font defineresource } bind odef
+/undefinefont
+ { /Font undefineresource } bind odef
+/findfont
+ { /Font findresource } bind def % Must be a procedure, not an operator
+
+% Remove initialization utilities.
+currentdict /.definecategory .undef
+currentdict /.emptydict .undef
+
+end % level2dict
+
+% Arrange to convert encodings to resources when we switch to Level 2.
+
+/.ressetll /.setlanguagelevel load def
+/.setlanguagelevel
+ { dup .ressetll 2 eq
+ { //systemdict /.setlanguagelevel //systemdict /.ressetll get put
+ //systemdict /.ressetll undef
+ EncodingDirectory
+ { dup length 256 eq
+ { /Encoding defineresource pop }
+ { pop pop }
+ ifelse
+ } forall
+ }
+ if
+ } bind def
diff --git a/pstoraster/gs_setpd.ps b/pstoraster/gs_setpd.ps
new file mode 100644
index 000000000..ef41bb519
--- /dev/null
+++ b/pstoraster/gs_setpd.ps
@@ -0,0 +1,644 @@
+% Copyright (C) 1994, 1996 Aladdin Enterprises. All rights reserved.
+%
+% This file is part of GNU Ghostscript.
+%
+% GNU Ghostscript is distributed in the hope that it will be useful, but
+% WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+% anyone for the consequences of using it or for whether it serves any
+% particular purpose or works at all, unless he says so in writing. Refer to
+% the GNU General Public License for full details.
+%
+% Everyone is granted permission to copy, modify and redistribute GNU
+% Ghostscript, but only under the conditions described in the GNU General
+% Public License. A copy of this license is supposed to have been given to
+% you along with GNU Ghostscript so you can know your rights and
+% responsibilities. It should be in a file named COPYING. Among other
+% things, the copyright notice and this notice must be preserved on all
+% copies.
+%
+% Aladdin Enterprises is not affiliated with the Free Software Foundation or
+% the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+% does not depend on any other GNU software.
+
+% The current implementation of setpagedevice has the following limitations:
+% - It doesn't attempt to "interact with the user" for Policy = 2.
+
+languagelevel 1 .setlanguagelevel
+level2dict begin
+
+% ---------------- Redefinitions ---------------- %
+
+% Redefine .beginpage and .endpage so that they call BeginPage and
+% EndPage respectively if appropriate.
+
+/.beginpage
+ { .currentshowpagecount
+ { .currentpagedevice pop /BeginPage .knownget { exec } { pop } ifelse }
+ if
+ } bind odef
+
+/.endpage
+ { .currentshowpagecount
+ { exch .currentpagedevice pop /EndPage .knownget
+ { exec }
+ { exch pop 2 ne }
+ ifelse
+ }
+ { 2 ne
+ }
+ ifelse
+ } bind odef
+
+% Define interpreter callouts for handling gstate-saving operators,
+% to make sure that they create a page device dictionary for use by
+% the corresponding gstate-restoring operator.
+% We'd really like to avoid the cost of doing this, but we don't see how.
+% The names %gsavepagedevice, %savepagedevice, %gstatepagedevice,
+% %copygstatepagedevice, and %currentgstatepagedevice are known to the
+% interpreter.
+
+(%gsavepagedevice) cvn
+ { currentpagedevice pop gsave
+ } bind def
+
+(%savepagedevice) cvn
+ { currentpagedevice pop save
+ } bind def
+
+(%gstatepagedevice) cvn
+ { currentpagedevice pop gstate
+ } bind def
+
+(%copygstatepagedevice) cvn
+ { currentpagedevice pop copy
+ } bind def
+
+(%currentgstatepagedevice) cvn
+ { currentpagedevice pop currentgstate
+ } bind def
+
+% Define interpreter callouts for handling gstate-restoring operators
+% when the current page device needs to be changed.
+% The names %grestorepagedevice, %grestoreallpagedevice,
+% %restorepagedevice, and %setgstatepagedevice are known to the interpreter.
+
+/.installpagedevice
+ { % Since setpagedevice doesn't create new device objects,
+ % we must (carefully) reinstall the old parameters in
+ % the same device.
+ .currentpagedevice pop null currentdevice null .trysetparams
+ dup type /booleantype eq
+ { pop pop }
+ { % This should never happen!
+ DEBUG { (Error in .trysetparams!\n) print pstack flush } if
+ cleartomark pop pop pop
+ /.installpagedevice cvx /rangecheck signalerror
+ }
+ ifelse pop pop
+ erasepage initgraphics .beginpage
+ } bind def
+
+/.uninstallpagedevice
+ { 2 .endpage { .currentnumcopies false .outputpage } if
+ nulldevice
+ } bind def
+
+(%grestorepagedevice) cvn
+ { .uninstallpagedevice grestore .installpagedevice
+ } bind def
+
+(%grestoreallpagedevice) cvn
+ { .uninstallpagedevice grestore .installpagedevice grestoreall
+ } bind def
+
+(%restorepagedevice) cvn
+ { .uninstallpagedevice grestore .installpagedevice restore
+ } bind def
+
+(%setgstatepagedevice) cvn
+ { .uninstallpagedevice setgstate .installpagedevice
+ } bind def
+
+% Redefine .currentnumcopies so it consults the NumCopies device parameter.
+/.numcopiesdict mark
+ /NumCopies dup
+.dicttomark readonly def
+
+/.currentnumcopies
+ { currentdevice //.numcopiesdict .getdeviceparams
+ dup type /integertype eq
+ { exch pop exch pop }
+ { cleartomark #copies }
+ ifelse
+ } bind odef
+
+% ---------------- Auxiliary definitions ---------------- %
+
+% Define the required attributes of all page devices, and their default values.
+% We don't include attributes such as .MediaSize, which all devices
+% are guaranteed to supply on their own.
+/.defaultpolicies mark
+ /PolicyNotFound 1
+ /PageSize 0
+ /PolicyReport {pop} bind
+.dicttomark readonly def
+/.requiredattrs mark
+ /PageOffset [0 0] readonly
+% We define InputAttributes and OutputAttributes with a single
+% dummy media type that handles pages of any size.
+% Devices that care will override this.
+ /InputAttributes mark 0
+ mark /PageSize [0 dup 16#7ffff dup] .dicttomark readonly
+ .dicttomark readonly
+ (%MediaSource) 0
+ /OutputAttributes mark 0
+ mark .dicttomark readonly
+ .dicttomark readonly
+ (%MediaDestination) 0
+ /Install {.callinstall} bind
+ /BeginPage {.callbeginpage} bind
+ /EndPage {.callendpage} bind
+ /Policies .defaultpolicies
+.dicttomark readonly def
+
+% Define currentpagedevice so it creates the dictionary on demand if needed,
+% adding all the required entries defined just above.
+% We have to deal specially with entries that the driver may change
+% on its own.
+/.dynamicppkeys mark
+ /.MediaSize dup % because it changes when PageSize is set
+ /PageCount dup
+.dicttomark readonly def
+/.makecurrentpagedevice % - .makecurrentpagedevice <dict>
+ { currentdevice null .getdeviceparams
+ % In case of duplicate keys, .dicttomark takes the entry
+ % lower on the stack, so we can just append the defaults here.
+ .requiredattrs { } forall .dicttomark
+ dup .setpagedevice
+ } bind def
+/currentpagedevice
+ { .currentpagedevice
+ { dup length 0 eq
+ { pop .makecurrentpagedevice
+ }
+ { % If any of the dynamic keys have changed,
+ % we must update the page device dictionary.
+ currentdevice //.dynamicppkeys .getdeviceparams .dicttomark
+ { % Stack: current key value
+ 2 index 2 index .knownget { 1 index ne } { true } ifelse
+ { 2 index wcheck not
+ { % This is the first entry being updated.
+ % Copy the dictionary to make it writable.
+ 3 -1 roll dup length dict .copydict
+ 3 1 roll
+ }
+ if
+ 2 index 3 1 roll put
+ }
+ { pop pop
+ }
+ ifelse
+ }
+ forall
+ % We would like to do a .setpagedevice so we don't keep
+ % re-creating the dictionary. Unfortunately, the effect
+ % of this is that if any dynamic key changes (PageCount
+ % in particular), we will do the equivalent of a
+ % setpagedevice at the next restore or grestore.
+ % Therefore, we make the dictionary read-only, but
+ % we don't store it away. I.e., NOT:
+ % dup wcheck { .setpagedevice .currentpagedevice pop } if
+ readonly
+ }
+ ifelse
+ }
+ if
+ } bind odef
+
+% The implementation of setpagedevice is quite complex. Currently,
+% everything but the media matching algorithm is implemented here.
+
+% By default, we only present the requested changes to the device,
+% but there are some parameters that require special merging action.
+% Define those parameters here, with the procedures that do the merging.
+% The procedures are called as follows:
+% <merged> <key> <new_value> -proc- <merged> <key> <new_value'>
+/.mergespecial mark
+ /InputAttributes
+ { dup null eq
+ { pop null
+ }
+ { 3 copy pop .knownget
+ { dup null eq
+ { pop dup length dict }
+ { dup length 2 index length add dict .copydict }
+ ifelse
+ }
+ { dup length dict
+ }
+ ifelse .copydict readonly
+ }
+ ifelse
+ } bind
+ /OutputAttributes 1 index
+ /Policies
+ { 3 copy pop .knownget
+ { dup length 2 index length add dict .copydict }
+ { dup length dict }
+ ifelse copy readonly
+ } bind
+.dicttomark readonly def
+
+% Define the keys used in input attribute matching.
+/.inputattrkeys [
+%%%% MRS - The current CUPS backend driver does not register any of
+%%%% these attributes with GhostScript from the PPD file. As
+%%%% a result, we must comment out everything but PageSize
+%%%% which is processed differently from all other attributes.
+% /PageSize /MediaColor /MediaWeight /MediaType /InsertSheet
+ /PageSize
+] readonly def
+% Define other keys used in media selection.
+/.inputselectionkeys [
+%%%% MRS - The current CUPS backend driver does not register any of
+%%%% these attributes with GhostScript from the PPD file. As
+%%%% a result, we must comment out everything.
+% /MediaPosition /Orientation
+] readonly def
+
+% Define the keys used in output attribute matching.
+/.outputattrkeys [
+%%%% MRS - The current CUPS backend driver does not register any of
+%%%% these attributes with GhostScript from the PPD file. As
+%%%% a result, we must comment out everything.
+% /OutputType
+] readonly def
+
+% Define all the parameters that should always be copied to the merged
+% dictionary.
+/.copiedkeys [
+ /OutputDevice
+ .mergespecial { pop } forall
+ .inputattrkeys aload pop
+ .inputselectionkeys aload pop
+ .outputattrkeys aload pop
+] readonly def
+
+% Define the parameters that should not be presented to the device.
+% The procedures are called as follows:
+% <merged> <key> <value> -proc-
+% The procedure leaves all its operands on the stack and returns
+% true iff the key/value pair should be presented to .putdeviceparams.
+/.presentspecial mark
+ .dynamicppkeys { pop false } forall
+ % We must ignore an explicit request for .MediaSize,
+ % because media matching always handles this.
+ /.MediaSize false
+ /Name false
+ /OutputDevice false
+ /PageOffset false
+ /PageSize false % obsolete alias for .MediaSize
+ /InputAttributes false
+ .inputattrkeys
+ { dup /PageSize eq
+ { pop }
+ { { 2 index /InputAttributes .knownget { null eq } { true } ifelse } }
+ ifelse
+ }
+ forall
+ .inputselectionkeys { false } forall
+ /OutputAttributes false
+ .outputattrkeys
+ { { 2 index /OutputAttributes .knownget { null eq } { true } ifelse } }
+ forall
+ /Install false
+ /BeginPage false
+ /EndPage false
+ /Policies false
+ % Our extensions:
+ /HWColorMap
+ { % HACK: don't transmit the color map, because
+ % window systems can change the color map on their own
+ % incrementally. Someday we'll have a better
+ % solution for this....
+ false
+ }
+ /ViewerPreProcess false
+.dicttomark readonly def
+
+% Define access to device defaults.
+/.defaultdeviceparams
+ { finddevice null .getdeviceparams
+ } bind def
+
+% Select media (input or output). The hard work is done in an operator:
+% <pagedict> <attrdict> <policydict> <keys> .matchmedia <key> true
+% <pagedict> <attrdict> <policydict> <keys> .matchmedia false
+% <pagedict> null <policydict> <keys> .matchmedia null true
+/.selectmedia % <orig> <request> <merged> <failed> <-- retained
+ % <attrdict> <policydict> <attrkeys> <mediakey>
+ % .selectmedia
+ { 5 index 5 -2 roll 4 index .matchmedia
+ % Stack: orig request merged failed attrkeys mediakey
+ % (key true | false)
+ { 4 index 3 1 roll put pop
+ }
+ { % Adobe's implementations have a "big hairy heuristic"
+ % to choose the set of keys to report as having failed the match.
+ % For the moment, we report any keys that are in the request
+ % and don't have the same value as in the original dictionary.
+ 5 index 1 index .knownget
+ { 4 index 3 1 roll put }
+ { 3 index exch .undef }
+ ifelse
+ { % Stack: <orig> <request> <merged> <failed> <attrkey>
+ 3 index 1 index .knownget
+ { 5 index 2 index .knownget { ne } { pop true } ifelse }
+ { true }
+ ifelse % Stack: ... <failed> <attrkey> <report>
+ { 2 copy /rangecheck put }
+ if pop
+ }
+ forall
+ }
+ ifelse
+ } bind def
+
+% Apply Policies to any unprocessed failed requests.
+% As we process each request entry, we replace the error name
+% in the <failed> dictionary with the policy value,
+% and we replace the key in the <merged> dictionary with its prior value
+% (or remove it if it had no prior value).
+/.applypolicies % <orig> <merged> <failed> .applypolicies
+ % <orig> <merged'> <failed'>
+ { 1 index /Policies get 1 index
+ { type /integertype eq
+ { pop % already processed
+ }
+ { 2 copy .knownget not { 1 index /PolicyNotFound get } if
+ % Stack: <orig> <merged> <failed> <Policies> <key>
+ % <policy>
+ dup 1 ne
+ { % Set errorinfo and signal a configurationerror.
+ % Note that we currently treat all Policy values other than 1
+ % the same as 0.
+ pop dup 4 index exch get 2 array astore
+ $error /errorinfo 3 -1 roll put
+ cleartomark
+ /setpagedevice load /configurationerror signalerror
+ }
+ { % Roll back the failed request to its previous status.
+DEBUG { (Rolling back.\n) print pstack flush } if
+ 3 index 2 index 3 -1 roll put
+ 4 index 1 index .knownget
+ { 4 index 3 1 roll put }
+ { 3 index exch .undef }
+ ifelse
+ }
+ ifelse
+ }
+ ifelse
+ }
+ forall pop
+ } bind def
+
+% Prepare to present parameters to the device, by spreading them onto the
+% operand stack and removing any that shouldn't be presented.
+/.prepareparams % <params> .prepareparams -mark- <key1> <value1> ...
+ { mark exch dup
+ { % Stack: -mark- key1 value1 ... merged key value
+ .presentspecial 2 index .knownget
+ { exec { 3 -1 roll } { pop pop } ifelse }
+ { 3 -1 roll }
+ ifelse
+ }
+ forall pop
+ } bind def
+
+% Put device parameters without resetting currentpagedevice.
+% (.putdeviceparams clears the current page device.)
+/.putdeviceparamsonly % <device> <Policies|null> <require_all> -mark-
+ % <key1> <value1> ... .putdeviceparamsonly
+ % On success: <device> <eraseflag>
+ % On failure: <device> <Policies|null> <req_all> -mark-
+ % <key1> <error1> ...
+ { .currentpagedevice
+ { counttomark 4 add 1 roll .putdeviceparams
+ dup type /booleantype eq { 3 } { counttomark 5 add } ifelse -1 roll
+ .setpagedevice
+ }
+ { pop .putdeviceparams
+ }
+ ifelse
+ } bind def
+
+% Try setting the device parameters from the merged request.
+/.trysetparams % <merged> <(ignored)> <device> <Policies>
+ % .trysetparams
+ { true 4 index .prepareparams
+ % Add the computed .MediaSize.
+ % Stack: merged (ignored) device Policies -true-
+ % -mark- key1 value1 ...
+ counttomark 5 add index .computemediasize
+ exch pop exch pop /.MediaSize exch
+DEBUG { (Putting.\n) print pstack flush } if
+ .putdeviceparamsonly
+DEBUG { (Result of putting.\n) print pstack flush } if
+ } bind def
+
+% Compute the media size and initial matrix from a merged request (after
+% media selection).
+/.computemediasize % <request> .computemediasize
+ % <request> <matrix> <[width height]>
+ { dup /PageSize get % requested page size
+ 1 index /InputAttributes get
+ 2 index (%MediaSource) get get /PageSize get % media size
+ % (may be a range)
+ 2 index /Policies get
+ dup /PageSize .knownget
+ { exch pop } { /PolicyNotFound get } ifelse % PageSize policy,
+ % affects scaling
+ 3 index /Orientation .knownget not { null } if
+ matrix .matchpagesize pop % (can't fail)
+ 2 array astore
+ } bind def
+
+% ---------------- setpagedevice itself ---------------- %
+
+/setpagedevice
+ { mark exch currentpagedevice
+
+ % Check whether we are changing OutputDevice;
+ % also handle the case where the current device
+ % is not a page device.
+ % Stack: mark <request> <current>
+DEBUG { (Checking.\n) print pstack flush } if
+
+ dup /OutputDevice .knownget
+ { % Current device is a page device.
+ 2 index /OutputDevice .knownget
+ { % A specific OutputDevice was requested.
+ 2 copy eq
+ { pop pop null }
+ { exch pop }
+ ifelse
+ }
+ { pop null
+ }
+ ifelse
+ }
+ { % Current device is not a page device.
+ % Use the default device.
+ 1 index /OutputDevice .knownget not { .defaultdevicename } if
+ }
+ ifelse
+ dup null eq
+ { pop
+ }
+ { exch pop .defaultdeviceparams
+ % In case of duplicate keys, .dicttomark takes the entry
+ % lower on the stack, so we can just append the defaults here.
+ .requiredattrs { } forall .dicttomark
+ }
+ ifelse
+
+ % Check whether a viewer wants to intervene.
+ % We must check both the request (which takes precedence)
+ % and the current dictionary.
+ % Stack: mark <request> <orig>
+ exch dup /ViewerPreProcess .knownget
+ { exec }
+ { 1 index /ViewerPreProcess .knownget { exec } if }
+ ifelse exch
+
+ % Construct a merged request from the actual request plus
+ % any keys that should always be propagated.
+ % Stack: mark <request> <orig>
+DEBUG { (Merging.\n) print pstack flush } if
+
+ exch 1 index length 1 index length add dict
+ .copiedkeys
+ { % Stack: <orig> <request> <merged> <key>
+ 3 index 1 index .knownget { 3 copy put pop } if pop
+ }
+ forall
+ % Stack: <orig> <request> <merged>
+ dup 2 index
+ { % stack: <orig> <request> <merged> <merged> <rkey> <rvalue>
+ .mergespecial 2 index .knownget { exec } if
+ put dup
+ }
+ forall pop
+ % Hack: if FIXEDRESOLUTION is true, discard any attempt to
+ % change HWResolution.
+ FIXEDRESOLUTION { dup /HWResolution .undef } if
+
+ % Select input and output media.
+ % Stack: mark <orig> <request> <merged>
+DEBUG { (Selecting.\n) print pstack flush } if
+
+ 0 dict % <failed>
+ 1 index /InputAttributes .knownget
+ { 2 index /Policies get
+ .inputattrkeys (%MediaSource) cvn .selectmedia
+ } if
+ 1 index /OutputAttributes .knownget
+ { 2 index /Policies get
+ .outputattrkeys (%MediaDestination) cvn .selectmedia
+ } if
+ 3 -1 roll 4 1 roll % temporarily swap orig & request
+ .applypolicies
+ 3 -1 roll 4 1 roll % swap back
+
+ % Construct the new device, and attempt to set its attributes.
+ % Stack: mark <orig> <request> <merged> <failed>
+DEBUG { (Constructing.\n) print pstack flush } if
+
+ currentdevice .devicename 2 index /OutputDevice get eq
+ { currentdevice }
+ { 1 index /OutputDevice get finddevice }
+ ifelse
+ %**************** We should copy the device here,
+ %**************** but since we can't close the old device,
+ %**************** we don't. This is WRONG.
+ %****************copydevice
+ 2 index /Policies get
+ .trysetparams
+ dup type /booleantype ne
+ { % The request failed.
+ % Stack: ... <orig> <request> <merged> <failed> <device>
+ % <Policies> true mark <name> <errorname> ...
+DEBUG { (Recovering.\n) print pstack flush } if
+ counttomark 4 add index
+ counttomark 2 idiv { dup 4 -2 roll put } repeat
+ pop pop pop
+ % Stack: mark ... <orig> <request> <merged> <failed> <device>
+ % <Policies>
+ 6 2 roll 3 -1 roll 4 1 roll
+ .applypolicies
+ 3 -1 roll 4 1 roll 6 -2 roll
+ .trysetparams % shouldn't fail!
+ dup type /booleantype ne
+ { 2 { counttomark 1 add 1 roll cleartomark } repeat
+ /setpagedevice load exch signalerror
+ }
+ if
+ }
+ if
+
+ % The attempt succeeded. Install the new device.
+ % Stack: mark ... <merged> <failed> <device> <eraseflag>
+DEBUG { (Installing.\n) print pstack flush } if
+
+ pop 2 .endpage
+ { 1 true .outputpage
+ (>>setpagedevice, press <return> to continue<<\n) .confirm
+ }
+ if
+ % .setdevice clears the current page device!
+ .currentpagedevice pop exch
+ .setdevice pop
+ .setpagedevice
+
+ % Merge the request into the current page device.
+ % Stack: mark ... <merged> <failed>
+ exch currentpagedevice dup length 2 index length add dict
+ .copydict .copydict
+ % Initialize the default matrix, taking media matching
+ % into account.
+ .computemediasize pop initmatrix concat
+ dup /PageOffset .knownget
+ { % Translate by the given number of 1/72" units in device X/Y.
+ dup 0 get exch 1 get
+ 2 index /HWResolution get dup 1 get exch 0 get
+ 4 -1 roll mul 72 div 3 1 roll mul 72 div
+ idtransform translate
+ }
+ if
+ % We must install the new page device dictionary
+ % before calling the Install procedure.
+ dup .setpagedevice
+ .setdefaultscreen % Set the default screen before calling Install.
+ dup /Install .knownget { exec } if
+ matrix currentmatrix .setdefaultmatrix
+ % Erase and initialize the page.
+ erasepage initgraphics
+ .beginpage
+
+ % Clean up, calling PolicyReport if needed.
+ % Stack: mark ... <failed> <merged>
+DEBUG { (Finishing.\n) print pstack flush } if
+
+ exch dup length 0 ne
+ { 1 index /Policies get /PolicyReport get
+ counttomark 1 add 2 roll cleartomark
+ exec
+ }
+ { cleartomark
+ }
+ ifelse
+
+ } odef
+
+end % level2dict
+.setlanguagelevel
diff --git a/pstoraster/gs_statd.ps b/pstoraster/gs_statd.ps
new file mode 100644
index 000000000..3f004d233
--- /dev/null
+++ b/pstoraster/gs_statd.ps
@@ -0,0 +1,275 @@
+% Copyright (C) 1989, 1996 Aladdin Enterprises. All rights reserved.
+%
+% This file is part of GNU Ghostscript.
+%
+% GNU Ghostscript is distributed in the hope that it will be useful, but
+% WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+% anyone for the consequences of using it or for whether it serves any
+% particular purpose or works at all, unless he says so in writing. Refer to
+% the GNU General Public License for full details.
+%
+% Everyone is granted permission to copy, modify and redistribute GNU
+% Ghostscript, but only under the conditions described in the GNU General
+% Public License. A copy of this license is supposed to have been given to
+% you along with GNU Ghostscript so you can know your rights and
+% responsibilities. It should be in a file named COPYING. Among other
+% things, the copyright notice and this notice must be preserved on all
+% copies.
+%
+% Aladdin Enterprises is not affiliated with the Free Software Foundation or
+% the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+% does not depend on any other GNU software.
+
+% This file provides statusdict, serverdict, and assorted LaserWriter
+% operators, mostly for the benefit of poorly designed PostScript programs
+% that 'know' they are running on a LaserWriter.
+
+systemdict begin
+ % We make statusdict a little larger for Level 2 stuff.
+ % Note that it must be allocated in local VM.
+ .currentglobal false .setglobal
+ /statusdict 89 dict def
+ % To support the Level 2 job control features,
+ % serverdict must also be in local VM.
+ /serverdict 10 dict def % ditto
+ .setglobal
+end
+
+% Define various paper formats. The Adobe documentation defines only these:
+% 11x17, a3, a4, a4small, b5, ledger, legal, letter, lettersmall, note.
+% These procedures are also accessed as data structures during initialization,
+% so the page dimensions must be the first two elements of the procedure.
+
+/.setpagesize { //systemdict /statusdict get begin .setpagesize end } bind def
+userdict begin
+ /letter {612 792 //.setpagesize exec} bind def
+% 'note' is an anomaly. It is apparently supposed to use a 540x720 region
+% centered on a 612x792 page, with (0,0) at the lower left corner of the
+% 612x792 page, but we have no way to represent this. In order to avoid
+% clipping the image, we define note as equivalent to letter.
+%/note {540 720 //.setpagesize exec} bind def
+ /note /letter load def
+ /legal {612 1008 //.setpagesize exec} bind def
+ /a0 {2380 3368 //.setpagesize exec} bind def % ISO standard
+ /a1 {1684 2380 //.setpagesize exec} bind def % ISO standard
+ /a2 {1190 1684 //.setpagesize exec} bind def % ISO standard
+ /a3 {842 1190 //.setpagesize exec} bind def % ISO standard
+ /a4 {595 842 //.setpagesize exec} bind def % ISO standard
+ /a5 {421 595 //.setpagesize exec} bind def % ISO standard
+ /a6 {297 421 //.setpagesize exec} bind def % ISO standard
+ /a7 {210 297 //.setpagesize exec} bind def % ISO standard
+ /a8 {148 210 //.setpagesize exec} bind def % ISO standard
+ /a9 {105 148 //.setpagesize exec} bind def % ISO standard
+ /a10 {74 105 //.setpagesize exec} bind def % ISO standard
+ /b0 {2836 4008 //.setpagesize exec} bind def % ISO standard
+ /b1 {2004 2836 //.setpagesize exec} bind def % ISO standard
+ /b2 {1418 2004 //.setpagesize exec} bind def % ISO standard
+ /b3 {1002 1418 //.setpagesize exec} bind def % ISO standard
+ /b4 {709 1002 //.setpagesize exec} bind def % ISO standard
+ /b5 {501 709 //.setpagesize exec} bind def % ISO standard
+ /archE {2592 3456 //.setpagesize exec} bind def % U.S. CAD std
+ /archD {1728 2592 //.setpagesize exec} bind def % U.S. CAD std
+ /archC {1296 1728 //.setpagesize exec} bind def % U.S. CAD std
+ /archB {864 1296 //.setpagesize exec} bind def % U.S. CAD std
+ /archA {648 864 //.setpagesize exec} bind def % U.S. CAD std
+ /flsa {612 936 //.setpagesize exec} bind def % U.S. foolscap
+ /flse {612 936 //.setpagesize exec} bind def % European foolscap
+ /halfletter {396 612 //.setpagesize exec} bind def
+ /11x17 {792 1224 //.setpagesize exec} bind def % 11x17 portrait
+% /tabloid {792 1224 //.setpagesize exec} bind def % 11x17 portrait
+ /ledger {1224 792 //.setpagesize exec} bind def % 11x17 landscape
+% /csheet {1224 1584 //.setpagesize exec} bind def % ANSI C 17x22
+% /dsheet {1584 2448 //.setpagesize exec} bind def % ANSI D 22x34
+% /esheet {2448 3168 //.setpagesize exec} bind def % ANSI E 34x44
+end
+currentdict /.setpagesize .undef
+
+statusdict begin
+
+% Define the pagetype values for the known page formats.
+% The values for all but letter and note are arbitrary.
+/.pagetypenames
+ { /letter /note /legal
+ /a0 /a1 /a2 /a3 /a4 /a5 /a6 /a7 /a8 /a9 /a10
+ /b0 /b1 /b2 /b3 /b4 /b5 /archE /archD /archC /archB /archA
+ /flsa /flse /halfletter /11x17 /ledger
+ } cvlit readonly def
+
+%%%%%% The following items were suggested by a user as useful.
+
+% Permanent definitions
+
+/ramsize 4194304 def
+/hardwareiomode 0 def
+ /sethardwareiomode {pop} bind def
+/softwareiomode 0 def
+ /setsoftwareiomode {pop} bind def
+/dosysstart false def
+ /setdosysstart {pop} bind def
+/allowjobreset true def
+ /setallowjobreset {pop} bind def
+/defaultpaperfamily 0 def
+ /setdefaultpaperfamily {pop} bind def
+/defaultpapertray 0 def
+ /setdefaultpapertray {pop} bind def
+/defaulttrayswitch false def
+ /setdefaulttrayswitch {pop} bind def
+
+% Tray and format selection
+
+ /11x17tray userdict /11x17 get def
+ /a3tray userdict /a3 get def
+ /a4tray userdict /a4 get def
+ /a5tray userdict /a5 get def
+ /a6tray userdict /a6 get def
+ /b4tray userdict /b4 get def
+ /flsatray userdict /flsa get def
+ /flsetray userdict /flse get def
+ /halflettertray userdict /halfletter get def
+ /ledgertray userdict /ledger get def
+ /legaltray userdict /legal get def
+ /lettertray userdict /letter get def
+
+% Per-job parameters
+
+/paperfamily 0 def % 0 is US, 1 is European
+/papertray 1 def
+ /setpapertray {statusdict exch /papertray exch put} bind def
+/trayswitch false def % paperout feeds from another tray
+% We don't implement the (undocumented by Adobe) papersize 'operator',
+% because it's very awkward to make it interact properly with all the
+% different ways of setting the paper size.
+%/papersize {/letter true} bind def % <name of paper size>, <short-edge-first-p>
+/appletalktype (LaserWriter) def
+
+%%%%%% The following items are defined in the PostScript Language
+%%%%%% Reference Manual, First Edition, and subsequent 'compatibility'
+%%%%%% documentation from Adobe.
+
+ /checkpassword {statusdict begin .password eq end} bind def
+ /defaulttimeouts {statusdict begin .timeouts aload pop end} bind def
+%/dostartpage
+ /eescratch {pop 0} bind def
+ /idlefonts {statusdict begin mark .idlefonts aload pop end} bind def
+ /jobname () def
+%/jobtimeout
+ /manualfeed false def
+ /manualfeedtimeout 60 def
+ /margins {statusdict begin .topmargin .leftmargin end} bind def
+ /pagecount {4711} bind def
+ /pagestackorder {false} bind def
+ /pagetype 0 def
+ /prefeed false def
+ /printererror {pop pop} bind def
+ /printername {statusdict /.printername get exch copy} bind def
+ /processcolors /processcolors load def % defined in systemdict
+ /product product def % product is defined in systemdict
+ /revision revision def % revision is defined in systemdict
+ /sccbatch {pop 9600 0} bind def
+ /sccinteractive {pop 9600 0} bind def
+ /setdefaulttimeouts {statusdict begin .timeouts astore pop end} bind def
+ /setdostartpage {statusdict exch /dostartpage exch put} bind def
+ /setduplexmode {mark /Duplex 3 -1 roll currentdevice putdeviceprops} bind def
+ /seteescratch {pop pop} bind def
+ /setidlefonts {] statusdict exch /.idlefonts exch put} bind def
+ /setjobtimeout {statusdict exch /jobtimeout exch put} bind def
+ /setmargins
+ { statusdict begin
+ /.leftmargin exch def /.topmargin exch def
+ end
+ } bind def
+
+% The following compatibility operators are only documented by Adobe in a
+% supplement to the Red Book.
+%
+% - pagemargin <offset>
+% - pageparams <width> <height> <offset> <orientation>
+% <width> <height> <orientation> setpage -
+% <offset> setpagemargin -
+% <width> <height> <offset> <orientation> setpageparams -
+%
+% width and height are in default units (and if orientation is odd, are
+% exchanged!). offset is the x margin, also in default units.
+% Unfortunately, because orientation is relative to the device paper feed,
+% it does not have a consistent meaning in terms of image orientation.
+% We follow the convention that ORIENT1 determines the orientation value
+% that means portait: false means 0, true means 1.
+
+ /pagemargin { 0 } bind def
+ /pageparams
+ { currentdevice 1 dict dup /.MediaSize dup put .getdeviceparams
+ exch pop exch pop aload pop 0 ORIENT1 { 1 } { 0 } ifelse
+ } bind def
+ /setpage
+ { ORIENT1 { 1 } { 0 } ifelse ne {exch} if
+ statusdict /.setpagesize get exec
+ } bind def
+ /setpagemargin {pop} bind def % can't do better without setpagedevice
+ /setpageparams
+ { exch pop ORIENT1 { 1 } { 0 } ifelse ne {exch} if
+ statusdict /.setpagesize get exec
+ } bind def
+ /setpagetype
+ { statusdict begin
+ dup .pagetypenames exch get //systemdict exch get exec
+ /pagetype exch def
+ end
+ } bind def
+ /setpassword
+ {exch checkpassword
+ {statusdict exch /.password exch put true}
+ {pop false}
+ ifelse} bind def
+ /setprintername
+ {dup length string copy statusdict exch /.printername exch put} bind def
+
+% setresolution is not documented by Adobe, but some applications
+% use it anyway, without testing whether or not it is present.
+%
+% <pixels_per_inch> setresolution -
+%
+% sets the resolution of the device.
+
+ /setresolution
+ { mark /HWResolution [ 4 -1 roll dup ] currentdevice putdeviceprops pop
+ initmatrix erasepage
+ } bind def
+ /setsccbatch {pop pop pop} bind def
+ /setsccinteractive {pop pop pop} bind def
+ /settumble {pop} bind def
+ /waittimeout 300 def
+
+%%%%%% End of documented items.
+
+/.setpagesize
+ { mark /HWSize [
+ 4 index 4 index matrix defaultmatrix dtransform
+ abs ceiling cvi exch abs ceiling cvi exch
+ ] currentdevice putdeviceprops pop pop pop
+ initmatrix initclip erasepage
+ } bind def
+/.password 0 def
+/.timeouts [0 60 30] def
+true setdostartpage
+mark setidlefonts
+0 setjobtimeout
+0 0 setmargins
+product setprintername
+
+end % statusdict
+
+% The following contents of serverdict are a complete guess,
+% based on some observed LaserWriter boilerplate.
+
+serverdict begin
+
+ /execjob { } bind def
+% The Red Book implies that something like the following is
+% an appropriate definition of exitserver.
+ /exitserver { clear stop } bind def
+% However, this interacts badly with our standard error handler,
+% so we override it with the following less appropriate definition.
+ /exitserver { 0 ne { clear cleardictstack } if } bind def
+ /setrealdevice { } bind def
+
+end % serverdict
diff --git a/pstoraster/gs_std_e.ps b/pstoraster/gs_std_e.ps
new file mode 100644
index 000000000..fb1c79295
--- /dev/null
+++ b/pstoraster/gs_std_e.ps
@@ -0,0 +1,79 @@
+% Copyright (C) 1993, 1994 Aladdin Enterprises. All rights reserved.
+%
+% This file is part of GNU Ghostscript.
+%
+% GNU Ghostscript is distributed in the hope that it will be useful, but
+% WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+% anyone for the consequences of using it or for whether it serves any
+% particular purpose or works at all, unless he says so in writing. Refer to
+% the GNU General Public License for full details.
+%
+% Everyone is granted permission to copy, modify and redistribute GNU
+% Ghostscript, but only under the conditions described in the GNU General
+% Public License. A copy of this license is supposed to have been given to
+% you along with GNU Ghostscript so you can know your rights and
+% responsibilities. It should be in a file named COPYING. Among other
+% things, the copyright notice and this notice must be preserved on all
+% copies.
+%
+% Aladdin Enterprises is not affiliated with the Free Software Foundation or
+% the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+% does not depend on any other GNU software.
+
+% Define the standard encoding vector.
+/StandardEncoding
+% \00x
+ /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
+ /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
+ /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
+ /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
+% \04x
+ /space /exclam /quotedbl /numbersign
+ /dollar /percent /ampersand /quoteright
+ /parenleft /parenright /asterisk /plus
+ /comma /minus /period /slash
+ /zero /one /two /three
+ /four /five /six /seven
+ /eight /nine /colon /semicolon
+ /less /equal /greater /question
+% \10x
+ /at /A /B /C /D /E /F /G
+ /H /I /J /K /L /M /N /O
+ /P /Q /R /S /T /U /V /W
+ /X /Y /Z /bracketleft /backslash /bracketright /asciicircum /underscore
+% \14x
+ /quoteleft /a /b /c /d /e /f /g
+ /h /i /j /k /l /m /n /o
+ /p /q /r /s /t /u /v /w
+ /x /y /z /braceleft /bar /braceright /asciitilde /.notdef
+% \20x
+ /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
+ /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
+ /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
+ /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
+% \24x
+ /.notdef /exclamdown /cent /sterling
+ /fraction /yen /florin /section
+ /currency /quotesingle /quotedblleft /guillemotleft
+ /guilsinglleft /guilsinglright /fi /fl
+ /.notdef /endash /dagger /daggerdbl
+ /periodcentered /.notdef /paragraph /bullet
+ /quotesinglbase /quotedblbase /quotedblright /guillemotright
+ /ellipsis /perthousand /.notdef /questiondown
+% \30x
+ /.notdef /grave /acute /circumflex /tilde /macron /breve /dotaccent
+ /dieresis /.notdef /ring /cedilla /.notdef /hungarumlaut /ogonek /caron
+ /emdash /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
+ /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
+% \34x
+ /.notdef /AE /.notdef /ordfeminine /.notdef /.notdef /.notdef /.notdef
+ /Lslash /Oslash /OE /ordmasculine /.notdef /.notdef /.notdef /.notdef
+ /.notdef /ae /.notdef /.notdef /.notdef /dotlessi /.notdef /.notdef
+ /lslash /oslash /oe /germandbls /.notdef /.notdef /.notdef /.notdef
+% Make an array on large systems, a packed array on small ones.
+256
+vmstatus exch pop exch pop
+100000 ge { array astore readonly } { packedarray } ifelse
+def
+0 StandardEncoding .registerencoding
+/StandardEncoding StandardEncoding .defineencoding
diff --git a/pstoraster/gs_sym_e.ps b/pstoraster/gs_sym_e.ps
new file mode 100644
index 000000000..4eacd015e
--- /dev/null
+++ b/pstoraster/gs_sym_e.ps
@@ -0,0 +1,89 @@
+% Copyright (C) 1991, 1994 Aladdin Enterprises. All rights reserved.
+%
+% This file is part of GNU Ghostscript.
+%
+% GNU Ghostscript is distributed in the hope that it will be useful, but
+% WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+% anyone for the consequences of using it or for whether it serves any
+% particular purpose or works at all, unless he says so in writing. Refer to
+% the GNU General Public License for full details.
+%
+% Everyone is granted permission to copy, modify and redistribute GNU
+% Ghostscript, but only under the conditions described in the GNU General
+% Public License. A copy of this license is supposed to have been given to
+% you along with GNU Ghostscript so you can know your rights and
+% responsibilities. It should be in a file named COPYING. Among other
+% things, the copyright notice and this notice must be preserved on all
+% copies.
+%
+% Aladdin Enterprises is not affiliated with the Free Software Foundation or
+% the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+% does not depend on any other GNU software.
+
+% Define the Symbol encoding vector.
+/currentglobal where
+ { pop currentglobal { setglobal } true setglobal }
+ { { } }
+ifelse
+/SymbolEncoding
+% \000
+ StandardEncoding 0 32 getinterval aload pop % /.notdef
+% \040
+ /space /exclam /universal /numbersign
+ /existential /percent /ampersand /suchthat
+ /parenleft /parenright /asteriskmath /plus
+ /comma /minus /period /slash
+ /zero /one /two /three
+ /four /five /six /seven
+ /eight /nine /colon /semicolon
+ /less /equal /greater /question
+% \100
+ /congruent /Alpha /Beta /Chi
+ /Delta /Epsilon /Phi /Gamma
+ /Eta /Iota /theta1 /Kappa
+ /Lambda /Mu /Nu /Omicron
+ /Pi /Theta /Rho /Sigma
+ /Tau /Upsilon /sigma1 /Omega
+ /Xi /Psi /Zeta /bracketleft
+ /therefore /bracketright /perpendicular /underscore
+% \140
+ /radicalex /alpha /beta /chi
+ /delta /epsilon /phi /gamma
+ /eta /iota /phi1 /kappa
+ /lambda /mu /nu /omicron
+ /pi /theta /rho /sigma
+ /tau /upsilon /omega1 /omega
+ /xi /psi /zeta /braceleft
+ /bar /braceright /similar /.notdef
+% \200
+ StandardEncoding 0 32 getinterval aload pop % /.notdef
+% \240
+ /.notdef /Upsilon1 /minute /lessequal
+ /fraction /infinity /florin /club
+ /diamond /heart /spade /arrowboth
+ /arrowleft /arrowup /arrowright /arrowdown
+ /degree /plusminus /second /greaterequal
+ /multiply /proportional /partialdiff /bullet
+ /divide /notequal /equivalence /approxequal
+ /ellipsis /arrowvertex /arrowhorizex /carriagereturn
+% \300
+ /aleph /Ifraktur /Rfraktur /weierstrass
+ /circlemultiply /circleplus /emptyset /intersection
+ /union /propersuperset /reflexsuperset /notsubset
+ /propersubset /reflexsubset /element /notelement
+ /angle /gradient /registerserif /copyrightserif
+ /trademarkserif /product /radical /dotmath
+ /logicalnot /logicaland /logicalor /arrowdblboth
+ /arrowdblleft /arrowdblup /arrowdblright /arrowdbldown
+% \340
+ /lozenge /angleleft /registersans /copyrightsans
+ /trademarksans /summation /parenlefttp /parenleftex
+ /parenleftbt /bracketlefttp /bracketleftex /bracketleftbt
+ /bracelefttp /braceleftmid /braceleftbt /braceex
+ /.notdef /angleright /integral /integraltp
+ /integralex /integralbt /parenrighttp /parenrightex
+ /parenrightbt /bracketrighttp /bracketrightex /bracketrightbt
+ /bracerighttp /bracerightmid /bracerightbt /.notdef
+256 packedarray .defineencoding
+2 SymbolEncoding .registerencoding
+exec
diff --git a/pstoraster/gs_ttf.ps b/pstoraster/gs_ttf.ps
new file mode 100644
index 000000000..15ac48db5
--- /dev/null
+++ b/pstoraster/gs_ttf.ps
@@ -0,0 +1,447 @@
+% Copyright (C) 1996 Aladdin Enterprises. All rights reserved.
+%
+% This file is part of GNU Ghostscript.
+%
+% GNU Ghostscript is distributed in the hope that it will be useful, but
+% WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+% anyone for the consequences of using it or for whether it serves any
+% particular purpose or works at all, unless he says so in writing. Refer to
+% the GNU General Public License for full details.
+%
+% Everyone is granted permission to copy, modify and redistribute GNU
+% Ghostscript, but only under the conditions described in the GNU General
+% Public License. A copy of this license is supposed to have been given to
+% you along with GNU Ghostscript so you can know your rights and
+% responsibilities. It should be in a file named COPYING. Among other
+% things, the copyright notice and this notice must be preserved on all
+% copies.
+%
+% Aladdin Enterprises is not affiliated with the Free Software Foundation or
+% the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+% does not depend on any other GNU software.
+
+% Support code for direct use of TrueType fonts.
+% (Not needed for Type 42 fonts.)
+
+% ---------------- Font loading machinery ---------------- %
+
+% Augment the FONTPATH machinery so it recognizes TrueType fonts.
+
+/.scanfontheaders where % only defined if DISKFONTS is recognized
+ { pop
+ /.scanfontheaders [ .scanfontheaders aload pop (\000\001\000\000*) ] def
+ }
+if
+/.findnonttfontvalue /.findfontvalue load def
+/.findfontvalue % <file> <key> .findfontvalue <value> true
+ % <file> <key> .findfontvalue false
+ % Closes the file in either case.
+ { 1 index read pop 2 index 1 index unread 0 eq
+ { % If this is a font at all, it's a TrueType font.
+ dup /FontType eq
+ { pop closefile 42 true }
+ { dup /FontName eq
+ { pop .findttfontname }
+ { pop closefile false }
+ ifelse
+ }
+ ifelse
+ }
+ { % Not a TrueType font.
+ .findnonttfontvalue
+ }
+ ifelse
+ } bind def
+/.findttfontname % <file> .findttfontname <fname> true
+ % <file> .findttfontname false
+ % Closes the file in either case.
+ { .loadttfonttables
+ (name) findtableentry
+ { dup 8 getu32 f exch setfileposition
+ 12 getu32 string f exch readstring pop
+ 6 findname
+ }
+ { false
+ }
+ ifelse
+ f closefile end end
+ } bind def
+
+% Load a font file that might be a TrueType font.
+
+/.loadnonttfontfile /.loadfontfile load def
+/.loadfontfile % <file> .loadfontfile -
+ { dup read pop 2 copy unread 0 eq
+ { % If this is a font at all, it's a TrueType font.
+ .loadttfont dup /FontName get exch definefont pop
+ }
+ { % Not a TrueType font.
+ .loadnonttfontfile
+ }
+ ifelse
+ } bind def
+
+% ---------------- Automatic Type 42 generation ---------------- %
+
+% Load a TrueType font from a file as a Type 42 PostScript font.
+% The thing that makes this really messy is the handling of encodings.
+% There are 3 interacting tables that affect the encoding:
+% 'cmap' provides multiple maps from character codes to glyph indices
+% 'glyf' maps glyph indices to outlines
+% 'post' maps glyph indices to glyph names (if present)
+% What we need to get out of this is:
+% Encoding mapping character codes to glyph names
+% (the composition of cmap and post)
+% CharStrings mapping glyph names to glyph indices
+% (the inverse of post)
+% If the post table is missing, we have to take a guess based on the cmap
+% table.
+
+/.loadttfontdict mark
+/orgXUID 107 def % Aladdin Enterprises organization XUID
+/maxstring 65500 def % maximum length of a PostScript string
+
+% Define the Macintosh standard mapping from characters to glyph indices.
+/MacRomanEncoding dup .findencoding def
+/MacGlyphEncoding mark
+ /.notdef /.null /CR
+MacRomanEncoding 32 95 getinterval aload pop
+MacRomanEncoding 128 45 getinterval aload pop
+% 143
+ /notequal /AE
+ /Oslash /infinity /plusinus /lessequal /greaterequal
+ /yen /mu1 /partialdiff /summation /product
+ /pi /integral /ordfeminine /ordmasculine /Ohm
+ /ae /oslash /questiondown /exclamdown /logicalnot
+ /radical /florin /approxequal /increment /guillemotleft
+ /guillemotright /ellipsis /nbspace
+MacRomanEncoding 203 12 getinterval aload pop
+ /lozenge
+MacRomanEncoding 216 24 getinterval aload pop
+ /applelogo
+MacRomanEncoding 241 7 getinterval aload pop
+ /overscore
+MacRomanEncoding 249 7 getinterval aload pop
+% 226
+ /Lslash /lslash /Scaron /scaron
+ /Zcaron /zcaron /brokenbar /Eth /eth
+ /Yacute /yacute /Thorn /thorn /minus
+ /multiply /onesuperior /twosuperior /threesuperior /onehalf
+ /onequarter /threequarters /franc /Gbreve /gbreve
+ /Idot /Scedilla /scedilla /Cacute /cacute
+ /Ccaron /ccaron /dmacron
+/packedarray where
+ { pop counttomark packedarray exch pop }
+ { ] readonly }
+ifelse def
+
+% Procedures
+/getu16 % <string> <index> getu16 <integer>
+ { 2 copy get 8 bitshift 3 1 roll 1 add get add
+ } bind def
+/gets16 % <string> <index> gets16 <integer>
+ { getu16 16#8000 xor 16#8000 sub
+ } bind def
+/getu32 % <string> <index> getu32 <integer>
+ { 2 copy getu16 16 bitshift 3 1 roll 2 add getu16 add
+ } bind def
+/gets32 % <string> <index> gets32 <integer>
+ { getu32 dup 16#7fffffff gt { 4 { 16#40000000 sub } repeat } if
+ } bind def
+/getrange % <offset> <length> getrange <string>
+ % Free variables: sfnts
+ { () sfnts
+ { % Stack: offset length () sfnt
+ dup length 4 index gt { exch pop exit } if
+ 4 -1 roll exch length sub 3 1 roll
+ }
+ forall 3 1 roll getinterval
+ } bind def
+/findtableentry % <name4> findtableentry <tableentry> true
+ % <name4> findtableentry false
+ % Free variables: tables
+ { false 0 16 tables length 16 sub
+ { % Stack: name4 false toffset
+ tables 1 index 4 getinterval 3 index eq
+ { tables exch 16 getinterval
+ exch pop exch true exit
+ }
+ if pop
+ }
+ for exch pop
+ } bind def
+/findtable % <name4> findtable <table> true
+ % <name4> findtable false
+ % Free variables: tables
+ { findtableentry
+ { dup 8 getu32 exch 12 getu32 getrange true }
+ { false }
+ ifelse
+ } bind def
+/findname % <nametable> <nameid> findname <string> true
+ % <nametable> <nameid> findname false
+ { false 3 1 roll 0 1 3 index 2 getu16 1 sub
+ { % Stack: false table id index
+ 12 mul 6 add 2 index exch 12 getinterval
+ dup 6 getu16 2 index eq
+ { % We found the name we want.
+ exch pop
+ % Stack: false table record
+ dup 10 getu16 2 index 4 getu16 add
+ 1 index 8 getu16 4 -1 roll 3 1 roll getinterval exch
+ % Stack: false string record
+ % Check for 8- vs. 16-bit characters.
+ is2byte { string2to1 } if true null 4 -1 roll exit
+ }
+ if pop
+ }
+ for pop pop
+ } bind def
+/is2byte % <namerecord> is2byte <bool>
+ { dup 0 getu16
+ { { pop true } % Apple Unicode
+ { pop false } % Macintosh Script manager
+ { 1 getu16 1 eq } % ISO
+ { 1 getu16 1 eq } % Microsoft
+ }
+ exch get exec
+ } bind def
+/string2to1 % <string2> string2to1 <string>
+ { dup length 2 idiv string dup
+ 0 1 3 index length 1 sub
+ { 3 index 1 index 2 mul 1 add get put dup }
+ for pop exch pop
+ } bind def
+/cmapformats mark
+ % Each procedure in this dictionary is called as follows:
+ % -mark- encodingtable <<proc>> -mark- glyphindices...
+ 0 % Apple standard 1-to-1 mapping.
+ { 6 256 getinterval { } forall
+ } bind
+ 4 % Microsoft/Adobe segmented mapping.
+ { /etab exch def
+ /nseg2 etab 6 getu16 def
+ 14 /endc etab 2 index nseg2 getinterval def
+ % The Apple TrueType documentation omits the 2-byte
+ % 'reserved pad' that follows the endCount vector!
+ 2 add
+ nseg2 add /startc etab 2 index nseg2 getinterval def
+ nseg2 add /iddelta etab 2 index nseg2 getinterval def
+ nseg2 add /idroff etab 2 index nseg2 getinterval def
+ pop 0 2 nseg2 3 sub
+ { /i2 exch def
+ /scode startc i2 getu16 def
+ counttomark scode 256 min exch sub 0 max { 0 } repeat
+ /ecode endc i2 getu16 def
+ /delta iddelta i2 getu16 def
+ idroff i2 getu16 dup 0 eq
+ { pop scode 1 ecode { delta add 65535 and } for
+ }
+ { % The +2 is for the 'reserved pad'.
+ /gloff exch 14 nseg2 3 mul add 2 add i2 add add def
+ 0 1 ecode scode sub
+ { 2 mul gloff add etab exch getu16
+ dup 0 ne { delta add 65535 and } if
+ }
+ for
+ }
+ ifelse
+ }
+ for
+ } bind
+ 6 % Single interval lookup.
+ { dup 6 getu16 { 0 exch } repeat
+ dup 8 getu16 0 exch 1 exch 1 sub
+ { 2 mul 10 add 2 copy getu16 exch pop exch }
+ repeat pop
+ } bind
+.dicttomark readonly def % cmapformats
+/cmaparray % <cmaptab> cmaparray -mark- <glyphs> ...
+ { mark exch dup 0 getu16 cmapformats exch .knownget
+ { exec }
+ { (Can't handle format ) print 0 getu16 = flush
+ 0 1 255 { } for
+ }
+ ifelse
+ } bind def
+/postformats mark
+ % Each procedure in this dictionary is called as follows:
+ % posttable <<proc>> glyphencoding
+ 16#00020000 % Detailed map, required by Microsoft fonts.
+ { /postglyphs exch def
+ postglyphs 32 getu16 /numglyphs exch def
+ /glyphnames numglyphs 2 mul 34 add def
+ [ 0 1 numglyphs 1 sub
+ { 2 mul 34 add postglyphs exch getu16
+ dup 258 lt
+ { MacGlyphEncoding exch get
+ }
+ { 258 sub glyphnames exch
+ { postglyphs 1 index get 1 add add }
+ repeat
+ 1 add postglyphs exch 2 copy 1 sub get getinterval cvn
+ }
+ ifelse
+ }
+ for ]
+ } bind
+.dicttomark readonly def % postformats
+.dicttomark readonly def % .loadttfontdict
+/.loadttfonttables % <file> .loadttfonttables -
+ % Pushes .loadttfontdict & scratch dict on d-stack,
+ % defines f, offsets, tables
+ { .loadttfontdict begin
+ 40 dict begin
+ /f exch def
+ /offsets f 12 string readstring pop def
+ /tables f offsets 4 getu16 16 mul string readstring pop def
+ } bind def
+/.makesfnts % - .makesfnts -
+ % Defines head, sfnts
+ { % Find the end of the last table, and also the end of
+ % the last table below the 64K mark.
+ 0 8 16 tables length
+ { % Stack: end toffset
+ DEBUG
+ { tables 1 index 8 sub 4 getinterval print ( ) print
+ tables 1 index getu32 =only ( ) print
+ tables 1 index 4 add getu32 =
+ }
+ if
+ tables 1 index getu32 exch tables exch 4 add getu32 add max
+ }
+ for
+ % Divide into 2+n strings: before glyf, glyfs, after glyf
+ (glyf) findtableentry pop
+ dup 8 getu32 /len0 exch def
+ 12 getu32 /len1 exch def
+ len0 len1 add sub /len2 exch def
+ /sfnts [
+ len0 string
+ dup 0 offsets putinterval
+ dup offsets length tables putinterval
+ dup offsets length tables length add
+ len0 1 index sub getinterval
+ f exch dup length 0 ne { readstring } if pop pop
+ len1 dup maxstring gt
+ { % Bad news: we'll have to split the glyfs.
+ % Right now we only provide for splitting into 2 parts,
+ % but we could generalize this without too much trouble.
+ f maxstring string readstring pop
+ exch maxstring sub
+ }
+ if string f exch readstring pop
+ len2 0 ne { f len2 string readstring pop } if
+ ] def
+ /head (head) findtable pop def
+ len1 maxstring gt
+ { % Determine where to split the glyfs by scanning loca.
+ % The very last entry in loca may be bogus.
+ % What a nuisance!
+ (loca) findtable pop dup length
+ head 50 getu16 0 ne
+ { % 32-bit loca
+ 8 sub -4 0
+ { 1 index exch getu32 dup maxstring le { exch pop exit } if pop }
+ }
+ { % 16-bit loca
+ 4 sub -2 0
+ { 1 index exch getu16 1 bitshift dup maxstring le { exch pop exit } if pop }
+ }
+ ifelse for
+ % Now the top element of the stack is the length of
+ % the first glyf string.
+ sfnts 1 get dup 0 3 index getinterval
+ sfnts exch 1 exch put
+ exch 1 index length 1 index sub getinterval
+ sfnts 2 get concatstrings sfnts exch 2 exch put
+ }
+ if
+ } bind def
+/.loadttfont % <file> .loadttfont <type42font>
+ { .loadttfonttables
+ .makesfnts
+ /upem head 18 getu16 def
+ (cmap) findtable pop
+ % The Apple cmap format is no help in determining the encoding.
+ % Look for a Microsoft table. If we can't find one,
+ % just use the first table, whatever it is.
+ dup 4 8 getinterval exch % the default
+ 0 1 2 index 2 getu16 1 sub
+ { 8 mul 4 add 1 index exch 8 getinterval
+ dup 0 getu16 3 eq { exch 3 -1 roll pop exit } if pop
+ }
+ for
+ % Stack: subentry table
+ /cmapsub 2 index def
+ exch 4 getu32 1 index length 1 index sub getinterval
+ /cmaptab exch def
+ % See if we have PostScript glyph name information.
+ /post (post) findtable pop def
+ /glyphencoding postformats post 0 getu32 .knownget
+ { post exch exec }
+ { MacGlyphEncoding }
+ ifelse def
+ /checksum head 8 getu32 def
+ mark
+ /FontType 42
+ /FontMatrix matrix
+ /PaintType 0
+ /FontBBox [ 36 2 42 { head exch gets16 upem div } for ]
+ (name) findtable
+ { % Find the names from the 'name' table.
+ /names exch def
+ /FontName names 6 findname not { checksum 16 8 string cvrs } if
+ /FontInfo mark
+ names 0 findname { /Notice exch } if
+ names 1 findname { /FamilyName exch } if
+ names 4 findname { /FullName exch } if
+ names 5 findname { /Version exch } if
+ }
+ { % No name table, fabricate a FontName.
+ /FontName checksum 16 8 string cvrs
+ /FontInfo mark
+ }
+ ifelse
+ % Stack: /FontInfo mark key1 value1 ...
+ post null ne
+ { /ItalicAngle post 4 gets32 65536.0 div
+ /isFixedPitch post 12 getu32 0 ne
+ /UnderlinePosition post 8 gets16 upem div
+ /UnderlineThickness post 10 gets16 upem div
+ }
+ if
+ counttomark 0 ne
+ { .dicttomark }
+ { pop pop }
+ ifelse
+ /Encoding
+ cmaptab cmaparray
+ % For some reason, the Symbol and WingDing fonts start with
+ % 192 unmapped character codes.
+ counttomark 384 ge
+ { counttomark 256 sub /skip exch def
+ counttomark skip neg roll skip { pop } repeat
+ }
+ if
+ counttomark array astore { glyphencoding exch get } forall
+ counttomark 256 sub dup 0 ge
+ { { pop } repeat }
+ { neg { /.notdef } repeat }
+ ifelse ]
+ % Until we can compute the MD5 fingerprint,
+ % just use the precomputed checksum.
+ /XUID [orgXUID 42 checksum]
+ /CharStrings glyphencoding dup length dict
+ 0 1 3 index length 1 sub
+ { % Stack: glyphencoding dict index
+ 2 index 1 index get 2 index 1 index known
+ { pop pop }
+ { 2 index exch 3 -1 roll put }
+ ifelse
+ }
+ for exch pop readonly
+ /sfnts sfnts
+ .dicttomark
+ end end dup /FontName get exch definefont
+ } bind def
diff --git a/pstoraster/gs_typ42.ps b/pstoraster/gs_typ42.ps
new file mode 100644
index 000000000..cc363d5a0
--- /dev/null
+++ b/pstoraster/gs_typ42.ps
@@ -0,0 +1,47 @@
+% Copyright (C) 1996 Aladdin Enterprises. All rights reserved.
+%
+% This file is part of GNU Ghostscript.
+%
+% GNU Ghostscript is distributed in the hope that it will be useful, but
+% WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+% anyone for the consequences of using it or for whether it serves any
+% particular purpose or works at all, unless he says so in writing. Refer to
+% the GNU General Public License for full details.
+%
+% Everyone is granted permission to copy, modify and redistribute GNU
+% Ghostscript, but only under the conditions described in the GNU General
+% Public License. A copy of this license is supposed to have been given to
+% you along with GNU Ghostscript so you can know your rights and
+% responsibilities. It should be in a file named COPYING. Among other
+% things, the copyright notice and this notice must be preserved on all
+% copies.
+%
+% Aladdin Enterprises is not affiliated with the Free Software Foundation or
+% the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+% does not depend on any other GNU software.
+
+% Type 42 font support code.
+
+% Here are the BuildChar and BuildGlyph implementation for Type 42 fonts.
+% The names %Type42BuildChar and %Type42BuildGlyph are known to the
+% interpreter. The real work is done in an operator:
+% <font> <code|name> <name> <charstring> .type42execchar -
+
+(%Type42BuildChar) cvn % <font> <code> %Type42BuildChar -
+ { 1 index /Encoding get 1 index get .type42build
+ } bind def
+(%Type42BuildGlyph) cvn % <font> <name> %Type42BuildGlyph -
+ { dup .type42build
+ } bind def
+/.type42build % <font> <code|name> <name> .type42build -
+ { 2 index begin
+ dup CharStrings exch .knownget not
+ { 2 copy eq { exch pop /.notdef exch } if
+ QUIET not
+ { (Substituting .notdef for ) print = flush }
+ { pop }
+ ifelse
+ /.notdef CharStrings /.notdef get
+ } if
+ end .type42execchar
+ } bind def
diff --git a/pstoraster/gs_type1.ps b/pstoraster/gs_type1.ps
new file mode 100644
index 000000000..bf1468cfe
--- /dev/null
+++ b/pstoraster/gs_type1.ps
@@ -0,0 +1,132 @@
+% Copyright (C) 1994, 1995, 1996 Aladdin Enterprises. All rights reserved.
+%
+% This file is part of GNU Ghostscript.
+%
+% GNU Ghostscript is distributed in the hope that it will be useful, but
+% WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+% anyone for the consequences of using it or for whether it serves any
+% particular purpose or works at all, unless he says so in writing. Refer to
+% the GNU General Public License for full details.
+%
+% Everyone is granted permission to copy, modify and redistribute GNU
+% Ghostscript, but only under the conditions described in the GNU General
+% Public License. A copy of this license is supposed to have been given to
+% you along with GNU Ghostscript so you can know your rights and
+% responsibilities. It should be in a file named COPYING. Among other
+% things, the copyright notice and this notice must be preserved on all
+% copies.
+%
+% Aladdin Enterprises is not affiliated with the Free Software Foundation or
+% the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+% does not depend on any other GNU software.
+
+% Type 1 font support code.
+
+% The standard representation for PostScript compatible fonts is described
+% in the book "Adobe Type 1 Font Format", published by Adobe Systems Inc.
+
+% Define an augmented version of .buildfont1 that inserts UnderlinePosition
+% and UnderlineThickness entries in FontInfo if they aren't there already.
+% (This works around the incorrect assumption, made by many word processors,
+% that these entries are present in the built-in fonts.)
+/.buildfont1
+ { dup /FontInfo known not
+ { .growfontdict dup /FontInfo 2 dict put }
+ if
+ dup dup /FontInfo get dup dup
+ /UnderlinePosition known exch /UnderlineThickness known and
+ { pop pop % entries already present
+ }
+ { dup length 2 add dict .copydict
+ dup /UnderlinePosition known not
+ { dup /UnderlinePosition 3 index /FontBBox get
+ 1 get 2 div put % 1/2 the font descent
+ }
+ if
+ dup /UnderlineThickness known not
+ { dup /UnderlineThickness 3 index /FontBBox get
+ dup 3 get exch 1 get sub 20 div put % 1/20 the font height
+ }
+ if
+ 1 index /FontInfo get wcheck not { readonly } if
+ /FontInfo exch put
+ }
+ ifelse //.buildfont1
+ } bind def
+% If the diskfont feature isn't included, define a dummy .loadfontdict.
+/.loadfontdict where
+ { pop }
+ { /.loadfontdict 0 dict readonly def }
+ifelse
+/.loadfontfile % <file> .loadfontfile -
+ { mark exch //systemdict begin
+ DISKFONTS { .loadfontdict begin } if
+ % We really would just like systemdict on the stack,
+ % but fonts produced by Fontographer require a writable dictionary....
+ userdict begin
+ % We can't just use `run', because we want to check for .PFB files.
+ currentpacking
+ { false setpacking .loadfont1 true setpacking }
+ { .loadfont1 }
+ ifelse
+ { stop } if
+ end
+ DISKFONTS { end } if
+ end cleartomark
+ } bind def
+/.loadfont1 % <file> .loadfont1 <errorflag>
+ { % We would like to use `false /PFBDecode filter',
+ % but this occasionally produces a whitespace character as
+ % the first of an eexec section, so we can't do it.
+ % Also, since the real input file never reaches EOF if we are using
+ % a PFBDecode filter (the filter stops just after reading the last
+ % character), we must explicitly close the real file in this case.
+ % Since the file might leave garbage on the operand stack,
+ % we have to create a procedure to close the file reliably.
+ { dup read not { -1 } if
+ 2 copy unread 16#80 eq
+ { [ exch dup true /PFBDecode filter cvx exch cvlit
+ //systemdict /closefile get ]
+ }
+ if cvx exec
+ } stopped
+ $error /newerror get and dup { .clearerror } if
+ } bind def
+
+
+% The CharStrings are a dictionary in which the key is the character name,
+% and the value is a compressed and encrypted representation of a path.
+% For detailed information, see the book "Adobe Type 1 Font Format",
+% published by Adobe Systems Inc.
+
+% Here are the BuildChar and BuildGlyph implementation for Type 1 fonts.
+% The names %Type1BuildChar and %Type1BuildGlyph are known to the interpreter.
+% The real work is done in an operator:
+% <font> <code|name> <name> <charstring> .type1execchar -
+
+(%Type1BuildChar) cvn % <font> <code> %Type1BuildChar -
+ { 1 index /Encoding get 1 index get .type1build
+ } bind def
+(%Type1BuildGlyph) cvn % <font> <name> %Type1BuildGlyph -
+ { dup .type1build
+ } bind def
+/.type1build % <font> <code|name> <name> .type1build -
+ { 2 index begin
+ dup CharStrings exch .knownget not
+ { 2 copy eq { exch pop /.notdef exch } if
+ QUIET not
+ { (Substituting .notdef for ) print = flush }
+ { pop }
+ ifelse
+ /.notdef CharStrings /.notdef get
+ } if
+ end .type1execchar
+ } bind def
+% CCRun is an undocumented procedure provided for Type 4 fonts.
+1183615869 internaldict begin
+/CCRun % <font> <code|name> <charstring> CCRun -
+ { 1 index dup type /integertype eq
+ { 3 index /Encoding get exch get }
+ if exch .type1execchar
+ } bind def
+end
diff --git a/pstoraster/gs_wan_e.ps b/pstoraster/gs_wan_e.ps
new file mode 100644
index 000000000..93399706a
--- /dev/null
+++ b/pstoraster/gs_wan_e.ps
@@ -0,0 +1,48 @@
+% Copyright (C) 1994, 1996 Aladdin Enterprises. All rights reserved.
+%
+% This file is part of GNU Ghostscript.
+%
+% GNU Ghostscript is distributed in the hope that it will be useful, but
+% WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+% anyone for the consequences of using it or for whether it serves any
+% particular purpose or works at all, unless he says so in writing. Refer to
+% the GNU General Public License for full details.
+%
+% Everyone is granted permission to copy, modify and redistribute GNU
+% Ghostscript, but only under the conditions described in the GNU General
+% Public License. A copy of this license is supposed to have been given to
+% you along with GNU Ghostscript so you can know your rights and
+% responsibilities. It should be in a file named COPYING. Among other
+% things, the copyright notice and this notice must be preserved on all
+% copies.
+%
+% Aladdin Enterprises is not affiliated with the Free Software Foundation or
+% the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+% does not depend on any other GNU software.
+
+% Define the WinAnsi encoding vector.
+/currentglobal where
+ { pop currentglobal { setglobal } true setglobal }
+ { { } }
+ifelse
+/WinAnsiEncoding
+ISOLatin1Encoding 0 39 getinterval aload pop
+ /quotesingle
+ISOLatin1Encoding 40 56 getinterval aload pop
+ /grave
+ISOLatin1Encoding 97 30 getinterval aload pop
+ /bullet
+% \20x
+ /bullet /bullet /quotesinglbase /florin
+ /quotedblbase /ellipsis /dagger /daggerdbl
+ /circumflex /perthousand /Scaron /guilsinglleft
+ /OE /bullet /bullet /bullet
+ /bullet /quoteleft /quoteright /quotedblleft
+ /quotedblright /bullet /endash /emdash
+ /tilde /trademark /scaron /guilsinglright
+ /oe /bullet /bullet /Ydieresis
+ISOLatin1Encoding 160 96 getinterval aload pop
+256 packedarray
+4 1 index .registerencoding
+.defineencoding
+exec
diff --git a/pstoraster/gs_wl1_e.ps b/pstoraster/gs_wl1_e.ps
new file mode 100644
index 000000000..af5c43bb2
--- /dev/null
+++ b/pstoraster/gs_wl1_e.ps
@@ -0,0 +1,72 @@
+% Copyright (C) 1996 Aladdin Enterprises. All rights reserved.
+%
+% This file is part of GNU Ghostscript.
+%
+% GNU Ghostscript is distributed in the hope that it will be useful, but
+% WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+% anyone for the consequences of using it or for whether it serves any
+% particular purpose or works at all, unless he says so in writing. Refer to
+% the GNU General Public License for full details.
+%
+% Everyone is granted permission to copy, modify and redistribute GNU
+% Ghostscript, but only under the conditions described in the GNU General
+% Public License. A copy of this license is supposed to have been given to
+% you along with GNU Ghostscript so you can know your rights and
+% responsibilities. It should be in a file named COPYING. Among other
+% things, the copyright notice and this notice must be preserved on all
+% copies.
+%
+% Aladdin Enterprises is not affiliated with the Free Software Foundation or
+% the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+% does not depend on any other GNU software.
+
+% Define the Windows 3.1 Latin 1 encoding vector (H-P Symbol set 19U).
+/currentglobal where
+ { pop currentglobal { setglobal } true setglobal }
+ { { } }
+ifelse
+/Win31Latin1Encoding
+ISOLatin1Encoding 0 39 getinterval aload pop
+ /quotesingle
+ISOLatin1Encoding 40 5 getinterval aload pop
+ /hyphen
+ISOLatin1Encoding 46 50 getinterval aload pop
+ /grave
+ISOLatin1Encoding 97 30 getinterval aload pop
+ /graybox
+% \20x
+ /.notdef /.notdef /quotesinglbase /florin
+ /quotedblbase /ellipsis /dagger /daggerdbl
+ /circumflex /perthousand /Scaron /guilsinglleft
+ /OE /.notdef /.notdef /.notdef
+ /.notdef /quoteleft /quoteright /quotedblleft
+ /quotedblright /bullet /endash /emdash
+ /tilde /trademark /scaron /guilsinglright
+ /oe /.notdef /.notdef /Ydieresis
+ /.notdef /exclamdown /cent /sterling
+ /currency /yen /brokenbar /section
+ /dieresis /copyright /ordfeminine /guillemotleft
+ /logicalnot /softhyphen /registered /overscore
+ /degree /plusminus /twosuperior /threesuperior
+ /acute /mu /paragraph /periodcentered
+ /cedilla /onesuperior /ordmasculine /guillemotright
+ /onequarter /onehalf /threequarters /questiondown
+% \30x
+ /Agrave /Aacute /Acircumflex /Atilde
+ /Adieresis /Aring /AE /Ccedilla
+ /Egrave /Eacute /Ecircumflex /Edieresis
+ /Igrave /Iacute /Icircumflex /Idieresis
+ /Eth /Ntilde /Ograve /Oacute
+ /Ocircumflex /Otilde /Odieresis /multiply
+ /Oslash /Ugrave /Uacute /Ucircumflex
+ /Udieresis /Yacute /Thorn /germandbls
+ /agrave /aacute /acircumflex /atilde
+ /adieresis /aring /ae /ccedilla
+ /egrave /eacute /ecircumflex /edieresis
+ /igrave /iacute /icircumflex /idieresis
+ /eth /ntilde /ograve /oacute
+ /ocircumflex /otilde /odieresis /divide
+ /oslash /ugrave /uacute /ucircumflex
+ /udieresis /yacute /thorn /ydieresis
+256 packedarray .defineencoding
+exec
diff --git a/pstoraster/gs_wl2_e.ps b/pstoraster/gs_wl2_e.ps
new file mode 100644
index 000000000..8fe21e55d
--- /dev/null
+++ b/pstoraster/gs_wl2_e.ps
@@ -0,0 +1,72 @@
+% Copyright (C) 1996 Aladdin Enterprises. All rights reserved.
+%
+% This file is part of GNU Ghostscript.
+%
+% GNU Ghostscript is distributed in the hope that it will be useful, but
+% WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+% anyone for the consequences of using it or for whether it serves any
+% particular purpose or works at all, unless he says so in writing. Refer to
+% the GNU General Public License for full details.
+%
+% Everyone is granted permission to copy, modify and redistribute GNU
+% Ghostscript, but only under the conditions described in the GNU General
+% Public License. A copy of this license is supposed to have been given to
+% you along with GNU Ghostscript so you can know your rights and
+% responsibilities. It should be in a file named COPYING. Among other
+% things, the copyright notice and this notice must be preserved on all
+% copies.
+%
+% Aladdin Enterprises is not affiliated with the Free Software Foundation or
+% the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+% does not depend on any other GNU software.
+
+% Define the Windows 3.1 Latin 2 encoding vector (H-P Symbol set 9E).
+/currentglobal where
+ { pop currentglobal { setglobal } true setglobal }
+ { { } }
+ifelse
+/Win32Latin2Encoding
+ISOLatin1Encoding 0 39 getinterval aload pop
+ /quotesingle
+ISOLatin1Encoding 40 5 getinterval aload pop
+ /hyphen
+ISOLatin1Encoding 46 50 getinterval aload pop
+ /grave
+ISOLatin1Encoding 97 30 getinterval aload pop
+ /graybox
+% \20x
+ /.notdef /.notdef /quotesinglbase /.notdef
+ /quotedblbase /ellipsis /dagger /daggerdbl
+ /.notdef /perthousand /Scaron /guilsinglleft
+ /Sacute /Tcaron /Zcaron /Zacute
+ /.notdef /quoteleft /quoteright /quotedblleft
+ /quotedblright /bullet /endash /emdash
+ /.notdef /trademark /scaron /guilsinglright
+ /sacute /tcaron /zcaron /zacute
+ /.notdef /caron /breve /Lslash
+ /currency /Aogonek /brokenbar /section
+ /dieresis /copyright /Scedilla /guillemotleft
+ /logicalnot /softhyphen /registered /Zdotaccent
+ /degree /plusminus /ogonek /lslash
+ /acute /mu /paragraph /periodcentered
+ /cedilla /aogonek /scedilla /guillemotright
+ /Lcaron /hungarumlaut /lcaron /zdotaccent
+% \30x
+ /Racute /Aacute /Acircumflex /Abreve
+ /Adieresis /Lacute /Cacute /Ccedilla
+ /Ccaron /Eacute /Eogonek /Edieresis
+ /Ecaron /Iacute /Icircumflex /Dcaron
+ /Dcroat /Nacute /Ncaron /Oacute
+ /Ocircumflex /Ohungarumlaut /Odieresis /multiply
+ /Rcaron /Uring /Uacute /Uhungarumlaut
+ /Udieresis /Yacute /Tcommaaccent /germandbls
+ /racute /aacute /acircumflex /abreve
+ /adieresis /lacute /cacute /ccedilla
+ /ccaron /eacute /eogonek /edieresis
+ /ecaron /iacute /icircumflex /dcaron
+ /dcroat /nacute /ncaron /oacute
+ /ocircumflex /ohungarumlaut /odieresis /divide
+ /rcaron /uring /uacute /uhungarumlaut
+ /udieresis /yacute /tcommaaccent /dotaccent
+256 packedarray .defineencoding
+exec
diff --git a/pstoraster/gs_wl5_e.ps b/pstoraster/gs_wl5_e.ps
new file mode 100644
index 000000000..16e4bfb6b
--- /dev/null
+++ b/pstoraster/gs_wl5_e.ps
@@ -0,0 +1,72 @@
+% Copyright (C) 1996 Aladdin Enterprises. All rights reserved.
+%
+% This file is part of GNU Ghostscript.
+%
+% GNU Ghostscript is distributed in the hope that it will be useful, but
+% WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+% anyone for the consequences of using it or for whether it serves any
+% particular purpose or works at all, unless he says so in writing. Refer to
+% the GNU General Public License for full details.
+%
+% Everyone is granted permission to copy, modify and redistribute GNU
+% Ghostscript, but only under the conditions described in the GNU General
+% Public License. A copy of this license is supposed to have been given to
+% you along with GNU Ghostscript so you can know your rights and
+% responsibilities. It should be in a file named COPYING. Among other
+% things, the copyright notice and this notice must be preserved on all
+% copies.
+%
+% Aladdin Enterprises is not affiliated with the Free Software Foundation or
+% the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+% does not depend on any other GNU software.
+
+% Define the Windows 3.1 Latin 5 encoding vector (H-P Symbol set 5T).
+/currentglobal where
+ { pop currentglobal { setglobal } true setglobal }
+ { { } }
+ifelse
+/Win32Latin5Encoding
+ISOLatin1Encoding 0 39 getinterval aload pop
+ /quotesingle
+ISOLatin1Encoding 40 5 getinterval aload pop
+ /hyphen
+ISOLatin1Encoding 46 50 getinterval aload pop
+ /grave
+ISOLatin1Encoding 97 30 getinterval aload pop
+ /graybox
+% \20x
+ /.notdef /.notdef /quotesinglbase /florin
+ /quotedblbase /ellipsis /dagger /daggerdbl
+ /circumflex /perthousand /Scaron /guilsinglleft
+ /OE /.notdef /.notdef /.notdef
+ /.notdef /quoteleft /quoteright /quotedblleft
+ /quotedblright /bullet /endash /emdash
+ /tilde /trademark /scaron /guilsinglright
+ /oe /.notdef /.notdef /Ydieresis
+ /.notdef /exclamdown /cent /sterling
+ /currency /yen /brokenbar /section
+ /dieresis /copyright /ordfeminine /guillemotleft
+ /logicalnot /softhyphen /registered /overscore
+ /degree /plusminus /twosuperior /threesuperior
+ /acute /mu /paragraph /periodcentered
+ /cedilla /onesuperior /ordmasculine /guillemotright
+ /onequarter /onehalf /threequarters /questiondown
+% \30x
+ /Agrave /Aacute /Acircumflex /Atilde
+ /Adieresis /Aring /AE /Ccedilla
+ /Egrave /Eacute /Ecircumflex /Edieresis
+ /Igrave /Iacute /Icircumflex /Idieresis
+ /Gbreve /Ntilde /Ograve /Oacute
+ /Ocircumflex /Otilde /Odieresis /multiply
+ /Oslash /Ugrave /Uacute /Ucircumflex
+ /Udieresis /Idotaccent /Scedilla /germandbls
+ /agrave /aacute /acircumflex /atilde
+ /adieresis /aring /ae /ccedilla
+ /egrave /eacute /ecircumflex /edieresis
+ /igrave /iacute /icircumflex /idieresis
+ /gbreve /ntilde /ograve /oacute
+ /ocircumflex /otilde /odieresis /divide
+ /oslash /ugrave /uacute /ucircumflex
+ /udieresis /dotlessi /scedilla /ydieresis
+256 packedarray .defineencoding
+exec
diff --git a/pstoraster/gsalloc.c b/pstoraster/gsalloc.c
new file mode 100644
index 000000000..3750bbc57
--- /dev/null
+++ b/pstoraster/gsalloc.c
@@ -0,0 +1,1010 @@
+/* Copyright (C) 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gsalloc.c */
+/* Standard memory allocator */
+#include "gx.h"
+#include "memory_.h"
+#include "gsmdebug.h"
+#include "gsstruct.h"
+#include "gxalloc.h"
+
+/*
+ * This allocator produces tracing messages of the form
+ * [aNMOTS]...
+ * where
+ * N is the VM space number,
+ * M is : for movable objects, | for immovable,
+ * O is {alloc = +, free = -, grow = >, shrink = <},
+ * T is {bytes = b, object = <, ref = $, string = >}, and
+ * S is {freelist = F, LIFO = space, own chunk = L, lost = #,
+ * lost own chunk = ~, other = .}.
+ */
+
+/* The structure descriptor for allocators. Even though allocators */
+/* are allocated outside GC space, they reference objects within it. */
+public_st_ref_memory();
+#define mptr ((gs_ref_memory_t *)vptr)
+private ENUM_PTRS_BEGIN(ref_memory_enum_ptrs) return 0;
+ ENUM_PTR(0, gs_ref_memory_t, changes);
+ ENUM_PTR(1, gs_ref_memory_t, saved);
+ENUM_PTRS_END
+private RELOC_PTRS_BEGIN(ref_memory_reloc_ptrs) {
+ RELOC_PTR(gs_ref_memory_t, changes);
+ /* Don't relocate the pointer now -- see igc.c for details. */
+ mptr->reloc_saved = gs_reloc_struct_ptr(mptr->saved, gcst);
+} RELOC_PTRS_END
+
+/* Forward references */
+private obj_header_t *alloc_obj(P5(gs_ref_memory_t *, ulong, gs_memory_type_ptr_t, bool, client_name_t));
+private chunk_t *alloc_add_chunk(P4(gs_ref_memory_t *, ulong, bool, client_name_t));
+void alloc_close_chunk(P1(gs_ref_memory_t *));
+
+#define imem ((gs_ref_memory_t *)mem)
+
+/*
+ * Define the standard implementation (with garbage collection)
+ * of Ghostscript's memory manager interface.
+ */
+private gs_memory_proc_alloc_bytes(i_alloc_bytes);
+private gs_memory_proc_alloc_bytes(i_alloc_bytes_immovable);
+private gs_memory_proc_alloc_struct(i_alloc_struct);
+private gs_memory_proc_alloc_struct(i_alloc_struct_immovable);
+private gs_memory_proc_alloc_byte_array(i_alloc_byte_array);
+private gs_memory_proc_alloc_byte_array(i_alloc_byte_array_immovable);
+private gs_memory_proc_alloc_struct_array(i_alloc_struct_array);
+private gs_memory_proc_alloc_struct_array(i_alloc_struct_array_immovable);
+private gs_memory_proc_resize_object(i_resize_object);
+private gs_memory_proc_object_size(i_object_size);
+private gs_memory_proc_object_type(i_object_type);
+private gs_memory_proc_free_object(i_free_object);
+private gs_memory_proc_alloc_string(i_alloc_string);
+private gs_memory_proc_alloc_string(i_alloc_string_immovable);
+private gs_memory_proc_resize_string(i_resize_string);
+private gs_memory_proc_free_string(i_free_string);
+private gs_memory_proc_register_root(i_register_root);
+private gs_memory_proc_unregister_root(i_unregister_root);
+private gs_memory_proc_status(i_status);
+private gs_memory_proc_enable_free(i_enable_free);
+private const gs_memory_procs_t imemory_procs = {
+ i_alloc_bytes,
+ i_alloc_bytes_immovable,
+ i_alloc_struct,
+ i_alloc_struct_immovable,
+ i_alloc_byte_array,
+ i_alloc_byte_array_immovable,
+ i_alloc_struct_array,
+ i_alloc_struct_array_immovable,
+ i_resize_object,
+ i_object_size,
+ i_object_type,
+ i_free_object,
+ i_alloc_string,
+ i_alloc_string_immovable,
+ i_resize_string,
+ i_free_string,
+ i_register_root,
+ i_unregister_root,
+ i_status,
+ i_enable_free
+};
+/*
+ * Allocate and mostly initialize the state of an allocator (system, global,
+ * or local). Does not initialize global or space.
+ */
+private void *ialloc_solo(P3(gs_memory_t *, gs_memory_type_ptr_t, chunk_t **));
+gs_ref_memory_t *
+ialloc_alloc_state(gs_memory_t *parent, uint chunk_size)
+{ chunk_t *cp;
+ gs_ref_memory_t *iimem = ialloc_solo(parent, &st_ref_memory, &cp);
+
+ if ( iimem == 0 )
+ return 0;
+ iimem->procs = imemory_procs;
+ iimem->parent = parent;
+ iimem->chunk_size = chunk_size;
+ iimem->large_size = ((chunk_size / 4) & -obj_align_mod) + 1;
+ iimem->gc_status.vm_threshold = chunk_size * 3L;
+ iimem->gc_status.max_vm = max_long;
+ iimem->gc_status.psignal = NULL;
+ iimem->gc_status.enabled = false;
+ iimem->previous_status.allocated = 0;
+ iimem->previous_status.used = 0;
+ ialloc_reset(iimem);
+ iimem->cfirst = iimem->clast = cp;
+ ialloc_set_limit(iimem);
+ iimem->cc.cbot = iimem->cc.ctop = 0;
+ iimem->pcc = 0;
+ iimem->roots = 0;
+ iimem->num_contexts = 1;
+ iimem->saved = 0;
+ return iimem;
+}
+/* Allocate a 'solo' object with its own chunk. */
+private void *
+ialloc_solo(gs_memory_t *parent, gs_memory_type_ptr_t pstype, chunk_t **pcp)
+{ /*
+ * We can't assume that the parent uses the same object header
+ * that we do, but the GC requires that allocators have
+ * such a header. Therefore, we prepend one explicitly.
+ */
+ chunk_t *cp = gs_alloc_struct_immovable(parent, chunk_t, &st_chunk,
+ "ialloc_solo(chunk)");
+ uint csize =
+ round_up(sizeof(chunk_head_t) + sizeof(obj_header_t) +
+ pstype->ssize,
+ obj_align_mod);
+ byte *cdata = gs_alloc_bytes_immovable(parent, csize, "ialloc_solo");
+ obj_header_t *obj = (obj_header_t *)(cdata + sizeof(chunk_head_t));
+
+ if ( cp == 0 || cdata == 0 )
+ return 0;
+ alloc_init_chunk(cp, cdata, cdata + csize, false, (chunk_t *)NULL);
+ cp->cbot = cp->ctop;
+ cp->cprev = cp->cnext = 0;
+ /* Construct the object header "by hand". */
+ obj->o_large = 0;
+ obj->o_size = pstype->ssize;
+ obj->o_type = pstype;
+ *pcp = cp;
+ return (void *)(obj + 1);
+}
+/* Initialize after a save. */
+void
+ialloc_reset(gs_ref_memory_t *mem)
+{ mem->cfirst = 0;
+ mem->clast = 0;
+ mem->cc.rcur = 0;
+ mem->cc.rtop = 0;
+ mem->cc.has_refs = false;
+ mem->allocated = 0;
+ mem->inherited = 0;
+ mem->changes = 0;
+ ialloc_reset_free(mem);
+}
+/* Initialize after a save or GC. */
+void
+ialloc_reset_free(gs_ref_memory_t *mem)
+{ int i;
+ obj_header_t **p;
+ mem->freed_lost = 0;
+ mem->cfreed.cp = 0;
+ for ( i = 0, p = &mem->freelists[0]; i < num_freelists; i++, p++ )
+ *p = 0;
+}
+/* Set the allocation limit after a change in one or more of */
+/* vm_threshold, max_vm, or enabled, or after a GC. */
+void
+ialloc_set_limit(register gs_ref_memory_t *mem)
+{ /*
+ * The following code is intended to set the limit so that
+ * we stop allocating when allocated + previous_status.allocated
+ * exceeds the lesser of max_vm or (if GC is enabled)
+ * gc_allocated + vm_threshold.
+ */
+ ulong max_allocated =
+ (mem->gc_status.max_vm > mem->previous_status.allocated ?
+ mem->gc_status.max_vm - mem->previous_status.allocated :
+ 0);
+ if ( mem->gc_status.enabled )
+ { ulong limit = mem->gc_allocated + mem->gc_status.vm_threshold;
+ if ( limit < mem->previous_status.allocated )
+ mem->limit = 0;
+ else
+ { limit -= mem->previous_status.allocated;
+ mem->limit = min(limit, max_allocated);
+ }
+ }
+ else
+ mem->limit = max_allocated;
+ if_debug7('0', "[0]space=%d, max_vm=%ld, prev.alloc=%ld, enabled=%d,\n\
+ gc_alloc=%ld, threshold=%ld => limit=%ld\n",
+ mem->space, (long)mem->gc_status.max_vm,
+ (long)mem->previous_status.allocated,
+ mem->gc_status.enabled, (long)mem->gc_allocated,
+ (long)mem->gc_status.vm_threshold, (long)mem->limit);
+}
+
+/* ================ Accessors ================ */
+
+/* Get the size of an object from the header. */
+private uint
+i_object_size(gs_memory_t *mem, const void /*obj_header_t*/ *obj)
+{ return pre_obj_contents_size((const obj_header_t *)obj - 1);
+}
+
+/* Get the type of a structure from the header. */
+private gs_memory_type_ptr_t
+i_object_type(gs_memory_t *mem, const void /*obj_header_t*/ *obj)
+{ return ((const obj_header_t *)obj - 1)->o_type;
+}
+
+/* Get the GC status of a memory. */
+void
+gs_memory_gc_status(const gs_ref_memory_t *mem, gs_memory_gc_status_t *pstat)
+{ *pstat = mem->gc_status;
+}
+
+/* Set the GC status of a memory. */
+void
+gs_memory_set_gc_status(gs_ref_memory_t *mem, const gs_memory_gc_status_t *pstat)
+{ mem->gc_status = *pstat;
+ ialloc_set_limit(mem);
+}
+
+/* ================ Objects ================ */
+
+/* Allocate a small object quickly if possible. */
+/* The size must be substantially less than max_uint. */
+/* ptr must be declared as obj_header_t *. */
+/* pfl must be declared as obj_header_t **. */
+#define IF_FREELIST_ALLOC(ptr, imem, size, pstype, pfl)\
+ if ( size <= max_freelist_size &&\
+ *(pfl = &imem->freelists[(size + obj_align_mask) >> log2_obj_align_mod]) != 0\
+ )\
+ { ptr = *pfl;\
+ *pfl = *(obj_header_t **)ptr;\
+ ptr[-1].o_size = size;\
+ ptr[-1].o_type = pstype;\
+ /* If debugging, clear the block in an attempt to */\
+ /* track down uninitialized data errors. */\
+ gs_alloc_fill(ptr, gs_alloc_fill_alloc, size);
+#define ELSEIF_LIFO_ALLOC(ptr, imem, size, pstype)\
+ }\
+ else if ( (imem->cc.ctop - (byte *)(ptr = (obj_header_t *)imem->cc.cbot))\
+ >= size + (obj_align_mod + sizeof(obj_header_t) * 2) &&\
+ size < imem->large_size\
+ )\
+ { imem->cc.cbot = (byte *)ptr + obj_size_round(size);\
+ ptr->o_large = 0;\
+ ptr->o_size = size;\
+ ptr->o_type = pstype;\
+ ptr++;\
+ /* If debugging, clear the block in an attempt to */\
+ /* track down uninitialized data errors. */\
+ gs_alloc_fill(ptr, gs_alloc_fill_alloc, size);
+#define ELSE_ALLOC\
+ }\
+ else
+
+private byte *
+i_alloc_bytes(gs_memory_t *mem, uint size, client_name_t cname)
+{ obj_header_t *obj;
+ obj_header_t **pfl;
+ IF_FREELIST_ALLOC(obj, imem, size, &st_bytes, pfl)
+ if_debug4('A', "[a%d:+bF]%s -bytes-(%u) = 0x%lx\n",
+ imem->space,
+ client_name_string(cname), size, (ulong)obj);
+ ELSEIF_LIFO_ALLOC(obj, imem, size, &st_bytes)
+ if_debug4('A', "[a%d:+b ]%s -bytes-(%u) = 0x%lx\n",
+ imem->space,
+ client_name_string(cname), size, (ulong)obj);
+ ELSE_ALLOC
+ { obj = alloc_obj(imem, size, &st_bytes, false, cname);
+ if ( obj == 0 )
+ return 0;
+ if_debug4('A', "[a%d:+b.]%s -bytes-(%u) = 0x%lx\n",
+ imem->space,
+ client_name_string(cname), size, (ulong)obj);
+ }
+ return (byte *)obj;
+}
+private byte *
+i_alloc_bytes_immovable(gs_memory_t *mem, uint size, client_name_t cname)
+{ obj_header_t *obj = alloc_obj(imem, size, &st_bytes, true, cname);
+ if ( obj == 0 )
+ return 0;
+ if_debug4('A', "[a%d|+b.]%s -bytes-(%u) = 0x%lx\n",
+ imem->space, client_name_string(cname), size, (ulong)obj);
+ return (byte *)obj;
+}
+private void *
+i_alloc_struct(gs_memory_t *mem, gs_memory_type_ptr_t pstype,
+ client_name_t cname)
+{ uint size = pstype->ssize;
+ obj_header_t *obj;
+ obj_header_t **pfl;
+ IF_FREELIST_ALLOC(obj, imem, size, pstype, pfl)
+ if_debug5('A', "[a%d:+<F]%s %s(%u) = 0x%lx\n",
+ imem->space, client_name_string(cname),
+ struct_type_name_string(pstype), size, (ulong)obj);
+ ELSEIF_LIFO_ALLOC(obj, imem, size, pstype)
+ if_debug5('A', "[a%d:+< ]%s %s(%u) = 0x%lx\n",
+ imem->space, client_name_string(cname),
+ struct_type_name_string(pstype), size, (ulong)obj);
+ ELSE_ALLOC
+ { obj = alloc_obj(imem, size, pstype, false, cname);
+ if ( obj == 0 )
+ return 0;
+ if_debug5('A', "[a%d:+<.]%s %s(%u) = 0x%lx\n",
+ imem->space, client_name_string(cname),
+ struct_type_name_string(pstype), size, (ulong)obj);
+ }
+ return obj;
+}
+private void *
+i_alloc_struct_immovable(gs_memory_t *mem, gs_memory_type_ptr_t pstype,
+ client_name_t cname)
+{ uint size = pstype->ssize;
+ obj_header_t *obj = alloc_obj(imem, size, pstype, true, cname);
+ if ( obj == 0 )
+ return 0;
+ if_debug5('A', "[a%d|+<.]%s %s(%u) = 0x%lx\n",
+ imem->space, client_name_string(cname),
+ struct_type_name_string(pstype), size, (ulong)obj);
+ return obj;
+}
+private byte *
+i_alloc_byte_array(gs_memory_t *mem, uint num_elements, uint elt_size,
+ client_name_t cname)
+{ obj_header_t *obj = alloc_obj(imem, (ulong)num_elements * elt_size,
+ &st_bytes, false, cname);
+ if_debug6('A', "[a%d:+b.]%s -bytes-*(%lu=%u*%u) = 0x%lx\n",
+ imem->space, client_name_string(cname),
+ (ulong)num_elements * elt_size,
+ num_elements, elt_size, (ulong)obj);
+ return (byte *)obj;
+}
+private byte *
+i_alloc_byte_array_immovable(gs_memory_t *mem, uint num_elements,
+ uint elt_size, client_name_t cname)
+{ obj_header_t *obj = alloc_obj(imem, (ulong)num_elements * elt_size,
+ &st_bytes, true, cname);
+ if_debug6('A', "[a%d|+b.]%s -bytes-*(%lu=%u*%u) = 0x%lx\n",
+ imem->space, client_name_string(cname),
+ (ulong)num_elements * elt_size,
+ num_elements, elt_size, (ulong)obj);
+ return (byte *)obj;
+}
+private void *
+i_alloc_struct_array(gs_memory_t *mem, uint num_elements,
+ gs_memory_type_ptr_t pstype, client_name_t cname)
+{ obj_header_t *obj = alloc_obj(imem,
+ (ulong)num_elements * pstype->ssize,
+ pstype, false, cname);
+ if_debug7('A', "[a%d:+<.]%s %s*(%lu=%u*%u) = 0x%lx\n",
+ imem->space,
+ client_name_string(cname), struct_type_name_string(pstype),
+ (ulong)num_elements * pstype->ssize,
+ num_elements, pstype->ssize, (ulong)obj);
+ return (char *)obj;
+}
+private void *
+i_alloc_struct_array_immovable(gs_memory_t *mem, uint num_elements,
+ gs_memory_type_ptr_t pstype, client_name_t cname)
+{ obj_header_t *obj = alloc_obj(imem,
+ (ulong)num_elements * pstype->ssize,
+ pstype, true, cname);
+ if_debug7('A', "[a%d|+<.]%s %s*(%lu=%u*%u) = 0x%lx\n",
+ imem->space,
+ client_name_string(cname), struct_type_name_string(pstype),
+ (ulong)num_elements * pstype->ssize,
+ num_elements, pstype->ssize, (ulong)obj);
+ return (char *)obj;
+}
+private void *
+i_resize_object(gs_memory_t *mem, void *obj, uint new_num_elements,
+ client_name_t cname)
+{ obj_header_t *pp = (obj_header_t *)obj - 1;
+ gs_memory_type_ptr_t pstype = pp->o_type;
+ ulong old_size = pre_obj_contents_size(pp);
+ ulong new_size = (ulong)pstype->ssize * new_num_elements;
+ void *new_obj;
+
+ if ( (byte *)obj + obj_align_round(old_size) == imem->cc.cbot &&
+ imem->cc.ctop - imem->cc.cbot < new_size + obj_align_mod
+ )
+ { imem->cc.cbot = (byte *)obj + obj_align_round(new_size);
+ if_debug8('A', "[a%d:%c%c ]%s %s(%lu=>%lu) 0x%lx\n",
+ imem->space,
+ (new_size > old_size ? '>' : '<'),
+ (pstype == &st_bytes ? 'b' : '<'),
+ client_name_string(cname),
+ struct_type_name_string(pstype),
+ old_size, new_size, (ulong)obj);
+ return obj;
+ }
+ /* Punt. */
+ new_obj = gs_alloc_struct_array(mem, new_num_elements, void,
+ pstype, cname);
+ if ( new_obj == 0 )
+ return 0;
+ memcpy(new_obj, obj, min(old_size, new_size));
+ gs_free_object(mem, obj, cname);
+ return new_obj;
+}
+private void
+i_free_object(gs_memory_t *mem, void *ptr, client_name_t cname)
+{ obj_header_t *pp;
+ struct_proc_finalize((*finalize));
+ uint size;
+
+ if ( ptr == 0 )
+ return;
+ pp = (obj_header_t *)ptr - 1;
+#ifdef DEBUG
+ if ( gs_debug_c('?') )
+ { if ( pp->o_type == &st_free )
+ { lprintf2("%s: object 0x%lx already free!\n",
+ client_name_string(cname), (ulong)pp);
+ return;/*gs_abort();*/
+ }
+ }
+#endif
+ size = pre_obj_contents_size(pp);
+ finalize = pp->o_type->finalize;
+ if ( finalize != 0 )
+ { if_debug3('u', "[u]finalizing %s 0x%lx (%s)\n",
+ struct_type_name_string(pp->o_type),
+ (ulong)ptr, client_name_string(cname));
+ (*finalize)(ptr);
+ }
+ if ( (byte *)ptr + obj_align_round(size) == imem->cc.cbot )
+ { if_debug4('A', "[a%d:-o ]%s(%u) 0x%lx\n", imem->space,
+ client_name_string(cname), size, (ulong)pp);
+ gs_alloc_fill(ptr, gs_alloc_fill_free, size);
+ imem->cc.cbot = (byte *)pp;
+ return;
+ }
+ if ( pp->o_large )
+ { /*
+ * We gave this object its own chunk. Free the entire chunk,
+ * unless it belongs to an older save level, in which case
+ * we mustn't overwrite it.
+ */
+ chunk_locator_t cl;
+#ifdef DEBUG
+ { chunk_locator_t cld;
+ cld.memory = imem;
+ cld.cp = 0;
+ if_debug5('a', "[a%d:-o%c]%s(%u) 0x%lx\n", imem->space,
+ (chunk_locate_ptr(ptr, &cld) ? 'L' : '~'),
+ client_name_string(cname), size, (ulong)pp);
+ }
+#endif
+ cl.memory = imem;
+ cl.cp = 0;
+ if ( chunk_locate_ptr(ptr, &cl) )
+ { alloc_free_chunk(cl.cp, imem);
+ return;
+ }
+ /* Don't overwrite even if gs_alloc_debug is set. */
+ }
+ if ( size <= max_freelist_size &&
+ size >= sizeof(obj_header_t *)
+ )
+ { /*
+ * Put the object on a freelist, unless it belongs to
+ * an older save level, in which case we mustn't
+ * overwrite it.
+ */
+ imem->cfreed.memory = imem;
+ if ( chunk_locate(ptr, &imem->cfreed) )
+ { obj_header_t **pfl =
+ &imem->freelists[(size + obj_align_mask) >>
+ log2_obj_align_mod];
+ pp->o_type = &st_free; /* don't confuse GC */
+ gs_alloc_fill(ptr, gs_alloc_fill_free, size);
+ *(obj_header_t **)ptr = *pfl;
+ *pfl = (obj_header_t *)ptr;
+ if_debug4('A', "[a%d:-oF]%s(%u) 0x%lx\n",
+ imem->space, client_name_string(cname),
+ size, (ulong)pp);
+ return;
+ }
+ /* Don't overwrite even if gs_alloc_debug is set. */
+ }
+ else
+ { pp->o_type = &st_free; /* don't confuse GC */
+ gs_alloc_fill(ptr, gs_alloc_fill_free, size);
+ }
+ if_debug4('A', "[a%d:-o#]%s(%u) 0x%lx\n", imem->space,
+ client_name_string(cname), size, (ulong)pp);
+ imem->freed_lost += obj_size_round(size);
+}
+private byte *
+i_alloc_string(gs_memory_t *mem, uint nbytes, client_name_t cname)
+{ byte *str;
+top: if ( imem->cc.ctop - imem->cc.cbot > nbytes )
+ { if_debug4('A', "[a%d:+> ]%s(%u) = 0x%lx\n", imem->space,
+ client_name_string(cname), nbytes,
+ (ulong)(imem->cc.ctop - nbytes));
+ str = imem->cc.ctop -= nbytes;
+ gs_alloc_fill(str, gs_alloc_fill_alloc, nbytes);
+ return str;
+ }
+ if ( nbytes > string_space_quanta(max_uint - sizeof(chunk_head_t)) *
+ string_data_quantum
+ )
+ { /* Can't represent the size in a uint! */
+ return 0;
+ }
+ if ( nbytes >= imem->large_size )
+ { /* Give it a chunk all its own. */
+ return i_alloc_string_immovable(mem, nbytes, cname);
+ }
+ else
+ { /* Add another chunk. */
+ chunk_t *cp =
+ alloc_add_chunk(imem, (ulong)imem->chunk_size, true,
+ "chunk");
+ if ( cp == 0 )
+ return 0;
+ alloc_close_chunk(imem);
+ imem->pcc = cp;
+ imem->cc = *imem->pcc;
+ goto top;
+ }
+}
+private byte *
+i_alloc_string_immovable(gs_memory_t *mem, uint nbytes, client_name_t cname)
+{ byte *str;
+ /* Give it a chunk all its own. */
+ uint asize = string_chunk_space(nbytes) + sizeof(chunk_head_t);
+ chunk_t *cp = alloc_add_chunk(imem, (ulong)asize, true,
+ "large string chunk");
+ if ( cp == 0 )
+ return 0;
+ str = cp->ctop = cp->climit - nbytes;
+ if_debug4('a', "[a%d|+>L]%s(%u) = 0x%lx\n", imem->space,
+ client_name_string(cname), nbytes, (ulong)str);
+ gs_alloc_fill(str, gs_alloc_fill_alloc, nbytes);
+ return str;
+}
+private byte *
+i_resize_string(gs_memory_t *mem, byte *data, uint old_num, uint new_num,
+ client_name_t cname)
+{ byte *ptr;
+ if ( data == imem->cc.ctop &&
+ (new_num < old_num ||
+ imem->cc.ctop - imem->cc.cbot > new_num - old_num)
+ )
+ { /* Resize in place. */
+ register uint i;
+ ptr = data + old_num - new_num;
+ if_debug6('A', "[a%d:%c> ]%s(%u->%u) 0x%lx\n",
+ imem->space, (new_num > old_num ? '>' : '<'),
+ client_name_string(cname), old_num, new_num,
+ (ulong)ptr);
+ imem->cc.ctop = ptr;
+ if ( new_num < old_num )
+ for ( i = new_num; i > 0; ) --i, ptr[i] = data[i];
+ else
+ for ( i = 0; i < old_num; ) ptr[i] = data[i], i++;
+ }
+ else
+ { /* Punt. */
+ ptr = gs_alloc_string(mem, new_num, cname);
+ if ( ptr == 0 )
+ return 0;
+ memcpy(ptr, data, min(old_num, new_num));
+ gs_free_string(mem, data, old_num, cname);
+ }
+ return ptr;
+}
+private void
+i_free_string(gs_memory_t *mem, byte *data, uint nbytes,
+ client_name_t cname)
+{ if ( data == imem->cc.ctop )
+ { if_debug4('A', "[a%d:-> ]%s(%u) 0x%lx\n", imem->space,
+ client_name_string(cname), nbytes, (ulong)data);
+ imem->cc.ctop += nbytes;
+ }
+ else
+ { if_debug4('A', "[a%d:->#]%s(%u) 0x%lx\n", imem->space,
+ client_name_string(cname), nbytes, (ulong)data);
+ imem->freed_lost += nbytes;
+ }
+}
+private void
+i_status(gs_memory_t *mem, gs_memory_status_t *pstat)
+{ ulong unused = imem->freed_lost;
+ ulong inner = 0;
+ alloc_close_chunk(imem);
+ /* Add up unallocated space within each chunk. */
+ /* Also keep track of space allocated to inner chunks, */
+ /* which are included in previous_status.allocated. */
+ { const chunk_t *cp = imem->cfirst;
+ while ( cp != 0 )
+ { unused += cp->ctop - cp->cbot;
+ if ( cp->outer )
+ inner += cp->cend - (byte *)cp->chead;
+ cp = cp->cnext;
+ }
+ }
+ /* Add up space on free lists. */
+ { int i;
+ const obj_header_t *pfree;
+ for ( i = 0; i < num_freelists; i++ )
+ { uint free_size =
+ (i << log2_obj_align_mod) + sizeof(obj_header_t);
+ for ( pfree = imem->freelists[i]; pfree != 0;
+ pfree = *(const obj_header_t * const *)pfree
+ )
+ unused += free_size;
+ }
+ }
+ pstat->used = imem->allocated + inner - unused +
+ imem->previous_status.used;
+ pstat->allocated = imem->allocated +
+ imem->previous_status.allocated;
+}
+private void
+i_enable_free(gs_memory_t *mem, bool enable)
+{ if ( enable )
+ mem->procs.free_object = i_free_object,
+ mem->procs.free_string = i_free_string;
+ else
+ mem->procs.free_object = gs_ignore_free_object,
+ mem->procs.free_string = gs_ignore_free_string;
+}
+
+/* ------ Internal procedures ------ */
+
+/* Allocate an object. This handles all but the fastest, simplest case. */
+private obj_header_t *
+alloc_obj(gs_ref_memory_t *mem, ulong lsize, gs_memory_type_ptr_t pstype,
+ bool immovable, client_name_t cname)
+{ obj_header_t *ptr;
+ if ( lsize >= mem->large_size || immovable )
+ { ulong asize =
+ ((lsize + obj_align_mask) & -obj_align_mod) +
+ sizeof(obj_header_t);
+ /* Give it a chunk all its own. */
+ chunk_t *cp =
+ alloc_add_chunk(mem, asize + sizeof(chunk_head_t), false,
+ "large object chunk");
+ if ( cp == 0 )
+ return 0;
+ ptr = (obj_header_t *)cp->cbot;
+ cp->cbot += asize;
+ if_debug5('a', "[a%d%c+ L]%s(%lu) = 0x%lx\n",
+ imem->space, (immovable ? '|' : ':'),
+ client_name_string(cname), lsize, (ulong)ptr);
+ ptr->o_large = 1;
+ pre_obj_set_large_size(ptr, lsize);
+ }
+ else
+ { uint asize = obj_size_round((uint)lsize);
+ while ( mem->cc.ctop -
+ (byte *)(ptr = (obj_header_t *)mem->cc.cbot)
+ <= asize + sizeof(obj_header_t) )
+ { /* Add another chunk. */
+ chunk_t *cp =
+ alloc_add_chunk(mem, (ulong)mem->chunk_size,
+ true, "chunk");
+ if ( cp == 0 )
+ return 0;
+ alloc_close_chunk(mem);
+ mem->pcc = cp;
+ mem->cc = *mem->pcc;
+ }
+ mem->cc.cbot = (byte *)ptr + asize;
+ ptr->o_large = 0;
+ ptr->o_size = (uint)lsize;
+ }
+ ptr->o_type = pstype;
+ ptr++;
+ gs_alloc_fill(ptr, gs_alloc_fill_alloc, lsize);
+ return ptr;
+}
+
+/* ================ Roots ================ */
+
+/* Register a root. */
+private void
+i_register_root(gs_memory_t *mem, gs_gc_root_t *rp, gs_ptr_type_t ptype,
+ void **up, client_name_t cname)
+{ if_debug3('8', "[8]register root(%s) 0x%lx -> 0x%lx\n",
+ client_name_string(cname), (ulong)rp, (ulong)up);
+ rp->ptype = ptype, rp->p = up;
+ rp->next = imem->roots, imem->roots = rp;
+}
+
+/* Unregister a root. */
+private void
+i_unregister_root(gs_memory_t *mem, gs_gc_root_t *rp, client_name_t cname)
+{ gs_gc_root_t **rpp = &imem->roots;
+ if_debug2('8', "[8]unregister root(%s) 0x%lx\n",
+ client_name_string(cname), (ulong)rp);
+ while ( *rpp != rp ) rpp = &(*rpp)->next;
+ *rpp = (*rpp)->next;
+}
+
+/* ================ Chunks ================ */
+
+public_st_chunk();
+
+/* Insert a chunk in the chain. This is exported for the GC and for */
+/* the forget_save operation. */
+void
+alloc_link_chunk(chunk_t *cp, gs_ref_memory_t *mem)
+{ byte *cdata = cp->cbase;
+ chunk_t *icp;
+ chunk_t *prev;
+ for ( icp = mem->cfirst; icp != 0 && ptr_ge(cdata, icp->ctop);
+ icp = icp->cnext
+ )
+ ;
+ cp->cnext = icp;
+ if ( icp == 0 ) /* add at end of chain */
+ { prev = imem->clast;
+ imem->clast = cp;
+ }
+ else /* insert before icp */
+ { prev = icp->cprev;
+ icp->cprev = cp;
+ }
+ cp->cprev = prev;
+ if ( prev == 0 )
+ imem->cfirst = cp;
+ else
+ prev->cnext= cp;
+ if ( imem->pcc != 0 )
+ { imem->cc.cnext = imem->pcc->cnext;
+ imem->cc.cprev = imem->pcc->cprev;
+ }
+}
+
+/* Allocate a chunk. If we would exceed MaxLocalVM (if relevant), */
+/* or if we would exceed the VMThreshold and psignal is NULL, */
+/* return 0; if we would exceed the VMThreshold but psignal is valid, */
+/* just set the signal and return successfully. */
+private chunk_t *
+alloc_add_chunk(gs_ref_memory_t *mem, ulong csize, bool has_strings,
+ client_name_t cname)
+{ gs_memory_t *parent = mem->parent;
+ chunk_t *cp = gs_alloc_struct_immovable(parent, chunk_t, &st_chunk,
+ cname);
+ byte *cdata;
+ /* If csize is larger than max_uint, */
+ /* we have to fake it using gs_alloc_byte_array. */
+ ulong elt_size = csize;
+ uint num_elts = 1;
+ if ( (ulong)(mem->allocated + mem->inherited) >= mem->limit )
+ { mem->gc_status.requested += csize;
+ if ( mem->limit >= mem->gc_status.max_vm ||
+ mem->gc_status.psignal == 0
+ )
+ return 0;
+ if_debug4('0', "[0]signaling space=%d, allocated=%ld, limit=%ld, requested=%ld\n",
+ mem->space, (long)mem->allocated,
+ (long)mem->limit, (long)mem->gc_status.requested);
+ *mem->gc_status.psignal = mem->gc_status.signal_value;
+ }
+ while ( (uint)elt_size != elt_size )
+ elt_size = (elt_size + 1) >> 1,
+ num_elts <<= 1;
+ cdata = gs_alloc_byte_array_immovable(parent, num_elts, elt_size,
+ cname);
+ if ( cp == 0 || cdata == 0 )
+ { gs_free_object(parent, cdata, cname);
+ gs_free_object(parent, cp, cname);
+ mem->gc_status.requested = csize;
+ return 0;
+ }
+ alloc_init_chunk(cp, cdata, cdata + csize, has_strings, (chunk_t *)0);
+ alloc_link_chunk(cp, mem);
+ mem->allocated +=
+ gs_object_size(parent, cdata) + gs_object_size(parent, cp);
+ return cp;
+}
+
+/* Initialize the pointers in a chunk. This is exported for save/restore. */
+/* The bottom pointer must be aligned, but the top pointer need not */
+/* be aligned. */
+void
+alloc_init_chunk(chunk_t *cp, byte *bot, byte *top, bool has_strings,
+ chunk_t *outer)
+{ byte *cdata = bot;
+ if ( outer != 0 )
+ outer->inner_count++;
+ cp->chead = (chunk_head_t *)cdata;
+ cdata += sizeof(chunk_head_t);
+ cp->cbot = cp->cbase = cdata;
+ cp->cend = top;
+ cp->rcur = 0;
+ cp->rtop = 0;
+ cp->outer = outer;
+ cp->inner_count = 0;
+ cp->has_refs = false;
+ cp->sbase = cdata;
+ if ( has_strings && top - cdata >= string_space_quantum + sizeof(long) - 1)
+ { /*
+ * We allocate a large enough string marking and reloc table
+ * to cover the entire chunk.
+ */
+ uint nquanta = string_space_quanta(top - cdata);
+ cp->climit = cdata + nquanta * string_data_quantum;
+ cp->smark = cp->climit;
+ cp->smark_size = string_quanta_mark_size(nquanta);
+ cp->sreloc =
+ (string_reloc_offset *)(cp->smark + cp->smark_size);
+ }
+ else
+ { /* No strings, don't need the string GC tables. */
+ cp->climit = cp->cend;
+ cp->smark = 0;
+ cp->smark_size = 0;
+ cp->sreloc = 0;
+ }
+ cp->ctop = cp->climit;
+}
+
+/* Close up the current chunk. */
+/* This is exported for save/restore and the GC. */
+void
+alloc_close_chunk(gs_ref_memory_t *mem)
+{ if ( mem->pcc != 0 )
+ { *mem->pcc = mem->cc;
+#ifdef DEBUG
+ if ( gs_debug_c('a') )
+ { dprintf1("[a%d]", mem->space);
+ dprintf_chunk("closing chunk", mem->pcc);
+ }
+#endif
+ }
+}
+
+/* Reopen the current chunk after a GC or restore. */
+void
+alloc_open_chunk(gs_ref_memory_t *mem)
+{ if ( mem->pcc != 0 )
+ { mem->cc = *mem->pcc;
+#ifdef DEBUG
+ if ( gs_debug_c('a') )
+ { dprintf1("[a%d]", mem->space);
+ dprintf_chunk("opening chunk", mem->pcc);
+ }
+#endif
+ }
+}
+
+/* Remove a chunk from the chain. This is exported for the GC. */
+void
+alloc_unlink_chunk(chunk_t *cp, gs_ref_memory_t *mem)
+{
+#ifdef DEBUG
+ if ( gs_alloc_debug )
+ { /* Check to make sure this chunk belongs to this allocator. */
+ const chunk_t *ap = mem->cfirst;
+ while ( ap != 0 && ap != cp )
+ ap = ap->cnext;
+ if ( ap != cp )
+ { lprintf2("unlink_chunk 0x%lx not owned by memory 0x%lx!\n",
+ (ulong)cp, (ulong)mem);
+ return;/*gs_abort();*/
+ }
+ }
+#endif
+ if ( cp->cprev == 0 )
+ mem->cfirst = cp->cnext;
+ else
+ cp->cprev->cnext = cp->cnext;
+ if ( cp->cnext == 0 )
+ mem->clast = cp->cprev;
+ else
+ cp->cnext->cprev = cp->cprev;
+ if ( mem->pcc != 0 )
+ { mem->cc.cnext = mem->pcc->cnext;
+ mem->cc.cprev = mem->pcc->cprev;
+ if ( mem->pcc == cp )
+ { mem->pcc = 0;
+ mem->cc.cbot = mem->cc.ctop = 0;
+ }
+ }
+}
+
+/* Free a chunk. This is exported for save/restore and for the GC. */
+void
+alloc_free_chunk(chunk_t *cp, gs_ref_memory_t *mem)
+{ gs_memory_t *parent = mem->parent;
+ alloc_unlink_chunk(cp, mem);
+ if ( mem->cfreed.cp == cp )
+ mem->cfreed.cp = 0;
+ if ( cp->outer == 0 )
+ { byte *cdata = (byte *)cp->chead;
+ mem->allocated -= gs_object_size(parent, cdata);
+ gs_free_object(parent, cdata, "alloc_free_chunk(data)");
+ }
+ else
+ cp->outer->inner_count--;
+ mem->allocated -= gs_object_size(parent, cp);
+ gs_free_object(parent, cp, "alloc_free_chunk(chunk struct)");
+}
+
+/* Find the chunk for a pointer. */
+/* Note that this only searches the current save level. */
+/* Since a given save level can't contain both a chunk and an inner chunk */
+/* of that chunk, we can stop when is_within_chunk succeeds, and just test */
+/* is_in_inner_chunk then. */
+bool
+chunk_locate_ptr(const void *vptr, chunk_locator_t *clp)
+{ register chunk_t *cp = clp->cp;
+ if ( cp == 0 )
+ { cp = clp->memory->cfirst;
+ if ( cp == 0 )
+ return false;
+ }
+#define ptr (const byte *)vptr
+ if ( ptr_lt(ptr, cp->cbase) )
+ { do
+ { cp = cp->cprev;
+ if ( cp == 0 )
+ return false;
+ }
+ while ( ptr_lt(ptr, cp->cbase) );
+ if ( ptr_ge(ptr, cp->cend) )
+ return false;
+ }
+ else
+ { while ( ptr_ge(ptr, cp->cend) )
+ { cp = cp->cnext;
+ if ( cp == 0 )
+ return false;
+ }
+ if ( ptr_lt(ptr, cp->cbase) )
+ return false;
+ }
+ clp->cp = cp;
+ return !ptr_is_in_inner_chunk(ptr, cp);
+#undef ptr
+}
+
+/* ------ Debugging printout ------ */
+
+#ifdef DEBUG
+
+void
+debug_print_chunk(const chunk_t *cp)
+{ dprintf1("chunk at 0x%lx:\n", (ulong)cp);
+ dprintf3(" chead=0x%lx cbase=0x%lx sbase=0x%lx\n",
+ (ulong)cp->chead, (ulong)cp->cbase, (ulong)cp->sbase);
+ dprintf3(" rcur=0x%lx rtop=0x%lx cbot=0x%lx\n",
+ (ulong)cp->rcur, (ulong)cp->rtop, (ulong)cp->cbot);
+ dprintf4(" ctop=0x%lx climit=0x%lx smark=0x%lx, size=%u\n",
+ (ulong)cp->ctop, (ulong)cp->climit, (ulong)cp->smark,
+ cp->smark_size);
+ dprintf2(" sreloc=0x%lx cend=0x%lx\n",
+ (ulong)cp->sreloc, (ulong)cp->cend);
+ dprintf5("cprev=0x%lx cnext=0x%lx outer=0x%lx inner_count=%u has_refs=%s\n",
+ (ulong)cp->cprev, (ulong)cp->cnext, (ulong)cp->outer,
+ cp->inner_count, (cp->has_refs? "true" : "false"));
+
+ SCAN_CHUNK_OBJECTS(cp)
+ DO_ALL
+ dprintf3(" pre=0x%lx type=0x%lx size=%lu ",
+ (ulong)pre, (ulong)pre->o_type, size);
+ if ( pre->o_large )
+ dprintf2("lmark=%d large size=%u\n",
+ pre->o_lmark, pre->o_lsize);
+ else
+ dprintf2("smark/back=%u (0x%x)\n", pre->o_smark, pre->o_smark);
+/* Temporarily redefine gs_exit so a chunk parsing error */
+/* won't actually exit. */
+#define gs_exit(n) DO_NOTHING
+ END_OBJECTS_SCAN
+#undef gs_exit
+}
+
+#endif /* DEBUG */
diff --git a/pstoraster/gsalloc.h b/pstoraster/gsalloc.h
new file mode 100644
index 000000000..ea24aee95
--- /dev/null
+++ b/pstoraster/gsalloc.h
@@ -0,0 +1,58 @@
+/* Copyright (C) 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gsalloc.h */
+/* Memory allocator extensions for standard allocator */
+
+#ifndef gsalloc_INCLUDED
+# define gsalloc_INCLUDED
+
+/*
+ * Define a structure and interface for GC-related allocator state.
+ */
+typedef struct gs_memory_gc_status_s {
+ /* Set by client */
+ long vm_threshold; /* GC interval */
+ long max_vm; /* maximum allowed allocation */
+ int *psignal; /* if not NULL, store signal_value */
+ /* here if we go over the vm_threshold */
+ int signal_value; /* value to store in *psignal */
+ bool enabled; /* auto GC enabled if true */
+ /* Set by allocator */
+ long requested; /* amount of last failing request */
+} gs_memory_gc_status_t;
+void gs_memory_gc_status(P2(const gs_ref_memory_t *, gs_memory_gc_status_t *));
+void gs_memory_set_gc_status(P2(gs_ref_memory_t *, const gs_memory_gc_status_t *));
+
+/* ------ Internal routines ------ */
+
+/* Initialize after a save. */
+void ialloc_reset(P1(gs_ref_memory_t *));
+
+/* Initialize after a save or GC. */
+void ialloc_reset_free(P1(gs_ref_memory_t *));
+
+/* Set the cached allocation limit of an alloctor from its GC parameters. */
+void ialloc_set_limit(P1(gs_ref_memory_t *));
+
+#endif /* gsalloc_INCLUDED */
diff --git a/pstoraster/gsbitops.c b/pstoraster/gsbitops.c
new file mode 100644
index 000000000..6b5233a0d
--- /dev/null
+++ b/pstoraster/gsbitops.c
@@ -0,0 +1,605 @@
+/* Copyright (C) 1994, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gsbitops.c */
+/* Bitmap filling, copying, and transforming operations */
+#include "stdio_.h"
+#include "memory_.h"
+#include "gdebug.h"
+#include "gstypes.h"
+#include "gsbitops.h"
+
+/*
+ * Define a compile-time option to reverse nibble order in alpha maps.
+ * Note that this does not reverse bit order within nibbles.
+ * This option is here for a very specialized purpose and does not
+ * interact well with the rest of the code.
+ */
+#ifndef ALPHA_LSB_FIRST
+# define ALPHA_LSB_FIRST 0
+#endif
+
+/* ---------------- Bit-oriented operations ---------------- */
+
+/* Define masks for little-endian operation. */
+/* masks[i] has the first i bits off and the rest on. */
+#if !arch_is_big_endian
+const bits16 mono_copy_masks[17] = {
+ 0xffff, 0xff7f, 0xff3f, 0xff1f,
+ 0xff0f, 0xff07, 0xff03, 0xff01,
+ 0xff00, 0x7f00, 0x3f00, 0x1f00,
+ 0x0f00, 0x0700, 0x0300, 0x0100,
+ 0x0000
+};
+# if arch_sizeof_int > 2
+const bits32 mono_fill_masks[33] = {
+ 0xffffffff, 0xffffff7f, 0xffffff3f, 0xffffff1f,
+ 0xffffff0f, 0xffffff07, 0xffffff03, 0xffffff01,
+ 0xffffff00, 0xffff7f00, 0xffff3f00, 0xffff1f00,
+ 0xffff0f00, 0xffff0700, 0xffff0300, 0xffff0100,
+ 0xffff0000, 0xff7f0000, 0xff3f0000, 0xff1f0000,
+ 0xff0f0000, 0xff070000, 0xff030000, 0xff010000,
+ 0xff000000, 0x7f000000, 0x3f000000, 0x1f000000,
+ 0x0f000000, 0x07000000, 0x03000000, 0x01000000,
+ 0x00000000
+};
+# endif
+#endif
+
+/* Fill a rectangle of bits with an 8x1 pattern. */
+/* The pattern argument must consist of the pattern in every byte, */
+/* e.g., if the desired pattern is 0xaa, the pattern argument must */
+/* have the value 0xaaaa (if ints are short) or 0xaaaaaaaa. */
+#undef chunk
+#define chunk mono_fill_chunk
+#undef mono_masks
+#define mono_masks mono_fill_masks
+void
+bits_fill_rectangle(byte *dest, int dest_bit, uint draster,
+ mono_fill_chunk pattern, int width_bits, int height)
+{ uint bit;
+ chunk right_mask;
+
+#define write_loop(stat)\
+ { int line_count = height;\
+ chunk *ptr = (chunk *)dest;\
+ do { stat; inc_ptr(ptr, draster); }\
+ while ( --line_count );\
+ }
+
+#define write_partial(msk)\
+ switch ( (byte)pattern )\
+ { case 0: write_loop(*ptr &= ~msk); break;\
+ case 0xff: write_loop(*ptr |= msk); break;\
+ default: write_loop(*ptr = (*ptr & ~msk) | (pattern & msk));\
+ }
+
+ dest += (dest_bit >> 3) & -chunk_align_bytes;
+ bit = dest_bit & chunk_align_bit_mask;
+
+#if 1 /* new code */
+
+#define write_span(lmsk, stat0, statx, stat1, n, rmsk)\
+ switch ( (byte)pattern )\
+ { case 0: write_loop((*ptr &= ~lmsk, stat0, ptr[n] &= ~rmsk)); break;\
+ case 0xff: write_loop((*ptr |= lmsk, stat1, ptr[n] |= rmsk)); break;\
+ default: write_loop((*ptr = (*ptr & ~lmsk) | (pattern & lmsk), statx,\
+ ptr[n] = (ptr[n] & ~rmsk) | (pattern & rmsk)));\
+ }
+
+ { int last_bit = width_bits + bit - (chunk_bits + 1);
+ if ( last_bit < 0 ) /* <=1 chunk */
+ { set_mono_thin_mask(right_mask, width_bits, bit);
+ write_partial(right_mask);
+ }
+ else
+ { chunk mask;
+ int last = last_bit >> chunk_log2_bits;
+ set_mono_left_mask(mask, bit);
+ set_mono_right_mask(right_mask, (last_bit & chunk_bit_mask) + 1);
+ switch ( last )
+ {
+ case 0: /* 2 chunks */
+ { write_span(mask, 0, 0, 0, 1, right_mask);
+ } break;
+ case 1: /* 3 chunks */
+ { write_span(mask, ptr[1] = 0, ptr[1] = pattern,
+ ptr[1] = ~(chunk)0, 2, right_mask);
+ } break;
+ default: /* >3 chunks */
+ { uint byte_count = (last_bit >> 3) & -chunk_bytes;
+ write_span(mask, memset(ptr + 1, 0, byte_count),
+ memset(ptr + 1, (byte)pattern, byte_count),
+ memset(ptr + 1, 0xff, byte_count),
+ last + 1, right_mask);
+ } break;
+ }
+ }
+ }
+
+#else /* old code */
+
+ if ( bit + width_bits <= chunk_bits )
+ { /* Only one word. */
+ set_mono_thin_mask(right_mask, width_bits, bit);
+ }
+ else
+ { int byte_count;
+ if ( bit )
+ { chunk mask;
+ set_mono_left_mask(mask, bit);
+ write_partial(mask);
+ inc_ptr(dest, chunk_bytes);
+ width_bits += bit - chunk_bits;
+ }
+ set_mono_right_mask(right_mask, width_bits & chunk_bit_mask);
+ if ( width_bits >= chunk_bits )
+ switch ( (byte_count = (width_bits >> 3) & -chunk_bytes) )
+ {
+ case chunk_bytes:
+ write_loop(*ptr = pattern);
+ inc_ptr(dest, chunk_bytes);
+ break;
+ case chunk_bytes * 2:
+ write_loop(ptr[1] = *ptr = pattern);
+ inc_ptr(dest, chunk_bytes * 2);
+ break;
+ default:
+ write_loop(memset(ptr, (byte)pattern, byte_count));
+ inc_ptr(dest, byte_count);
+ break;
+ }
+ }
+ if ( right_mask )
+ write_partial(right_mask);
+
+#endif
+
+}
+
+/* Replicate a bitmap horizontally in place. */
+void
+bits_replicate_horizontally(byte *data, uint width, uint height,
+ uint raster, uint replicated_width, uint replicated_raster)
+{ /* The current algorithm is extremely inefficient. */
+ uint y;
+ for ( y = height; y-- > 0; )
+ { const byte *orig_row = data + y * raster;
+ byte *tile_row = data + y * replicated_raster;
+ uint sx;
+ if ( !(width & 7) )
+ { uint wbytes = width >> 3;
+ for ( sx = wbytes; sx-- > 0; )
+ { byte sb = orig_row[sx];
+ uint dx;
+ for ( dx = sx + (replicated_width >> 3); dx >= wbytes; )
+ tile_row[dx -= wbytes] = sb;
+ }
+ }
+ else
+ for ( sx = width; sx-- > 0; )
+ { byte sm = orig_row[sx >> 3] & (0x80 >> (sx & 7));
+ uint dx;
+ for ( dx = sx + replicated_width; dx >= width;
+ )
+ { byte *dp =
+ (dx -= width, tile_row + (dx >> 3));
+ byte dm = 0x80 >> (dx & 7);
+ if ( sm ) *dp |= dm;
+ else *dp &= ~dm;
+ }
+ }
+ }
+}
+
+/* Replicate a bitmap vertically in place. */
+void
+bits_replicate_vertically(byte *data, uint height, uint raster,
+ uint replicated_height)
+{ byte *dest = data;
+ uint h = replicated_height;
+ uint size = raster * height;
+ while ( h >= height )
+ { memcpy(dest + size, dest, size);
+ dest += size;
+ h -= height;
+ }
+}
+
+/* Find the bounding box of a bitmap. */
+/* Assume bits beyond the width are zero. */
+void
+bits_bounding_box(const byte *data, uint height, uint raster,
+ gs_int_rect *pbox)
+{ register const ulong *lp;
+ static const byte first_1[16] =
+ { 4, 3, 2,2, 1,1,1,1, 0,0,0,0,0,0,0,0 };
+ static const byte last_1[16] =
+ { 0,4,3,4,2,4,3,4,1,4,3,4,2,4,3,4 };
+
+ /* Count trailing blank rows. */
+ /* Since the raster is a multiple of sizeof(long), */
+ /* we don't need to scan by bytes, only by longs. */
+
+ lp = (const ulong *)(data + raster * height);
+ while ( (const byte *)lp > data && !lp[-1] )
+ --lp;
+ if ( (const byte *)lp == data )
+ { pbox->p.x = pbox->q.x = pbox->p.y = pbox->q.y = 0;
+ return;
+ }
+ pbox->q.y = height = ((const byte *)lp - data + raster - 1) / raster;
+
+ /* Count leading blank rows. */
+
+ lp = (const ulong *)data;
+ while ( !*lp )
+ ++lp;
+ { uint n = ((const byte *)lp - data) / raster;
+ pbox->p.y = n;
+ if ( n )
+ height -= n, data += n * raster;
+ }
+
+ /* Find the left and right edges. */
+ /* We know that the first and last rows are non-blank. */
+
+ { uint raster_longs = raster >> arch_log2_sizeof_long;
+ uint left = raster_longs - 1, right = 0;
+ ulong llong = 0, rlong = 0;
+ const byte *q;
+ uint h, n;
+
+ for ( q = data, h = height; h-- > 0; q += raster )
+ { /* Work from the left edge by longs. */
+ for ( lp = (const ulong *)q, n = 0;
+ n < left && !*lp; lp++, n++
+ )
+ ;
+ if ( n < left )
+ left = n, llong = *lp;
+ else
+ llong |= *lp;
+ /* Work from the right edge by longs. */
+ for ( lp = (const ulong *)(q + raster - sizeof(long)),
+ n = raster_longs - 1;
+ n > right && !*lp; lp--, n--
+ )
+ ;
+ if ( n > right )
+ right = n, rlong = *lp;
+ else
+ rlong |= *lp;
+ }
+
+ /* Do binary subdivision on edge longs. We assume that */
+ /* sizeof(long) = 4 or 8. */
+#if arch_sizeof_long > 8
+ Error_longs_are_too_large();
+#endif
+
+#if arch_is_big_endian
+# define last_bits(n) ((1L << (n)) - 1)
+# define shift_out_last(x,n) ((x) >>= (n))
+# define right_justify_last(x,n) DO_NOTHING
+#else
+# define last_bits(n) (-1L << ((arch_sizeof_long * 8) - (n)))
+# define shift_out_last(x,n) ((x) <<= (n))
+# define right_justify_last(x,n) (x) >>= ((arch_sizeof_long * 8) - (n))
+#endif
+
+ left <<= arch_log2_sizeof_long + 3;
+#if arch_sizeof_long == 8
+ if ( llong & ~last_bits(32) ) shift_out_last(llong, 32);
+ else left += 32;
+#endif
+ if ( llong & ~last_bits(16) ) shift_out_last(llong, 16);
+ else left += 16;
+ if ( llong & ~last_bits(8) ) shift_out_last(llong, 8);
+ else left += 8;
+ right_justify_last(llong, 8);
+ if ( llong & 0xf0 )
+ left += first_1[(byte)llong >> 4];
+ else
+ left += first_1[(byte)llong] + 4;
+
+ right <<= arch_log2_sizeof_long + 3;
+#if arch_sizeof_long == 8
+ if ( !(rlong & last_bits(32)) ) shift_out_last(rlong, 32);
+ else right += 32;
+#endif
+ if ( !(rlong & last_bits(16)) ) shift_out_last(rlong, 16);
+ else right += 16;
+ if ( !(rlong & last_bits(8)) ) shift_out_last(rlong, 8);
+ else right += 8;
+ right_justify_last(rlong, 8);
+ if ( !(rlong & 0xf) )
+ right += last_1[(byte)rlong >> 4];
+ else
+ right += last_1[(uint)rlong & 0xf] + 4;
+
+ pbox->p.x = left;
+ pbox->q.x = right;
+ }
+}
+
+/* Count the number of 1-bits in a half-byte. */
+static const byte half_byte_1s[16] = {0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4};
+/* Count the number of trailing 1s in an up-to-5-bit value, -1. */
+static const byte bits5_trailing_1s[32] =
+{ 0,0,0,1,0,0,0,2,0,0,0,1,0,0,0,3,0,0,0,1,0,0,0,2,0,0,0,1,0,0,0,4 };
+/* Count the number of leading 1s in an up-to-5-bit value, -1. */
+static const byte bits5_leading_1s[32] =
+{ 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,2,2,3,4 };
+
+/*
+ * Compress a value between 0 and 2^M to a value between 0 and 2^N-1.
+ * Possible values of M are 1, 2, 3, or 4; of N are 1, 2, and 4.
+ * The name of the table is compress_count_M_N.
+ * As noted below, we require that N <= M.
+ */
+static const byte compress_1_1[3] =
+ { 0, 1, 1 };
+static const byte compress_2_1[5] =
+ { 0, 0, 1, 1, 1 };
+static const byte compress_2_2[5] =
+ { 0, 1, 2, 2, 3 };
+static const byte compress_3_1[9] =
+ { 0, 0, 0, 0, 1, 1, 1, 1, 1 };
+static const byte compress_3_2[9] =
+ { 0, 0, 1, 1, 2, 2, 2, 3, 3 };
+static const byte compress_4_1[17] =
+ { 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 };
+static const byte compress_4_2[17] =
+ { 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3 };
+static const byte compress_4_4[17] =
+ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9,10,11,12,13,14,15 };
+/* The table of tables is indexed by log2(N) and then by M-1. */
+static const byte _ds *compress_tables[4][4] =
+{ { compress_1_1, compress_2_1, compress_3_1, compress_4_1 },
+ { 0, compress_2_2, compress_3_2, compress_4_2 },
+ { 0, 0, 0, compress_4_4 }
+};
+
+/*
+ * Compress an XxY-oversampled bitmap to Nx1 by counting 1-bits. The X and
+ * Y oversampling factors are 1, 2, or 4, but may be different. N, the
+ * resulting number of (alpha) bits per pixel, may be 1, 2, or 4; we allow
+ * compression in place, in which case N must not exceed the X oversampling
+ * factor. Width and height are the source dimensions, and hence reflect
+ * the oversampling; both are multiples of the relevant scale factor. The
+ * same is true for srcx.
+ */
+void
+bits_compress_scaled(const byte *src, int srcx, uint width, uint height,
+ uint sraster, byte *dest, uint draster,
+ const gs_log2_scale_point *plog2_scale, int log2_out_bits)
+{ int log2_x = plog2_scale->x, log2_y = plog2_scale->y;
+ int xscale = 1 << log2_x;
+ int yscale = 1 << log2_y;
+ int out_bits = 1 << log2_out_bits;
+ int input_byte_out_bits;
+ byte input_byte_out_mask;
+ const byte _ds *table =
+ compress_tables[log2_out_bits][log2_x + log2_y - 1];
+ uint sskip = sraster << log2_y;
+ uint dwidth = (width >> log2_x) << log2_out_bits;
+ uint dskip = draster - ((dwidth + 7) >> 3);
+ uint mask = (1 << xscale) - 1;
+ uint count_max = 1 << (log2_x + log2_y);
+ /*
+ * For right now, we don't attempt to take advantage of the fact
+ * that the input is aligned.
+ */
+ const byte *srow = src + (srcx >> 3);
+ int in_shift_initial = 8 - xscale - (srcx & 7);
+ int in_shift_check = (out_bits <= xscale ? 8 - xscale : -1);
+ byte *d = dest;
+ uint h;
+
+ if ( out_bits <= xscale )
+ input_byte_out_bits = out_bits << (3 - log2_x),
+ input_byte_out_mask = (1 << input_byte_out_bits) - 1;
+ for ( h = height; h; srow += sskip, h -= yscale )
+ { const byte *s = srow;
+#if ALPHA_LSB_FIRST
+# define out_shift_initial 0
+# define out_shift_update(out_shift, nbits) ((out_shift += (nbits)) >= 8)
+#else
+# define out_shift_initial (8 - out_bits)
+# define out_shift_update(out_shift, nbits) ((out_shift -= (nbits)) < 0)
+#endif
+ int out_shift = out_shift_initial;
+ byte out = 0;
+ int in_shift = in_shift_initial;
+ int dw = 8 - (srcx & 7);
+ int w;
+
+ /* Loop over source bytes. */
+ for ( w = width; w > 0; w -= dw, dw = 8 )
+ { int index;
+ int in_shift_final =
+ (w >= dw ? 0 : dw - w);
+ /*
+ * Check quickly for all-0s or all-1s, but only if each
+ * input byte generates no more than one output byte,
+ * we're at an input byte boundary, and we're processing
+ * an entire input byte (i.e., this isn't a final
+ * partial byte.)
+ */
+ if ( in_shift == in_shift_check && in_shift_final == 0 )
+ switch ( *s )
+ {
+ case 0:
+ for ( index = sraster; index != sskip; index += sraster )
+ if ( s[index] != 0 )
+ goto p;
+ if ( out_shift_update(out_shift, input_byte_out_bits) )
+ *d++ = out, out_shift &= 7, out = 0;
+ s++;
+ continue;
+#if !ALPHA_LSB_FIRST /* too messy to make it work */
+ case 0xff:
+ for ( index = sraster; index != sskip; index += sraster )
+ if ( s[index] != 0xff )
+ goto p;
+ { int shift =
+ (out_shift -= input_byte_out_bits) + out_bits;
+ if ( shift > 0 )
+ out |= input_byte_out_mask << shift;
+ else
+ { out |= input_byte_out_mask >> -shift;
+ *d++ = out;
+ out_shift += 8;
+ out = input_byte_out_mask << (8 + shift);
+ }
+ }
+ s++;
+ continue;
+#endif
+ default:
+ ;
+ }
+p: /* Loop over source pixels within a byte. */
+ do
+ { uint count;
+ for ( index = 0, count = 0; index != sskip;
+ index += sraster
+ )
+ count += half_byte_1s[(s[index] >> in_shift) & mask];
+ if ( count != 0 && table[count] == 0 )
+ { /* Look at adjacent cells to help prevent */
+ /* dropouts. */
+ uint orig_count = count;
+ uint shifted_mask = mask << in_shift;
+ byte in;
+ if_debug3('B', "[B]count(%d,%d)=%d\n",
+ (width - w) / xscale,
+ (height - h) / yscale, count);
+ if ( yscale > 1 )
+ { /* Look at the next "lower" cell. */
+ if ( h < height && (in = s[0] & shifted_mask) != 0 )
+ { uint lower;
+ for ( index = 0, lower = 0;
+ -(index -= sraster) <= sskip &&
+ (in &= s[index]) != 0;
+ )
+ lower += half_byte_1s[in >> in_shift];
+ if_debug1('B', "[B] lower adds %d\n",
+ lower);
+ if ( lower <= orig_count )
+ count += lower;
+ }
+ /* Look at the next "higher" cell. */
+ if ( h > yscale && (in = s[sskip - sraster] & shifted_mask) != 0 )
+ { uint upper;
+ for ( index = sskip, upper = 0;
+ index < sskip << 1 &&
+ (in &= s[index]) != 0;
+ index += sraster
+ )
+ upper += half_byte_1s[in >> in_shift];
+ if_debug1('B', "[B] upper adds %d\n",
+ upper);
+ if ( upper < orig_count )
+ count += upper;
+ }
+ }
+ if ( xscale > 1 )
+ { uint mask1 = (mask << 1) + 1;
+ /* Look at the next cell to the left. */
+ if ( w < width )
+ { int lshift = in_shift + xscale - 1;
+ uint left;
+ for ( index = 0, left = 0;
+ index < sskip; index += sraster
+ )
+ { uint bits =
+ ((s[index - 1] << 8) +
+ s[index]) >> lshift;
+ left += bits5_trailing_1s[bits & mask1];
+ }
+ if_debug1('B', "[B] left adds %d\n",
+ left);
+ if ( left < orig_count )
+ count += left;
+ }
+ /* Look at the next cell to the right. */
+ if ( w > xscale )
+ { int rshift = in_shift - xscale + 8;
+ uint right;
+ for ( index = 0, right = 0;
+ index < sskip; index += sraster
+ )
+ { uint bits =
+ ((s[index] << 8) +
+ s[index + 1]) >> rshift;
+ right += bits5_leading_1s[(bits & mask1) << (4 - xscale)];
+ }
+ if_debug1('B', "[B] right adds %d\n",
+ right);
+ if ( right <= orig_count )
+ count += right;
+ }
+ }
+ if ( count > count_max )
+ count = count_max;
+ }
+ out += table[count] << out_shift;
+ if ( out_shift_update(out_shift, out_bits) )
+ *d++ = out, out_shift &= 7, out = 0;
+ }
+ while ( (in_shift -= xscale) >= in_shift_final );
+ s++, in_shift += 8;
+ }
+ if ( out_shift != out_shift_initial )
+ *d++ = out;
+ for ( w = dskip; w != 0; w-- )
+ *d++ = 0;
+#undef out_shift_initial
+#undef out_shift_update
+ }
+}
+
+/* ---------------- Byte-oriented operations ---------------- */
+
+/* Fill a rectangle of bytes. */
+void
+bytes_fill_rectangle(byte *dest, uint raster,
+ byte value, int width_bytes, int height)
+{ while ( height-- > 0 )
+ { memset(dest, value, width_bytes);
+ dest += raster;
+ }
+}
+
+/* Copy a rectangle of bytes. */
+void
+bytes_copy_rectangle(byte *dest, uint dest_raster,
+ const byte *src, uint src_raster, int width_bytes, int height)
+{ while ( height-- > 0 )
+ { memcpy(dest, src, width_bytes);
+ src += src_raster;
+ dest += dest_raster;
+ }
+}
diff --git a/pstoraster/gsbitops.h b/pstoraster/gsbitops.h
new file mode 100644
index 000000000..5e5e12729
--- /dev/null
+++ b/pstoraster/gsbitops.h
@@ -0,0 +1,178 @@
+/* Copyright (C) 1991, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gsbitops.h */
+/* Definitions for bitmap operations */
+
+/* ---------------- Definitions ---------------- */
+
+/*
+ * Macros for processing bitmaps in the largest possible chunks.
+ * Bits within a byte are always stored big-endian;
+ * bytes are likewise stored in left-to-right order, i.e., big-endian.
+ * Note that this is the format used for the source of copy_mono.
+ * It used to be the case that bytes were stored in the natural
+ * platform order, and the client had force them into big-endian order
+ * by calling gdev_mem_ensure_byte_order, but this no longer necessary.
+ *
+ * Note that we use type uint for register variables holding a chunk:
+ * for this reason, the chunk size cannot be larger than uint.
+ */
+/* Generic macros for chunk accessing. */
+#define cbytes(ct) size_of(ct) /* sizeof may be unsigned */
+# define chunk_bytes cbytes(chunk)
+/* The clog2_bytes macro assumes that ints are 2, 4, or 8 bytes in size. */
+#define clog2_bytes(ct) (size_of(ct) == 8 ? 3 : size_of(ct)>>1)
+# define chunk_log2_bytes clog2_bytes(chunk)
+#define cbits(ct) (size_of(ct)*8) /* sizeof may be unsigned */
+# define chunk_bits cbits(chunk)
+#define clog2_bits(ct) (clog2_bytes(ct)+3)
+# define chunk_log2_bits clog2_bits(chunk)
+#define cbit_mask(ct) (cbits(ct)-1)
+# define chunk_bit_mask cbit_mask(chunk)
+#define calign_bytes(ct)\
+ (sizeof(ct) == 1 ? 1:\
+ sizeof(ct) == sizeof(short) ? arch_align_short_mod :\
+ sizeof(ct) == sizeof(int) ? arch_align_int_mod : arch_align_long_mod)
+# define chunk_align_bytes calign_bytes(chunk)
+#define calign_bit_mask(ct) (calign_bytes(ct)*8-1)
+# define chunk_align_bit_mask calign_bit_mask(chunk)
+/*
+ * The obvious definition for cmask is:
+ * #define cmask(ct) ((ct)~(ct)0)
+ * but this doesn't work on the VAX/VMS compiler, which fails to truncate
+ * the value to 16 bits when ct is ushort.
+ * Instead, we have to generate the mask with no extra 1-bits.
+ * We can't do this in the obvious way:
+ * #define cmask(ct) ((1 << (size_of(ct) * 8)) - 1)
+ * because some compilers won't allow a shift of the full type size.
+ * Instead, we have to do something really awkward:
+ */
+#define cmask(ct) ((ct) (((((ct)1 << (size_of(ct)*8-2)) - 1) << 2) + 3))
+# define chunk_all_bits cmask(chunk)
+/*
+ * The obvious definition for chi_bits is:
+ * #define chi_bits(ct,n) (cmask(ct)-(cmask(ct)>>(n)))
+ * but this doesn't work on the DEC/MIPS compilers.
+ * Instead, we have to restrict chi_bits to only working for values of n
+ * between 0 and cbits(ct)-1, and use
+ */
+#define chi_bits(ct,n) (ct)(~(ct)1 << (cbits(ct)-1 - (n)))
+# define chunk_hi_bits(n) chi_bits(chunk,n)
+
+/* Define whether this is a machine where chunks are long, */
+/* but the machine can't shift a long by its full width. */
+#define arch_cant_shift_full_chunk\
+ (arch_is_big_endian && !arch_ints_are_short && !arch_can_shift_full_long)
+
+/* Pointer arithmetic macros. */
+#define inc_ptr(ptr,delta)\
+ ptr = (void *)((byte *)ptr + (delta))
+
+/* Define macros for setting up left- and right-end masks. */
+/* These are used for monobit operations, and for filling */
+/* with 2- and 4-bit-per-pixel patterns. */
+
+/*
+ * Define the chunk size for monobit copying operations.
+ * (The chunk size for monobit filling is always uint.)
+ */
+#define mono_fill_chunk uint
+#define mono_fill_chunk_bytes arch_sizeof_int
+#if arch_is_big_endian
+# define mono_copy_chunk uint
+# define set_mono_right_mask(var, w)\
+ var = ((w) == chunk_bits ? chunk_all_bits : chunk_hi_bits(w))
+/*
+ * We have to split the following statement because of a bug in the Xenix C
+ * compiler (it produces a signed rather than an unsigned shift if we don't
+ * split).
+ */
+# define set_mono_thin_mask(var, w, bit)\
+ set_mono_right_mask(var, w), var >>= (bit)
+/*
+ * We have to split the following statement in two because of a bug
+ * in the DEC VAX/VMS C compiler.
+ */
+# define set_mono_left_mask(var, bit)\
+ var = chunk_all_bits, var >>= (bit)
+#else
+# define mono_copy_chunk bits16
+extern const bits16 mono_copy_masks[17];
+# if mono_fill_chunk_bytes == 2
+# define mono_fill_masks mono_copy_masks
+# else
+extern const bits32 mono_fill_masks[33];
+# endif
+/*
+ * We define mono_masks as either mono_fill_masks or
+ * mono_copy_masks before using the following macros.
+ */
+# define set_mono_left_mask(var, bit)\
+ var = mono_masks[bit]
+# define set_mono_thin_mask(var, w, bit)\
+ var = ~mono_masks[(w) + (bit)] & mono_masks[bit]
+# define set_mono_right_mask(var, ebit)\
+ var = ~mono_masks[ebit]
+#endif
+
+/* ---------------- Procedures ---------------- */
+
+/* Fill a rectangle of bits with an 8x1 pattern. */
+/* The pattern argument must consist of the pattern in every byte, */
+/* e.g., if the desired pattern is 0xaa, the pattern argument must */
+/* have the value 0xaaaa (if ints are short) or 0xaaaaaaaa. */
+#if mono_fill_chunk_bytes == 2
+# define mono_fill_make_pattern(byt) (uint)((uint)(byt) * 0x0101)
+#else
+# define mono_fill_make_pattern(byt) (uint)((uint)(byt) * 0x01010101)
+#endif
+void bits_fill_rectangle(P6(byte *dest, int dest_bit, uint raster,
+ mono_fill_chunk pattern, int width_bits, int height));
+
+/* Replicate a bitmap horizontally in place. */
+void bits_replicate_horizontally(P6(byte *data, uint width, uint height,
+ uint raster, uint replicated_width, uint replicated_raster));
+
+/* Replicate a bitmap vertically in place. */
+void bits_replicate_vertically(P4(byte *data, uint height, uint raster,
+ uint replicated_height));
+
+/* Find the bounding box of a bitmap. */
+void bits_bounding_box(P4(const byte *data, uint height, uint raster,
+ gs_int_rect *pbox));
+
+/* Compress an oversampled image, possibly in place. */
+/* The width and height must be multiples of the respective scale factors. */
+/* The source must be an aligned bitmap, as usual. */
+void bits_compress_scaled(P9(const byte *src, int srcx, uint width,
+ uint height, uint sraster, byte *dest, uint draster,
+ const gs_log2_scale_point *plog2_scale, int log2_out_bits));
+
+/* Fill a rectangle of bytes. */
+void bytes_fill_rectangle(P5(byte *dest, uint raster,
+ byte value, int width_bytes, int height));
+
+/* Copy a rectangle of bytes. */
+void bytes_copy_rectangle(P6(byte *dest, uint dest_raster,
+ const byte *src, uint src_raster, int width_bytes, int height));
diff --git a/pstoraster/gsbittab.c b/pstoraster/gsbittab.c
new file mode 100644
index 000000000..3a9425efd
--- /dev/null
+++ b/pstoraster/gsbittab.c
@@ -0,0 +1,135 @@
+/* Copyright (C) 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gsbittab.c */
+/* Tables for bit operations */
+#include "stdpre.h"
+#include "gsbittab.h"
+
+/* ---------------- Byte processing tables ---------------- */
+
+/* Reverse the bits of a byte. */
+const byte 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
+};
+
+/* Define masks with a given number of trailing 1-bits. */
+const byte byte_right_mask[9] =
+{ 0, 1, 3, 7, 0xf, 0x1f, 0x3f, 0x7f, 0xff
+};
+
+/* ---------------- Scanning tables ---------------- */
+
+/*
+ * byte_bit_run_length_N[B], for 0 <= N <= 7, gives the length of the
+ * run of 1-bits starting at bit N in a byte with value B,
+ * numbering the bits in the byte as 01234567. If the run includes
+ * the low-order bit (i.e., might be continued into a following byte),
+ * the run length is increased by 8.
+ */
+
+#define t8(n) n,n,n,n,n+1,n+1,n+2,n+11
+#define r8(n) n,n,n,n,n,n,n,n
+#define r16(n) r8(n),r8(n)
+#define r32(n) r16(n),r16(n)
+#define r64(n) r32(n),r32(n)
+#define r128(n) r64(n),r64(n)
+const byte byte_bit_run_length_0[256] =
+{ r128(0), r64(1), r32(2), r16(3), r8(4), t8(5)
+};
+const byte far_data byte_bit_run_length_1[256] =
+{ r64(0), r32(1), r16(2), r8(3), t8(4),
+ r64(0), r32(1), r16(2), r8(3), t8(4)
+};
+const byte far_data byte_bit_run_length_2[256] =
+{ r32(0), r16(1), r8(2), t8(3),
+ r32(0), r16(1), r8(2), t8(3),
+ r32(0), r16(1), r8(2), t8(3),
+ r32(0), r16(1), r8(2), t8(3)
+};
+const byte far_data byte_bit_run_length_3[256] =
+{ r16(0), r8(1), t8(2), r16(0), r8(1), t8(2),
+ r16(0), r8(1), t8(2), r16(0), r8(1), t8(2),
+ r16(0), r8(1), t8(2), r16(0), r8(1), t8(2),
+ r16(0), r8(1), t8(2), r16(0), r8(1), t8(2)
+};
+const byte far_data byte_bit_run_length_4[256] =
+{ r8(0), t8(1), r8(0), t8(1), r8(0), t8(1), r8(0), t8(1),
+ r8(0), t8(1), r8(0), t8(1), r8(0), t8(1), r8(0), t8(1),
+ r8(0), t8(1), r8(0), t8(1), r8(0), t8(1), r8(0), t8(1),
+ r8(0), t8(1), r8(0), t8(1), r8(0), t8(1), r8(0), t8(1),
+};
+#define rr8(a,b,c,d,e,f,g,h)\
+ a,b,c,d,e,f,g,h, a,b,c,d,e,f,g,h, a,b,c,d,e,f,g,h, a,b,c,d,e,f,g,h,\
+ a,b,c,d,e,f,g,h, a,b,c,d,e,f,g,h, a,b,c,d,e,f,g,h, a,b,c,d,e,f,g,h,\
+ a,b,c,d,e,f,g,h, a,b,c,d,e,f,g,h, a,b,c,d,e,f,g,h, a,b,c,d,e,f,g,h,\
+ a,b,c,d,e,f,g,h, a,b,c,d,e,f,g,h, a,b,c,d,e,f,g,h, a,b,c,d,e,f,g,h,\
+ a,b,c,d,e,f,g,h, a,b,c,d,e,f,g,h, a,b,c,d,e,f,g,h, a,b,c,d,e,f,g,h,\
+ a,b,c,d,e,f,g,h, a,b,c,d,e,f,g,h, a,b,c,d,e,f,g,h, a,b,c,d,e,f,g,h,\
+ a,b,c,d,e,f,g,h, a,b,c,d,e,f,g,h, a,b,c,d,e,f,g,h, a,b,c,d,e,f,g,h,\
+ a,b,c,d,e,f,g,h, a,b,c,d,e,f,g,h, a,b,c,d,e,f,g,h, a,b,c,d,e,f,g,h
+const byte far_data byte_bit_run_length_5[256] =
+{ rr8(0,0,0,0,1,1,2,11)
+};
+const byte far_data byte_bit_run_length_6[256] =
+{ rr8(0,0,1,10,0,0,1,10)
+};
+const byte far_data byte_bit_run_length_7[256] =
+{ rr8(0,9,0,9,0,9,0,9)
+};
+
+/* Pointer tables indexed by bit number. */
+
+const byte *byte_bit_run_length[8] =
+{ byte_bit_run_length_0, byte_bit_run_length_1,
+ byte_bit_run_length_2, byte_bit_run_length_3,
+ byte_bit_run_length_4, byte_bit_run_length_5,
+ byte_bit_run_length_6, byte_bit_run_length_7
+};
+const byte *byte_bit_run_length_neg[8] =
+{ byte_bit_run_length_0, byte_bit_run_length_7,
+ byte_bit_run_length_6, byte_bit_run_length_5,
+ byte_bit_run_length_4, byte_bit_run_length_3,
+ byte_bit_run_length_2, byte_bit_run_length_1
+};
+
+/* Some C compilers insist on having executable code in every file.... */
+void
+gsbittab_dummy(void)
+{
+}
diff --git a/pstoraster/gsbittab.h b/pstoraster/gsbittab.h
new file mode 100644
index 000000000..5b1e73d28
--- /dev/null
+++ b/pstoraster/gsbittab.h
@@ -0,0 +1,55 @@
+/* Copyright (C) 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gsbittab.h */
+/* Interface to tables for bit operations */
+
+/*
+ * byte_reverse_bits[B] = the byte B with the order of bits reversed.
+ */
+extern const byte byte_reverse_bits[256];
+
+/*
+ * byte_right_mask[N] = a byte with N trailing 1s, 0 <= N <= 8.
+ */
+extern const byte byte_right_mask[9];
+
+/*
+ * byte_bit_run_length_N[B], for 0 <= N <= 7, gives the length of the
+ * run of 1-bits starting at bit N in a byte with value B,
+ * numbering the bits in the byte as 01234567. If the run includes
+ * the low-order bit (i.e., might be continued into a following byte),
+ * the run length is increased by 8.
+ */
+extern const byte
+ byte_bit_run_length_0[256], byte_bit_run_length_1[256],
+ byte_bit_run_length_2[256], byte_bit_run_length_3[256],
+ byte_bit_run_length_4[256], byte_bit_run_length_5[256],
+ byte_bit_run_length_6[256], byte_bit_run_length_7[256];
+
+/*
+ * byte_bit_run_length[N] points to byte_bit_run_length_N.
+ * byte_bit_run_length_neg[N] = byte_bit_run_length[-N & 7].
+ */
+extern const byte *byte_bit_run_length[8];
+extern const byte *byte_bit_run_length_neg[8];
diff --git a/pstoraster/gsccode.h b/pstoraster/gsccode.h
new file mode 100644
index 000000000..94c09d0ec
--- /dev/null
+++ b/pstoraster/gsccode.h
@@ -0,0 +1,58 @@
+/* Copyright (C) 1993 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gsccode.h */
+/* Types for character codes */
+
+#ifndef gsccode_INCLUDED
+# define gsccode_INCLUDED
+
+/* Define a character code. Normally this is just a single byte from a */
+/* string, but because of composite fonts, character codes must be */
+/* at least 32 bits. */
+typedef ulong gs_char;
+#define gs_no_char ((gs_char)~0L)
+
+/* Define a character glyph code, a.k.a. character name. */
+typedef uint gs_glyph;
+#define gs_no_glyph ((gs_glyph)~0)
+
+/* Define a procedure for mapping a gs_glyph to its (string) name. */
+#define gs_proc_glyph_name(proc)\
+ const char *proc(P2(gs_glyph, uint *))
+/* The following typedef is needed because ansi2knr can't handle */
+/* gs_proc_glyph_name((*procname)) in a formal argument list. */
+typedef gs_proc_glyph_name((*gs_proc_glyph_name_t));
+
+/* Define a procedure for accessing the known encodings. */
+#define gs_proc_known_encode(proc)\
+ gs_glyph proc(P2(gs_char, int))
+typedef gs_proc_known_encode((*gs_proc_known_encode_t));
+
+/* Define the callback procedure vector for character to xglyph mapping. */
+typedef struct gx_xfont_callbacks_s {
+ gs_proc_glyph_name((*glyph_name));
+ gs_proc_known_encode((*known_encode));
+} gx_xfont_callbacks;
+
+#endif /* gsccode_INCLUDED */
diff --git a/pstoraster/gsccolor.h b/pstoraster/gsccolor.h
new file mode 100644
index 000000000..c5d7ef077
--- /dev/null
+++ b/pstoraster/gsccolor.h
@@ -0,0 +1,51 @@
+/* Copyright (C) 1993 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gsccolor.h */
+/* Client color structure definition */
+
+#ifndef gsccolor_INCLUDED
+# define gsccolor_INCLUDED
+
+#include "gsstruct.h" /* for extern_st */
+
+/* Pattern instance, usable in color. */
+typedef struct gs_pattern_instance_s gs_pattern_instance;
+
+/* Paint (non-pattern) colors (Device, CIE, Indexed, Separation) */
+typedef struct gs_paint_color_s {
+ float values[4];
+} gs_paint_color;
+
+/* General colors */
+typedef struct gs_client_color_s {
+ gs_paint_color paint; /* also color for uncolored pattern */
+ gs_pattern_instance *pattern;
+} gs_client_color;
+extern_st(st_client_color);
+#define public_st_client_color() /* in gscolor.c */\
+ gs_public_st_ptrs1(st_client_color, gs_client_color, "gs_client_color",\
+ client_color_enum_ptrs, client_color_reloc_ptrs, pattern)
+#define st_client_color_max_ptrs 1
+
+#endif /* gsccolor_INCLUDED */
diff --git a/pstoraster/gscdef.c b/pstoraster/gscdef.c
new file mode 100644
index 000000000..ffb3394db
--- /dev/null
+++ b/pstoraster/gscdef.c
@@ -0,0 +1,83 @@
+/*
+ Copyright 1993-1999 by Easy Software Products.
+ Copyright (C) 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gscdef.c */
+/* Configuration scalars */
+#include "stdpre.h"
+#include "gscdefs.h" /* interface */
+#include "gconfig.h" /* for #defines */
+
+/* ---------------- Miscellaneous system parameters ---------------- */
+
+/* All of these can be set in the makefile. */
+/* They should all be const; see gscdefs.h for more information. */
+
+#ifndef GS_BUILDTIME
+# define GS_BUILDTIME\
+ 0 /****** HOW TO SET THIS? ******/
+#endif
+long gs_buildtime = GS_BUILDTIME;
+
+#ifndef GS_COPYRIGHT
+# define GS_COPYRIGHT\
+ "Copyright 1993-1999 Easy Software Products, All Rights Reserved.\n"\
+ "Copyright 1996 Aladdin Enterprises, Menlo Park, CA. All rights reserved."
+#endif
+const char *gs_copyright = GS_COPYRIGHT;
+
+#ifndef GS_PRODUCT
+# define GS_PRODUCT\
+ "ESP Print Pro v4.0"
+#endif
+const char *gs_product = GS_PRODUCT;
+
+#ifndef GS_REVISION
+# define GS_REVISION\
+ 40000 /* MMmmPP */
+#endif
+long gs_revision = GS_REVISION; /* should be const, see gscdefs.h */
+
+#ifndef GS_REVISIONDATE
+# define GS_REVISIONDATE\
+ 19990507 /* year x 10000 + month x 100 + day. */
+#endif
+long gs_revisiondate = GS_REVISIONDATE; /* should be const, see gscdefs.h */
+
+#ifndef GS_SERIALNUMBER
+# define GS_SERIALNUMBER\
+ 40000 /* MMmmPP */
+#endif
+long gs_serialnumber = GS_SERIALNUMBER; /* should be const, see gscdefs.h */
+
+/* ---------------- Installation directories and files ---------------- */
+
+/* Here is where the library search path and the name of the */
+/* initialization file are defined. */
+
+/* Define the default library search path. */
+const char *gs_lib_default_path = CUPS_DATADIR "/pstoraster:" CUPS_DATADIR "/fonts";
+
+/* Define the interpreter initialization file. */
+const char *gs_init_file = "gs_init.ps";
diff --git a/pstoraster/gscdefs.h b/pstoraster/gscdefs.h
new file mode 100644
index 000000000..9139e9022
--- /dev/null
+++ b/pstoraster/gscdefs.h
@@ -0,0 +1,55 @@
+/* Copyright (C) 1994, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gscdefs.h */
+/* Prototypes for configuration definitions in gconfig.c. */
+
+/* Miscellaneous system constants (read-only systemparams). */
+/* They should all be const, but one application needs some of them */
+/* to be writable.... */
+extern long gs_buildtime;
+extern const char *gs_copyright;
+extern const char *gs_product;
+extern long gs_revision;
+extern long gs_revisiondate;
+extern long gs_serialnumber;
+
+/* Installation directories and files */
+extern const char *gs_doc_directory;
+extern const char *gs_lib_default_path;
+extern const char *gs_init_file;
+
+/* Resource tables. In order to avoid importing a large number of types, */
+/* we only provide macros for the externs, not the externs themselves. */
+
+#define extern_gx_init_table()\
+ extern void (*gx_init_table[])(P1(gs_memory_t *))
+
+#define extern_gx_io_device_table()\
+ extern gx_io_device *gx_io_device_table[]
+
+/* Return the list of device prototypes, the list of their structure */
+/* descriptors, and (as the value) the length of the lists. */
+#define extern_gs_lib_device_list()\
+ int gs_lib_device_list(P2(const gx_device ***plist,\
+ gs_memory_struct_type_t **pst))
diff --git a/pstoraster/gschar.c b/pstoraster/gschar.c
new file mode 100644
index 000000000..c3e7ecd45
--- /dev/null
+++ b/pstoraster/gschar.c
@@ -0,0 +1,1353 @@
+/* Copyright (C) 1989, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gschar.c */
+/* Character writing operators for Ghostscript library */
+#include "gx.h"
+#include "memory_.h"
+#include "string_.h"
+#include "gserrors.h"
+#include "gsstruct.h"
+#include "gxfixed.h" /* ditto */
+#include "gxarith.h"
+#include "gxmatrix.h"
+#include "gzstate.h"
+#include "gxcoord.h"
+#include "gxdevice.h"
+#include "gxdevmem.h"
+#include "gxchar.h"
+#include "gxfont.h"
+#include "gxfont0.h"
+#include "gxfcache.h"
+#include "gspath.h"
+#include "gzpath.h"
+
+/* Define whether or not to cache characters rotated by angles other than */
+/* multiples of 90 degrees. */
+#define CACHE_ROTATED_CHARS 1
+
+/* Define whether or not to oversample characters at small sizes. */
+#define OVERSAMPLE 1
+
+/* Define the maximum size of a full temporary bitmap when rasterizing, */
+/* in bits (not bytes). */
+#define MAX_TEMP_BITMAP_BITS 80000
+
+/* Structure descriptors */
+private_st_gs_show_enum();
+#define eptr ((gs_show_enum *)vptr)
+private ENUM_PTRS_BEGIN(show_enum_enum_ptrs) {
+ if ( index > eptr->fstack.depth + 5 )
+ return 0;
+ *pep = eptr->fstack.items[index - 5].font;
+ break;
+ }
+ ENUM_PTR(0, gs_show_enum, pgs);
+ ENUM_CONST_STRING_PTR(1, gs_show_enum, str);
+ ENUM_PTR(2, gs_show_enum, dev_cache);
+ ENUM_PTR(3, gs_show_enum, dev_cache2);
+ ENUM_PTR(4, gs_show_enum, dev_null);
+ENUM_PTRS_END
+private RELOC_PTRS_BEGIN(show_enum_reloc_ptrs) {
+ int i;
+ RELOC_PTR(gs_show_enum, pgs);
+ RELOC_CONST_STRING_PTR(gs_show_enum, str);
+ RELOC_PTR(gs_show_enum, dev_cache);
+ RELOC_PTR(gs_show_enum, dev_cache2);
+ RELOC_PTR(gs_show_enum, dev_null);
+ for ( i = 0; i <= eptr->fstack.depth; i++ )
+ RELOC_PTR(gs_show_enum, fstack.items[i].font);
+} RELOC_PTRS_END
+#undef eptr
+
+/* Forward declarations */
+private int continue_kshow(P1(gs_show_enum *));
+private int continue_show(P1(gs_show_enum *));
+private int continue_show_update(P1(gs_show_enum *));
+private int show_setup(P3(gs_show_enum *, gs_state *, const char *));
+private void show_set_scale(P1(gs_show_enum *));
+private int show_cache_setup(P1(gs_show_enum *));
+private int show_state_setup(P1(gs_show_enum *));
+private int show_origin_setup(P4(gs_state *, fixed, fixed, gs_char_path_mode));
+private int stringwidth_setup(P3(gs_show_enum *, gs_state *, const char *));
+
+/* Print the ctm if debugging */
+#define print_ctm(s,pgs)\
+ dprintf7("[p]%sctm=[%g %g %g %g %g %g]\n", s,\
+ pgs->ctm.xx, pgs->ctm.xy, pgs->ctm.yx, pgs->ctm.yy,\
+ pgs->ctm.tx, pgs->ctm.ty)
+
+/* ------ Font procedures ------ */
+
+/* Dummy (ineffective) BuildChar/BuildGlyph procedure */
+int
+gs_no_build_char(gs_show_enum *penum, gs_state *pgs,
+ gs_font *pfont, gs_char chr, gs_glyph glyph)
+{ return 1; /* failure, but not error */
+}
+
+/* Dummy character encoding procedure */
+gs_glyph
+gs_no_encode_char(gs_show_enum *penum,
+ gs_font *pfont, gs_char *pchr)
+{ return gs_no_glyph;
+}
+
+/* ------ String writing operators ------ */
+
+/* Allocate a show enumerator. */
+gs_show_enum *
+gs_show_enum_alloc(gs_memory_t *mem, gs_state *pgs, client_name_t cname)
+{ gs_show_enum *penum =
+ gs_alloc_struct(mem, gs_show_enum, &st_gs_show_enum, cname);
+ if ( penum == 0 )
+ return 0;
+ /* Initialize pointers for GC */
+ penum->pgs = pgs;
+ penum->str.data = 0;
+ penum->str.size = 0;
+ penum->dev_cache = 0;
+ penum->dev_cache2 = 0;
+ penum->dev_null = 0;
+ penum->fstack.depth = -1;
+ return penum;
+}
+
+/* Free the contents of a show enumerator. */
+void
+gs_show_enum_release(gs_show_enum *penum, gs_memory_t *emem)
+{ gs_state *pgs = penum->pgs;
+ gs_memory_t *mem = pgs->memory;
+
+ penum->cc = 0;
+ if ( penum->dev_cache2 != 0 )
+ { gs_free_object(mem, penum->dev_cache2,
+ "gs_show_enum_release(dev_cache2)");
+ penum->dev_cache2 = 0;
+ }
+ if ( penum->dev_cache != 0 )
+ { gs_free_object(mem, penum->dev_cache,
+ "gs_show_enum_release(dev_cache)");
+ penum->dev_cache = 0;
+ }
+ if ( penum->dev_null != 0 )
+ { gs_free_object(mem, penum->dev_null,
+ "gs_show_enum_release(dev_null)");
+ penum->dev_null = 0;
+ }
+ if ( emem != 0 )
+ gs_free_object(emem, penum, "gs_show_enum_release(enum)");
+}
+
+/* Setup macros for show operators */
+#define setup_show_n()\
+ penum->str.size = size
+#define setup_a()\
+ penum->add = true, penum->ax = ax, penum->ay = ay,\
+ penum->slow_show = true
+#define setup_width()\
+ penum->wchr = chr, penum->wcx = cx, penum->wcy = cy,\
+ penum->slow_show = true
+
+/* show[_n] */
+int
+gs_show_n_init(register gs_show_enum *penum,
+ gs_state *pgs, const char *str, uint size)
+{ setup_show_n();
+ penum->slow_show = false;
+ return show_setup(penum, pgs, str);
+}
+int
+gs_show_init(gs_show_enum *penum, gs_state *pgs, const char *str)
+{ return gs_show_n_init(penum, pgs, str, strlen(str));
+}
+
+/* ashow[_n] */
+int
+gs_ashow_n_init(register gs_show_enum *penum,
+ gs_state *pgs, floatp ax, floatp ay, const char *str, uint size)
+{ int code;
+ setup_show_n();
+ code = show_setup(penum, pgs, str);
+ setup_a();
+ return code;
+}
+int
+gs_ashow_init(gs_show_enum *penum,
+ gs_state *pgs, floatp ax, floatp ay, const char *str)
+{ return gs_ashow_n_init(penum, pgs, ax, ay, str, strlen(str));
+}
+
+/* widthshow[_n] */
+int
+gs_widthshow_n_init(register gs_show_enum *penum,
+ gs_state *pgs, floatp cx, floatp cy, gs_char chr, const char *str, uint size)
+{ int code;
+ setup_show_n();
+ code = show_setup(penum, pgs, str);
+ setup_width();
+ return code;
+}
+int
+gs_widthshow_init(gs_show_enum *penum,
+ gs_state *pgs, floatp cx, floatp cy, gs_char chr, const char *str)
+{ return gs_widthshow_n_init(penum, pgs, cx, cy, chr, str, strlen(str));
+}
+
+/* awidthshow[_n] */
+int
+gs_awidthshow_n_init(register gs_show_enum *penum,
+ gs_state *pgs, floatp cx, floatp cy, gs_char chr, floatp ax, floatp ay,
+ const char *str, uint size)
+{ int code;
+ setup_show_n();
+ code = show_setup(penum, pgs, str);
+ setup_a();
+ setup_width();
+ return code;
+}
+int
+gs_awidthshow_init(gs_show_enum *penum,
+ gs_state *pgs, floatp cx, floatp cy, gs_char chr, floatp ax, floatp ay,
+ const char *str)
+{ return gs_awidthshow_n_init(penum, pgs, cx, cy, chr, ax, ay,
+ str, strlen(str));
+}
+
+/* kshow[_n] */
+int
+gs_kshow_n_init(register gs_show_enum *penum,
+ gs_state *pgs, const char *str, uint size)
+{ int code;
+ if ( pgs->font->FontType == ft_composite)
+ return_error(gs_error_invalidfont);
+ setup_show_n();
+ code = show_setup(penum, pgs, str);
+ penum->do_kern = 1;
+ penum->slow_show = true;
+ return code;
+}
+int
+gs_kshow_init(gs_show_enum *penum, gs_state *pgs, const char *str)
+{ return gs_kshow_n_init(penum, pgs, str, strlen(str));
+}
+
+/* xyshow[_n] */
+int
+gs_xyshow_n_init(register gs_show_enum *penum,
+ gs_state *pgs, const char *str, uint size)
+{ int code;
+ setup_show_n();
+ code = show_setup(penum, pgs, str);
+ penum->do_kern = -1;
+ penum->slow_show = true;
+ return code;
+}
+int
+gs_xyshow_init(gs_show_enum *penum, gs_state *pgs, const char *str)
+{ return gs_xyshow_n_init(penum, pgs, str, strlen(str));
+}
+
+/* glyphshow */
+private font_proc_encode_char(gs_glyphshow_encode_char);
+int
+gs_glyphshow_init(gs_show_enum *penum, gs_state *pgs, gs_glyph glyph)
+{ int code;
+ if ( pgs->font->FontType == ft_composite)
+ return_error(gs_error_invalidfont);
+ penum->str.size = 1;
+ penum->slow_show = false;
+ code = show_setup(penum, pgs, "\000"); /* arbitrary char */
+ penum->current_glyph = glyph;
+ penum->encode_char = gs_glyphshow_encode_char;
+ return code;
+}
+private gs_glyph
+gs_glyphshow_encode_char(gs_show_enum *penum, gs_font *pfont, gs_char *pchr)
+{ /* We just nil out the character, and return the pre-loaded glyph. */
+ *pchr = gs_no_char;
+ return penum->current_glyph;
+}
+
+/* ------ Related operators ------ */
+
+/* cshow[_n] */
+int
+gs_cshow_n_init(register gs_show_enum *penum,
+ gs_state *pgs, const char *str, uint size)
+{ int code;
+ setup_show_n();
+ code = show_setup(penum, pgs, str);
+ penum->do_kern = -1;
+ penum->stringwidth_flag = -1;
+ penum->slow_show = true;
+ return code;
+}
+int
+gs_cshow_init(gs_show_enum *penum, gs_state *pgs, const char *str)
+{ return gs_cshow_n_init(penum, pgs, str, strlen(str));
+}
+
+/* stringwidth[_n] */
+int
+gs_stringwidth_n_init(gs_show_enum *penum, gs_state *pgs, const char *str, uint size)
+{ setup_show_n();
+ return stringwidth_setup(penum, pgs, str);
+}
+int
+gs_stringwidth_init(gs_show_enum *penum, gs_state *pgs, const char *str)
+{ return gs_stringwidth_n_init(penum, pgs, str, strlen(str));
+}
+
+/* Common code for stringwidth[_n] */
+private int
+stringwidth_setup(gs_show_enum *penum, gs_state *pgs, const char *str)
+{ int code = (penum->slow_show = false, show_setup(penum, pgs, str));
+ gs_memory_t *mem = pgs->memory;
+ gx_device_null *dev_null;
+ if ( code < 0 )
+ return code;
+ dev_null = gs_alloc_struct(mem, gx_device_null, &st_device_null,
+ "stringwidth_setup(dev_null)");
+ if ( dev_null == 0 )
+ return_error(gs_error_VMerror);
+ penum->stringwidth_flag = 1;
+ /* Do an extra gsave and suppress output */
+ if ( (code = gs_gsave(pgs)) < 0 )
+ return code;
+ penum->level = pgs->level; /* for level check in show_update */
+ /* Set up a null device that forwards xfont requests properly. */
+ gs_make_null_device(dev_null, mem);
+ dev_null->target = gs_currentdevice_inline(pgs);
+ pgs->device = (gx_device *)dev_null;
+ pgs->ctm_default_set = false;
+ penum->dev_null = dev_null;
+ /* Establish an arbitrary translation and current point. */
+ gs_newpath(pgs);
+ gx_translate_to_fixed(pgs, fixed_0, fixed_0);
+ return gx_path_add_point(pgs->path, fixed_0, fixed_0);
+}
+
+/* charpath[_n] */
+int
+gs_charpath_n_init(gs_show_enum *penum, gs_state *pgs,
+ const char *str, uint size, bool stroke_path)
+{ int code;
+ setup_show_n();
+ code = show_setup(penum, pgs, str);
+ penum->charpath_flag =
+ (stroke_path ? cpm_true_charpath : cpm_false_charpath);
+ penum->can_cache = -1;
+ if_debug1('k', "[k]charpath, can_cache=%d", penum->can_cache);
+ return code;
+}
+int
+gs_charpath_init(gs_show_enum *penum, gs_state *pgs,
+ const char *str, bool stroke_path)
+{ return gs_charpath_n_init(penum, pgs, str, strlen(str), stroke_path);
+}
+
+/* charboxpath[_n] */
+int
+gs_charboxpath_n_init(gs_show_enum *penum, gs_state *pgs,
+ const char *str, uint size, bool use_boxes)
+{ int code;
+ setup_show_n();
+ code = show_setup(penum, pgs, str);
+ penum->charpath_flag =
+ (use_boxes ? cpm_true_charboxpath : cpm_false_charboxpath);
+ penum->can_cache = 0; /* different from charpath! */
+ if_debug1('k', "[k]charboxpath, can_cache=%d", penum->can_cache);
+ return code;
+}
+int
+gs_charboxpath_init(gs_show_enum *penum, gs_state *pgs,
+ const char *str, bool stroke_path)
+{ return gs_charboxpath_n_init(penum, pgs, str, strlen(str),
+ stroke_path);
+}
+
+/* ------ Width/cache operators ------ */
+
+private int set_cache_device(P3(gs_show_enum *, gs_state *,
+ const float * /*[4]*/));
+
+/* setcachedevice */
+/* The elements of pw are: wx, wy, llx, lly, urx, ury. */
+/* Note that this returns 1 if we just set up the cache device. */
+int
+gs_setcachedevice(gs_show_enum *penum, gs_state *pgs, const float *pw)
+{ int code = gs_setcharwidth(penum, pgs, pw[0], pw[1]); /* default is don't cache */
+
+ if ( code < 0 )
+ return code;
+ return set_cache_device(penum, pgs, pw + 2);
+}
+
+/* setcachedevice2 */
+/* The elements of pw2 are: w0x, w0y, llx, lly, urx, ury, w1x, w1y, vx, vy. */
+/* Note that this returns 1 if we just set up the cache device. */
+int
+gs_setcachedevice2(gs_show_enum *penum, gs_state *pgs, const float *pw2)
+{ int code;
+
+ if ( gs_rootfont(pgs)->WMode )
+ { float vx = pw2[8], vy = pw2[9];
+ gs_fixed_point pvxy, dvxy;
+ cached_char *cc;
+ if ( (code = gs_point_transform2fixed(&pgs->ctm, -vx, -vy, &pvxy)) < 0 ||
+ (code = gs_distance_transform2fixed(&pgs->ctm, vx, vy, &dvxy)) < 0 ||
+ (code = gs_setcharwidth(penum, pgs, pw2[6], pw2[7])) < 0
+ )
+ return code;
+ /* Adjust the origin by (vx, vy). */
+ gx_translate_to_fixed(pgs, pvxy.x, pvxy.y);
+ code = set_cache_device(penum, pgs, pw2 + 2);
+ if ( code != 1 )
+ return code;
+ /* Adjust the character origin too. */
+ cc = penum->cc;
+ cc->offset.x += dvxy.x;
+ cc->offset.y += dvxy.y;
+ }
+ else
+ { code = gs_setcharwidth(penum, pgs, pw2[0], pw2[1]);
+ if ( code < 0 )
+ return code;
+ code = set_cache_device(penum, pgs, pw2 + 2);
+ }
+ return code;
+}
+
+/* Set up the cache device if relevant. */
+/* Return 1 if we just set up a cache device. */
+/* Used by setcachedevice and setcachedevice2. */
+/* The elements of pb are: llx, lly, urx, ury. */
+private int
+set_cache_device(register gs_show_enum *penum, gs_state *pgs, const float *pb)
+{ float llx = pb[0], lly = pb[1], urx = pb[2], ury = pb[3];
+ gs_glyph glyph;
+ /* See if we want to cache this character. */
+ if ( pgs->in_cachedevice ) /* no recursion! */
+ return 0;
+ pgs->in_cachedevice = 1; /* disable color/gray/image operators */
+ /* We can only use the cache if we know the glyph. */
+ glyph = gs_show_current_glyph(penum);
+ if ( glyph == gs_no_glyph )
+ return 0;
+ /* We can only use the cache if ctm is unchanged */
+ /* (aside from a possible translation). */
+ if ( penum->can_cache <= 0 || !pgs->char_tm_valid )
+ { if_debug2('k', "[k]no cache: can_cache=%d, char_tm_valid=%d\n",
+ penum->can_cache, (int)pgs->char_tm_valid);
+ return 0;
+ }
+ { const gs_font *pfont = pgs->font;
+ gs_font_dir *dir = pfont->dir;
+ gx_device *dev = gs_currentdevice_inline(pgs);
+ int alpha_bits =
+ (*dev_proc(dev, get_alpha_bits))(dev, go_text);
+ gs_log2_scale_point log2_scale;
+ static const fixed max_cdim[3] = {
+#define max_cd(n)\
+ (fixed_1 << (arch_sizeof_short * 8 - n)) - (fixed_1 >> n) * 3
+ max_cd(0), max_cd(1), max_cd(2)
+#undef max_cd
+ };
+ ushort iwidth, iheight;
+ cached_char *cc;
+ gs_fixed_rect clip_box;
+ int code;
+ /* Compute the bounding box of the transformed character. */
+ /* Since we accept arbitrary transformations, the extrema */
+ /* may occur in any order; however, we can save some work */
+ /* by observing that opposite corners before transforming */
+ /* are still opposite afterwards. */
+ gs_fixed_point cll, clr, cul, cur, cdim;
+
+ if ( (code = gs_distance_transform2fixed(&pgs->ctm, llx, lly, &cll)) < 0 ||
+ (code = gs_distance_transform2fixed(&pgs->ctm, llx, ury, &clr)) < 0 ||
+ (code = gs_distance_transform2fixed(&pgs->ctm, urx, lly, &cul)) < 0 ||
+ (code = gs_distance_transform2fixed(&pgs->ctm, urx, ury, &cur)) < 0
+ )
+ return code;
+ { fixed ctemp;
+#define swap(a, b) ctemp = a, a = b, b = ctemp
+#define make_min(a, b) if ( (a) > (b) ) swap(a, b)
+ make_min(cll.x, cur.x);
+ make_min(cll.y, cur.y);
+ make_min(clr.x, cul.x);
+ make_min(clr.y, cul.y);
+#undef make_min
+#undef swap
+ }
+ /* Now take advantage of symmetry. */
+ if ( clr.x < cll.x ) cll.x = clr.x, cur.x = cul.x;
+ if ( clr.y < cll.y ) cll.y = clr.y, cur.y = cul.y;
+ /* Now cll and cur are the extrema of the box. */
+ cdim.x = cur.x - cll.x;
+ cdim.y = cur.y - cll.y;
+ show_set_scale(penum);
+ log2_scale.x = penum->log2_suggested_scale.x;
+ log2_scale.y = penum->log2_suggested_scale.y;
+#ifdef DEBUG
+if ( gs_debug_c('k') )
+ { dprintf6("[k]cbox=[%g %g %g %g] scale=%dx%d\n",
+ fixed2float(cll.x), fixed2float(cll.y),
+ fixed2float(cur.x), fixed2float(cur.y),
+ 1 << log2_scale.x, 1 << log2_scale.y);
+ print_ctm(" ", pgs);
+ }
+#endif
+ /*
+ * If the device wants anti-aliased text,
+ * increase the sampling scale to ensure that
+ * if we want N bits of alpha, we generate
+ * at least 2^N sampled bits per pixel.
+ */
+ if ( alpha_bits > 1 )
+ { int more_bits =
+ alpha_bits - (log2_scale.x + log2_scale.y);
+ if ( more_bits > 0 )
+ { if (log2_scale.x <= log2_scale.y)
+ { log2_scale.x += (more_bits + 1) >> 1;
+ log2_scale.y += more_bits >> 1;
+ }
+ else
+ { log2_scale.x += more_bits >> 1;
+ log2_scale.y += (more_bits + 1) >> 1;
+ }
+ }
+ }
+ else
+#if OVERSAMPLE
+ if ( pfont->PaintType == 2 )
+#endif
+ { /* Don't oversample artificially stroked fonts. */
+ log2_scale.x = log2_scale.y = 0;
+ }
+ if ( cdim.x > max_cdim[log2_scale.x] ||
+ cdim.y > max_cdim[log2_scale.y]
+ )
+ return 0; /* much too big */
+ iwidth = ((ushort)fixed2int_var(cdim.x) + 2) << log2_scale.x;
+ iheight = ((ushort)fixed2int_var(cdim.y) + 2) << log2_scale.y;
+ if_debug3('k', "[k]iwidth=%u iheight=%u dev_cache %s\n",
+ (uint)iwidth, (uint)iheight,
+ (penum->dev_cache == 0 ? "not set" : "set"));
+ if ( penum->dev_cache == 0 )
+ { code = show_cache_setup(penum);
+ if ( code < 0 )
+ return code;
+ }
+ /*
+ * If we're oversampling (i.e., the temporary bitmap is
+ * larger than the final monobit or alpha array) and the
+ * temporary bitmap is large, use incremental conversion
+ * from oversampled bitmap strips to alpha values instead of
+ * full oversampling with compression at the end.
+ */
+ cc = gx_alloc_char_bits(dir, penum->dev_cache,
+ (iwidth > MAX_TEMP_BITMAP_BITS / iheight &&
+ (1 << (log2_scale.x + log2_scale.y)) > alpha_bits ?
+ penum->dev_cache2 : NULL),
+ iwidth, iheight, &log2_scale, alpha_bits);
+ if ( cc == 0 )
+ return 0; /* too big for cache */
+ /* The mins handle transposed coordinate systems.... */
+ /* Truncate the offsets to avoid artifacts later. */
+ cc->offset.x = fixed_ceiling(-cll.x);
+ cc->offset.y = fixed_ceiling(-cll.y);
+ if_debug4('k', "[k]width=%u, height=%u, offset=[%g %g]\n",
+ (uint)iwidth, (uint)iheight,
+ fixed2float(cc->offset.x),
+ fixed2float(cc->offset.y));
+ if ( (code = gs_gsave(pgs)) < 0 )
+ { gx_free_cached_char(dir, cc);
+ return code;
+ }
+ /* Nothing can go wrong now.... */
+ penum->cc = cc;
+ cc->code = glyph;
+ cc->wmode = gs_rootfont(pgs)->WMode;
+ cc->wxy = penum->wxy;
+ /* Install the device */
+ pgs->device = (gx_device *)penum->dev_cache;
+ pgs->ctm_default_set = false;
+ /* Adjust the transformation in the graphics context */
+ /* so that the character lines up with the cache. */
+ gx_translate_to_fixed(pgs,
+ cc->offset.x << log2_scale.x,
+ cc->offset.y << log2_scale.y);
+ if ( (log2_scale.x | log2_scale.y) != 0 )
+ gx_scale_char_matrix(pgs, 1 << log2_scale.x,
+ 1 << log2_scale.y);
+ /* Set the initial matrix for the cache device. */
+ penum->dev_cache->initial_matrix = ctm_only(pgs);
+ /* Set the oversampling factor. */
+ penum->log2_current_scale.x = log2_scale.x;
+ penum->log2_current_scale.y = log2_scale.y;
+ /* Reset the clipping path to match the metrics. */
+ clip_box.p.x = clip_box.p.y = 0;
+ clip_box.q.x = int2fixed(iwidth);
+ clip_box.q.y = int2fixed(iheight);
+ if ( (code = gx_clip_to_rectangle(pgs, &clip_box)) < 0 )
+ return code;
+ gx_set_device_color_1(pgs); /* write 1's */
+ pgs->in_cachedevice = 2; /* we are caching */
+ }
+ penum->width_status = sws_cache;
+ return 1;
+}
+
+/* setcharwidth */
+/* Note that this returns 1 if the current show operation is */
+/* non-displaying (stringwidth or cshow). */
+int
+gs_setcharwidth(register gs_show_enum *penum, gs_state *pgs,
+ floatp wx, floatp wy)
+{ int code;
+ if ( penum->width_status != sws_none )
+ return_error(gs_error_undefined);
+ if ( (code = gs_distance_transform2fixed(&pgs->ctm, wx, wy, &penum->wxy)) < 0 )
+ return code;
+ /* Check whether we're setting the scalable width */
+ /* for a cached xfont character. */
+ if ( penum->cc != 0 )
+ { penum->cc->wxy = penum->wxy;
+ penum->width_status = sws_cache_width_only;
+ }
+ else
+ { penum->width_status = sws_no_cache;
+ }
+ return (penum->stringwidth_flag != 0 ? 1 : 0);
+}
+
+/* ------ Enumerator ------ */
+
+/* Do the next step of a show (or stringwidth) operation */
+int
+gs_show_next(gs_show_enum *penum)
+{ return (*penum->continue_proc)(penum);
+}
+
+/* Continuation procedures */
+#define show_fast_move(wxy, pgs)\
+ gx_path_add_rel_point_inline(pgs->path, wxy.x, wxy.y)
+private int show_update(P1(gs_show_enum *penum));
+private int show_move(P1(gs_show_enum *penum));
+private int show_proceed(P1(gs_show_enum *penum));
+private int show_finish(P1(gs_show_enum *penum));
+private int
+continue_show_update(register gs_show_enum *penum)
+{ int code = show_update(penum);
+ if ( code < 0 )
+ return code;
+ code = show_move(penum);
+ if ( code != 0 )
+ return code;
+ return show_proceed(penum);
+}
+private int
+continue_show(register gs_show_enum *penum)
+{ return show_proceed(penum);
+}
+/* For kshow, the CTM or font may have changed, so we have to reestablish */
+/* the cached values in the enumerator. */
+private int
+continue_kshow(register gs_show_enum *penum)
+{ int code = show_state_setup(penum);
+ if ( code < 0 )
+ return code;
+ return show_proceed(penum);
+}
+
+/* Update position */
+private int
+show_update(register gs_show_enum *penum)
+{ register gs_state *pgs = penum->pgs;
+ cached_char *cc = penum->cc;
+ int code;
+ /* Update position for last character */
+ switch ( penum->width_status )
+ {
+ case sws_none:
+ /* Adobe interpreters assume a character width of 0, */
+ /* even though the documentation says this is an error.... */
+ penum->wxy.x = penum->wxy.y = 0;
+ break;
+ case sws_cache:
+ /* Finish installing the cache entry. */
+ /* If the BuildChar/BuildGlyph procedure did a save and a */
+ /* restore, it already undid the gsave in setcachedevice. */
+ /* We have to check for this by comparing levels. */
+ switch ( pgs->level - penum->level )
+ {
+ default:
+ return_error(gs_error_invalidfont); /* WRONG */
+ case 2:
+ code = gs_grestore(pgs);
+ if ( code < 0 )
+ return code;
+ case 1:
+ ;
+ }
+ gx_add_cached_char(pgs->font->dir, penum->dev_cache,
+ cc, gx_lookup_fm_pair(pgs->font, pgs),
+ &penum->log2_current_scale);
+ if ( penum->stringwidth_flag != 0 ||
+ penum->charpath_flag != cpm_show
+ )
+ break;
+ /* falls through */
+ case sws_cache_width_only:
+ /* Copy the bits to the real output device. */
+ code = gs_grestore(pgs);
+ if ( code < 0 )
+ return code;
+ code = gx_color_load(pgs->dev_color, pgs);
+ if ( code < 0 )
+ return code;
+ return gx_image_cached_char(penum, cc);
+ case sws_no_cache:
+ ;
+ }
+ if ( penum->charpath_flag != cpm_show )
+ { /* Move back to the character origin, so that */
+ /* show_move will get us to the right place. */
+ code = gx_path_add_point(pgs->show_gstate->path,
+ penum->origin.x, penum->origin.y);
+ if ( code < 0 )
+ return code;
+ }
+ return gs_grestore(pgs);
+}
+
+/* Move to next character */
+private int
+show_move(register gs_show_enum *penum)
+{ register gs_state *pgs = penum->pgs;
+ if ( penum->do_kern < 0 )
+ { /* xyshow or cshow */
+ penum->continue_proc = continue_show;
+ return gs_show_move;
+ }
+ if ( penum->add )
+ gs_rmoveto(pgs, penum->ax, penum->ay);
+ if ( penum->wchr != gs_no_char )
+ { gs_char chr = penum->current_char;
+ int fdepth = penum->fstack.depth;
+ if ( fdepth > 0 )
+ { /* Add in the shifted font number. */
+ uint fidx = penum->fstack.items[fdepth].index;
+ switch ( ((gs_font_type0 *)(penum->fstack.items[fdepth - 1].font))->data.FMapType )
+ {
+ case fmap_1_7:
+ case fmap_9_7:
+ chr += fidx << 7;
+ break;
+ default:
+ chr += fidx << 8;
+ }
+ }
+ if ( chr == penum->wchr )
+ gs_rmoveto(pgs, penum->wcx, penum->wcy);
+ }
+ /* wxy is in device coordinates */
+ { int code = show_fast_move(penum->wxy, pgs);
+ if ( code < 0 )
+ return code;
+ }
+ /* Check for kerning, but not on the last character. */
+ if ( penum->do_kern && penum->index < penum->str.size )
+ { penum->continue_proc = continue_kshow;
+ return gs_show_kern;
+ }
+ return 0;
+}
+/* Process next character */
+private int
+show_proceed(register gs_show_enum *penum)
+{ register gs_state *pgs = penum->pgs;
+ gs_font *pfont;
+ cached_fm_pair *pair = 0;
+ gs_font *rfont = gs_rootfont(pgs);
+ int wmode = rfont->WMode;
+ font_proc_next_char((*next_char)) = rfont->procs.next_char;
+ gs_char chr;
+ gs_glyph glyph;
+ int code;
+ cached_char *cc;
+ gx_device *dev = gs_currentdevice_inline(pgs);
+ int alpha_bits = (*dev_proc(dev, get_alpha_bits))(dev, go_text);
+
+ if ( penum->charpath_flag == cpm_show && !penum->stringwidth_flag )
+ { code = gx_color_load(pgs->dev_color, pgs);
+ if ( code < 0 )
+ return code;
+ }
+more: /* Proceed to next character */
+ pfont = (penum->fstack.depth < 0 ? pgs->font :
+ penum->fstack.items[penum->fstack.depth].font);
+ /* can_cache >= 0 allows us to use cached characters, */
+ /* even if we can't make new cache entries. */
+ if ( penum->can_cache >= 0 )
+ { /* Loop with cache */
+ for ( ; ; )
+ { switch ( (code = (*next_char)(penum, &chr)) )
+ {
+ default: /* error */
+ return code;
+ case 2: /* done */
+ return show_finish(penum);
+ case 1: /* font change */
+ pfont = penum->fstack.items[penum->fstack.depth].font;
+ pgs->char_tm_valid = false;
+ show_state_setup(penum);
+ pair = 0;
+ /* falls through */
+ case 0: /* plain char */
+ /*
+ * We don't need to set penum->current_char in the
+ * normal cases, but it's needed for widthshow,
+ * kshow, and one strange client, so we may as well
+ * do it here.
+ */
+ penum->current_char = chr;
+ glyph = (*penum->encode_char)(penum, pfont, &chr);
+ penum->current_char = chr;
+ if ( glyph == gs_no_glyph )
+ { cc = 0;
+ goto no_cache;
+ }
+ if ( pair == 0 )
+ pair = gx_lookup_fm_pair(pfont, pgs);
+ cc = gx_lookup_cached_char(pfont, pair, glyph, wmode,
+ alpha_bits);
+ if ( cc == 0 )
+ { /* Character is not in cache. */
+ /* If possible, try for an xfont before */
+ /* rendering from the outline. */
+ if ( pfont->ExactSize == fbit_use_outlines ||
+ pfont->PaintType == 2
+ )
+ goto no_cache;
+ if ( pfont->BitmapWidths )
+ { cc = gx_lookup_xfont_char(pgs, pair, chr,
+ glyph, &pfont->procs.callbacks, wmode);
+ if ( cc == 0 )
+ goto no_cache;
+ }
+ else
+ { if ( penum->stringwidth_flag != 0 ||
+ penum->charpath_flag != cpm_show
+ )
+ goto no_cache;
+ /* We might have an xfont, but we still */
+ /* want the scalable widths. */
+ cc = gx_lookup_xfont_char(pgs, pair, chr,
+ glyph, &pfont->procs.callbacks, wmode);
+ /* Render up to the point of */
+ /* setcharwidth or setcachedevice, */
+ /* just as for stringwidth. */
+ /* This is the only case in which we can */
+ /* to go no_cache with cc != 0. */
+ goto no_cache;
+ }
+ }
+ /* Character is in cache. */
+ /* We might be doing .charboxpath or stringwidth; */
+ /* check for these now. */
+ if ( penum->charpath_flag != cpm_show )
+ { /* This is .charboxpath. Get the bounding box */
+ /* and append it to a path. */
+ gx_path box_path;
+ gs_fixed_point pt;
+ fixed llx, lly, urx, ury;
+ code = gx_path_current_point(pgs->path, &pt);
+ if ( code < 0 )
+ return code;
+ llx = fixed_rounded(pt.x - cc->offset.x) +
+ int2fixed(penum->ftx);
+ lly = fixed_rounded(pt.y - cc->offset.y) +
+ int2fixed(penum->fty);
+ urx = llx + int2fixed(cc->width),
+ ury = lly + int2fixed(cc->height);
+ gx_path_init(&box_path, pgs->memory);
+ code =
+ gx_path_add_rectangle(&box_path, llx, lly,
+ urx, ury);
+ if ( code >= 0 )
+ code =
+ gx_path_add_char_path(pgs->show_gstate->path,
+ &box_path,
+ penum->charpath_flag);
+ if ( code >= 0 )
+ code = gx_path_add_point(pgs->path, pt.x, pt.y);
+ gx_path_release(&box_path);
+ if ( code < 0 )
+ return code;
+ }
+ else if ( !penum->stringwidth_flag )
+ { code = gx_image_cached_char(penum, cc);
+ if ( code < 0 )
+ return code;
+ else if ( code > 0 )
+ { cc = 0;
+ goto no_cache;
+ }
+ }
+ if ( penum->slow_show )
+ { /* Split up the assignment so that the */
+ /* Watcom compiler won't reserve esi/edi. */
+ penum->wxy.x = cc->wxy.x;
+ penum->wxy.y = cc->wxy.y;
+ code = show_move(penum);
+ }
+ else
+ code = show_fast_move(cc->wxy, pgs);
+ if ( code )
+ { /* Might be kshow, so store the state. */
+ penum->current_glyph = glyph;
+ return code;
+ }
+ }
+ }
+ }
+ else
+ { /* Can't use cache */
+ switch ( (code = (*next_char)(penum, &chr)) )
+ {
+ default:
+ return code;
+ case 2:
+ return show_finish(penum);
+ case 1:
+ pfont = penum->fstack.items[penum->fstack.depth].font;
+ show_state_setup(penum);
+ case 0:
+ ;
+ }
+ penum->current_char = chr;
+ glyph = (*penum->encode_char)(penum, pfont, &chr);
+ penum->current_char = chr;
+ cc = 0;
+ }
+no_cache:
+ /*
+ * We must call the client's rendering code. Normally,
+ * we only do this if the character is not cached (cc = 0);
+ * however, we also must do this if we have an xfont but
+ * are using scalable widths. In this case, and only this case,
+ * we get here with cc != 0. penum->current_char has already
+ * been set, but not penum->current_glyph.
+ */
+ penum->current_glyph = glyph;
+ if ( (code = gs_gsave(pgs)) < 0 )
+ return code;
+ /* Set the font to the current descendant font. */
+ pgs->font = pfont;
+ /* Reset the in_cachedevice flag, so that a recursive show */
+ /* will use the cache properly. */
+ pgs->in_cachedevice = 0;
+ /* Reset the sampling scale. */
+ penum->log2_current_scale.x = penum->log2_current_scale.y = 0;
+ /* Set the charpath flag in the graphics context if necessary, */
+ /* so that fill and stroke will add to the path */
+ /* rather than having their usual effect. */
+ pgs->in_charpath = penum->charpath_flag;
+ pgs->stroke_adjust = false; /* per specification */
+ { gs_fixed_point cpt;
+ gx_path *ppath = pgs->path;
+ if ( (code = gx_path_current_point_inline(ppath, &cpt)) < 0 )
+ goto rret;
+ /* Normally, char_tm is valid because of show_state_setup, */
+ /* but if we're in a cshow, it may not be. */
+ gs_currentcharmatrix(pgs, NULL, true);
+#if 1 /*USE_FPU <= 0*/
+ if ( pgs->ctm.txy_fixed_valid && pgs->char_tm.txy_fixed_valid )
+ { fixed tx = pgs->ctm.tx_fixed;
+ fixed ty = pgs->ctm.ty_fixed;
+
+ gs_settocharmatrix(pgs);
+ cpt.x += pgs->ctm.tx_fixed - tx;
+ cpt.y += pgs->ctm.ty_fixed - ty;
+ }
+ else
+#endif
+ { double tx = pgs->ctm.tx;
+ double ty = pgs->ctm.ty;
+ double fpx, fpy;
+
+ gs_settocharmatrix(pgs);
+ fpx = fixed2float(cpt.x) + (pgs->ctm.tx - tx);
+ fpy = fixed2float(cpt.y) + (pgs->ctm.ty - ty);
+#define f_fits_in_fixed(f) f_fits_in_bits(f, fixed_int_bits)
+ if ( !(f_fits_in_fixed(fpx) && f_fits_in_fixed(fpy)) )
+ { gs_note_error(code = gs_error_limitcheck);
+ goto rret;
+ }
+ cpt.x = float2fixed(fpx);
+ cpt.y = float2fixed(fpy);
+ }
+ penum->origin.x = cpt.x;
+ penum->origin.y = cpt.y;
+ gs_newpath(pgs);
+ code = show_origin_setup(pgs, cpt.x, cpt.y,
+ penum->charpath_flag);
+ if ( code < 0 )
+ goto rret;
+ }
+ penum->width_status = sws_none;
+ penum->continue_proc = continue_show_update;
+ /* Try using the build procedure in the font. */
+ /* < 0 means error, 0 means success, 1 means failure. */
+ penum->cc = cc; /* set this now for build procedure */
+ code = (*pfont->procs.build_char)(penum, pgs, pfont, chr, glyph);
+ if ( code < 0 )
+ { discard(gs_note_error(code));
+ goto rret;
+ }
+ if ( code == 0 )
+ { code = show_update(penum);
+ if ( code < 0 )
+ goto rret;
+ /* Note that show_update does a grestore.... */
+ code = show_move(penum);
+ if ( code )
+ return code; /* ... so don't go to rret here. */
+ goto more;
+ }
+ /*
+ * Some BuildChar procedures do a save before the setcachedevice,
+ * and a restore at the end. If we waited to allocate the cache
+ * device until the setcachedevice, we would attempt to free it
+ * after the restore. Therefore, allocate it now.
+ */
+ if ( penum->dev_cache == 0 )
+ { code = show_cache_setup(penum);
+ if ( code < 0 )
+ goto rret;
+ }
+ return gs_show_render;
+ /* If we get an error while setting up for BuildChar, */
+ /* we must undo the partial setup. */
+rret: gs_grestore(pgs);
+ return code;
+}
+
+/* Finish show or stringwidth */
+private int
+show_finish(gs_show_enum *penum)
+{ gs_state *pgs = penum->pgs;
+ int code;
+ gs_show_enum_release(penum, NULL);
+ if ( penum->stringwidth_flag <= 0 ) /* could be cshow */
+ return 0;
+ /* Save the accumulated width before returning, */
+ /* and undo the extra gsave. */
+ code = gs_currentpoint(pgs, &penum->width);
+ if ( code < 0 )
+ return code;
+ return gs_grestore(pgs);
+}
+
+/* Return the current character for rendering. */
+gs_char
+gs_show_current_char(const gs_show_enum *penum)
+{ return penum->current_char;
+}
+
+/* Return the current glyph for rendering. */
+gs_glyph
+gs_show_current_glyph(const gs_show_enum *penum)
+{ return penum->current_glyph;
+}
+
+/* Return the width of the just-enumerated character (for cshow). */
+int
+gs_show_current_width(const gs_show_enum *penum, gs_point *ppt)
+{ return gs_idtransform(penum->pgs,
+ fixed2float(penum->wxy.x),
+ fixed2float(penum->wxy.y), ppt);
+}
+
+/* Return the just-displayed character for kerning. */
+gs_char
+gs_kshow_previous_char(const gs_show_enum *penum)
+{ return penum->current_char;
+}
+
+/* Return the about-to-be-displayed character for kerning. */
+gs_char
+gs_kshow_next_char(const gs_show_enum *penum)
+{ return penum->str.data[penum->index];
+}
+
+/* ------ Miscellaneous accessors ------ */
+
+/* Return the current font for cshow. */
+gs_font *
+gs_show_current_font(const gs_show_enum *penum)
+{ return (penum->fstack.depth < 0 ? penum->pgs->font :
+ penum->fstack.items[penum->fstack.depth].font);
+}
+
+/* Return the charpath mode. */
+gs_char_path_mode
+gs_show_in_charpath(const gs_show_enum *penum)
+{ return penum->charpath_flag;
+}
+
+/* Return the accumulated width for stringwidth. */
+void
+gs_show_width(const gs_show_enum *penum, gs_point *ppt)
+{ *ppt = penum->width;
+}
+
+/* Return true if we only need the width from the rasterizer */
+/* and can short-circuit the full rendering of the character, */
+/* false if we need the actual character bits. */
+/* This is only meaningful just before calling gs_setcharwidth or */
+/* gs_setcachedevice[2]. */
+/* Note that we can't do this if the procedure has done any extra [g]saves. */
+bool
+gs_show_width_only(const gs_show_enum *penum)
+{ /* penum->cc will be non-zero iff we are calculating */
+ /* the scalable width for an xfont character. */
+ return ((penum->stringwidth_flag != 0 || penum->cc != 0) &&
+ penum->pgs->level == penum->level + 1);
+}
+
+/* ------ Internal routines ------ */
+
+/* Initialize a show enumerator. */
+/* Note that this does not set str.size. */
+private int
+show_setup(register gs_show_enum *penum, gs_state *pgs, const char *str)
+{ int code;
+ gs_font *pfont;
+ gx_set_dev_color(pgs);
+ pfont = pgs->font;
+ penum->pgs = pgs;
+ penum->level = pgs->level;
+ penum->str.data = (const byte *)str; /* avoid signed chars */
+ penum->wchr = gs_no_char;
+ penum->add = false;
+ penum->do_kern = 0;
+ penum->charpath_flag = cpm_show;
+ penum->stringwidth_flag = 0;
+ penum->dev_cache = 0;
+ penum->dev_cache2 = 0;
+ penum->dev_null = 0;
+ penum->index = 0;
+ penum->cc = 0;
+ penum->continue_proc = continue_show;
+ code = (*pfont->procs.init_fstack)(penum, pfont);
+ if ( code < 0 )
+ return code;
+ penum->can_cache = 1; /* show_state_setup may reset */
+ code = show_state_setup(penum);
+ if ( code < 0 )
+ return code;
+ pgs->show_gstate = pgs;
+ return 0;
+}
+
+/* Initialize the gstate-derived parts of a show enumerator. */
+/* We do this both when starting the show operation, */
+/* and when returning from the kshow callout. */
+private int
+show_state_setup(gs_show_enum *penum)
+{ gs_state *pgs = penum->pgs;
+ const gs_font *pfont;
+ if ( penum->fstack.depth <= 0 )
+ { pfont = pgs->font;
+ gs_currentcharmatrix(pgs, NULL, 1); /* make char_tm valid */
+ }
+ else
+ { /* We have to concatenate the parent's FontMatrix as well. */
+ gs_matrix mat;
+ const gx_font_stack_item *pfsi =
+ &penum->fstack.items[penum->fstack.depth];
+ pfont = pfsi->font;
+ gs_matrix_multiply(&pfont->FontMatrix,
+ &pfsi[-1].font->FontMatrix, &mat);
+ gs_setcharmatrix(pgs, &mat);
+ }
+ if ( (penum->can_cache >= 0
+#if !CACHE_ROTATED_CHARS
+ &= /* no skewing or non-rectangular rotation */
+ (is_fzero2(pgs->char_tm.xy, pgs->char_tm.yx) ||
+ is_fzero2(pgs->char_tm.xx, pgs->char_tm.yy))
+#endif
+ ) )
+ { gs_fixed_rect cbox;
+ gx_cpath_inner_box(pgs->clip_path, &cbox);
+ /* Since characters occupy an integral number of pixels, */
+ /* we can (and should) round the inner clipping box */
+ /* outward rather than inward. */
+ penum->ibox.p.x = fixed2int_var(cbox.p.x);
+ penum->ibox.p.y = fixed2int_var(cbox.p.y);
+ penum->ibox.q.x = fixed2int_var_ceiling(cbox.q.x);
+ penum->ibox.q.y = fixed2int_var_ceiling(cbox.q.y);
+ gx_cpath_outer_box(pgs->clip_path, &cbox);
+ penum->obox.p.x = fixed2int_var(cbox.p.x);
+ penum->obox.p.y = fixed2int_var(cbox.p.y);
+ penum->obox.q.x = fixed2int_var_ceiling(cbox.q.x);
+ penum->obox.q.y = fixed2int_var_ceiling(cbox.q.y);
+#if 1 /*USE_FPU <= 0*/
+ if ( pgs->ctm.txy_fixed_valid && pgs->char_tm.txy_fixed_valid )
+ { penum->ftx = (int)fixed2long(pgs->char_tm.tx_fixed -
+ pgs->ctm.tx_fixed);
+ penum->fty = (int)fixed2long(pgs->char_tm.ty_fixed -
+ pgs->ctm.ty_fixed);
+ }
+ else
+#endif
+ { double fdx = pgs->char_tm.tx - pgs->ctm.tx;
+ double fdy = pgs->char_tm.ty - pgs->ctm.ty;
+#define int_bits (arch_sizeof_int * 8 - 1)
+ if ( !(f_fits_in_bits(fdx, int_bits) &&
+ f_fits_in_bits(fdy, int_bits))
+ )
+ return_error(gs_error_limitcheck);
+#undef int_bits
+ penum->ftx = (int)fdx;
+ penum->fty = (int)fdy;
+ }
+ }
+ penum->encode_char = pfont->procs.encode_char;
+ return 0;
+}
+
+/* Set the suggested oversampling scale for character rendering. */
+private void
+show_set_scale(gs_show_enum *penum)
+{ /* Decide whether to oversample. */
+ /* We have to decide this each time setcachedevice is called. */
+ const gs_state *pgs = penum->pgs;
+ if ( penum->charpath_flag == cpm_show &&
+ !penum->stringwidth_flag &&
+ gx_path_is_void_inline(pgs->path) &&
+ /* Oversampling rotated characters doesn't work well. */
+ (is_fzero2(pgs->char_tm.xy, pgs->char_tm.yx) ||
+ is_fzero2(pgs->char_tm.xx, pgs->char_tm.yy))
+ )
+ { const gs_font_base *pfont = (gs_font_base *)pgs->font;
+ gs_fixed_point extent;
+ int code = gs_distance_transform2fixed(&pgs->char_tm,
+ pfont->FontBBox.q.x - pfont->FontBBox.p.x,
+ pfont->FontBBox.q.y - pfont->FontBBox.p.y,
+ &extent);
+ if ( code >= 0 )
+ { int sx =
+ (extent.x == 0 ? 0 :
+ any_abs(extent.x) < int2fixed(25) ? 2 :
+ any_abs(extent.x) < int2fixed(60) ? 1 :
+ 0);
+ int sy =
+ (extent.y == 0 ? 0 :
+ any_abs(extent.y) < int2fixed(25) ? 2 :
+ any_abs(extent.y) < int2fixed(60) ? 1 :
+ 0);
+ /* If we oversample at all, make sure we do it */
+ /* in both X and Y. */
+ if ( sx == 0 && sy != 0 ) sx = 1;
+ else if ( sy == 0 && sx != 0 ) sy = 1;
+ penum->log2_suggested_scale.x = sx;
+ penum->log2_suggested_scale.y = sy;
+ return;
+ }
+ }
+ /* By default, don't scale. */
+ penum->log2_suggested_scale.x =
+ penum->log2_suggested_scale.y = 0;
+}
+
+/* Set up the cache device and related information. */
+/* Note that we always allocate both cache devices, */
+/* even if we only use one of them. */
+private int
+show_cache_setup(gs_show_enum *penum)
+{ gs_state *pgs = penum->pgs;
+ gs_memory_t *mem = pgs->memory;
+ gx_device_memory *dev =
+ gs_alloc_struct(mem, gx_device_memory, &st_device_memory,
+ "show_cache_setup(dev_cache)");
+ gx_device_memory *dev2 =
+ gs_alloc_struct(mem, gx_device_memory, &st_device_memory,
+ "show_cache_setup(dev_cache2)");
+ if ( dev == 0 || dev2 == 0 )
+ { gs_free_object(mem, dev2, "show_cache_setup(dev_cache2)");
+ gs_free_object(mem, dev, "show_cache_setup(dev_cache)");
+ return_error(gs_error_VMerror);
+ }
+ /* We only initialize the device for the sake of the GC, */
+ /* (since we have to re-initialize it as either a mem_mono */
+ /* or a mem_abuf device before actually using it) and also */
+ /* to set its memory pointer. */
+ gs_make_mem_mono_device(dev, mem, gs_currentdevice_inline(pgs));
+ penum->dev_cache = dev;
+ penum->dev_cache2 = dev2;
+ /* Initialize dev2 for the sake of the GC. */
+ *dev2 = *dev;
+ return 0;
+}
+
+/* Set the character origin as the origin of the coordinate system. */
+/* Used before rendering characters, and for moving the origin */
+/* in setcachedevice2 when WMode=1. */
+private int
+show_origin_setup(gs_state *pgs, fixed cpt_x, fixed cpt_y,
+ gs_char_path_mode charpath_flag)
+{ if ( charpath_flag == cpm_show )
+ { /* Round the translation in the graphics state. */
+ /* This helps prevent rounding artifacts later. */
+ cpt_x = fixed_rounded(cpt_x);
+ cpt_y = fixed_rounded(cpt_y);
+ }
+ /*
+ * BuildChar procedures expect the current point to be undefined,
+ * so we omit the gx_path_add_point with ctm.t*_fixed.
+ */
+ return gx_translate_to_fixed(pgs, cpt_x, cpt_y);
+}
+
+/* Default fstack initialization procedure. */
+int
+gs_default_init_fstack(gs_show_enum *penum, gs_font *pfont)
+{ penum->fstack.depth = -1;
+ return 0;
+}
+
+/* Default next-character procedure. */
+int
+gs_default_next_char(gs_show_enum *penum, gs_char *pchr)
+{ if ( penum->index == penum->str.size )
+ return 2;
+ else
+ { *pchr = penum->str.data[penum->index++];
+ return 0;
+ }
+}
diff --git a/pstoraster/gschar.h b/pstoraster/gschar.h
new file mode 100644
index 000000000..88bf2ddc5
--- /dev/null
+++ b/pstoraster/gschar.h
@@ -0,0 +1,120 @@
+/* Copyright (C) 1989, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gschar.h */
+/* Client interface to character operations */
+#include "gsccode.h"
+#include "gscpm.h"
+
+/* String display, like image display, uses an enumeration structure */
+/* to keep track of what's going on (aka 'poor man's coroutine'). */
+#ifndef gs_show_enum_DEFINED
+# define gs_show_enum_DEFINED
+typedef struct gs_show_enum_s gs_show_enum;
+#endif
+
+/* Define an opaque type for fonts if necessary. */
+#ifndef gs_font_DEFINED
+# define gs_font_DEFINED
+typedef struct gs_font_s gs_font;
+#endif
+
+/* Allocate an enumerator. */
+gs_show_enum *gs_show_enum_alloc(P3(gs_memory_t *, gs_state *, client_name_t));
+/* Release the contents of an enumerator. */
+/* (This happens automatically if the enumeration finishes normally.) */
+/* If the second argument is not NULL, also free the enumerator. */
+void gs_show_enum_release(P2(gs_show_enum *, gs_memory_t *));
+
+/* The initialization routines all come in two versions, */
+/* one that uses the C convention of null-terminated strings, */
+/* and one that supplies a length. */
+int gs_show_init(P3(gs_show_enum *, gs_state *, const char *)),
+ gs_show_n_init(P4(gs_show_enum *, gs_state *, const char *, uint)),
+ gs_ashow_init(P5(gs_show_enum *, gs_state *, floatp, floatp, const char *)),
+ gs_ashow_n_init(P6(gs_show_enum *, gs_state *, floatp, floatp, const char *, uint)),
+ gs_widthshow_init(P6(gs_show_enum *, gs_state *, floatp, floatp, gs_char, const char *)),
+ gs_widthshow_n_init(P7(gs_show_enum *, gs_state *, floatp, floatp, gs_char, const char *, uint)),
+ gs_awidthshow_init(P8(gs_show_enum *, gs_state *, floatp, floatp, gs_char, floatp, floatp, const char *)),
+ gs_awidthshow_n_init(P9(gs_show_enum *, gs_state *, floatp, floatp, gs_char, floatp, floatp, const char *, uint)),
+ gs_kshow_init(P3(gs_show_enum *, gs_state *, const char *)),
+ gs_kshow_n_init(P4(gs_show_enum *, gs_state *, const char *, uint)),
+ gs_xyshow_init(P3(gs_show_enum *, gs_state *, const char *)),
+ gs_xyshow_n_init(P4(gs_show_enum *, gs_state *, const char *, uint)),
+ gs_glyphshow_init(P3(gs_show_enum *, gs_state *, gs_glyph)),
+ gs_cshow_init(P3(gs_show_enum *, gs_state *, const char *)),
+ gs_cshow_n_init(P4(gs_show_enum *, gs_state *, const char *, uint)),
+ gs_stringwidth_init(P3(gs_show_enum *, gs_state *, const char *)),
+ gs_stringwidth_n_init(P4(gs_show_enum *, gs_state *, const char *, uint)),
+ gs_charpath_init(P4(gs_show_enum *, gs_state *, const char *, bool)),
+ gs_charpath_n_init(P5(gs_show_enum *, gs_state *, const char *, uint, bool)),
+ gs_charboxpath_init(P4(gs_show_enum *, gs_state *, const char *, bool)),
+ gs_charboxpath_n_init(P5(gs_show_enum *, gs_state *, const char *, uint, bool));
+
+/* After setting up the enumeration, all the string-related routines */
+/* work the same way. The client calls gs_show_next until it returns */
+/* a zero (successful completion) or negative (error) value. */
+/* Other values indicate the following situations: */
+
+ /* The client must render a character: obtain the code from */
+ /* gs_show_current_char, do whatever is necessary, and then */
+ /* call gs_show_next again. */
+#define gs_show_render 1
+
+ /* The client has asked to intervene between characters (kshow). */
+ /* Obtain the previous and next codes from gs_kshow_previous_char */
+ /* and gs_kshow_next_char, do whatever is necessary, and then */
+ /* call gs_show_next again. */
+#define gs_show_kern 2
+
+ /* The client has asked to handle characters individually */
+ /* (xshow, yshow, xyshow, cshow). Obtain the current code */
+ /* from gs_show_current_char, do whatever is necessary, and then */
+ /* call gs_show_next again. */
+#define gs_show_move 3
+
+int gs_show_next(P1(gs_show_enum *));
+gs_char
+ gs_show_current_char(P1(const gs_show_enum *)),
+ gs_kshow_previous_char(P1(const gs_show_enum *)),
+ gs_kshow_next_char(P1(const gs_show_enum *));
+gs_font *
+ gs_show_current_font(P1(const gs_show_enum *));
+gs_glyph
+ gs_show_current_glyph(P1(const gs_show_enum *));
+int gs_show_current_width(P2(const gs_show_enum *, gs_point *));
+void gs_show_width(P2(const gs_show_enum *, gs_point *)); /* cumulative width */
+gs_char_path_mode
+ gs_show_in_charpath(P1(const gs_show_enum *)); /* return charpath flag */
+
+/* Character cache and metrics operators. */
+/* gs_setcachedevice[2] return 1 iff the cache device was just installed. */
+int gs_setcachedevice(P3(gs_show_enum *, gs_state *, const float * /*[6]*/));
+int gs_setcachedevice2(P3(gs_show_enum *, gs_state *, const float * /*[10]*/));
+int gs_setcharwidth(P4(gs_show_enum *, gs_state *, floatp, floatp));
+/* Return true if we only need the width from the rasterizer */
+/* and can short-circuit the full rendering of the character, */
+/* false if we need the actual character bits. */
+/* This is only meaningful just before calling gs_setcharwidth or */
+/* gs_setcachedevice[2]. */
+bool gs_show_width_only(P1(const gs_show_enum *));
diff --git a/pstoraster/gschar0.c b/pstoraster/gschar0.c
new file mode 100644
index 000000000..bbdb94ea2
--- /dev/null
+++ b/pstoraster/gschar0.c
@@ -0,0 +1,342 @@
+/* Copyright (C) 1991, 1992, 1993 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gschar0.c */
+/* Composite font decoding for Ghostscript library */
+#include "memory_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gsstruct.h"
+#include "gxfixed.h"
+#include "gxdevice.h"
+#include "gxdevmem.h" /* for gxchar.h */
+#include "gxchar.h"
+#include "gxfont.h"
+#include "gxfont0.h"
+
+/* Stack up modal composite fonts, down to a non-modal or base font. */
+private int
+gs_stack_modal_fonts(gs_show_enum *penum)
+{ int fdepth = penum->fstack.depth;
+ gs_font *cfont = penum->fstack.items[fdepth].font;
+#define cmfont ((gs_font_type0 *)cfont)
+ while ( cfont->FontType == ft_composite &&
+ fmap_type_is_modal(cmfont->data.FMapType)
+ )
+ { if ( fdepth == max_font_depth )
+ return_error(gs_error_invalidfont);
+ fdepth++;
+ cfont = cmfont->data.FDepVector[cmfont->data.Encoding[0]];
+ penum->fstack.items[fdepth].font = cfont;
+ penum->fstack.items[fdepth].index = 0;
+ if_debug2('j', "[j]stacking depth=%d font=0x%lx\n",
+ fdepth, (ulong)cfont);
+ }
+ penum->fstack.depth = fdepth;
+ return 0;
+#undef cmfont
+}
+/* Initialize the composite font stack for a show enumerator. */
+int
+gs_type0_init_fstack(gs_show_enum *penum, gs_font *pfont)
+{ if_debug1('j', "[j]stacking depth=0 font=0x%lx\n",
+ (ulong)pfont);
+ penum->fstack.depth = 0;
+ penum->fstack.items[0].font = pfont;
+ penum->fstack.items[0].index = 0;
+ return gs_stack_modal_fonts(penum);
+}
+
+/* Select the appropriate descendant of a font. */
+/* Uses free variables: penum. */
+/* Uses pdata, uses & updates fdepth, sets pfont. */
+#define select_descendant(pfont, pdata, fidx, fdepth)\
+ if ( fidx >= pdata->encoding_size )\
+ return_error(gs_error_rangecheck);\
+ if ( fdepth == max_font_depth )\
+ return_error(gs_error_invalidfont);\
+ pfont = pdata->FDepVector[pdata->Encoding[fidx]];\
+ if ( ++fdepth > orig_depth || pfont != penum->fstack.items[fdepth].font )\
+ penum->fstack.items[fdepth].font = pfont,\
+ changed = 1;\
+ penum->fstack.items[fdepth].index = fidx
+
+/* Get the next character from a composite string. */
+/* If we run off the end of the string in the middle of a */
+/* multi-byte sequence, return gs_error_rangecheck. */
+/* If the string is empty, return 2. */
+/* If the current (base) font changed, return 1. Otherwise, return 0. */
+int
+gs_type0_next_char(register gs_show_enum *penum, gs_char *pchr)
+{ const byte *p = penum->str.data + penum->index;
+ const byte *end = penum->str.data + penum->str.size;
+ int fdepth = penum->fstack.depth;
+ int orig_depth = fdepth;
+ gs_font *pfont;
+#define pfont0 ((gs_font_type0 *)pfont)
+ gs_type0_data *pdata;
+ uint fidx;
+ gs_char chr;
+ int changed = 0;
+#define need_left(n)\
+ if ( end - p < n ) return_error(gs_error_rangecheck)
+#define root_EscChar\
+ (((gs_font_type0 *)(penum->fstack.items[0].font))->data.EscChar) /* root overrides */
+
+ /*
+ * Although the Adobe documentation doesn't say anything about this,
+ * if the root font is modal and the very first character of the
+ * string being decoded is an escape or shift character, then
+ * font selection via the escape mechanism works down from the root,
+ * rather than up from the lowest modal font. (This was first
+ * reported by Norio Katayama, and confirmed by someone at Adobe.)
+ */
+
+ if ( penum->index == 0 )
+ { int idepth = 0;
+ pfont = penum->fstack.items[0].font;
+ for ( ; pfont->FontType == ft_composite; )
+ { fmap_type fmt = (pdata = &pfont0->data)->FMapType;
+ if ( p == end )
+ return 2;
+ chr = *p;
+ switch ( fmt )
+ {
+ case fmap_escape:
+ if ( chr != root_EscChar )
+ break;
+ need_left(2);
+ fidx = p[1];
+ p += 2;
+ if_debug1('j', "[j]from root: escape %d\n", fidx);
+rdown: select_descendant(pfont, pdata, fidx, idepth);
+ if_debug2('j', "[j]... new depth=%d, new font=0x%lx\n",
+ idepth, (ulong)pfont);
+ continue;
+ case fmap_double_escape:
+ if ( chr != root_EscChar )
+ break;
+ need_left(2);
+ fidx = p[1];
+ p += 2;
+ if ( fidx == chr )
+ { need_left(1);
+ fidx = *p++ + 256;
+ }
+ if_debug1('j', "[j]from root: double escape %d\n", fidx);
+ goto rdown;
+ case fmap_shift:
+ if ( chr == pdata->ShiftIn )
+ fidx = 0;
+ else if ( chr == pdata->ShiftOut )
+ fidx = 1;
+ else break;
+ p++;
+ if_debug1('j', "[j]from root: shift %d\n", fidx);
+ goto rdown;
+ default:
+ break;
+ }
+ break;
+ }
+ /* If we saw any initial escapes or shifts, */
+ /* compute a new initial base font. */
+ if ( idepth != 0 )
+ { int code;
+ penum->fstack.depth = idepth;
+ code = gs_stack_modal_fonts(penum);
+ if ( code < 0 )
+ return code;
+ if ( penum->fstack.depth > idepth )
+ changed = 1;
+ orig_depth = fdepth = penum->fstack.depth;
+ }
+ }
+
+ /* Handle initial escapes or shifts. */
+
+up: if ( p == end )
+ return 2;
+ chr = *p;
+ while ( fdepth > 0 )
+ { pfont = penum->fstack.items[fdepth - 1].font;
+ pdata = &pfont0->data;
+ switch ( pdata->FMapType )
+ {
+ default: /* non-modal */
+ fdepth--;
+ continue;
+
+ case fmap_escape:
+ if ( chr != root_EscChar )
+ break;
+ need_left(2);
+ fidx = *++p;
+ if_debug1('j', "[j]next: escape %d\n", fidx);
+ /* Per Adobe, if we get an escape at the root, */
+ /* treat it as an ordinary character (font index). */
+ if ( fidx == chr && fdepth > 1 )
+ { fdepth--;
+ goto up;
+ }
+down: if ( ++p == end )
+ return 2;
+ chr = *p;
+ fdepth--;
+ do
+ { select_descendant(pfont, pdata, fidx, fdepth);
+ if_debug3('j', "[j]down from modal: new depth=%d, index=%d, new font=0x%lx\n",
+ fdepth, fidx, (ulong)pfont);
+ if ( pfont->FontType != ft_composite )
+ break;
+ pdata = &pfont0->data;
+ fidx = 0;
+ }
+ while ( pdata->FMapType == fmap_escape );
+ continue;
+
+ case fmap_double_escape:
+ if ( chr != root_EscChar )
+ break;
+ need_left(2);
+ fidx = *++p;
+ if ( fidx == chr )
+ { need_left(2);
+ fidx = *++p + 256;
+ }
+ if_debug1('j', "[j]next: double escape %d\n", fidx);
+ goto down;
+
+ case fmap_shift:
+ if ( chr == pdata->ShiftIn )
+ fidx = 0;
+ else if ( chr == pdata->ShiftOut )
+ fidx = 1;
+ else break;
+ if_debug1('j', "[j]next: shift %d\n", fidx);
+ goto down;
+ }
+ break;
+ }
+ p++;
+
+ /*
+ * Now handle non-modal descendants.
+ * The PostScript language manual has some confusing
+ * wording about the parent supplying the "first part"
+ * of the child's decoding information; what this means
+ * is not (as one might imagine) the font index, but
+ * simply the first byte of the data.
+ */
+
+ while ( (pfont = penum->fstack.items[fdepth].font)->FontType == ft_composite )
+ { pdata = &pfont0->data;
+ switch ( pdata->FMapType )
+ {
+ default: /* can't happen */
+ return_error(gs_error_invalidfont);
+
+ case fmap_8_8:
+ need_left(1);
+ fidx = chr;
+ chr = *p++;
+ if_debug2('J', "[J]8/8 index=%d, char=%ld\n",
+ fidx, chr);
+ break;
+
+ case fmap_1_7:
+ fidx = chr >> 7;
+ chr &= 0x7f;
+ if_debug2('J', "[J]1/7 index=%d, char=%ld\n",
+ fidx, chr);
+ break;
+
+ case fmap_9_7:
+ need_left(1);
+ fidx = ((uint)chr << 1) + (*p >> 7);
+ chr = *p & 0x7f;
+ if_debug2('J', "[J]9/7 index=%d, char=%ld\n",
+ fidx, chr);
+ p++;
+ break;
+
+ case fmap_SubsVector:
+ { int width = pdata->subs_width;
+ uint subs_count = pdata->subs_size;
+ const byte *psv = pdata->SubsVector.data;
+#define subs_loop(subs_elt, width)\
+ while ( subs_count != 0 && tchr >= (schr = subs_elt) )\
+ subs_count--, tchr -= schr, psv += width;\
+ chr = tchr; p += width - 1; break
+ switch ( width )
+ {
+ default: /* can't happen */
+ return_error(gs_error_invalidfont);
+ case 1:
+ { byte tchr = (byte)chr, schr;
+ subs_loop(*psv, 1);
+ }
+ case 2:
+ need_left(1);
+#define w2(p) (((ushort)*p << 8) + p[1])
+ { ushort tchr = ((ushort)chr << 8) + *p, schr;
+ subs_loop(w2(psv), 2);
+ }
+ case 3:
+ need_left(2);
+#define w3(p) (((ulong)*p << 16) + ((uint)p[1] << 8) + p[2])
+ { ulong tchr = ((ulong)chr << 16) + w2(p), schr;
+ subs_loop(w3(psv), 3);
+ }
+ case 4:
+ need_left(3);
+#define w4(p) (((ulong)*p << 24) + ((ulong)p[1] << 16) + ((uint)p[2] << 8) + p[3])
+ { ulong tchr = ((ulong)chr << 24) + w3(p), schr;
+ subs_loop(w4(psv), 4);
+ }
+#undef w2
+#undef w3
+#undef w4
+#undef subs_loop
+ }
+ fidx = pdata->subs_size - subs_count;
+ if_debug2('J', "[J]SubsVector index=%d, char=%ld\n",
+ fidx, chr);
+ break;
+ }
+
+ }
+
+ select_descendant(pfont, pdata, fidx, fdepth);
+ if_debug2('J', "... new depth=%d, new font=0x%lx\n",
+ fdepth, (ulong)pfont);
+ }
+ *pchr = chr;
+ penum->index = p - penum->str.data;
+ penum->fstack.depth = fdepth;
+ if_debug4('J', "[J]depth=%d font=0x%lx index=%d changed=%d\n",
+ fdepth, (ulong)penum->fstack.items[fdepth].font,
+ penum->fstack.items[fdepth].index, changed);
+ return changed;
+#undef pfont0
+}
diff --git a/pstoraster/gscie.c b/pstoraster/gscie.c
new file mode 100644
index 000000000..76e5c79cc
--- /dev/null
+++ b/pstoraster/gscie.c
@@ -0,0 +1,1206 @@
+/* Copyright (C) 1992, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gscie.c */
+/* CIE color rendering for Ghostscript */
+#include "math_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gsstruct.h"
+#include "gsmatrix.h" /* for gscolor2.h */
+#include "gxcspace.h"
+#include "gscolor2.h" /* for gs_set/currentcolorrendering */
+#include "gscie.h"
+#include "gxarith.h"
+#include "gxdevice.h" /* for gxcmap.h */
+#include "gxcmap.h"
+#include "gzstate.h"
+
+/* Forward references */
+private void cie_joint_caches_init(P3(gx_cie_joint_caches *,
+ const gs_cie_common *, const gs_cie_render *));
+private void cie_joint_caches_complete(P3(gx_cie_joint_caches *,
+ const gs_cie_common *, const gs_cie_render *));
+private void near cie_mult3(P3(const gs_vector3 *, const gs_matrix3 *,
+ gs_vector3 *));
+private void near cie_matrix_mult3(P3(const gs_matrix3 *, const gs_matrix3 *,
+ gs_matrix3 *));
+private void near cie_invert3(P2(const gs_matrix3 *, gs_matrix3 *));
+private void near cie_matrix_init(P1(gs_matrix3 *));
+
+#define set_restrict_index(i, v, n)\
+ if ( (uint)(i = (int)(v)) >= (n) )\
+ i = (i < 0 ? 0 : (n) - 1)
+#define restrict_index(v, n, itemp)\
+ ((uint)(itemp = (int)(v)) >= (n) ?\
+ (itemp < 0 ? 0 : (n) - 1) : itemp)
+
+/* Compute a cache index as (vin - base) * factor. */
+/* vin, base, factor, and the result are cie_cached_values. */
+/* We know that the result doesn't exceed (gx_cie_cache_size - 1) << fbits. */
+#define lookup_index(vin, pcache, fbits)\
+ ((vin) <= (pcache)->vecs.params.base ? 0 :\
+ (vin) >= (pcache)->vecs.params.limit ? (gx_cie_cache_size - 1) << (fbits) :\
+ cie_cached_product2int( ((vin) - (pcache)->vecs.params.base),\
+ (pcache)->vecs.params.factor, fbits ))
+#define lookup_value(vin, pcache)\
+ ((pcache)->vecs.values[lookup_index(vin, pcache, 0)])
+
+#define if_restrict(v, range)\
+ if ( (v) < (range).rmin ) v = (range).rmin;\
+ else if ( (v) > (range).rmax ) v = (range).rmax
+
+/* Define the template for loading a cache. */
+/* If we had parameterized types, or a more flexible type system, */
+/* this could be done with a single procedure. */
+#define cie_cache_init3(pcache3, plp3, prange3, cname)\
+ gs_cie_cache_init(&(pcache3)->floats.params, &(plp3)[0], &(prange3)[0], cname);\
+ gs_cie_cache_init(&(pcache3)[1].floats.params, &(plp3)[1], &(prange3)[1], cname);\
+ gs_cie_cache_init(&(pcache3)[2].floats.params, &(plp3)[2], &(prange3)[2], cname)
+#define CIE_LOAD_CACHE3_BODY(pcache, domains, rprocs, pcie, cname)\
+{ int i, j;\
+ gs_for_loop_params lp[3];\
+ cie_cache_init3(pcache, lp, domains, cname);\
+ for ( i = 0; i < gx_cie_cache_size; i++ )\
+ for ( j = 0; j < 3; lp[j].init += lp[j].step, j++ )\
+ pcache[j].floats.values[i] =\
+ (*(rprocs)->procs[j])(lp[j].init, pcie);\
+}
+
+/* ================ Color space definition ================ */
+
+/* Allocator structure types */
+private_st_joint_caches();
+private_st_const_string();
+public_st_const_string_element();
+#define sptr ((gs_const_string *)vptr)
+private ENUM_PTRS_BEGIN(const_string_enum_ptrs) return 0;
+ case 0: *pep = (void *)sptr; return ptr_const_string_type;
+} }
+private RELOC_PTRS_BEGIN(const_string_reloc_ptrs) {
+ gs_reloc_const_string(sptr, gcst);
+} RELOC_PTRS_END
+#undef sptr
+
+/* Define the CIE color space types. */
+/* We use CIExxx rather than CIEBasedxxx in some places because */
+/* gcc under VMS only retains 23 characters of procedure names, */
+/* and DEC C truncates all identifiers at 31 characters. */
+private cs_proc_concrete_space(gx_concrete_space_CIE);
+cs_declare_procs(private, gx_concretize_CIEDEFG, gx_install_CIEDEFG,
+ gx_adjust_cspace_CIEDEFG,
+ gx_enum_ptrs_CIEDEFG, gx_reloc_ptrs_CIEDEFG);
+cs_declare_procs(private, gx_concretize_CIEDEF, gx_install_CIEDEF,
+ gx_adjust_cspace_CIEDEF,
+ gx_enum_ptrs_CIEDEF, gx_reloc_ptrs_CIEDEF);
+private cs_proc_remap_color(gx_remap_CIEABC);
+cs_declare_procs(private, gx_concretize_CIEABC, gx_install_CIEABC,
+ gx_adjust_cspace_CIEABC,
+ gx_enum_ptrs_CIEABC, gx_reloc_ptrs_CIEABC);
+cs_declare_procs(private, gx_concretize_CIEA, gx_install_CIEA,
+ gx_adjust_cspace_CIEA,
+ gx_enum_ptrs_CIEA, gx_reloc_ptrs_CIEA);
+const gs_color_space_type
+ gs_color_space_type_CIEDEFG =
+ { gs_color_space_index_CIEDEFG, 3, true,
+ gx_init_paint_3, gx_concrete_space_CIE,
+ gx_concretize_CIEDEFG, NULL,
+ gx_default_remap_color, gx_install_CIEDEFG,
+ gx_adjust_cspace_CIEDEFG, gx_no_adjust_color_count,
+ gx_enum_ptrs_CIEDEFG, gx_reloc_ptrs_CIEDEFG
+ },
+ gs_color_space_type_CIEDEF =
+ { gs_color_space_index_CIEDEF, 3, true,
+ gx_init_paint_3, gx_concrete_space_CIE,
+ gx_concretize_CIEDEF, NULL,
+ gx_default_remap_color, gx_install_CIEDEF,
+ gx_adjust_cspace_CIEDEF, gx_no_adjust_color_count,
+ gx_enum_ptrs_CIEDEF, gx_reloc_ptrs_CIEDEF
+ },
+ gs_color_space_type_CIEABC =
+ { gs_color_space_index_CIEABC, 3, true,
+ gx_init_paint_3, gx_concrete_space_CIE,
+ gx_concretize_CIEABC, NULL,
+ gx_remap_CIEABC, gx_install_CIEABC,
+ gx_adjust_cspace_CIEABC, gx_no_adjust_color_count,
+ gx_enum_ptrs_CIEABC, gx_reloc_ptrs_CIEABC
+ },
+ gs_color_space_type_CIEA =
+ { gs_color_space_index_CIEA, 1, true,
+ gx_init_paint_1, gx_concrete_space_CIE,
+ gx_concretize_CIEA, NULL,
+ gx_default_remap_color, gx_install_CIEA,
+ gx_adjust_cspace_CIEA, gx_no_adjust_color_count,
+ gx_enum_ptrs_CIEA, gx_reloc_ptrs_CIEA
+ };
+
+/* GC procedures for CIE color spaces. */
+/* These have to come after the cs_declare_procs because of */
+/* a bug in the VAX C compiler. */
+
+#define pcs ((gs_color_space *)vptr)
+
+private ENUM_PTRS_BEGIN(gx_enum_ptrs_CIEDEFG) return 0;
+ ENUM_PTR(0, gs_color_space, params.defg);
+ENUM_PTRS_END
+private RELOC_PTRS_BEGIN(gx_reloc_ptrs_CIEDEFG) {
+ RELOC_PTR(gs_color_space, params.defg);
+} RELOC_PTRS_END
+
+private ENUM_PTRS_BEGIN(gx_enum_ptrs_CIEDEF) return 0;
+ ENUM_PTR(0, gs_color_space, params.def);
+ENUM_PTRS_END
+private RELOC_PTRS_BEGIN(gx_reloc_ptrs_CIEDEF) {
+ RELOC_PTR(gs_color_space, params.def);
+} RELOC_PTRS_END
+
+private ENUM_PTRS_BEGIN(gx_enum_ptrs_CIEABC) return 0;
+ ENUM_PTR(0, gs_color_space, params.abc);
+ENUM_PTRS_END
+private RELOC_PTRS_BEGIN(gx_reloc_ptrs_CIEABC) {
+ RELOC_PTR(gs_color_space, params.abc);
+} RELOC_PTRS_END
+
+private ENUM_PTRS_BEGIN(gx_enum_ptrs_CIEA) return 0;
+ ENUM_PTR(0, gs_color_space, params.a);
+ENUM_PTRS_END
+private RELOC_PTRS_BEGIN(gx_reloc_ptrs_CIEA) {
+ RELOC_PTR(gs_color_space, params.a);
+} RELOC_PTRS_END
+
+#undef pcs
+
+/* ------ Default values for CIE dictionary elements ------ */
+
+/* Default transformation procedures. */
+
+private float
+a_identity(floatp in, const gs_cie_a *pcie)
+{ return in;
+}
+private float
+abc_identity(floatp in, const gs_cie_abc *pcie)
+{ return in;
+}
+private float
+def_identity(floatp in, const gs_cie_def *pcie)
+{ return in;
+}
+private float
+defg_identity(floatp in, const gs_cie_defg *pcie)
+{ return in;
+}
+private float
+common_identity(floatp in, const gs_cie_common *pcie)
+{ return in;
+}
+private float
+render_identity(floatp in, const gs_cie_render *pcie)
+{ return in;
+}
+private float
+tpqr_identity(floatp in, const gs_cie_wbsd *pwbsd, const gs_cie_render *pcie)
+{ return in;
+}
+private frac
+render_table_identity(byte in, const gs_cie_render *pcie)
+{ return byte2frac(in);
+}
+
+/* Default vectors and matrices. */
+
+const gs_range3 Range3_default = { {{0,1}, {0,1}, {0,1}} };
+const gs_range4 Range4_default = { {{0,1}, {0,1}, {0,1}, {0,1}} };
+const gs_cie_defg_proc4 DecodeDEFG_default =
+ { { defg_identity, defg_identity, defg_identity, defg_identity } };
+const gs_cie_def_proc3 DecodeDEF_default =
+ { { def_identity, def_identity, def_identity } };
+const gs_cie_abc_proc3 DecodeABC_default =
+ { { abc_identity, abc_identity, abc_identity } };
+const gs_cie_common_proc3 DecodeLMN_default =
+ { { common_identity, common_identity, common_identity } };
+const gs_matrix3 Matrix3_default = { {1,0,0}, {0,1,0}, {0,0,1}, 1 /*true*/ };
+const gs_range RangeA_default = {0,1};
+const gs_cie_a_proc DecodeA_default = a_identity;
+const gs_vector3 MatrixA_default = { 1, 1, 1 };
+const gs_vector3 BlackPoint_default = { 0, 0, 0 };
+const gs_cie_render_proc3 Encode_default =
+ { { render_identity, render_identity, render_identity } };
+const gs_cie_transform_proc3 TransformPQR_default =
+ { { tpqr_identity, tpqr_identity, tpqr_identity } };
+const gs_cie_render_table_procs RenderTableT_default =
+ { { render_table_identity, render_table_identity, render_table_identity,
+ render_table_identity
+ } };
+
+/* ------ Adjust reference counts for a CIE color space ------ */
+
+private void
+gx_adjust_cspace_CIEDEFG(const gs_color_space *pcs, gs_state *pgs, int delta)
+{ rc_adjust_const(pcs->params.defg, delta, pgs->memory,
+ "gx_adjust_cspace_CIEDEFG");
+}
+
+private void
+gx_adjust_cspace_CIEDEF(const gs_color_space *pcs, gs_state *pgs, int delta)
+{ rc_adjust_const(pcs->params.def, delta, pgs->memory,
+ "gx_adjust_cspace_CIEDEF");
+}
+
+private void
+gx_adjust_cspace_CIEABC(const gs_color_space *pcs, gs_state *pgs, int delta)
+{ rc_adjust_const(pcs->params.abc, delta, pgs->memory,
+ "gx_adjust_cspace_CIEABC");
+}
+
+private void
+gx_adjust_cspace_CIEA(const gs_color_space *pcs, gs_state *pgs, int delta)
+{ rc_adjust_const(pcs->params.a, delta, pgs->memory,
+ "gx_adjust_cspace_CIEA");
+}
+
+/* ================ Table setup ================ */
+
+/* ------ Install a CIE color space ------ */
+
+private int cie_load_common_cache(P3(gs_cie_common *, gs_state *,
+ client_name_t));
+private void near cie_cache_mult(P3(gx_cie_vector_cache *, const gs_vector3 *,
+ const cie_cache_floats *));
+private bool near cie_cache_mult3(P2(gx_cie_vector_cache *,
+ const gs_matrix3 *));
+
+private int
+gx_install_CIEDEFG(gs_color_space *pcs, gs_state *pgs)
+{ /****** NOT IMPLEMENTED YET ******/
+ return_error(gs_error_undefined);
+}
+
+private int
+gx_install_CIEDEF(gs_color_space *pcs, gs_state *pgs)
+{ /****** NOT IMPLEMENTED YET ******/
+ return_error(gs_error_undefined);
+}
+
+private int
+gx_install_CIEABC(gs_color_space *pcs, gs_state *pgs)
+{ gs_cie_abc *pcie = pcs->params.abc;
+ cie_matrix_init(&pcie->MatrixABC);
+ CIE_LOAD_CACHE3_BODY(pcie->caches.DecodeABC, pcie->RangeABC.ranges,
+ &pcie->DecodeABC, pcie, "DecodeABC");
+ gs_cie_abc_complete(pcie);
+ return cie_load_common_cache(&pcie->common, pgs, "gx_install_CIEABC");
+}
+
+private int
+gx_install_CIEA(gs_color_space *pcs, gs_state *pgs)
+{ gs_cie_a *pcie = pcs->params.a;
+ int i;
+ gs_for_loop_params lp;
+ float in;
+ gs_cie_cache_init(&pcie->caches.DecodeA.floats.params, &lp,
+ &pcie->RangeA, "DecodeA");
+ for ( i = 0, in = lp.init; i < gx_cie_cache_size; in += lp.step, i++ )
+ pcie->caches.DecodeA.floats.values[i] =
+ (*pcie->DecodeA)(in, pcie);
+ gs_cie_a_complete(pcie);
+ return cie_load_common_cache(&pcie->common, pgs, "gx_install_CIEA");
+}
+
+/* Load the common caches when installing the color space. */
+private int
+cie_load_common_cache(gs_cie_common *pcie, gs_state *pgs, client_name_t cname)
+{ gx_cie_joint_caches *pjc;
+
+ cie_matrix_init(&pcie->MatrixLMN);
+ CIE_LOAD_CACHE3_BODY(pcie->caches.DecodeLMN, pcie->RangeLMN.ranges,
+ &pcie->DecodeLMN, pcie, "DecodeLMN")
+ if ( pgs->cie_render == 0 )
+ return 0;
+ pjc = gx_currentciecaches(pgs);
+ if ( pjc == 0 )
+ return_error(gs_error_VMerror);
+ cie_joint_caches_init(pjc, pcie, pgs->cie_render);
+ cie_joint_caches_complete(pjc, pcie, pgs->cie_render);
+ return 0;
+}
+
+/* Complete loading a CIEBasedABC color space. */
+/* This routine is not idempotent. */
+void
+gs_cie_abc_complete(gs_cie_abc *pcie)
+{ pcie->caches.skipABC =
+ cie_cache_mult3(pcie->caches.DecodeABC, &pcie->MatrixABC);
+}
+
+/* Complete loading a CIEBasedA color space. */
+/* This routine is not idempotent. */
+void
+gs_cie_a_complete(gs_cie_a *pcie)
+{ cie_cache_mult(&pcie->caches.DecodeA, &pcie->MatrixA,
+ &pcie->caches.DecodeA.floats);
+}
+
+/* Convert a scalar cache to a vector cache by multiplying */
+/* the scalar values by a vector. */
+private void near
+cie_cache_mult(gx_cie_vector_cache *pcache, const gs_vector3 *pvec,
+ const cie_cache_floats *pcf)
+{ int i;
+ cie_vector_cache_params params;
+ params.is_identity = pcf->params.is_identity;
+ params.base = float2cie_cached(pcf->params.base);
+ params.factor = float2cie_cached(pcf->params.factor);
+ params.limit =
+ float2cie_cached((gx_cie_cache_size - 1) / pcf->params.factor +
+ pcf->params.base);
+ /* Loop from top to bottom so that we don't */
+ /* overwrite elements before they're used, */
+ /* in case pcf is an alias for pcache->floats. */
+ for ( i = gx_cie_cache_size; --i >= 0; )
+ { float f = pcf->values[i];
+ pcache->vecs.values[i].u = float2cie_cached(f * pvec->u);
+ pcache->vecs.values[i].v = float2cie_cached(f * pvec->v);
+ pcache->vecs.values[i].w = float2cie_cached(f * pvec->w);
+ }
+ pcache->vecs.params = params;
+}
+/* Convert 3 scalar caches to vector caches by multiplying by a matrix. */
+/* Return true iff the resulting cache is an identity transformation. */
+private bool near
+cie_cache_mult3(gx_cie_vector_cache *pc /*[3]*/, const gs_matrix3 *pmat)
+{ cie_cache_mult(pc, &pmat->cu, &pc->floats);
+ cie_cache_mult(pc + 1, &pmat->cv, &pc[1].floats);
+ cie_cache_mult(pc + 2, &pmat->cw, &pc[2].floats);
+ return pmat->is_identity & pc[0].vecs.params.is_identity &
+ pc[1].vecs.params.is_identity & pc[2].vecs.params.is_identity;
+}
+
+/* ------ Install a rendering dictionary ------ */
+
+/* setcolorrendering */
+int
+gs_setcolorrendering(gs_state *pgs, gs_cie_render *pcie)
+{ int code = gs_cie_render_init(pcie);
+ if ( code < 0 )
+ return code;
+ rc_assign(pgs->cie_render, pcie, pgs->memory,
+ "gs_setcolorrendering");
+ /* Load the caches. */
+ CIE_LOAD_CACHE3_BODY(pcie->caches.EncodeLMN, pcie->DomainLMN.ranges,
+ &pcie->EncodeLMN, pcie, "EncodeLMN");
+ CIE_LOAD_CACHE3_BODY(pcie->caches.EncodeABC, pcie->DomainABC.ranges,
+ &pcie->EncodeABC, pcie, "EncodeABC");
+ if ( pcie->RenderTable.lookup.table != 0 )
+ { int i, j, m = pcie->RenderTable.lookup.m;
+ gs_for_loop_params flp;
+ for ( j = 0; j < m; j++ )
+ gs_cie_cache_init(&pcie->caches.RenderTableT[j].fracs.params,
+ &flp, &Range3_default.ranges[0],
+ "RenderTableT");
+ /****** ASSUMES gx_cie_cache_size >= 256 ******/
+ for ( i = 0; i < 256; i++ )
+ for ( j = 0; j < m; j++ )
+ pcie->caches.RenderTableT[j].fracs.values[i] =
+ (*pcie->RenderTable.T.procs[j])((byte)i, pcie);
+ }
+ code = gs_cie_render_complete(pcie);
+ if ( code < 0 )
+ return code;
+ /* Initialize the joint caches if needed. */
+ gs_cie_cs_complete(pgs, true);
+ gx_unset_dev_color(pgs);
+ return code;
+}
+
+/* currentcolorrendering */
+const gs_cie_render *
+gs_currentcolorrendering(const gs_state *pgs)
+{ return pgs->cie_render;
+}
+
+/* Unshare (allocating if necessary) the joint caches. */
+gx_cie_joint_caches *
+gx_currentciecaches(gs_state *pgs)
+{ rc_unshare_struct(pgs->cie_joint_caches, gx_cie_joint_caches,
+ &st_joint_caches, pgs->memory,
+ return 0, "gx_currentciecaches");
+ return pgs->cie_joint_caches;
+}
+
+/* ------ Compute the parameters for loading a cache. ------ */
+/* Sets base and factor. */
+
+void
+gs_cie_cache_init(cie_cache_params *pcache, gs_for_loop_params *pflp,
+ const gs_range *domain, client_name_t cname)
+{ /*
+ * We need to map the values in the range
+ * [domain->rmin..domain->rmax]. However, if neither rmin
+ * nor rmax is zero and the function is non-linear,
+ * this can lead to anomalies at zero, which is the
+ * default value for CIE colors. The "correct" way to
+ * approach this is to run the mapping functions on demand,
+ * but we don't want to deal with the complexities of the
+ * callbacks this would involve (especially in the middle of
+ * rendering images); instead, we adjust the range so that zero
+ * maps precisely to a cache slot. Define:
+ * a = domain->rmin;
+ * b = domain->rmax;
+ * R = b - a;
+ * N = gx_cie_cache_size - 1;
+ * f(v) = N(v-a)/R;
+ * x = f(0).
+ * If x is not an integer, we can either increase b or
+ * decrease a to make it one. In the former case, compute:
+ * Kb = floor(x); R'b = N(0-a)/Kb; b' = a + R'b.
+ * In the latter case, compute:
+ * Ka = ceiling(x-N); R'a = N(0-b)/Ka; a' = b - R'a.
+ * We choose whichever method stretches the range the least,
+ * i.e., the one whose R' value (R'a or R'b) is smaller.
+ */
+ double a = domain->rmin, b = domain->rmax;
+ double R = b - a;
+#define N (gx_cie_cache_size - 1)
+ double delta;
+ /* Adjust the range if necessary. */
+ if ( a < 0 && b >= 0 )
+ { double x = -N * a / R; /* must be > 0 */
+ double Kb = floor(x); /* must be >= 0 */
+ double Ka = ceil(x) - N; /* must be <= 0 */
+ if ( Kb == 0 || (Ka != 0 && -b / Ka < -a / Kb) ) /* use R'a */
+ R = -N * b / Ka, a = b - R;
+ else /* use R'b */
+ R = -N * a / Kb, b = a + R;
+ }
+ delta = R / N;
+#ifdef CIE_CACHE_INTERPOLATE
+ pcache->base = a; /* no rounding */
+#else
+ pcache->base = a - delta / 2; /* so lookup will round */
+#endif
+ pcache->factor = (delta == 0 ? 0 : N / R);
+ if_debug4('c', "[c]cache %s 0x%lx base=%g, factor=%g\n",
+ (const char *)cname, (ulong)pcache,
+ pcache->base, pcache->factor);
+ pflp->init = a;
+ pflp->step = delta;
+ pflp->limit = b + delta / 2;
+}
+
+/* ------ Complete a rendering structure ------ */
+
+/* Compute values derived from the rendering structure parameters */
+/* other than the cached procedure values. This routine is idempotent. */
+private void near cie_transform_range3(P3(const gs_range3 *,
+ const gs_matrix3 *, gs_range3 *));
+int
+gs_cie_render_init(gs_cie_render *pcie)
+{ gs_matrix3 PQR_inverse;
+ cie_matrix_init(&pcie->MatrixLMN);
+ cie_matrix_init(&pcie->MatrixABC);
+ cie_matrix_init(&pcie->MatrixPQR);
+ cie_invert3(&pcie->MatrixPQR, &PQR_inverse);
+ cie_matrix_mult3(&pcie->MatrixLMN, &PQR_inverse,
+ &pcie->MatrixPQR_inverse_LMN);
+ cie_transform_range3(&pcie->RangePQR, &pcie->MatrixPQR_inverse_LMN,
+ &pcie->DomainLMN);
+ cie_transform_range3(&pcie->RangeLMN, &pcie->MatrixABC,
+ &pcie->DomainABC);
+ cie_mult3(&pcie->points.WhitePoint, &pcie->MatrixPQR, &pcie->wdpqr);
+ cie_mult3(&pcie->points.BlackPoint, &pcie->MatrixPQR, &pcie->bdpqr);
+ return 0;
+}
+
+/* Transform a set of ranges. */
+private void near
+cie_transform_range(const gs_range3 *in, floatp mu, floatp mv, floatp mw,
+ gs_range *out)
+{ float umin = mu * in->ranges[0].rmin,
+ umax = mu * in->ranges[0].rmax;
+ float vmin = mv * in->ranges[1].rmin,
+ vmax = mv * in->ranges[1].rmax;
+ float wmin = mw * in->ranges[2].rmin,
+ wmax = mw * in->ranges[2].rmax;
+ float temp;
+#define swap(x, y) temp = x, x = y, y = temp
+
+ if ( umin > umax ) swap(umin, umax);
+ if ( vmin > vmax ) swap(vmin, vmax);
+ if ( wmin > wmax ) swap(wmin, wmax);
+ out->rmin = umin + vmin + wmin;
+ out->rmax = umax + vmax + wmax;
+#undef swap
+}
+private void near
+cie_transform_range3(const gs_range3 *in, const gs_matrix3 *mat,
+ gs_range3 *out)
+{ cie_transform_range(in, mat->cu.u, mat->cv.u, mat->cw.u,
+ &out->ranges[0]);
+ cie_transform_range(in, mat->cu.v, mat->cv.v, mat->cw.v,
+ &out->ranges[1]);
+ cie_transform_range(in, mat->cu.w, mat->cv.w, mat->cw.w,
+ &out->ranges[2]);
+}
+
+/* Complete the loading of the rendering caches. */
+/* Note that this routine may make non-idempotent changes to */
+/* the values in the caches. */
+private void near cie_cache_restrict(P2(cie_cache_floats *, const gs_range *));
+#define cie_cache_restrict3(pcache3, pr3)\
+ cie_cache_restrict(&(pcache3)[0].floats, &(pr3)->ranges[0]);\
+ cie_cache_restrict(&(pcache3)[1].floats, &(pr3)->ranges[1]);\
+ cie_cache_restrict(&(pcache3)[2].floats, &(pr3)->ranges[2])
+int
+gs_cie_render_complete(gs_cie_render *pcie)
+{ /* Since range restriction happens immediately after */
+ /* the cache lookup, we can save a step by restricting */
+ /* the values in the cache entries. */
+ cie_cache_restrict3(pcie->caches.EncodeLMN, &pcie->RangeLMN);
+ cie_cache_restrict3(pcie->caches.EncodeABC, &pcie->RangeABC);
+ /* If there is no lookup table, we want the final ABC values */
+ /* to be fracs; if there is a table, we want them to be */
+ /* appropriately scaled ints. */
+ pcie->MatrixABCEncode = pcie->MatrixABC;
+ { int c;
+ double f;
+ for ( c = 0; c < 3; c++ )
+ { gx_cie_scalar_cache *pcache =
+ &pcie->caches.EncodeABC[c];
+ if ( pcie->RenderTable.lookup.table == 0 )
+ { cie_cache_restrict(&pcache->floats,
+ &Range3_default.ranges[0]);
+ gs_cie_cache_to_fracs(pcache);
+ pcache->fracs.params.is_identity = false;
+ }
+ else
+ { int i;
+ int n = pcie->RenderTable.lookup.dims[c];
+#ifdef CIE_RENDER_TABLE_INTERPOLATE
+# define scale_index(f, n, itemp)\
+ restrict_index(f * (1 << _cie_interpolate_bits),\
+ (n) << _cie_interpolate_bits, itemp)
+#else
+ int m = pcie->RenderTable.lookup.m;
+ int k =
+ (c == 0 ? 1 : c == 1 ?
+ m * pcie->RenderTable.lookup.dims[2] : m);
+# define scale_index(f, n, itemp)\
+ (restrict_index(f, n, itemp) * k)
+#endif
+ const gs_range *prange =
+ pcie->RangeABC.ranges + c;
+ /* Loop from top to bottom so that we don't */
+ /* overwrite elements before they're used. */
+ for ( i = gx_cie_cache_size; --i >= 0; )
+ { float v =
+ (pcache->floats.values[i] -
+ prange->rmin) * (n - 1) /
+ (prange->rmax - prange->rmin)
+#ifndef CIE_RENDER_TABLE_INTERPOLATE
+ + 0.5
+#endif
+ ;
+ int itemp;
+
+ if_debug5('c',
+ "[c]cache[%d][%d] = %g => %g => %d\n",
+ c, i, pcache->floats.values[i], v,
+ scale_index(v, n, itemp));
+ pcache->ints.values[i] =
+ scale_index(v, n, itemp);
+ }
+ pcache->ints.params = pcache->floats.params; /* (not necessary) */
+ pcache->ints.params.is_identity = false;
+#undef scale_index
+ }
+ }
+ /* Fold the scaling of the EncodeABC cache index */
+ /* into MatrixABC. */
+#define mabc(i, t)\
+ f = pcie->caches.EncodeABC[i].floats.params.factor;\
+ pcie->MatrixABCEncode.cu.t *= f;\
+ pcie->MatrixABCEncode.cv.t *= f;\
+ pcie->MatrixABCEncode.cw.t *= f;\
+ pcie->EncodeABC_base[i] =\
+ float2cie_cached(pcie->caches.EncodeABC[i].floats.params.base * f)
+ mabc(0, u);
+ mabc(1, v);
+ mabc(2, w);
+ pcie->MatrixABCEncode.is_identity = 0;
+ }
+#undef mabc
+ cie_cache_mult3(pcie->caches.EncodeLMN, &pcie->MatrixABCEncode);
+ return 0;
+}
+
+/* Apply a range restriction to one cache or 3 caches. */
+private void near
+cie_cache_restrict(cie_cache_floats *pcache, const gs_range *prange)
+{ int i;
+ for ( i = 0; i < gx_cie_cache_size; i++ )
+ if_restrict(pcache->values[i], *prange);
+}
+
+/* Convert a cache from floats to fracs. */
+void
+gs_cie_cache_to_fracs(gx_cie_scalar_cache *pcache)
+{ int i;
+ /* Loop from bottom to top so that we don't */
+ /* overwrite elements before they're used. */
+ for ( i = 0; i < gx_cie_cache_size; ++i )
+ pcache->fracs.values[i] = float2frac(pcache->floats.values[i]);
+ pcache->fracs.params = pcache->floats.params; /* (not necessary) */
+}
+
+/* ------ Fill in the joint cache ------ */
+
+/* If the current color space is a CIE space, or has a CIE base space, */
+/* return a pointer to the common part of the space; otherwise return 0. */
+const gs_cie_common *
+gs_cie_cs_common(gs_state *pgs)
+{ const gs_color_space *pcs = pgs->color_space;
+
+sw: switch ( pcs->type->index )
+ {
+ case gs_color_space_index_CIEABC:
+ return &pcs->params.abc->common;
+ case gs_color_space_index_CIEA:
+ return &pcs->params.a->common;
+ case gs_color_space_index_Separation:
+ pcs = (const gs_color_space *)&pcs->params.separation.alt_space;
+ goto sw;
+ case gs_color_space_index_Indexed:
+ pcs = gs_color_space_indexed_base_space(pcs);
+ goto sw;
+ case gs_color_space_index_Pattern:
+ /****** WHAT? ******/
+ default:
+ return 0;
+ }
+}
+
+/* Finish loading the joint caches for the current color space. */
+void
+gs_cie_cs_complete(gs_state *pgs, bool init)
+{ const gs_cie_common *common = gs_cie_cs_common(pgs);
+
+ if ( common )
+ { if ( init )
+ cie_joint_caches_init(pgs->cie_joint_caches, common,
+ pgs->cie_render);
+ cie_joint_caches_complete(pgs->cie_joint_caches, common,
+ pgs->cie_render);
+ }
+}
+
+/* Compute values derived from the color space and rendering parameters */
+/* other than the cached procedure values. This routine is idempotent. */
+private void
+cie_joint_caches_init(gx_cie_joint_caches *pjc,
+ const gs_cie_common *pcie, const gs_cie_render *pcier)
+{ pjc->points_sd.ws.xyz = pcie->points.WhitePoint;
+ cie_mult3(&pjc->points_sd.ws.xyz, &pcier->MatrixPQR,
+ &pjc->points_sd.ws.pqr);
+ pjc->points_sd.bs.xyz = pcie->points.BlackPoint;
+ cie_mult3(&pjc->points_sd.bs.xyz, &pcier->MatrixPQR,
+ &pjc->points_sd.bs.pqr);
+ pjc->points_sd.wd.xyz = pcier->points.WhitePoint;
+ pjc->points_sd.wd.pqr = pcier->wdpqr;
+ pjc->points_sd.bd.xyz = pcier->points.BlackPoint;
+ pjc->points_sd.bd.pqr = pcier->bdpqr;
+ cie_matrix_mult3(&pcier->MatrixPQR, &pcie->MatrixLMN,
+ &pjc->MatrixLMN_PQR);
+ /* Load the TransformPQR caches. */
+ { int i, j;
+ gs_for_loop_params lp[3];
+ cie_cache_init3(pjc->TransformPQR, lp, pcier->RangePQR.ranges,
+ "TransformPQR");
+ for ( i = 0; i < gx_cie_cache_size; i++ )
+ for ( j = 0; j < 3; lp[j].init += lp[j].step, j++ )
+ pjc->TransformPQR[j].floats.values[i] =
+ (*pcier->TransformPQR.procs[j])(lp[j].init, &pjc->points_sd, pcier);
+ }
+}
+
+/* Complete the loading of the joint caches. Note that this routine */
+/* may make non-idempotent changes to the values in the caches. */
+private void
+cie_joint_caches_complete(gx_cie_joint_caches *pjc,
+ const gs_cie_common *pcie, const gs_cie_render *pcier)
+{ int j;
+ cie_cache_restrict3(pjc->TransformPQR, &pcier->RangePQR);
+ for ( j = 0; j < 3; j++ )
+ cie_cache_mult(&pjc->DecodeLMN[j],
+ &pjc->MatrixLMN_PQR.cu + j,
+ &pcie->caches.DecodeLMN[j].floats);
+ pjc->skipLMN = pjc->MatrixLMN_PQR.is_identity &
+ pjc->DecodeLMN[0].vecs.params.is_identity &
+ pjc->DecodeLMN[1].vecs.params.is_identity &
+ pjc->DecodeLMN[2].vecs.params.is_identity;
+ pjc->skipPQR =
+ cie_cache_mult3(pjc->TransformPQR, &pcier->MatrixPQR_inverse_LMN);
+}
+
+/* ================ Color rendering (using the caches) ================ */
+
+private int near cie_remap_finish(P3(const cie_cached_vector3 *,
+ frac *, const gs_state *));
+private void near cie_lookup_mult3(P2(cie_cached_vector3 *, const gx_cie_vector_cache *));
+#ifdef DEBUG
+private void near
+cie_lookup_map3(cie_cached_vector3 *pvec,
+ const gx_cie_vector_cache *pc /*[3]*/, const char _ds *cname)
+{ if_debug5('c', "[c]lookup %s 0x%lx [%g %g %g]\n",
+ (const char *)cname, (ulong)pc,
+ cie_cached2float(pvec->u), cie_cached2float(pvec->v),
+ cie_cached2float(pvec->w));
+ cie_lookup_mult3(pvec, pc);
+ if_debug3('c', " =[%g %g %g]\n",
+ cie_cached2float(pvec->u), cie_cached2float(pvec->v),
+ cie_cached2float(pvec->w));
+}
+#else
+# define cie_lookup_map3(pvec, pc, cname) cie_lookup_mult3(pvec, pc)
+#endif
+
+/* Determine the concrete space underlying a CIEBased space. */
+private const gs_color_space cie_rgb_space =
+ { &gs_color_space_type_DeviceRGB };
+private const gs_color_space cie_cmyk_space =
+ { &gs_color_space_type_DeviceCMYK };
+private const gs_color_space *
+gx_concrete_space_CIE(const gs_color_space *pcs, const gs_state *pgs)
+{ const gs_cie_render *pcie = pgs->cie_render;
+ if ( pcie == 0 || pcie->RenderTable.lookup.table == 0 ||
+ pcie->RenderTable.lookup.m == 3
+ )
+ return &cie_rgb_space;
+ else /* pcie->RenderTable.lookup.m == 4 */
+ return &cie_cmyk_space;
+}
+
+/* Render a CIEBasedDEFG color. */
+private int
+gx_concretize_CIEDEFG(const gs_client_color *pc, const gs_color_space *pcs,
+ frac *pconc, const gs_state *pgs)
+{ /****** NOT IMPLEMENTED YET ******/
+ return_error(gs_error_undefined);
+}
+
+/* Render a CIEBasedDEF color. */
+private int
+gx_concretize_CIEDEF(const gs_client_color *pc, const gs_color_space *pcs,
+ frac *pconc, const gs_state *pgs)
+{ /****** NOT IMPLEMENTED YET ******/
+ return_error(gs_error_undefined);
+}
+
+/* Render a CIEBasedABC color. */
+/* We provide both remap and concretize, but only the former */
+/* needs to be efficient. */
+private int
+gx_remap_CIEABC(const gs_client_color *pc, const gs_color_space *pcs,
+ gx_device_color *pdc, const gs_state *pgs)
+{ frac conc[4];
+ const gs_cie_abc *pcie = pcs->params.abc;
+ cie_cached_vector3 vec3;
+
+ if_debug3('c', "[c]remap CIEABC [%g %g %g]\n",
+ pc->paint.values[0], pc->paint.values[1],
+ pc->paint.values[2]);
+ vec3.u = float2cie_cached(pc->paint.values[0]);
+ vec3.v = float2cie_cached(pc->paint.values[1]);
+ vec3.w = float2cie_cached(pc->paint.values[2]);
+
+ /* Apply DecodeABC and MatrixABC. */
+#define vabc vec3
+#define vlmn vec3
+ if ( !pcie->caches.skipABC )
+ cie_lookup_map3(&vabc /*&vlmn*/, &pcie->caches.DecodeABC[0],
+ "Decode/MatrixABC");
+#undef vabc
+ switch ( cie_remap_finish(&vlmn, conc, pgs) )
+ {
+ case 3:
+ if_debug3('c', "[c]=RGB [%g %g %g]\n",
+ frac2float(conc[0]), frac2float(conc[1]),
+ frac2float(conc[2]));
+ gx_remap_concrete_rgb(conc[0], conc[1], conc[2], pdc, pgs);
+ return 0;
+ case 4:
+ if_debug4('c', "[c]=CMYK [%g %g %g %g]\n",
+ frac2float(conc[0]), frac2float(conc[1]),
+ frac2float(conc[2]), frac2float(conc[3]));
+ gx_remap_concrete_cmyk(conc[0], conc[1], conc[2], conc[3],
+ pdc, pgs);
+ return 0;
+ }
+ /* Can't happen. */
+ return_error(gs_error_unknownerror);
+#undef vlmn
+}
+private int
+gx_concretize_CIEABC(const gs_client_color *pc, const gs_color_space *pcs,
+ frac *pconc, const gs_state *pgs)
+{ const gs_cie_abc *pcie = pcs->params.abc;
+ cie_cached_vector3 vec3;
+
+ if_debug3('c', "[c]concretize CIEABC [%g %g %g]\n",
+ pc->paint.values[0], pc->paint.values[1],
+ pc->paint.values[2]);
+ vec3.u = float2cie_cached(pc->paint.values[0]);
+ vec3.v = float2cie_cached(pc->paint.values[1]);
+ vec3.w = float2cie_cached(pc->paint.values[2]);
+#define vabc vec3
+#define vlmn vec3
+ if ( !pcie->caches.skipABC )
+ cie_lookup_map3(&vabc /*&vlmn*/, &pcie->caches.DecodeABC[0],
+ "Decode/MatrixABC");
+#undef vabc
+ cie_remap_finish(&vlmn, pconc, pgs);
+#undef vlmn
+ return 0;
+}
+
+/* Render a CIEBasedA color. */
+private int
+gx_concretize_CIEA(const gs_client_color *pc, const gs_color_space *pcs,
+ frac *pconc, const gs_state *pgs)
+{ const gs_cie_a *pcie = pcs->params.a;
+ cie_cached_value a = float2cie_cached(pc->paint.values[0]);
+ cie_cached_vector3 vlmn;
+
+ if_debug1('c', "[c]concretize CIEA %g\n", pc->paint.values[0]);
+
+ /* Apply DecodeA and MatrixA. */
+ vlmn = lookup_value(a, &pcie->caches.DecodeA);
+ return cie_remap_finish(&vlmn, pconc, pgs);
+}
+
+/* Common rendering code. */
+/* Return 3 if RGB, 4 if CMYK. */
+private int near
+cie_remap_finish(const cie_cached_vector3 *plmn, frac *pconc,
+ const gs_state *pgs)
+{ const gs_cie_render *pcie = pgs->cie_render;
+ const gx_cie_joint_caches *pjc = pgs->cie_joint_caches;
+ const gs_const_string *table;
+ cie_cached_vector3 vec3;
+ int tabc[3]; /* indices for final EncodeABC lookup */
+
+ if ( pcie == 0 )
+ { /* No rendering has been defined yet. */
+ /* Just return black. */
+ pconc[0] = pconc[1] = pconc[2] = frac_0;
+ return 3;
+ }
+
+ /* Apply DecodeLMN, MatrixLMN(decode), and MatrixPQR. */
+#define vlmn vec3
+ vlmn = *plmn;
+#define vpqr vec3
+ if ( !pjc->skipLMN )
+ cie_lookup_map3(&vlmn /*&vpqr*/, &pjc->DecodeLMN[0],
+ "Decode/MatrixLMN+MatrixPQR");
+#undef vlmn
+
+ /* Apply TransformPQR, MatrixPQR', and MatrixLMN(encode). */
+#define vlmn vec3
+ if ( !pjc->skipPQR )
+ cie_lookup_map3(&vpqr /*&vlmn*/, &pjc->TransformPQR[0],
+ "Transform/Matrix'PQR+MatrixLMN");
+#undef vpqr
+
+ /* Apply EncodeLMN and MatrixABC(encode). */
+#define vabc vec3
+ cie_lookup_map3(&vlmn /*&vabc*/, &pcie->caches.EncodeLMN[0],
+ "EncodeLMN+MatrixABC");
+#undef vlmn
+ /* MatrixABCEncode includes the scaling of the EncodeABC */
+ /* cache index. */
+#define set_tabc(i, t)\
+ set_restrict_index(tabc[i],\
+ cie_cached2int(vabc.t - pcie->EncodeABC_base[i],\
+ _cie_interpolate_bits),\
+ gx_cie_cache_size << _cie_interpolate_bits)
+ set_tabc(0, u);
+ set_tabc(1, v);
+ set_tabc(2, w);
+ table = pcie->RenderTable.lookup.table;
+ if ( table == 0 )
+ { /* No further transformation. */
+ /* The final mapping step includes both restriction to */
+ /* the range [0..1] and conversion to fracs. */
+#define eabc(i)\
+ cie_interpolate_fracs(pcie->caches.EncodeABC[i].fracs.values, tabc[i])
+ pconc[0] = eabc(0);
+ pconc[1] = eabc(1);
+ pconc[2] = eabc(2);
+#undef eabc
+ return 3;
+ }
+ else
+ { /* Use the RenderTable. */
+ int m = pcie->RenderTable.lookup.m;
+#define rt_lookup(j, i) pcie->caches.RenderTableT[j].fracs.values[i]
+#ifdef CIE_RENDER_TABLE_INTERPOLATE
+
+ /* The final mapping step includes restriction to the */
+ /* ranges [0..dims[c]] as ints with interpolation bits. */
+ fixed rfix[3];
+
+#define eabc(i)\
+ cie_interpolate_fracs(pcie->caches.EncodeABC[i].ints.values, tabc[i])
+#define fabc(i)\
+ (eabc(i) << (_fixed_shift - _cie_interpolate_bits))
+ rfix[0] = fabc(0);
+ rfix[1] = fabc(1);
+ rfix[2] = fabc(2);
+ if_debug6('c', "[c]ABC=%g,%g,%g => iabc=%g,%g,%g\n",
+ cie_cached2float(vabc.u), cie_cached2float(vabc.v),
+ cie_cached2float(vabc.w), fixed2float(rfix[0]),
+ fixed2float(rfix[1]), fixed2float(rfix[2]));
+ gx_color_interpolate_linear(rfix, &pcie->RenderTable.lookup,
+ pconc);
+ if_debug3('c', "[c] interpolated => %g,%g,%g\n",
+ frac2float(pconc[0]), frac2float(pconc[1]),
+ frac2float(pconc[2]));
+ if ( !pcie->caches.RenderTableT_is_identity )
+ { /* Map the interpolated values. */
+#define frac2cache_index(v) frac2bits(v, gx_cie_log2_cache_size)
+ pconc[0] = rt_lookup(0, frac2cache_index(pconc[0]));
+ pconc[1] = rt_lookup(1, frac2cache_index(pconc[1]));
+ pconc[2] = rt_lookup(2, frac2cache_index(pconc[2]));
+ if ( m > 3 )
+ pconc[3] = rt_lookup(3, frac2cache_index(pconc[3]));
+#undef frac2cache_index
+ }
+
+#else /* !CIE_RENDER_TABLE_INTERPOLATE */
+
+ /* The final mapping step includes restriction to the */
+ /* ranges [0..dims[c]], plus scaling of the indices */
+ /* in the strings. */
+#define ri(i)\
+ pcie->caches.EncodeABC[i].ints.values[tabc[i] >> _cie_interpolate_bits]
+ int ia = ri(0);
+ int ib = ri(1); /* pre-multiplied by m * NC */
+ int ic = ri(2); /* pre-multiplied by m */
+ const byte *prtc = table[ia].data + ib + ic;
+ /* (*pcie->RenderTable.T)(prtc, m, pcie, pconc); */
+
+ if_debug6('c', "[c]ABC=%g,%g,%g => iabc=%d,%d,%d\n",
+ cie_cached2float(vabc.u), cie_cached2float(vabc.v),
+ cie_cached2float(vabc.w), ia, ib, ic);
+ if ( pcie->caches.RenderTableT_is_identity )
+ { pconc[0] = byte2frac(prtc[0]);
+ pconc[1] = byte2frac(prtc[1]);
+ pconc[2] = byte2frac(prtc[2]);
+ if ( m > 3 )
+ pconc[3] = byte2frac(prtc[3]);
+ }
+ else
+ {
+#if gx_cie_log2_cache_size == 8
+# define byte2cache_index(b) (b)
+#else
+# if gx_cie_log2_cache_size > 8
+# define byte2cache_index(b)\
+ ( ((b) << (gx_cie_log2_cache_size - 8)) +\
+ ((b) >> (16 - gx_cie_log2_cache_size)) )
+# else /* < 8 */
+# define byte2cache_index(b) ((b) >> (8 - gx_cie_log2_cache_size))
+# endif
+#endif
+ pconc[0] = rt_lookup(0, byte2cache_index(prtc[0]));
+ pconc[1] = rt_lookup(1, byte2cache_index(prtc[1]));
+ pconc[2] = rt_lookup(2, byte2cache_index(prtc[2]));
+ if ( m > 3 )
+ pconc[3] = rt_lookup(3, byte2cache_index(prtc[3]));
+#undef byte2cache_index
+ }
+
+#endif /* !CIE_RENDER_TABLE_INTERPOLATE */
+#undef ri
+#undef rt_lookup
+ return m;
+ }
+}
+
+/* ================ Utilities ================ */
+
+#define if_debug_vector3(str, vec)\
+ if_debug4('c', "%s[%g %g %g]\n", str, vec->u, vec->v, vec->w)
+#define if_debug_matrix3(str, mat)\
+ if_debug10('c', "%s[%g %g %g / %g %g %g / %g %g %g]\n", str,\
+ mat->cu.u, mat->cu.v, mat->cu.w, mat->cv.u, mat->cv.v, mat->cv.w,\
+ mat->cw.u, mat->cw.v, mat->cw.w)
+
+/* Multiply a vector by a matrix. */
+/* Note that we are computing M * V where v is a column vector. */
+private void near
+cie_mult3(const gs_vector3 *in, register const gs_matrix3 *mat,
+ gs_vector3 *out)
+{ if_debug_vector3("[c]mult", in);
+ if_debug_matrix3(" *", mat);
+ { float u = in->u, v = in->v, w = in->w;
+ out->u = (u * mat->cu.u) + (v * mat->cv.u) + (w * mat->cw.u);
+ out->v = (u * mat->cu.v) + (v * mat->cv.v) + (w * mat->cw.v);
+ out->w = (u * mat->cu.w) + (v * mat->cv.w) + (w * mat->cw.w);
+ }
+ if_debug_vector3(" =", out);
+}
+
+/* Multiply two matrices. We assume the result is not an alias for */
+/* either of the operands. Note that the composition of the transformations */
+/* M1 followed by M2 is M2 * M1, not M1 * M2. (See gscie.h for details.) */
+private void near
+cie_matrix_mult3(const gs_matrix3 *ma, const gs_matrix3 *mb, gs_matrix3 *mc)
+{ if_debug_matrix3("[c]matrix_mult", ma);
+ if_debug_matrix3(" *", mb);
+ cie_mult3(&mb->cu, ma, &mc->cu);
+ cie_mult3(&mb->cv, ma, &mc->cv);
+ cie_mult3(&mb->cw, ma, &mc->cw);
+ cie_matrix_init(mc);
+ if_debug_matrix3(" =", mc);
+}
+
+/* Invert a matrix. */
+/* The output must not be an alias for the input. */
+private void near
+cie_invert3(register const gs_matrix3 *in, register gs_matrix3 *out)
+{ /* This is a brute force algorithm; maybe there are better. */
+ /* We label the array elements */
+ /* [ A B C ] */
+ /* [ D E F ] */
+ /* [ G H I ] */
+#define A cu.u
+#define B cv.u
+#define C cw.u
+#define D cu.v
+#define E cv.v
+#define F cw.v
+#define G cu.w
+#define H cv.w
+#define I cw.w
+ double coA = in->E * in->I - in->F * in->H;
+ double coB = in->F * in->G - in->D * in->I;
+ double coC = in->D * in->H - in->E * in->G;
+ double det = in->A * coA + in->B * coB + in->C * coC;
+ if_debug_matrix3("[c]invert", in);
+ out->A = coA / det;
+ out->D = coB / det;
+ out->G = coC / det;
+ out->B = (in->C * in->H - in->B * in->I) / det;
+ out->E = (in->A * in->I - in->C * in->G) / det;
+ out->H = (in->B * in->G - in->A * in->H) / det;
+ out->C = (in->B * in->F - in->C * in->E) / det;
+ out->F = (in->C * in->D - in->A * in->F) / det;
+ out->I = (in->A * in->E - in->B * in->D) / det;
+ if_debug_matrix3(" =", out);
+#undef A
+#undef B
+#undef C
+#undef D
+#undef E
+#undef F
+#undef G
+#undef H
+#undef I
+ out->is_identity = in->is_identity;
+}
+
+/* Look up 3 values in a cache, with cached post-multiplication. */
+private void near
+cie_lookup_mult3(cie_cached_vector3 *pvec, const gx_cie_vector_cache *pc /*[3]*/)
+{
+ /****** Interpolating at intermediate stages doesn't seem to ******/
+ /****** make things better, and slows things down, so.... ******/
+#ifdef CIE_INTERPOLATE_INTERMEDIATE
+ /* Interpolate between adjacent cache entries. */
+ /* This is expensive! */
+#ifdef CIE_CACHE_USE_FIXED
+# define lookup_interpolate_between(v0, v1, i, ftemp)\
+ cie_interpolate_between(v0, v1, i)
+#else
+ float ftu, ftv, ftw;
+# define lookup_interpolate_between(v0, v1, i, ftemp)\
+ ((v0) + ((v1) - (v0)) *\
+ ((ftemp = float_rshift(i, _cie_interpolate_bits)), ftemp - (int)ftemp))
+#endif
+
+ cie_cached_value iu =
+ lookup_index(pvec->u, pc, _cie_interpolate_bits);
+ const cie_cached_vector3 *pu =
+ &pc[0].vecs.values[(int)cie_cached_rshift(iu,
+ _cie_interpolate_bits)];
+ const cie_cached_vector3 *pu1 =
+ (iu >= (gx_cie_cache_size - 1) << _cie_interpolate_bits ?
+ pu : pu + 1);
+
+ cie_cached_value iv =
+ lookup_index(pvec->v, pc + 1, _cie_interpolate_bits);
+ const cie_cached_vector3 *pv =
+ &pc[1].vecs.values[(int)cie_cached_rshift(iv,
+ _cie_interpolate_bits)];
+ const cie_cached_vector3 *pv1 =
+ (iv >= (gx_cie_cache_size - 1) << _cie_interpolate_bits ?
+ pv : pv + 1);
+
+ cie_cached_value iw =
+ lookup_index(pvec->w, pc + 2, _cie_interpolate_bits);
+ const cie_cached_vector3 *pw =
+ &pc[2].vecs.values[(int)cie_cached_rshift(iw,
+ _cie_interpolate_bits)];
+ const cie_cached_vector3 *pw1 =
+ (iw >= (gx_cie_cache_size - 1) << _cie_interpolate_bits ?
+ pw : pw + 1);
+
+ pvec->u = lookup_interpolate_between(pu->u, pu1->u, iu, ftu) +
+ lookup_interpolate_between(pv->u, pv1->u, iv, ftv) +
+ lookup_interpolate_between(pw->u, pw1->u, iw, ftw);
+ pvec->v = lookup_interpolate_between(pu->v, pu1->v, iu, ftu) +
+ lookup_interpolate_between(pv->v, pv1->v, iv, ftv) +
+ lookup_interpolate_between(pw->v, pw1->v, iw, ftw);
+ pvec->w = lookup_interpolate_between(pu->w, pu1->w, iu, ftu) +
+ lookup_interpolate_between(pv->w, pv1->w, iv, ftv) +
+ lookup_interpolate_between(pw->w, pw1->w, iw, ftw);
+#else
+ const cie_cached_vector3 *pu = &lookup_value(pvec->u, pc);
+ const cie_cached_vector3 *pv = &lookup_value(pvec->v, pc + 1);
+ const cie_cached_vector3 *pw = &lookup_value(pvec->w, pc + 2);
+ pvec->u = pu->u + pv->u + pw->u;
+ pvec->v = pu->v + pv->v + pw->v;
+ pvec->w = pu->w + pv->w + pw->w;
+#endif
+}
+
+/* Set the is_identity flag that accelerates multiplication. */
+private void near
+cie_matrix_init(register gs_matrix3 *mat)
+{ mat->is_identity =
+ mat->cu.u == 1.0 && is_fzero2(mat->cu.v, mat->cu.w) &&
+ mat->cv.v == 1.0 && is_fzero2(mat->cv.u, mat->cv.w) &&
+ mat->cw.w == 1.0 && is_fzero2(mat->cw.u, mat->cw.v);
+}
diff --git a/pstoraster/gscie.h b/pstoraster/gscie.h
new file mode 100644
index 000000000..dabfe30bc
--- /dev/null
+++ b/pstoraster/gscie.h
@@ -0,0 +1,485 @@
+/* Copyright (C) 1992, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gscie.h */
+/* Structures for CIE color algorithms */
+/* (requires gscspace.h, gscolor2.h) */
+#include "gsrefct.h"
+#include "gxctable.h"
+
+/* Define the size of the Encode/Decode/Transform procedure value caches. */
+/* With the current design, these caches must all have the same size. */
+#ifndef CIE_LOG2_CACHE_SIZE
+# define CIE_LOG2_CACHE_SIZE 9
+#endif
+#define gx_cie_log2_cache_size CIE_LOG2_CACHE_SIZE
+#define gx_cie_cache_size (1 << gx_cie_log2_cache_size)
+
+/* Define whether to use fixed- or floating-point values in the caches. */
+/*#define CIE_CACHE_USE_FIXED*/
+
+/* If we are using fixed-point values, define the number of fraction bits. */
+#define CIE_FIXED_FRACTION_BITS 12
+#ifndef CIE_FIXED_FRACTION_BITS
+/* Take as many bits as we can without having to multiply in two pieces. */
+# define CIE_FIXED_FRACTION_BITS\
+ ((arch_sizeof_long * 8 - gx_cie_log2_cache_size) / 2 - 1)
+#endif
+
+/* Define whether to interpolate between cached values. */
+#define CIE_CACHE_INTERPOLATE
+
+/* Define whether to interpolate at all intermediate lookup steps. */
+/* This is computationally expensive and doesn't seem to improve */
+/* the accuracy of the result. */
+/*#define CIE_INTERPOLATE_INTERMEDIATE*/
+
+/* Define whether to interpolate in the RenderTable. */
+/* This is computationally very expensive, so it is normally disabled. */
+#define CIE_RENDER_TABLE_INTERPOLATE
+#ifdef CIE_RENDER_TABLE_INTERPOLATE
+# define CIE_CACHE_INTERPOLATE
+#endif
+
+/* Mark code intended for later use. */
+/****** NOTE: this is also used in zcie.c. ******/
+/*#define NEW_CIE*/
+
+#define float_lshift(v, nb) ((v) * (1L << (nb)))
+#define float_rshift(v, nb) ((v) * (1.0 / (1L << (nb))))
+
+#ifdef CIE_CACHE_INTERPOLATE
+/* We have to have room for both a cache index and the interpolation bits */
+/* in a positive int (i.e., leaving 1 bit for the sign), plus a little slop. */
+/* The values for interpolation are cie_cached_values by default. */
+# define _cie_interpolate_bits\
+ min(arch_sizeof_int * 8 - gx_cie_log2_cache_size - 2, 10)
+# define _cix(i) ((i) >> _cie_interpolate_bits)
+# define _cif(i) ((int)(i) & ((1 << _cie_interpolate_bits) - 1))
+# define cie_interpolate_between(v0, v1, i)\
+ ((v0) + cie_cached_rshift(((v1) - (v0)) * _cif(i) +\
+ (1 << (_cie_interpolate_bits - 1)),\
+ _cie_interpolate_bits))
+# define cie_interpolate(p, i)\
+ cie_interpolate_between((p)[_cix(i)], (p)[_cix(i) + 1], i)
+# define cie_interpolate_fracs(p, i)\
+ ((p)[_cix(i)] + (frac)arith_rshift((long)((p)[_cix(i) + 1] - (p)[_cix(i)]) * _cif(i), _cie_interpolate_bits))
+#else
+# define _cie_interpolate_bits 0
+# define cie_interpolate_between(v0, v1, i) (v0)
+# define cie_interpolate(p, i) ((p)[i])
+# define cie_interpolate_fracs(p, i) ((p)[i])
+#endif
+
+#ifdef CIE_CACHE_USE_FIXED
+typedef long cie_cached_value;
+# define _cie_fixed_shift CIE_FIXED_FRACTION_BITS
+# define float2cie_cached(v)\
+ ((cie_cached_value)float_lshift(v, _cie_fixed_shift))
+# define cie_cached2float(v)\
+ float_rshift(v, _cie_fixed_shift)
+# define cie_cached2int(v, fbits)\
+ arith_rshift(v, _cie_fixed_shift - (fbits))
+/* We are multiplying two cie_cached_values to produce a result that */
+/* lies between 0 and gx_cie_cache_size - 1. If the intermediate result */
+/* might overflow, compute it in pieces (being a little sloppy). */
+# define _cie_product_excess_bits\
+ (_cie_fixed_shift * 2 + gx_cie_log2_cache_size - (arch_sizeof_long * 8 - 1))
+# define cie_cached_product2int(v, factor, fbits)\
+ (_cie_product_excess_bits > 0 ?\
+ arith_rshift( (v) * arith_rshift(factor, _cie_product_excess_bits) +\
+ arith_rshift(v, _cie_product_excess_bits) *\
+ ((factor) & ((1 << _cie_product_excess_bits) - 1)),\
+ _cie_fixed_shift * 2 - _cie_product_excess_bits - (fbits)) :\
+ arith_rshift((v) * (factor), _cie_fixed_shift * 2 - (fbits)))
+# define cie_cached_rshift(v, n) arith_rshift(v, n)
+#else
+typedef float cie_cached_value;
+# define float2cie_cached(v) (v)
+# define cie_cached2float(v) (v)
+# define cie_cached2int(v, fbits)\
+ ((int)float_lshift(v, fbits))
+# define cie_cached_product2int(v, factor, fbits)\
+ ((int)float_lshift((v) * (factor), fbits))
+# define cie_cached_rshift(v, n) float_rshift(v, n)
+#endif
+
+/* ------ Common definitions ------ */
+
+/*
+ * For the purposes of the CIE routines, we consider that all the vectors
+ * are column vectors, that the matrices are specified in column order
+ * (e.g., the matrix
+ * [ A B C ]
+ * [ D E F ]
+ * [ G H I ]
+ * is represented as [A D G B E H C F I]), and that to transform a vector
+ * V by a matrix M, we compute M * V to produce another column vector.
+ * Note in particular that in order to produce a matrix M that is
+ * equivalent to transforming by M1 and then by M2, we must compute
+ * M = M2 * M1. This probably isn't the most intuitive way to specify
+ * these things, but that's how the code turned out, and it isn't worth
+ * changing at this point.
+ */
+
+/* A 3-element vector. */
+typedef struct gs_vector3_s {
+ float u, v, w;
+} gs_vector3;
+
+/* A 3x3 matrix, stored in column order. */
+typedef struct gs_matrix3_s {
+ gs_vector3 cu, cv, cw;
+ bool is_identity;
+} gs_matrix3;
+
+/* 3- and 4-element vectors of ranges. */
+typedef struct gs_range_s {
+ float rmin, rmax;
+} gs_range;
+typedef struct gs_range3_s {
+ gs_range ranges[3];
+} gs_range3;
+typedef struct gs_range4_s {
+ gs_range ranges[4];
+} gs_range4;
+
+/* Client-supplied transformation procedures. */
+typedef struct gs_cie_common_s gs_cie_common;
+#ifdef NEW_CIE
+typedef struct gs_cie_abc_common_s gs_cie_abc_common;
+#else
+typedef struct gs_cie_abc_s gs_cie_abc_common;
+#endif
+typedef struct gs_cie_wbsd_s gs_cie_wbsd;
+
+typedef float (*gs_cie_a_proc)(P2(floatp, const gs_cie_a *));
+
+typedef float (*gs_cie_abc_proc)(P2(floatp, const gs_cie_abc *));
+typedef struct gs_cie_abc_proc3_s {
+ gs_cie_abc_proc procs[3];
+} gs_cie_abc_proc3;
+
+typedef float (*gs_cie_def_proc)(P2(floatp, const gs_cie_def *));
+typedef struct gs_cie_def_proc3_s {
+ gs_cie_def_proc procs[3];
+} gs_cie_def_proc3;
+
+typedef float (*gs_cie_defg_proc)(P2(floatp, const gs_cie_defg *));
+typedef struct gs_cie_defg_proc4_s {
+ gs_cie_defg_proc procs[4];
+} gs_cie_defg_proc4;
+
+typedef float (*gs_cie_common_proc)(P2(floatp, const gs_cie_common *));
+typedef struct gs_cie_common_proc3_s {
+ gs_cie_common_proc procs[3];
+} gs_cie_common_proc3;
+
+typedef float (*gs_cie_render_proc)(P2(floatp, const gs_cie_render *));
+typedef struct gs_cie_render_proc3_s {
+ gs_cie_render_proc procs[3];
+} gs_cie_render_proc3;
+
+typedef float (*gs_cie_transform_proc)(P3(floatp, const gs_cie_wbsd *,
+ const gs_cie_render *));
+typedef struct gs_cie_transform_proc3_s {
+ gs_cie_transform_proc procs[3];
+} gs_cie_transform_proc3;
+
+typedef frac (*gs_cie_render_table_proc)(P2(byte, const gs_cie_render *));
+typedef struct gs_cie_render_table_procs_s {
+ gs_cie_render_table_proc procs[4];
+} gs_cie_render_table_procs;
+
+/* CIE white and black points. */
+typedef struct gs_cie_wb_s {
+ gs_vector3 WhitePoint;
+ gs_vector3 BlackPoint;
+} gs_cie_wb;
+
+/* ------ Caches ------ */
+
+/*
+ * Given that all the client-supplied procedures involved in CIE color
+ * mapping and rendering are monotonic, and given that we can determine
+ * the minimum and maximum input values for them, we can cache their values.
+ * This takes quite a lot of space, but eliminates the need for callbacks
+ * deep in the graphics code (particularly the image operator).
+ *
+ * The procedures, and how we determine their domains, are as follows:
+
+Stage Name Domain determination
+----- ---- --------------------
+pre-decode DecodeDEF RangeDEF
+pre-decode DecodeDEFG RangeDEFG
+color space DecodeA RangeA
+color space DecodeABC RangeABC
+color space DecodeLMN RangeLMN
+rendering TransformPQR RangePQR
+ (but depends on color space White/BlackPoints)
+rendering EncodeLMN RangePQR transformed by the inverse of
+ MatrixPQR and then by MatrixLMN
+rendering EncodeABC RangeLMN transformed by MatrixABC
+rendering RenderTable.T [0..1]*m
+
+ * Note that we can mostly cache the results of the color space procedures
+ * without knowing the color rendering parameters, and vice versa,
+ * because of the range parameters supplied in the dictionaries.
+ * Unfortunately, TransformPQR is an exception.
+ */
+/*
+ * The index into a cache is (value - base) * factor, where
+ * factor is computed as (cie_cache_size - 1) / (rmax - rmin).
+ */
+/*
+ * We have two kinds of caches: ordinary caches, where each value is
+ * a scalar, and vector caches, where each value is a gs_cached_vector3.
+ * The latter allow us to pre-multiply the values by one column of
+ * a gs_matrix3, avoiding multiplications at lookup time.
+ * Since we sometimes alias the two types of caches for access to
+ * the floats, values must come last.
+ */
+typedef struct cie_cache_params_s {
+ bool is_identity; /* must come first */
+ float base, factor;
+} cie_cache_params;
+#define cie_cache_struct(sname, vtype)\
+ struct sname {\
+ cie_cache_params params;\
+ vtype values[gx_cie_cache_size];\
+ }
+typedef cie_cache_struct(gx_cie_cache_s, float) cie_cache_floats;
+typedef union gx_cie_scalar_cache_s {
+ cie_cache_floats floats;
+ cie_cache_struct(_scf, frac) fracs;
+ cie_cache_struct(_sci, int) ints;
+} gx_cie_scalar_cache;
+typedef struct cie_cached_vector3_s {
+ cie_cached_value u, v, w;
+} cie_cached_vector3;
+typedef struct cie_vector_cache_params_s {
+ bool is_identity; /* must come first */
+ cie_cached_value base, factor, limit;
+} cie_vector_cache_params;
+typedef struct cie_cache_vectors_s {
+ cie_vector_cache_params params; /* must come first for is_identity */
+ cie_cached_vector3 values[gx_cie_cache_size];
+} cie_cache_vectors;
+typedef union gx_cie_vector_cache_s {
+ cie_cache_floats floats;
+ cie_cache_vectors vecs;
+} gx_cie_vector_cache;
+
+/* ------ Color space dictionaries ------ */
+
+/* Elements common to all CIE dictionaries. */
+struct gs_cie_common_s {
+ gs_range3 RangeLMN;
+ gs_cie_common_proc3 DecodeLMN;
+ gs_matrix3 MatrixLMN;
+ gs_cie_wb points;
+ /* Following are computed when structure is initialized. */
+ struct {
+ gx_cie_scalar_cache DecodeLMN[3];
+ } caches;
+};
+
+/* A CIEBasedA dictionary. */
+struct gs_cie_a_s {
+ gs_cie_common common; /* must be first */
+ rc_header rc;
+ gs_range RangeA;
+ gs_cie_a_proc DecodeA;
+ gs_vector3 MatrixA;
+ /* Following are computed when structure is initialized. */
+ struct {
+ gx_cie_vector_cache DecodeA; /* mult. by MatrixA */
+ } caches;
+};
+#define private_st_cie_a() /* in zcie.c */\
+ gs_private_st_simple(st_cie_a, gs_cie_a, "gs_cie_a")
+
+/* A CIEBasedABC dictionary. */
+#ifdef NEW_CIE
+struct gs_cie_abc_common_s {
+ gs_cie_common common; /* must be first */
+#else
+struct gs_cie_abc_s {
+ gs_cie_common common; /* must be first */
+ rc_header rc;
+#endif
+ gs_range3 RangeABC;
+ gs_cie_abc_proc3 DecodeABC;
+ gs_matrix3 MatrixABC;
+ /* Following are computed when structure is initialized. */
+ struct {
+ bool skipABC;
+ gx_cie_vector_cache DecodeABC[3]; /* mult. by MatrixABC */
+ } caches;
+};
+#ifdef NEW_CIE
+/* A CIEBasedABC dictionary. */
+struct gs_cie_abc_s {
+ gs_cie_abc_common abc; /* must be first */
+ rc_header rc;
+};
+#endif
+#define private_st_cie_abc() /* in zcie.c */\
+ gs_private_st_simple(st_cie_abc, gs_cie_abc, "gs_cie_abc")
+
+/* A CIEBasedDEF dictionary. */
+/****** NOT IMPLEMENTED YET ******/
+struct gs_cie_def_s {
+ gs_cie_abc_common abc; /* must be first */
+#ifndef NEW_CIE
+ rc_header rc;
+#endif
+ gs_range3 RangeDEF;
+ gs_cie_def_proc3 DecodeDEF;
+ gs_range3 RangeHIJ;
+ gx_color_lookup_table Table; /* [NH][NI * NJ * 3] */
+ struct {
+ gx_cie_scalar_cache DecodeDEF[3];
+ } caches;
+};
+#define private_st_cie_def() /* in zcie.c */\
+ gs_private_st_ptrs1(st_cie_def, gs_cie_def, "gs_cie_def",\
+ cie_def_enum_ptrs, cie_def_reloc_ptrs, Table.table)
+
+/* A CIEBasedDEFG dictionary. */
+/****** NOT IMPLEMENTED YET ******/
+struct gs_cie_defg_s {
+ gs_cie_abc_common abc; /* must be first */
+#ifndef NEW_CIE
+ rc_header rc;
+#endif
+ gs_range4 RangeDEFG;
+ gs_cie_defg_proc4 DecodeDEFG;
+ gs_range4 RangeHIJK;
+ gx_color_lookup_table Table; /* [NH * NI][NJ * NK * 3] */
+ struct {
+ gx_cie_scalar_cache DecodeDEFG[4];
+ } caches;
+};
+#define private_st_cie_defg() /* in zcie.c */\
+ gs_private_st_ptrs1(st_cie_defg, gs_cie_defg, "gs_cie_defg",\
+ cie_defg_enum_ptrs, cie_defg_reloc_ptrs, Table.table)
+
+/* Default values for components */
+extern const gs_range3 Range3_default;
+extern const gs_range4 Range4_default;
+extern const gs_cie_defg_proc4 DecodeDEFG_default;
+extern const gs_cie_def_proc3 DecodeDEF_default;
+extern const gs_cie_abc_proc3 DecodeABC_default;
+extern const gs_cie_common_proc3 DecodeLMN_default;
+extern const gs_matrix3 Matrix3_default;
+extern const gs_range RangeA_default;
+extern const gs_cie_a_proc DecodeA_default;
+extern const gs_vector3 MatrixA_default;
+extern const gs_vector3 BlackPoint_default;
+extern const gs_cie_render_proc3 Encode_default;
+extern const gs_cie_transform_proc3 TransformPQR_default;
+extern const gs_cie_render_table_procs RenderTableT_default;
+
+/* ------ Rendering dictionaries ------ */
+
+struct gs_cie_wbsd_s {
+ struct { gs_vector3 xyz, pqr; } ws, bs, wd, bd;
+};
+/* The main dictionary */
+struct gs_cie_render_s {
+ rc_header rc;
+ gs_cie_wb points;
+ gs_matrix3 MatrixPQR;
+ gs_range3 RangePQR;
+ gs_cie_transform_proc3 TransformPQR;
+ gs_matrix3 MatrixLMN;
+ gs_cie_render_proc3 EncodeLMN;
+ gs_range3 RangeLMN;
+ gs_matrix3 MatrixABC;
+ gs_cie_render_proc3 EncodeABC;
+ gs_range3 RangeABC;
+ struct {
+ gx_color_lookup_table lookup; /* if table is 0, other */
+ /* members are not set */
+ gs_cie_render_table_procs T;
+ } RenderTable;
+ /* Following are computed when structure is initialized. */
+ gs_range3 DomainLMN;
+ gs_range3 DomainABC;
+ gs_matrix3 MatrixABCEncode;
+ cie_cached_value EncodeABC_base[3];
+ gs_matrix3 MatrixPQR_inverse_LMN;
+ gs_vector3 wdpqr, bdpqr;
+ struct {
+ gx_cie_vector_cache EncodeLMN[3]; /* mult. by M'ABCEncode */
+ gx_cie_scalar_cache EncodeABC[3];
+ gx_cie_scalar_cache RenderTableT[4];
+ bool RenderTableT_is_identity;
+ } caches;
+};
+#define private_st_cie_render() /* in zcrd.c */\
+ gs_private_st_ptrs1(st_cie_render, gs_cie_render, "gs_cie_render",\
+ cie_render_enum_ptrs, cie_render_reloc_ptrs, RenderTable.lookup.table)
+/* RenderTable.lookup.table points to an array of st_const_string_elements. */
+#define private_st_const_string() /* in gscie.c */\
+ gs_private_st_composite(st_const_string, gs_const_string, "gs_const_string",\
+ const_string_enum_ptrs, const_string_reloc_ptrs)
+extern_st(st_const_string_element);
+#define public_st_const_string_element() /* in gscie.c */\
+ gs_public_st_element(st_const_string_element, gs_const_string,\
+ "gs_const_string[]", const_string_elt_enum_ptrs,\
+ const_string_elt_reloc_ptrs, st_const_string)
+
+/* ------ Joint caches ------ */
+
+/* This cache depends on both the color space and the rendering */
+/* dictionary -- see above. */
+
+typedef struct gx_cie_joint_caches_s {
+ rc_header rc;
+ bool skipLMN;
+ gx_cie_vector_cache DecodeLMN[3]; /* mult. by dLMN_PQR */
+ gs_cie_wbsd points_sd;
+ gs_matrix3 MatrixLMN_PQR;
+ bool skipPQR;
+ gx_cie_vector_cache TransformPQR[3]; /* mult. by PQR_inverse_eLMN */
+} gx_cie_joint_caches;
+#define private_st_joint_caches() /* in gscie.c */\
+ gs_private_st_simple(st_joint_caches, gx_cie_joint_caches,\
+ "gx_cie_joint_caches")
+
+/* Internal routines */
+typedef struct gs_for_loop_params_s {
+ float init, step, limit;
+} gs_for_loop_params;
+void gs_cie_cache_init(P4(cie_cache_params *, gs_for_loop_params *,
+ const gs_range *, client_name_t));
+void gs_cie_cache_to_fracs(P1(gx_cie_scalar_cache *));
+void gs_cie_abc_complete(P1(gs_cie_abc *));
+void gs_cie_a_complete(P1(gs_cie_a *));
+int gs_cie_render_init(P1(gs_cie_render *));
+int gs_cie_render_complete(P1(gs_cie_render *));
+gx_cie_joint_caches *gx_currentciecaches(P1(gs_state *));
+const gs_cie_common *gs_cie_cs_common(P1(gs_state *));
+void gs_cie_cs_complete(P2(gs_state *, bool));
diff --git a/pstoraster/gscolor.c b/pstoraster/gscolor.c
new file mode 100644
index 000000000..6b0ccb0c8
--- /dev/null
+++ b/pstoraster/gscolor.c
@@ -0,0 +1,361 @@
+/* Copyright (C) 1989, 1992, 1993, 1994, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gscolor.c */
+/* Color and halftone operators for Ghostscript library */
+#include "gx.h"
+#include "gserrors.h"
+#include "gsstruct.h"
+#include "gsutil.h" /* for gs_next_ids */
+#include "gsccolor.h"
+#include "gxcspace.h"
+#include "gxdcconv.h"
+#include "gxdevice.h" /* for gx_color_index */
+#include "gxcmap.h"
+#include "gzstate.h"
+
+/* Imported from gsht.c */
+void gx_set_effective_transfer(P1(gs_state *));
+
+/* Define the standard color space types. */
+extern cs_proc_remap_color(gx_remap_DeviceGray);
+extern cs_proc_concretize_color(gx_concretize_DeviceGray);
+extern cs_proc_remap_concrete_color(gx_remap_concrete_DGray);
+extern cs_proc_remap_color(gx_remap_DeviceRGB);
+extern cs_proc_concretize_color(gx_concretize_DeviceRGB);
+extern cs_proc_remap_concrete_color(gx_remap_concrete_DRGB);
+const gs_color_space_type
+ gs_color_space_type_DeviceGray =
+ { gs_color_space_index_DeviceGray, 1, true,
+ gx_init_paint_1, gx_same_concrete_space,
+ gx_concretize_DeviceGray, gx_remap_concrete_DGray,
+ gx_remap_DeviceGray, gx_no_install_cspace,
+ gx_no_adjust_cspace_count, gx_no_adjust_color_count,
+ gx_no_cspace_enum_ptrs, gx_no_cspace_reloc_ptrs
+ },
+ gs_color_space_type_DeviceRGB =
+ { gs_color_space_index_DeviceRGB, 3, true,
+ gx_init_paint_3, gx_same_concrete_space,
+ gx_concretize_DeviceRGB, gx_remap_concrete_DRGB,
+ gx_remap_DeviceRGB, gx_no_install_cspace,
+ gx_no_adjust_cspace_count, gx_no_adjust_color_count,
+ gx_no_cspace_enum_ptrs, gx_no_cspace_reloc_ptrs
+ };
+
+/* Structure descriptors */
+public_st_color_space();
+public_st_client_color();
+
+/*
+ * Define permanent instances of the 3 device color spaces.
+ * The one for CMYK is initialized by gscolor1.c and may be NULL.
+ */
+const gs_color_space *gs_cs_static_DeviceGray;
+private gs_gc_root_t cs_gray_root;
+const gs_color_space *gs_cs_static_DeviceRGB;
+private gs_gc_root_t cs_rgb_root;
+const gs_color_space *gs_cs_static_DeviceCMYK = 0;
+private gs_gc_root_t cs_cmyk_root;
+void
+gs_gscolor_init(gs_memory_t *mem)
+{
+#define cs_init(cs_var, cs_type, cname)\
+ { gs_color_space *pcs =\
+ gs_alloc_struct(mem, gs_color_space, &st_color_space, cname);\
+ pcs->type = &cs_type;\
+ cs_var = pcs;\
+ }
+ cs_init(gs_cs_static_DeviceGray, gs_color_space_type_DeviceGray,
+ "gs_cs_static_DeviceGray");
+ gs_register_struct_root(mem, &cs_gray_root,
+ (void **)&gs_cs_static_DeviceGray,
+ "gs_cs_static_DeviceGray");
+ cs_init(gs_cs_static_DeviceRGB, gs_color_space_type_DeviceRGB,
+ "gs_cs_static_DeviceRGB");
+ gs_register_struct_root(mem, &cs_rgb_root,
+ (void **)&gs_cs_static_DeviceRGB,
+ "gs_cs_static_DeviceRGB");
+ gs_register_struct_root(mem, &cs_cmyk_root,
+ (void **)&gs_cs_static_DeviceCMYK,
+ "gs_cs_static_DeviceCMYK");
+#undef cs_init
+}
+const gs_color_space *
+gs_color_space_DeviceGray(void)
+{ return gs_cs_static_DeviceGray;
+}
+const gs_color_space *
+gs_color_space_DeviceRGB(void)
+{ return gs_cs_static_DeviceRGB;
+}
+const gs_color_space *
+gs_color_space_DeviceCMYK(void)
+{ return gs_cs_static_DeviceCMYK;
+}
+
+/* Get the index of a color space. */
+gs_color_space_index
+gs_color_space_get_index(const gs_color_space *pcs)
+{ return pcs->type->index;
+}
+
+/* Get the number of components in a color space. */
+int
+gs_color_space_num_components(const gs_color_space *pcs)
+{ return pcs->type->num_components;
+}
+
+/* Get the base space of an Indexed color space. */
+const gs_color_space *
+gs_color_space_indexed_base_space(const gs_color_space *pcs)
+{ return (const gs_color_space *)&pcs->params.indexed.base_space;
+}
+
+/* Initialize colors with 1, or 3, or 4 paint components. */
+/* (These are only used by setcolorspace.) */
+void
+gx_init_paint_1(gs_client_color *pcc, const gs_color_space *pcs)
+{ pcc->paint.values[0] = 0.0;
+}
+void
+gx_init_paint_3(gs_client_color *pcc, const gs_color_space *pcs)
+{ pcc->paint.values[2] = 0.0;
+ pcc->paint.values[1] = 0.0;
+ pcc->paint.values[0] = 0.0;
+}
+void
+gx_init_paint_4(gs_client_color *pcc, const gs_color_space *pcs)
+{ /* DeviceCMYK and CIEBasedDEFG spaces initialize to 0,0,0,1. */
+ pcc->paint.values[3] = 1.0;
+ gx_init_paint_3(pcc, pcs);
+}
+
+/* Null color space installation procedure. */
+int
+gx_no_install_cspace(gs_color_space *pcs, gs_state *pgs)
+{ return 0;
+}
+
+/* Null reference count adjustment procedures. */
+void
+gx_no_adjust_cspace_count(const gs_color_space *pcs, gs_state *pgs, int delta)
+{
+}
+void
+gx_no_adjust_color_count(const gs_client_color *pcc, const gs_color_space *pcs, gs_state *pgs, int delta)
+{
+}
+
+/* GC procedures */
+
+#define pcs ((gs_color_space *)vptr)
+
+private ENUM_PTRS_BEGIN_PROC(color_space_enum_ptrs) {
+ return (*pcs->type->enum_ptrs)(pcs, size, index, pep);
+ENUM_PTRS_END_PROC }
+private RELOC_PTRS_BEGIN(color_space_reloc_ptrs) {
+ (*pcs->type->reloc_ptrs)(pcs, size, gcst);
+} RELOC_PTRS_END
+
+#undef pcs
+
+/* Force a parameter into the range [0.0..1.0]. */
+#define force_unit(p) (p < 0.0 ? 0.0 : p > 1.0 ? 1.0 : p)
+
+/* Forward declarations */
+void load_transfer_map(P3(gs_state *, gx_transfer_map *, floatp));
+
+/* setgray */
+int
+gs_setgray(gs_state *pgs, floatp gray)
+{ if ( pgs->in_cachedevice ) return_error(gs_error_undefined);
+ cs_adjust_counts(pgs, -1);
+ pgs->ccolor->paint.values[0] = gray;
+ pgs->color_space->type = &gs_color_space_type_DeviceGray;
+ gx_unset_dev_color(pgs);
+ return 0;
+}
+
+/* currentgray */
+float
+gs_currentgray(const gs_state *pgs)
+{ const gs_client_color *pcc = pgs->ccolor;
+ switch ( pgs->color_space->type->index )
+ {
+ case gs_color_space_index_DeviceGray:
+ return pcc->paint.values[0];
+ case gs_color_space_index_DeviceRGB:
+ return frac2float(color_rgb_to_gray(
+ float2frac(pcc->paint.values[0]),
+ float2frac(pcc->paint.values[1]),
+ float2frac(pcc->paint.values[2]),
+ pgs));
+ case gs_color_space_index_DeviceCMYK:
+ return frac2float(color_cmyk_to_gray(
+ float2frac(pcc->paint.values[0]),
+ float2frac(pcc->paint.values[1]),
+ float2frac(pcc->paint.values[2]),
+ float2frac(pcc->paint.values[3]),
+ pgs));
+ default:
+ return 0.0;
+ }
+}
+
+/* setrgbcolor */
+int
+gs_setrgbcolor(gs_state *pgs, floatp r, floatp g, floatp b)
+{ gs_client_color *pcc = pgs->ccolor;
+ if ( pgs->in_cachedevice ) return_error(gs_error_undefined);
+ cs_adjust_counts(pgs, -1);
+ pcc->paint.values[0] = r;
+ pcc->paint.values[1] = g;
+ pcc->paint.values[2] = b;
+ pcc->pattern = 0; /* for GC */
+ pgs->color_space->type = &gs_color_space_type_DeviceRGB;
+ gx_unset_dev_color(pgs);
+ return 0;
+}
+
+/* currentrgbcolor */
+int
+gs_currentrgbcolor(const gs_state *pgs, float pr3[3])
+{ const gs_client_color *pcc = pgs->ccolor;
+ switch ( pgs->color_space->type->index )
+ {
+ case gs_color_space_index_DeviceGray:
+ pr3[0] = pr3[1] = pr3[2] = pcc->paint.values[0];
+ break;
+ case gs_color_space_index_DeviceRGB:
+ pr3[0] = pcc->paint.values[0];
+ pr3[1] = pcc->paint.values[1];
+ pr3[2] = pcc->paint.values[2];
+ break;
+ case gs_color_space_index_DeviceCMYK:
+ { frac frgb[3];
+ color_cmyk_to_rgb(
+ float2frac(pcc->paint.values[0]),
+ float2frac(pcc->paint.values[1]),
+ float2frac(pcc->paint.values[2]),
+ float2frac(pcc->paint.values[3]),
+ pgs, frgb);
+ pr3[0] = frac2float(frgb[0]);
+ pr3[1] = frac2float(frgb[1]);
+ pr3[2] = frac2float(frgb[2]);
+ } break;
+ default:
+ pr3[0] = pr3[1] = pr3[2] = 0.0;
+ }
+ return 0;
+}
+
+/* setalpha */
+int
+gs_setalpha(gs_state *pgs, floatp alpha)
+{ pgs->alpha = (gx_color_value)(force_unit(alpha) * gx_max_color_value);
+ gx_unset_dev_color(pgs);
+ return 0;
+}
+
+/* currentalpha */
+float
+gs_currentalpha(const gs_state *pgs)
+{ return (float)pgs->alpha / gx_max_color_value;
+}
+
+/* settransfer */
+/* Remap=0 is used by the interpreter. */
+int
+gs_settransfer(gs_state *pgs, gs_mapping_proc tproc)
+{ return gs_settransfer_remap(pgs, tproc, true);
+}
+int
+gs_settransfer_remap(gs_state *pgs, gs_mapping_proc tproc, bool remap)
+{ gx_transfer_colored *ptran = &pgs->set_transfer.colored;
+ /* We can safely decrement the reference counts */
+ /* of the non-gray transfer maps, because */
+ /* if any of them get freed, the rc_unshare can't fail. */
+ rc_decrement(ptran->red, pgs->memory, "gs_settransfer");
+ rc_decrement(ptran->green, pgs->memory, "gs_settransfer");
+ rc_decrement(ptran->blue, pgs->memory, "gs_settransfer");
+ rc_unshare_struct(ptran->gray,
+ gx_transfer_map, &st_transfer_map,
+ pgs->memory, goto fail, "gs_settransfer");
+ ptran->gray->proc = tproc;
+ ptran->gray->id = gs_next_ids(1);
+ ptran->red = ptran->gray;
+ ptran->green = ptran->gray;
+ ptran->blue = ptran->gray;
+ ptran->gray->rc.ref_count += 3;
+ if ( remap )
+ { load_transfer_map(pgs, ptran->gray, 0.0);
+ gx_set_effective_transfer(pgs);
+ gx_unset_dev_color(pgs);
+ }
+ return 0;
+fail: rc_increment(ptran->red);
+ rc_increment(ptran->green);
+ rc_increment(ptran->blue);
+ return_error(gs_error_VMerror);
+}
+
+/* currenttransfer */
+gs_mapping_proc
+gs_currenttransfer(const gs_state *pgs)
+{ return pgs->set_transfer.colored.gray->proc;
+}
+
+/* ------ Non-operator routines ------ */
+
+/* Set device color = 1 for writing into the character cache. */
+void
+gx_set_device_color_1(gs_state *pgs)
+{ gx_device_color *pdc = pgs->dev_color;
+ gs_client_color *pcc = pgs->ccolor;
+ cs_adjust_counts(pgs, -1);
+ pcc->paint.values[0] = 0.0;
+ pcc->pattern = 0; /* for GC */
+ pgs->color_space->type = &gs_color_space_type_DeviceGray;
+ color_set_pure(pdc, 1);
+ pgs->log_op = lop_default;
+}
+
+/* ------ Internal routines ------ */
+
+/* Load one cached transfer map. */
+/* This is exported for gscolor1.c. */
+void
+load_transfer_map(gs_state *pgs, gx_transfer_map *pmap, floatp min_value)
+{ gs_mapping_proc proc = pmap->proc;
+ frac *values = pmap->values;
+ frac fmin = float2frac(min_value);
+ int i;
+ for ( i = 0; i < transfer_map_size; i++ )
+ { float fval =
+ (*proc)((float)i / (transfer_map_size - 1), pmap);
+ values[i] =
+ (fval < min_value ? fmin :
+ fval >= 1.0 ? frac_1 :
+ float2frac(fval));
+ }
+}
diff --git a/pstoraster/gscolor.h b/pstoraster/gscolor.h
new file mode 100644
index 000000000..0bfe2a96c
--- /dev/null
+++ b/pstoraster/gscolor.h
@@ -0,0 +1,44 @@
+/* Copyright (C) 1991, 1992, 1993 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gscolor.h */
+/* Client interface to color routines */
+
+#ifndef gscolor_INCLUDED
+# define gscolor_INCLUDED
+
+#include "gxtmap.h"
+
+/* Color and gray interface */
+int gs_setgray(P2(gs_state *, floatp));
+float gs_currentgray(P1(const gs_state *));
+int gs_setrgbcolor(P4(gs_state *, floatp, floatp, floatp)),
+ gs_currentrgbcolor(P2(const gs_state *, float [3])),
+ gs_setalpha(P2(gs_state *, floatp));
+float gs_currentalpha(P1(const gs_state *));
+/* Transfer function */
+int gs_settransfer(P2(gs_state *, gs_mapping_proc)),
+ gs_settransfer_remap(P3(gs_state *, gs_mapping_proc, bool));
+gs_mapping_proc gs_currenttransfer(P1(const gs_state *));
+
+#endif /* gscolor_INCLUDED */
diff --git a/pstoraster/gscolor1.c b/pstoraster/gscolor1.c
new file mode 100644
index 000000000..5f1fdda07
--- /dev/null
+++ b/pstoraster/gscolor1.c
@@ -0,0 +1,231 @@
+/* Copyright (C) 1989, 1992, 1993, 1994, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gscolor.c */
+/* Level 1 extended color operators for Ghostscript library */
+#include "gx.h"
+#include "gserrors.h"
+#include "gsstruct.h"
+#include "gsutil.h" /* for gs_next_ids */
+#include "gsccolor.h"
+#include "gxcspace.h"
+#include "gxdcconv.h"
+#include "gxdevice.h" /* for gx_color_index */
+#include "gxcmap.h"
+#include "gzstate.h"
+#include "gscolor1.h"
+
+/* Imports from gscolor.c */
+void load_transfer_map(P3(gs_state *, gx_transfer_map *, floatp));
+
+/* Imported from gsht.c */
+void gx_set_effective_transfer(P1(gs_state *));
+
+/* Define the CMYK color space type. */
+extern cs_proc_remap_color(gx_remap_DeviceCMYK);
+extern cs_proc_concretize_color(gx_concretize_DeviceCMYK);
+extern cs_proc_remap_concrete_color(gx_remap_concrete_DCMYK);
+const gs_color_space_type
+ gs_color_space_type_DeviceCMYK =
+ { gs_color_space_index_DeviceCMYK, 4, true,
+ gx_init_paint_4, gx_same_concrete_space,
+ gx_concretize_DeviceCMYK, gx_remap_concrete_DCMYK,
+ gx_remap_DeviceCMYK, gx_no_install_cspace,
+ gx_no_adjust_cspace_count, gx_no_adjust_color_count,
+ gx_no_cspace_enum_ptrs, gx_no_cspace_reloc_ptrs
+ };
+
+/* Initialize the permanent color space instance (declared in gscolor.c). */
+extern const gs_color_space *gs_cs_static_DeviceCMYK;
+void
+gs_gscolor1_init(gs_memory_t *mem)
+{ gs_color_space *pcs =
+ gs_alloc_struct(mem, gs_color_space, &st_color_space,
+ "gs_cs_static_DeviceCMYK");
+ pcs->type = &gs_color_space_type_DeviceCMYK;
+ gs_cs_static_DeviceCMYK = pcs;
+}
+
+/* setcmykcolor */
+int
+gs_setcmykcolor(gs_state *pgs, floatp c, floatp m, floatp y, floatp k)
+{ gs_client_color *pcc = pgs->ccolor;
+ if ( pgs->in_cachedevice ) return_error(gs_error_undefined);
+ cs_adjust_counts(pgs, -1);
+ pcc->paint.values[0] = c;
+ pcc->paint.values[1] = m;
+ pcc->paint.values[2] = y;
+ pcc->paint.values[3] = k;
+ pcc->pattern = 0; /* for GC */
+ pgs->color_space->type = &gs_color_space_type_DeviceCMYK;
+ gx_unset_dev_color(pgs);
+ return 0;
+}
+
+/* currentcmykcolor */
+int
+gs_currentcmykcolor(const gs_state *pgs, float pr4[4])
+{ const gs_client_color *pcc = pgs->ccolor;
+ switch ( pgs->color_space->type->index )
+ {
+ case gs_color_space_index_DeviceGray:
+ pr4[0] = pr4[1] = pr4[2] = 0.0;
+ pr4[3] = 1.0 - pcc->paint.values[0];
+ break;
+ case gs_color_space_index_DeviceRGB:
+ { frac fcmyk[4];
+ color_rgb_to_cmyk(
+ float2frac(pcc->paint.values[0]),
+ float2frac(pcc->paint.values[1]),
+ float2frac(pcc->paint.values[2]),
+ pgs, fcmyk);
+ pr4[0] = frac2float(fcmyk[0]);
+ pr4[1] = frac2float(fcmyk[1]);
+ pr4[2] = frac2float(fcmyk[2]);
+ pr4[3] = frac2float(fcmyk[3]);
+ } break;
+ case gs_color_space_index_DeviceCMYK:
+ pr4[0] = pcc->paint.values[0];
+ pr4[1] = pcc->paint.values[1];
+ pr4[2] = pcc->paint.values[2];
+ pr4[3] = pcc->paint.values[3];
+ break;
+ default:
+ pr4[0] = pr4[1] = pr4[2] = 0.0;
+ pr4[3] = 1.0;
+ }
+ return 0;
+}
+
+/* setblackgeneration */
+/* Remap=0 is used by the interpreter. */
+int
+gs_setblackgeneration(gs_state *pgs, gs_mapping_proc proc)
+{ return gs_setblackgeneration_remap(pgs, proc, true);
+}
+int
+gs_setblackgeneration_remap(gs_state *pgs, gs_mapping_proc proc, bool remap)
+{ rc_unshare_struct(pgs->black_generation, gx_transfer_map,
+ &st_transfer_map, pgs->memory,
+ return_error(gs_error_VMerror),
+ "gs_setblackgeneration");
+ pgs->black_generation->proc = proc;
+ pgs->black_generation->id = gs_next_ids(1);
+ if ( remap )
+ { load_transfer_map(pgs, pgs->black_generation, 0.0);
+ gx_unset_dev_color(pgs);
+ }
+ return 0;
+}
+
+/* currentblackgeneration */
+gs_mapping_proc
+gs_currentblackgeneration(const gs_state *pgs)
+{ return pgs->black_generation->proc;
+}
+
+/* setundercolorremoval */
+/* Remap=0 is used by the interpreter. */
+int
+gs_setundercolorremoval(gs_state *pgs, gs_mapping_proc proc)
+{ return gs_setundercolorremoval_remap(pgs, proc, true);
+}
+int
+gs_setundercolorremoval_remap(gs_state *pgs, gs_mapping_proc proc, bool remap)
+{ rc_unshare_struct(pgs->undercolor_removal, gx_transfer_map,
+ &st_transfer_map, pgs->memory,
+ return_error(gs_error_VMerror),
+ "gs_setundercolorremoval");
+ pgs->undercolor_removal->proc = proc;
+ pgs->undercolor_removal->id = gs_next_ids(1);
+ if ( remap )
+ { load_transfer_map(pgs, pgs->undercolor_removal, -1.0);
+ gx_unset_dev_color(pgs);
+ }
+ return 0;
+}
+
+/* currentundercolorremoval */
+gs_mapping_proc
+gs_currentundercolorremoval(const gs_state *pgs)
+{ return pgs->undercolor_removal->proc;
+}
+
+/* setcolortransfer */
+/* Remap=0 is used by the interpreter. */
+int
+gs_setcolortransfer_remap(gs_state *pgs, gs_mapping_proc red_proc,
+ gs_mapping_proc green_proc, gs_mapping_proc blue_proc,
+ gs_mapping_proc gray_proc, bool remap)
+{ gx_transfer_colored *ptran = &pgs->set_transfer.colored;
+ gx_transfer_colored old;
+ gs_id new_ids = gs_next_ids(4);
+
+ old = *ptran;
+ rc_unshare_struct(ptran->gray, gx_transfer_map, &st_transfer_map,
+ pgs->memory, goto fgray, "gs_setcolortransfer");
+ rc_unshare_struct(ptran->red, gx_transfer_map, &st_transfer_map,
+ pgs->memory, goto fred, "gs_setcolortransfer");
+ rc_unshare_struct(ptran->green, gx_transfer_map, &st_transfer_map,
+ pgs->memory, goto fgreen, "gs_setcolortransfer");
+ rc_unshare_struct(ptran->blue, gx_transfer_map, &st_transfer_map,
+ pgs->memory, goto fblue, "gs_setcolortransfer");
+ ptran->gray->proc = gray_proc;
+ ptran->gray->id = new_ids;
+ ptran->red->proc = red_proc;
+ ptran->red->id = new_ids + 1;
+ ptran->green->proc = green_proc;
+ ptran->green->id = new_ids + 2;
+ ptran->blue->proc = blue_proc;
+ ptran->blue->id = new_ids + 3;
+ if ( remap )
+ { load_transfer_map(pgs, ptran->red, 0.0);
+ load_transfer_map(pgs, ptran->green, 0.0);
+ load_transfer_map(pgs, ptran->blue, 0.0);
+ load_transfer_map(pgs, ptran->gray, 0.0);
+ gx_set_effective_transfer(pgs);
+ gx_unset_dev_color(pgs);
+ }
+ return 0;
+fblue: rc_assign(ptran->green, old.green, pgs->memory, "setcolortransfer");
+fgreen: rc_assign(ptran->red, old.red, pgs->memory, "setcolortransfer");
+fred: rc_assign(ptran->gray, old.gray, pgs->memory, "setcolortransfer");
+fgray: return_error(gs_error_VMerror);
+}
+int
+gs_setcolortransfer(gs_state *pgs, gs_mapping_proc red_proc,
+ gs_mapping_proc green_proc, gs_mapping_proc blue_proc,
+ gs_mapping_proc gray_proc)
+{ return gs_setcolortransfer_remap(pgs, red_proc, green_proc,
+ blue_proc, gray_proc, true);
+}
+
+/* currentcolortransfer */
+void
+gs_currentcolortransfer(const gs_state *pgs, gs_mapping_proc procs[4])
+{ const gx_transfer_colored *ptran = &pgs->set_transfer.colored;
+ procs[0] = ptran->red->proc;
+ procs[1] = ptran->green->proc;
+ procs[2] = ptran->blue->proc;
+ procs[3] = ptran->gray->proc;
+}
diff --git a/pstoraster/gscolor1.h b/pstoraster/gscolor1.h
new file mode 100644
index 000000000..e7c859d7b
--- /dev/null
+++ b/pstoraster/gscolor1.h
@@ -0,0 +1,49 @@
+/* Copyright (C) 1993 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gscolor1.h */
+/* Client interface to Level 1 extended color facilities */
+/* Requires gscolor.h */
+
+#ifndef gscolor1_INCLUDED
+# define gscolor1_INCLUDED
+
+/* Color and gray interface */
+int gs_setcmykcolor(P5(gs_state *, floatp, floatp, floatp, floatp)),
+ gs_currentcmykcolor(P2(const gs_state *, float [4])),
+ gs_setblackgeneration(P2(gs_state *, gs_mapping_proc)),
+ gs_setblackgeneration_remap(P3(gs_state *, gs_mapping_proc, bool));
+gs_mapping_proc gs_currentblackgeneration(P1(const gs_state *));
+int gs_setundercolorremoval(P2(gs_state *, gs_mapping_proc)),
+ gs_setundercolorremoval_remap(P3(gs_state *, gs_mapping_proc, bool));
+gs_mapping_proc gs_currentundercolorremoval(P1(const gs_state *));
+/* Transfer function */
+int gs_setcolortransfer(P5(gs_state *, gs_mapping_proc /*red*/,
+ gs_mapping_proc /*green*/, gs_mapping_proc /*blue*/,
+ gs_mapping_proc /*gray*/)),
+ gs_setcolortransfer_remap(P6(gs_state *, gs_mapping_proc /*red*/,
+ gs_mapping_proc /*green*/, gs_mapping_proc /*blue*/,
+ gs_mapping_proc /*gray*/, bool));
+void gs_currentcolortransfer(P2(const gs_state *, gs_mapping_proc [4]));
+
+#endif /* gscolor1_INCLUDED */
diff --git a/pstoraster/gscolor2.c b/pstoraster/gscolor2.c
new file mode 100644
index 000000000..0c7d49643
--- /dev/null
+++ b/pstoraster/gscolor2.c
@@ -0,0 +1,205 @@
+/* Copyright (C) 1992, 1994, 1994 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gscolor2.c */
+/* Level 2 color operators for Ghostscript library */
+#include "gx.h"
+#include "gserrors.h"
+#include "gxarith.h"
+#include "gxfixed.h" /* ditto */
+#include "gxmatrix.h" /* for gzstate.h */
+#include "gxcspace.h" /* for gscolor2.h */
+#include "gxcolor2.h"
+#include "gzstate.h"
+
+/* Define the Indexed color space type. */
+cs_declare_procs(private, gx_concretize_Indexed, gx_install_Indexed,
+ gx_adjust_cspace_Indexed,
+ gx_enum_ptrs_Indexed, gx_reloc_ptrs_Indexed);
+private cs_proc_concrete_space(gx_concrete_space_Indexed);
+const gs_color_space_type
+ gs_color_space_type_Indexed =
+ { gs_color_space_index_Indexed, 1, false,
+ gx_init_paint_1, gx_concrete_space_Indexed,
+ gx_concretize_Indexed, NULL,
+ gx_default_remap_color, gx_install_Indexed,
+ gx_adjust_cspace_Indexed, gx_no_adjust_color_count,
+ gx_enum_ptrs_Indexed, gx_reloc_ptrs_Indexed
+ };
+
+/* setcolorspace */
+int
+gs_setcolorspace(gs_state *pgs, gs_color_space *pcs)
+{ int code;
+ gs_color_space cs_old;
+ gs_client_color cc_old;
+ if ( pgs->in_cachedevice )
+ return_error(gs_error_undefined);
+ cs_old = *pgs->color_space;
+ cc_old = *pgs->ccolor;
+ (*pcs->type->adjust_cspace_count)(pcs, pgs, 1);
+ *pgs->color_space = *pcs;
+ if ( (code = (*pcs->type->install_cspace)(pcs, pgs)) < 0 )
+ goto rcs;
+ cs_full_init_color(pgs->ccolor, pcs);
+ (*cs_old.type->adjust_color_count)(&cc_old, &cs_old, pgs, -1);
+ (*cs_old.type->adjust_cspace_count)(&cs_old, pgs, -1);
+ gx_unset_dev_color(pgs);
+ return code;
+ /* Restore the color space if installation failed. */
+rcs: *pgs->color_space = cs_old;
+ (*pcs->type->adjust_cspace_count)(pcs, pgs, -1);
+ return code;
+}
+
+/* currentcolorspace */
+const gs_color_space *
+gs_currentcolorspace(const gs_state *pgs)
+{ return pgs->color_space;
+}
+
+/* setcolor */
+int
+gs_setcolor(gs_state *pgs, const gs_client_color *pcc)
+{ gs_color_space *pcs = pgs->color_space;
+ if ( pgs->in_cachedevice )
+ return_error(gs_error_undefined);
+ (*pcs->type->adjust_color_count)(pcc, pcs, pgs, 1);
+ (*pcs->type->adjust_color_count)(pgs->ccolor, pcs, pgs, -1);
+ *pgs->ccolor = *pcc;
+ gx_unset_dev_color(pgs);
+ return 0;
+}
+
+/* currentcolor */
+const gs_client_color *
+gs_currentcolor(const gs_state *pgs)
+{ return pgs->ccolor;
+}
+
+/* setoverprint */
+void
+gs_setoverprint(gs_state *pgs, bool ovp)
+{ pgs->overprint = ovp;
+}
+
+/* currentoverprint */
+bool
+gs_currentoverprint(const gs_state *pgs)
+{ return pgs->overprint;
+}
+
+/* ------ Internal routines ------ */
+
+/* Color remapping for Indexed color spaces. */
+
+private const gs_color_space *
+gx_concrete_space_Indexed(const gs_color_space *pcs, const gs_state *pgs)
+{ const gs_color_space *pbcs =
+ (const gs_color_space *)&pcs->params.indexed.base_space;
+ return cs_concrete_space(pbcs, pgs);
+}
+
+private int
+gx_concretize_Indexed(const gs_client_color *pc, const gs_color_space *pcs,
+ frac *pconc, const gs_state *pgs)
+{ float value = pc->paint.values[0];
+ int index =
+ (is_fneg(value) ? 0 :
+ value >= pcs->params.indexed.hival ?
+ pcs->params.indexed.hival :
+ (int)value);
+ gs_client_color cc;
+ const gs_color_space *pbcs =
+ (const gs_color_space *)&pcs->params.indexed.base_space;
+ if ( pcs->params.indexed.use_proc )
+ { int code = (*pcs->params.indexed.lookup.map->proc.lookup_index)(&pcs->params.indexed, index, &cc.paint.values[0]);
+ if ( code < 0 ) return code;
+ }
+ else
+ { int m = pcs->params.indexed.base_space.type->num_components;
+ const byte *pcomp =
+ pcs->params.indexed.lookup.table.data + m * index;
+ switch ( m )
+ {
+ default: return_error(gs_error_rangecheck);
+ case 4: cc.paint.values[3] = pcomp[3] * (1.0 / 255.0);
+ case 3: cc.paint.values[2] = pcomp[2] * (1.0 / 255.0);
+ cc.paint.values[1] = pcomp[1] * (1.0 / 255.0);
+ case 1: cc.paint.values[0] = pcomp[0] * (1.0 / 255.0);
+ }
+ }
+ return (*pbcs->type->concretize_color)(&cc, pbcs, pconc, pgs);
+}
+
+/* Color space installation ditto. */
+
+private int
+gx_install_Indexed(gs_color_space *pcs, gs_state *pgs)
+{ return (*pcs->params.indexed.base_space.type->install_cspace)
+ ((gs_color_space *)&pcs->params.indexed.base_space, pgs);
+}
+
+/* Color space reference count adjustment ditto. */
+
+private void
+gx_adjust_cspace_Indexed(const gs_color_space *pcs, gs_state *pgs, int delta)
+{ if ( pcs->params.indexed.use_proc )
+ { rc_adjust_const(pcs->params.indexed.lookup.map, delta,
+ pgs->memory, "gx_adjust_Indexed");
+ }
+ (*pcs->params.indexed.base_space.type->adjust_cspace_count)
+ ((const gs_color_space *)&pcs->params.indexed.base_space, pgs, delta);
+}
+
+/* GC procedures ditto. */
+
+#define pcs ((gs_color_space *)vptr)
+
+private ENUM_PTRS_BEGIN(gx_enum_ptrs_Indexed) {
+ return (*pcs->params.indexed.base_space.type->enum_ptrs)
+ (&pcs->params.indexed.base_space,
+ sizeof(pcs->params.indexed.base_space), index-1, pep);
+ }
+ case 0:
+ if ( pcs->params.indexed.use_proc )
+ *pep = (void *)pcs->params.indexed.lookup.map;
+ else
+ { pcs->params.indexed.lookup.table.size =
+ (pcs->params.indexed.hival + 1) *
+ pcs->params.indexed.base_space.type->num_components;
+ *pep = &pcs->params.indexed.lookup.table;
+ return ptr_const_string_type;
+ }
+ break;
+ENUM_PTRS_END
+private RELOC_PTRS_BEGIN(gx_reloc_ptrs_Indexed) {
+ (*pcs->params.indexed.base_space.type->reloc_ptrs)
+ (&pcs->params.indexed.base_space, sizeof(gs_base_color_space), gcst);
+ if ( pcs->params.indexed.use_proc )
+ RELOC_PTR(gs_color_space, params.indexed.lookup.map);
+ else
+ RELOC_CONST_STRING_PTR(gs_color_space, params.indexed.lookup.table);
+} RELOC_PTRS_END
+
+#undef pcs
diff --git a/pstoraster/gscolor2.h b/pstoraster/gscolor2.h
new file mode 100644
index 000000000..36e923dc8
--- /dev/null
+++ b/pstoraster/gscolor2.h
@@ -0,0 +1,82 @@
+/* Copyright (C) 1992, 1993 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gscolor2.h */
+/* Client interface to Level 2 color facilities */
+/* (requires gscspace.h, gsmatrix.h) */
+#include "gsccolor.h"
+#include "gsuid.h" /* for pattern template */
+#include "gxbitmap.h" /* for makebitmappattern */
+
+/* Note: clients should use rc_alloc_struct_0 (in gsrefct.h) to allocate */
+/* CIE color spaces or rendering structures; makepattern uses */
+/* rc_alloc_struct_1 to allocate pattern instances. */
+
+/* General color routines */
+const gs_color_space *gs_currentcolorspace(P1(const gs_state *));
+int gs_setcolorspace(P2(gs_state *, gs_color_space *));
+const gs_client_color *gs_currentcolor(P1(const gs_state *));
+int gs_setcolor(P2(gs_state *, const gs_client_color *));
+bool gs_currentoverprint(P1(const gs_state *));
+void gs_setoverprint(P2(gs_state *, bool));
+
+/* CIE-specific routines */
+#ifndef gs_cie_render_DEFINED
+# define gs_cie_render_DEFINED
+typedef struct gs_cie_render_s gs_cie_render;
+#endif
+const gs_cie_render *gs_currentcolorrendering(P1(const gs_state *));
+int gs_setcolorrendering(P2(gs_state *, gs_cie_render *));
+
+/* Pattern template */
+typedef struct gs_client_pattern_s {
+ gs_uid uid; /* XUID or nothing */
+ int PaintType;
+ int TilingType;
+ gs_rect BBox;
+ float XStep;
+ float YStep;
+ int (*PaintProc)(P2(const gs_client_color *, gs_state *));
+ void *client_data; /* additional client data */
+} gs_client_pattern;
+#define private_st_client_pattern() /* in gspcolor.c */\
+ gs_private_st_ptrs1(st_client_pattern, gs_client_pattern,\
+ "client pattern", client_pattern_enum_ptrs, client_pattern_reloc_ptrs,\
+ client_data)
+#define st_client_pattern_max_ptrs 1
+
+/* Pattern-specific routines */
+/* The gs_memory_t argument for makepattern may be null, meaning use the */
+/* same allocator as for the gs_state argument. */
+int gs_makepattern(P5(gs_client_color *, const gs_client_pattern *,
+ const gs_matrix *, gs_state *, gs_memory_t *));
+int gs_setpattern(P2(gs_state *, const gs_client_color *));
+int gs_setpatternspace(P1(gs_state *));
+const gs_client_pattern *gs_getpattern(P1(const gs_client_color *));
+
+/* makebitmappattern is a hack for PCL printing. */
+/* If the Boolean argument is true, the result is a mask; */
+/* if false, the result is a black-and-white solid pattern */
+/* (with black=1). */
+int gs_makebitmappattern(P5(gs_client_color *, const gx_tile_bitmap *,
+ bool, gs_state *, gs_memory_t *));
diff --git a/pstoraster/gscoord.c b/pstoraster/gscoord.c
new file mode 100644
index 000000000..91526afe6
--- /dev/null
+++ b/pstoraster/gscoord.c
@@ -0,0 +1,463 @@
+/* Copyright (C) 1989, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gscoord.c */
+/* Coordinate system operators for Ghostscript library */
+#include "math_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gsccode.h" /* for gxfont.h */
+#include "gxfarith.h"
+#include "gxfixed.h"
+#include "gxmatrix.h"
+#include "gxfont.h" /* for char_tm */
+#include "gxpath.h" /* for gx_path_translate */
+#include "gzstate.h"
+#include "gxcoord.h" /* requires gsmatrix, gsstate */
+#include "gxdevice.h"
+
+/* Choose whether to enable the rounding code in update_ctm. */
+#define ROUND_CTM_FIXED 1
+
+/* Forward declarations */
+#ifdef DEBUG
+#define trace_ctm(pgs) trace_matrix_fixed(&(pgs)->ctm)
+private void near trace_matrix_fixed(P1(const gs_matrix_fixed *));
+private void near trace_matrix(P1(const gs_matrix *));
+#endif
+
+/* Macro for ensuring ctm_inverse is valid */
+#ifdef DEBUG
+#define print_inverse(pgs)\
+if ( gs_debug_c('x') )\
+ dprintf("[x]Inverting:\n"), trace_ctm(pgs), trace_matrix(&pgs->ctm_inverse)
+#else
+#define print_inverse(pgs) DO_NOTHING
+#endif
+#define ensure_inverse_valid(pgs)\
+ if ( !pgs->ctm_inverse_valid )\
+ { int code = ctm_set_inverse(pgs);\
+ if ( code < 0 ) return code;\
+ }
+
+private int
+ctm_set_inverse(gs_state *pgs)
+{ int code = gs_matrix_invert(&ctm_only(pgs), &pgs->ctm_inverse);
+ print_inverse(pgs);
+ if ( code < 0 ) return code;
+ pgs->ctm_inverse_valid = true;
+ return 0;
+}
+
+/* Machinery for updating fixed version of ctm. */
+/*
+ * We (conditionally) adjust the floating point translation
+ * so that it exactly matches the (rounded) fixed translation.
+ * This avoids certain unpleasant rounding anomalies, such as
+ * 0 0 moveto currentpoint not returning 0 0, and () stringwidth
+ * not returning 0 0.
+ */
+#if ROUND_CTM_FIXED
+# define update_t_fixed(mat, t, t_fixed, v)\
+ (set_float2fixed_vars((mat).t_fixed, v),\
+ set_fixed2float_var((mat).t, (mat).t_fixed))
+#else /* !ROUND_CTM_FIXED */
+# define update_t_fixed(mat, t, t_fixed, v)\
+ ((mat).t = (v),\
+ set_float2fixed_vars((mat).t_fixed, (mat).t))
+#endif /* (!)ROUND_CTM_FIXED */
+#define f_fits_in_fixed(f) f_fits_in_bits(f, fixed_int_bits)
+#define update_matrix_fixed(mat, xt, yt)\
+ ((mat).txy_fixed_valid = (f_fits_in_fixed(xt) && f_fits_in_fixed(yt) ?\
+ (update_t_fixed(mat, tx, tx_fixed, xt),\
+ update_t_fixed(mat, ty, ty_fixed, yt), true) :\
+ ((mat).tx = (xt), (mat).ty = (yt), false)))
+#define update_ctm(pgs, xt, yt)\
+ (pgs->ctm_inverse_valid = false,\
+ pgs->char_tm_valid = false,\
+ update_matrix_fixed(pgs->ctm, xt, yt))
+
+/* ------ Coordinate system definition ------ */
+
+int
+gs_initmatrix(gs_state *pgs)
+{ gs_matrix imat;
+ gs_defaultmatrix(pgs, &imat);
+ update_ctm(pgs, imat.tx, imat.ty);
+ set_ctm_only(pgs, imat);
+#ifdef DEBUG
+if ( gs_debug_c('x') )
+ dprintf("[x]initmatrix:\n"), trace_ctm(pgs);
+#endif
+ return 0;
+}
+
+int
+gs_defaultmatrix(const gs_state *pgs, gs_matrix *pmat)
+{ gx_device *dev;
+ if ( pgs->ctm_default_set ) /* set after Install */
+ { *pmat = pgs->ctm_default;
+ return 1;
+ }
+ dev = gs_currentdevice_inline(pgs);
+ gs_deviceinitialmatrix(dev, pmat);
+ /* Add in the translation for the Margins. */
+ pmat->tx += dev->Margins[0] *
+ dev->HWResolution[0] / dev->MarginsHWResolution[0];
+ pmat->ty += dev->Margins[1] *
+ dev->HWResolution[1] / dev->MarginsHWResolution[1];
+ return 0;
+}
+
+int
+gs_setdefaultmatrix(gs_state *pgs, const gs_matrix *pmat)
+{ if ( pmat == NULL )
+ pgs->ctm_default_set = false;
+ else
+ { pgs->ctm_default = *pmat;
+ pgs->ctm_default_set = true;
+ }
+ return 0;
+}
+
+int
+gs_currentmatrix(const gs_state *pgs, gs_matrix *pmat)
+{ *pmat = ctm_only(pgs);
+ return 0;
+}
+
+/* Set the current transformation matrix for rendering text. */
+/* Note that this may be based on a font other than the current font. */
+int
+gs_setcharmatrix(gs_state *pgs, const gs_matrix *pmat)
+{ gs_matrix cmat;
+ int code = gs_matrix_multiply(pmat, &ctm_only(pgs), &cmat);
+ if ( code < 0 )
+ return code;
+ update_matrix_fixed(pgs->char_tm, cmat.tx, cmat.ty);
+ char_tm_only(pgs) = cmat;
+#ifdef DEBUG
+if ( gs_debug_c('x') )
+ dprintf("[x]setting char_tm:"), trace_matrix_fixed(&pgs->char_tm);
+#endif
+ pgs->char_tm_valid = true;
+ return 0;
+}
+
+/* Read (after possibly computing) the current transformation matrix */
+/* for rendering text. If force=true, update char_tm if it is invalid; */
+/* if force=false, don't update char_tm, and return an error code. */
+int
+gs_currentcharmatrix(gs_state *pgs, gs_matrix *ptm, bool force)
+{ if ( !pgs->char_tm_valid )
+ { int code;
+ if ( !force )
+ return_error(gs_error_undefinedresult);
+ code = gs_setcharmatrix(pgs, &pgs->font->FontMatrix);
+ if ( code < 0 )
+ return code;
+ }
+ if ( ptm != NULL )
+ *ptm = char_tm_only(pgs);
+ return 0;
+}
+
+int
+gs_setmatrix(gs_state *pgs, const gs_matrix *pmat)
+{ update_ctm(pgs, pmat->tx, pmat->ty);
+ set_ctm_only(pgs, *pmat);
+#ifdef DEBUG
+if ( gs_debug_c('x') )
+ dprintf("[x]setmatrix:\n"), trace_ctm(pgs);
+#endif
+ return 0;
+}
+
+int
+gs_settocharmatrix(gs_state *pgs)
+{ if ( pgs->char_tm_valid )
+ { pgs->ctm = pgs->char_tm;
+ pgs->ctm_inverse_valid = false;
+ return 0;
+ }
+ else
+ return_error(gs_error_undefinedresult);
+}
+
+int
+gs_translate(gs_state *pgs, floatp dx, floatp dy)
+{ gs_point pt;
+ int code;
+ if ( (code = gs_distance_transform(dx, dy, &ctm_only(pgs), &pt)) < 0 )
+ return code;
+ pt.x += pgs->ctm.tx;
+ pt.y += pgs->ctm.ty;
+ update_ctm(pgs, pt.x, pt.y);
+#ifdef DEBUG
+if ( gs_debug_c('x') )
+ dprintf4("[x]translate: %f %f -> %f %f\n",
+ dx, dy, pt.x, pt.y),
+ trace_ctm(pgs);
+#endif
+ return 0;
+}
+
+int
+gs_scale(gs_state *pgs, floatp sx, floatp sy)
+{ pgs->ctm.xx *= sx;
+ pgs->ctm.xy *= sx;
+ pgs->ctm.yx *= sy;
+ pgs->ctm.yy *= sy;
+ pgs->ctm_inverse_valid = false, pgs->char_tm_valid = false;
+#ifdef DEBUG
+if ( gs_debug_c('x') )
+ dprintf2("[x]scale: %f %f\n", sx, sy), trace_ctm(pgs);
+#endif
+ return 0;
+}
+
+int
+gs_rotate(gs_state *pgs, floatp ang)
+{ int code = gs_matrix_rotate(&ctm_only(pgs), ang,
+ &ctm_only_writable(pgs));
+ pgs->ctm_inverse_valid = false, pgs->char_tm_valid = false;
+#ifdef DEBUG
+if ( gs_debug_c('x') )
+ dprintf1("[x]rotate: %f\n", ang), trace_ctm(pgs);
+#endif
+ return code;
+}
+
+int
+gs_concat(gs_state *pgs, const gs_matrix *pmat)
+{ gs_matrix cmat;
+ int code = gs_matrix_multiply(pmat, &ctm_only(pgs), &cmat);
+ if ( code < 0 )
+ return code;
+ update_ctm(pgs, cmat.tx, cmat.ty);
+ set_ctm_only(pgs, cmat);
+#ifdef DEBUG
+if ( gs_debug_c('x') )
+ dprintf("[x]concat:\n"), trace_matrix(pmat), trace_ctm(pgs);
+#endif
+ return code;
+}
+
+/* ------ Coordinate transformation ------ */
+
+int
+gs_transform(gs_state *pgs, floatp x, floatp y, gs_point *pt)
+{ return gs_point_transform(x, y, &ctm_only(pgs), pt);
+}
+
+int
+gs_dtransform(gs_state *pgs, floatp dx, floatp dy, gs_point *pt)
+{ return gs_distance_transform(dx, dy, &ctm_only(pgs), pt);
+}
+
+int
+gs_itransform(gs_state *pgs, floatp x, floatp y, gs_point *pt)
+{ /* If the matrix isn't skewed, we get more accurate results */
+ /* by using transform_inverse than by using the inverse matrix. */
+ if ( !is_skewed(&pgs->ctm) )
+ { return gs_point_transform_inverse(x, y, &ctm_only(pgs), pt);
+ }
+ else
+ { ensure_inverse_valid(pgs);
+ return gs_point_transform(x, y, &pgs->ctm_inverse, pt);
+ }
+}
+
+int
+gs_idtransform(gs_state *pgs, floatp dx, floatp dy, gs_point *pt)
+{ /* If the matrix isn't skewed, we get more accurate results */
+ /* by using transform_inverse than by using the inverse matrix. */
+ if ( !is_skewed(&pgs->ctm) )
+ { return gs_distance_transform_inverse(dx, dy,
+ &ctm_only(pgs), pt);
+ }
+ else
+ { ensure_inverse_valid(pgs);
+ return gs_distance_transform(dx, dy, &pgs->ctm_inverse, pt);
+ }
+}
+
+int
+gs_imager_idtransform(const gs_imager_state *pis, floatp dx, floatp dy,
+ gs_point *pt)
+{ return gs_distance_transform_inverse(dx, dy, &ctm_only(pis), pt);
+}
+
+/* ------ For internal use only ------ */
+
+/* Set the translation to a fixed value, and translate any existing path. */
+/* Used by gschar.c to prepare for a BuildChar or BuildGlyph procedure. */
+int
+gx_translate_to_fixed(register gs_state *pgs, fixed px, fixed py)
+{ double fpx = fixed2float(px);
+ double fdx = fpx - pgs->ctm.tx;
+ double fpy = fixed2float(py);
+ double fdy = fpy - pgs->ctm.ty;
+ fixed dx, dy;
+ int code;
+
+ if ( pgs->ctm.txy_fixed_valid )
+ { dx = float2fixed(fdx);
+ dy = float2fixed(fdy);
+ code = gx_path_translate(pgs->path, dx, dy);
+ if ( code < 0 )
+ return code;
+ if ( pgs->char_tm_valid && pgs->char_tm.txy_fixed_valid )
+ pgs->char_tm.tx_fixed += dx,
+ pgs->char_tm.ty_fixed += dy;
+ }
+ else
+ { if ( !gx_path_is_null(pgs->path) )
+ return_error(gs_error_limitcheck);
+ }
+ pgs->ctm.tx = fpx;
+ pgs->ctm.tx_fixed = px;
+ pgs->ctm.ty = fpy;
+ pgs->ctm.ty_fixed = py;
+ pgs->ctm.txy_fixed_valid = true;
+ pgs->ctm_inverse_valid = false;
+ if ( pgs->char_tm_valid )
+ { /* Update char_tm now, leaving it valid. */
+ pgs->char_tm.tx += fdx;
+ pgs->char_tm.ty += fdy;
+ }
+#ifdef DEBUG
+if ( gs_debug_c('x') )
+ dprintf2("[x]translate_to_fixed %g, %g:\n",
+ fixed2float(px), fixed2float(py)),
+ trace_ctm(pgs),
+ dprintf("[x] char_tm:\n"),
+ trace_matrix_fixed(&pgs->char_tm);
+#endif
+ return 0;
+}
+
+/* Scale the CTM and character matrix for oversampling. */
+int
+gx_scale_char_matrix(register gs_state *pgs, int sx, int sy)
+{
+#define scale_cxy(s, vx, vy)\
+ if ( s != 1 )\
+ { pgs->ctm.vx *= s;\
+ pgs->ctm.vy *= s;\
+ pgs->ctm_inverse_valid = false;\
+ if ( pgs->char_tm_valid )\
+ { pgs->char_tm.vx *= s;\
+ pgs->char_tm.vy *= s;\
+ }\
+ }
+ scale_cxy(sx, xx, yx);
+ scale_cxy(sy, xy, yy);
+#undef scale_cxy
+ if_debug2('x', "[x]char scale: %d %d\n", sx, sy);
+ return 0;
+}
+
+/* Compute the coefficients for fast fixed-point distance transformations */
+/* from a transformation matrix. */
+/* We should cache the coefficients with the ctm.... */
+int
+gx_matrix_to_fixed_coeff(const gs_matrix *pmat, register fixed_coeff *pfc,
+ int max_bits)
+{ gs_matrix ctm;
+ int scale = -10000;
+ int expt, shift;
+ ctm = *pmat;
+ pfc->skewed = 0;
+ if ( !is_fzero(ctm.xx) )
+ { discard(frexp(ctm.xx, &scale));
+ }
+ if ( !is_fzero(ctm.xy) )
+ { discard(frexp(ctm.xy, &expt));
+ if ( expt > scale ) scale = expt;
+ pfc->skewed = 1;
+ }
+ if ( !is_fzero(ctm.yx) )
+ { discard(frexp(ctm.yx, &expt));
+ if ( expt > scale ) scale = expt;
+ pfc->skewed = 1;
+ }
+ if ( !is_fzero(ctm.yy) )
+ { discard(frexp(ctm.yy, &expt));
+ if ( expt > scale ) scale = expt;
+ }
+ scale = sizeof(long) * 8 - 1 - max_bits - scale;
+ shift = scale - _fixed_shift;
+ if ( shift > 0 )
+ { pfc->shift = shift;
+ pfc->round = (fixed)1 << (shift - 1);
+ }
+ else
+ { pfc->shift = 0;
+ pfc->round = 0;
+ scale -= shift;
+ }
+#define set_c(c)\
+ if ( is_fzero(ctm.c) ) pfc->c.f = 0, pfc->c.l = 0;\
+ else pfc->c.f = ldexp(ctm.c, _fixed_shift), pfc->c.l = (long)ldexp(ctm.c, scale)
+ set_c(xx);
+ set_c(xy);
+ set_c(yx);
+ set_c(yy);
+#ifdef DEBUG
+if ( gs_debug_c('x') )
+ { dprintf6("[x]ctm: [%6g %6g %6g %6g %6g %6g]\n",
+ ctm.xx, ctm.xy, ctm.yx, ctm.yy, ctm.tx, ctm.ty);
+ dprintf6(" scale=%d fc: [0x%lx 0x%lx 0x%lx 0x%lx] shift=%d\n",
+ scale, pfc->xx.l, pfc->xy.l, pfc->yx.l, pfc->yy.l,
+ pfc->shift);
+ }
+#endif
+ pfc->max_bits = max_bits;
+ return 0;
+}
+
+/* ------ Debugging printout ------ */
+
+#ifdef DEBUG
+
+/* Print a matrix */
+private void near
+trace_matrix_fixed(const gs_matrix_fixed *pmat)
+{ trace_matrix((const gs_matrix *)pmat);
+ if ( pmat->txy_fixed_valid )
+ { dprintf2("\t\tt_fixed: [%6g %6g]\n",
+ fixed2float(pmat->tx_fixed),
+ fixed2float(pmat->ty_fixed));
+ }
+ else
+ { dputs("\t\tt_fixed not valid\n");
+ }
+}
+private void near
+trace_matrix(register const gs_matrix *pmat)
+{ dprintf6("\t[%6g %6g %6g %6g %6g %6g]\n",
+ pmat->xx, pmat->xy, pmat->yx, pmat->yy, pmat->tx, pmat->ty);
+}
+
+#endif
diff --git a/pstoraster/gscoord.h b/pstoraster/gscoord.h
new file mode 100644
index 000000000..e9b677888
--- /dev/null
+++ b/pstoraster/gscoord.h
@@ -0,0 +1,55 @@
+/* Copyright (C) 1989, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gscoord.h */
+/* Client interface to coordinate system operations */
+/* Requires gsmatrix.h and gsstate.h */
+
+/* Coordinate system modification */
+int gs_initmatrix(P1(gs_state *)),
+ gs_defaultmatrix(P2(const gs_state *, gs_matrix *)),
+ gs_currentmatrix(P2(const gs_state *, gs_matrix *)),
+ gs_setmatrix(P2(gs_state *, const gs_matrix *)),
+ gs_translate(P3(gs_state *, floatp, floatp)),
+ gs_scale(P3(gs_state *, floatp, floatp)),
+ gs_rotate(P2(gs_state *, floatp)),
+ gs_concat(P2(gs_state *, const gs_matrix *));
+/* Extensions */
+int gs_setdefaultmatrix(P2(gs_state *, const gs_matrix *)),
+ gs_currentcharmatrix(P3(gs_state *, gs_matrix *, bool)),
+ gs_setcharmatrix(P2(gs_state *, const gs_matrix *)),
+ gs_settocharmatrix(P1(gs_state *));
+
+/* Coordinate transformation */
+int gs_transform(P4(gs_state *, floatp, floatp, gs_point *)),
+ gs_dtransform(P4(gs_state *, floatp, floatp, gs_point *)),
+ gs_itransform(P4(gs_state *, floatp, floatp, gs_point *)),
+ gs_idtransform(P4(gs_state *, floatp, floatp, gs_point *));
+
+#ifndef gs_imager_state_DEFINED
+# define gs_imager_state_DEFINED
+typedef struct gs_imager_state_s gs_imager_state;
+#endif
+
+int gs_imager_idtransform(P4(const gs_imager_state *, floatp, floatp,
+ gs_point *));
diff --git a/pstoraster/gscpm.h b/pstoraster/gscpm.h
new file mode 100644
index 000000000..f6186ddd4
--- /dev/null
+++ b/pstoraster/gscpm.h
@@ -0,0 +1,38 @@
+/* Copyright (C) 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gscpm.h */
+/* Charpath mode definitions */
+
+#ifndef gscpm_INCLUDED
+# define gscpm_INCLUDED
+
+typedef enum {
+ cpm_show, /* *show (default, must be 0) */
+ cpm_false_charpath, /* false charpath */
+ cpm_true_charpath, /* true charpath */
+ cpm_false_charboxpath, /* false charboxpath (not standard PS) */
+ cpm_true_charboxpath /* true charboxpath (ditto) */
+} gs_char_path_mode;
+
+#endif /* gscpm_INCLUDED */
diff --git a/pstoraster/gscrypt1.h b/pstoraster/gscrypt1.h
new file mode 100644
index 000000000..739d6a2d2
--- /dev/null
+++ b/pstoraster/gscrypt1.h
@@ -0,0 +1,44 @@
+/* Copyright (C) 1990, 1992 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gscrypt1.h */
+/* Interface to Adobe Type 1 encryption/decryption. */
+
+/* Normal public interface */
+typedef ushort crypt_state;
+int gs_type1_encrypt(P4(byte *dest, const byte *src, uint len, crypt_state *pstate));
+int gs_type1_decrypt(P4(byte *dest, const byte *src, uint len, crypt_state *pstate));
+
+/* Define the encryption parameters and procedures */
+#define crypt_c1 ((ushort)52845)
+#define crypt_c2 ((ushort)22719)
+#define encrypt_next(ch, state, chvar)\
+ chvar = ((ch) ^ (state >> 8)),\
+ state = (chvar + state) * crypt_c1 + crypt_c2
+#define decrypt_this(ch, state)\
+ ((ch) ^ (state >> 8))
+#define decrypt_next(ch, state, chvar)\
+ chvar = decrypt_this(ch, state),\
+ decrypt_skip_next(ch, state)
+#define decrypt_skip_next(ch, state)\
+ state = ((ch) + state) * crypt_c1 + crypt_c2
diff --git a/pstoraster/gscsepr.c b/pstoraster/gscsepr.c
new file mode 100644
index 000000000..0adfc8100
--- /dev/null
+++ b/pstoraster/gscsepr.c
@@ -0,0 +1,161 @@
+/* Copyright (C) 1994, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gscsepr.c */
+/* Separation color space type definition */
+#include "gx.h"
+#include "gserrors.h"
+#include "gsrefct.h"
+#include "gsmatrix.h" /* for gscolor2.h */
+#include "gxcspace.h"
+#include "gxfixed.h" /* for gxcolor2.h */
+#include "gxcolor2.h" /* for gs_indexed_map */
+
+extern gs_memory_t *gs_state_memory(P1(const gs_state *));
+
+/* Define the Separation color space type. */
+cs_declare_procs(private, gx_concretize_Separation, gx_install_Separation,
+ gx_adjust_cspace_Separation,
+ gx_enum_ptrs_Separation, gx_reloc_ptrs_Separation);
+private cs_proc_concrete_space(gx_concrete_space_Separation);
+private cs_proc_remap_concrete_color(gx_remap_concrete_Separation);
+private cs_proc_init_color(gx_init_Separation);
+const gs_color_space_type
+ gs_color_space_type_Separation =
+ { gs_color_space_index_Separation, 1, false,
+ gx_init_Separation, gx_concrete_space_Separation,
+ gx_concretize_Separation, gx_remap_concrete_Separation,
+ gx_default_remap_color, gx_install_Separation,
+ gx_adjust_cspace_Separation, gx_no_adjust_color_count,
+ gx_enum_ptrs_Separation, gx_reloc_ptrs_Separation
+ };
+
+/* Initialize a Separation color. */
+
+private void
+gx_init_Separation(gs_client_color *pcc, const gs_color_space *pcs)
+{ pcc->paint.values[0] = 1.0;
+}
+
+/* Remap a Separation color. */
+
+private const gs_color_space *
+gx_concrete_space_Separation(const gs_color_space *pcs, const gs_state *pgs)
+{ /* We don't support concrete Separation spaces yet. */
+ const gs_color_space *pacs =
+ (const gs_color_space *)&pcs->params.separation.alt_space;
+ return cs_concrete_space(pacs, pgs);
+}
+
+private int
+gx_concretize_Separation(const gs_client_color *pc, const gs_color_space *pcs,
+ frac *pconc, const gs_state *pgs)
+{ float tint = pc->paint.values[0];
+ int code;
+ gs_client_color cc;
+ const gs_color_space *pacs =
+ (const gs_color_space *)&pcs->params.separation.alt_space;
+ if ( tint < 0 ) tint = 0;
+ else if ( tint > 1 ) tint = 1;
+ /* We always map into the alternate color space. */
+ code = (*pcs->params.separation.map->proc.tint_transform)(&pcs->params.separation, tint, &cc.paint.values[0]);
+ if ( code < 0 ) return code;
+ return (*pacs->type->concretize_color)(&cc, pacs, pconc, pgs);
+}
+
+private int
+gx_remap_concrete_Separation(const frac *pconc,
+ gx_device_color *pdc, const gs_state *pgs)
+{ /* We don't support concrete Separation colors yet. */
+ return_error(gs_error_rangecheck);
+}
+
+/* Install a Separation color space. */
+
+private int
+gx_install_Separation(gs_color_space *pcs, gs_state *pgs)
+{ return (*pcs->params.separation.alt_space.type->install_cspace)
+ ((gs_color_space *)&pcs->params.separation.alt_space, pgs);
+}
+
+/* Adjust the reference count of a Separation color space. */
+
+private void
+gx_adjust_cspace_Separation(const gs_color_space *pcs, gs_state *pgs, int delta)
+{ rc_adjust_const(pcs->params.separation.map, delta,
+ gs_state_memory(pgs), "gx_adjust_Separation");
+ (*pcs->params.separation.alt_space.type->adjust_cspace_count)
+ ((const gs_color_space *)&pcs->params.separation.alt_space, pgs, delta);
+}
+
+/* GC procedures */
+
+#define pcs ((gs_color_space *)vptr)
+
+private ENUM_PTRS_BEGIN(gx_enum_ptrs_Separation) {
+ return (*pcs->params.separation.alt_space.type->enum_ptrs)
+ (&pcs->params.separation.alt_space,
+ sizeof(pcs->params.separation.alt_space), index-1, pep);
+ }
+ ENUM_PTR(0, gs_color_space, params.separation.map);
+ENUM_PTRS_END
+private RELOC_PTRS_BEGIN(gx_reloc_ptrs_Separation) {
+ RELOC_PTR(gs_color_space, params.separation.map);
+ (*pcs->params.separation.alt_space.type->reloc_ptrs)
+ (&pcs->params.separation.alt_space, sizeof(gs_base_color_space), gcst);
+} RELOC_PTRS_END
+
+#undef pcs
+
+/* ---------------- Notes on real Separation colors ---------------- */
+
+typedef ulong gs_separation; /* BOGUS */
+#define gs_no_separation ((gs_separation)(-1L))
+
+#define dev_proc_lookup_separation(proc)\
+ gs_separation proc(P4(gx_device *dev, const byte *sname, uint len,\
+ gx_color_value *num_levels))
+
+#define dev_proc_map_tint_color(proc)\
+ gx_color_index proc(P4(gx_device *dev, gs_separation sepr, bool overprint,\
+ gx_color_value tint))
+
+/*
+ * In principle, setting a Separation color space, or setting the device
+ * when the current color space is a Separation space, calls the
+ * lookup_separation device procedure to obtain the separation ID and
+ * the number of achievable levels. Currently, the only hooks for doing
+ * this are unsuitable: gx_set_cmap_procs isn't called when the color
+ * space changes, and doing it in gx_remap_Separation is inefficient.
+ * Probably the best approach is to call gx_set_cmap_procs whenever the
+ * color space changes. In fact, if we do this, we can probably short-cut
+ * two levels of procedure call in color remapping (gx_remap_color, by
+ * turning it into a macro, and gx_remap_DeviceXXX, by calling the
+ * cmap_proc procedure directly). Some care will be required for the
+ * implicit temporary resetting of the color space in [color]image.
+ *
+ * For actual remapping of Separation colors, we need cmap_separation_direct
+ * and cmap_separation_halftoned, just as for the other device color spaces.
+ * So we need to break apart gx_render_gray in gxdither.c so it can also
+ * do the job for separations.
+ */
diff --git a/pstoraster/gscspace.h b/pstoraster/gscspace.h
new file mode 100644
index 000000000..2cfbfdf54
--- /dev/null
+++ b/pstoraster/gscspace.h
@@ -0,0 +1,194 @@
+/* Copyright (C) 1991, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gscspace.h */
+/* Client interface to color spaces */
+
+#ifndef gscspace_INCLUDED
+# define gscspace_INCLUDED
+
+/* Color space type indices */
+typedef enum {
+ /* Supported in all configurations */
+ gs_color_space_index_DeviceGray = 0,
+ gs_color_space_index_DeviceRGB,
+ /* Supported in extended Level 1, and in Level 2 */
+ gs_color_space_index_DeviceCMYK,
+ /* Supported in Level 2 only */
+ /* DEC C truncates identifiers at 32 characters, so.... */
+ gs_color_space_index_CIEDEFG,
+ gs_color_space_index_CIEDEF,
+ gs_color_space_index_CIEABC,
+ gs_color_space_index_CIEA,
+ gs_color_space_index_Separation,
+ gs_color_space_index_Indexed,
+ gs_color_space_index_Pattern
+} gs_color_space_index;
+
+/* Define the abstract type for color space objects. */
+#ifndef gs_color_space_DEFINED
+# define gs_color_space_DEFINED
+typedef struct gs_color_space_s gs_color_space;
+#endif
+
+/*
+ * We preallocate instances of the 3 device color spaces, and provide
+ * procedures that return them (to avoid upsetting compilers that don't
+ * allow extern pointers to abstract types). Note that
+ * gs_color_space_DeviceCMYK() may return NULL if CMYK color support is not
+ * included in this configuration.
+ */
+extern const gs_color_space *gs_color_space_DeviceGray(P0());
+extern const gs_color_space *gs_color_space_DeviceRGB(P0());
+extern const gs_color_space *gs_color_space_DeviceCMYK(P0());
+
+/*
+ * Color spaces are complicated because different spaces involve
+ * different kinds of parameters, as follows:
+
+Space Space parameters Color parameters
+----- ---------------- ----------------
+DeviceGray (none) 1 real [0-1]
+DeviceRGB (none) 3 reals [0-1]
+DeviceCMYK (none) 4 reals [0-1]
+CIEBasedDEFG dictionary 4 reals
+CIEBasedDEF dictionary 3 reals
+CIEBasedABC dictionary 3 reals
+CIEBasedA dictionary 1 real
+Separation name, alt_space, tint_xform 1 real [0-1]
+Indexed base_space, hival, lookup 1 int [0-hival]
+Pattern colored: (none) dictionary
+ uncolored: base_space dictionary + base space params
+
+Space Underlying or alternate space
+----- -----------------------------
+Separation Device, CIE
+Indexed Device, CIE
+Pattern Device, CIE, Separation, Indexed
+
+ * Logically speaking, each color space type should be a different
+ * structure type at the allocator level. This would potentially require
+ * either reference counting or garbage collector for color spaces, but that
+ * is probably better than the current design, which uses fixed-size color
+ * space objects and a second level of type discrimination.
+ */
+
+/* Define abstract structure types for color space parameters. */
+typedef struct gs_cie_defg_s gs_cie_defg;
+typedef struct gs_cie_def_s gs_cie_def;
+typedef struct gs_cie_abc_s gs_cie_abc;
+typedef struct gs_cie_a_s gs_cie_a;
+typedef struct gs_separation_params_s gs_separation_params;
+typedef struct gs_indexed_params_s gs_indexed_params;
+
+/* Define an abstract type for color space types. */
+typedef struct gs_color_space_type_s gs_color_space_type;
+
+/* ---------------- Color spaces per se ---------------- */
+
+ /* Base color spaces (Device and CIE) */
+
+/*typedef struct gs_cie_defg_s gs_cie_defg;*/
+/*typedef struct gs_cie_def_s gs_cie_def;*/
+/*typedef struct gs_cie_abc_s gs_cie_abc;*/
+/*typedef struct gs_cie_a_s gs_cie_a;*/
+#define gs_base_cspace_params\
+ gs_cie_defg *defg;\
+ gs_cie_def *def;\
+ gs_cie_abc *abc;\
+ gs_cie_a *a
+typedef struct gs_base_color_space_s {
+ const gs_color_space_type _ds *type;
+ union {
+ gs_base_cspace_params;
+ } params;
+} gs_base_color_space;
+
+ /* Paint (non-pattern) color spaces (base + Separation + Indexed) */
+
+typedef ulong gs_separation_name; /* BOGUS */
+
+typedef struct gs_indexed_map_s gs_indexed_map;
+/*typedef struct gs_separation_params_s gs_separation_params;*/
+struct gs_separation_params_s {
+ gs_separation_name sname;
+ gs_base_color_space alt_space;
+ gs_indexed_map *map;
+};
+/*typedef struct gs_indexed_params_s gs_indexed_params;*/
+struct gs_indexed_params_s {
+ gs_base_color_space base_space;
+ int hival;
+ union {
+ gs_const_string table; /* size is implicit */
+ gs_indexed_map *map;
+ } lookup;
+ bool use_proc; /* 0 = use table, 1 = use proc & map */
+};
+#define gs_paint_cspace_params\
+ gs_base_cspace_params;\
+ gs_separation_params separation;\
+ gs_indexed_params indexed
+typedef struct gs_paint_color_space_s {
+ const gs_color_space_type _ds *type;
+ union {
+ gs_paint_cspace_params;
+ } params;
+} gs_paint_color_space;
+
+ /* General color spaces (including patterns) */
+
+typedef struct gs_pattern_params_s {
+ bool has_base_space;
+ gs_paint_color_space base_space;
+} gs_pattern_params;
+struct gs_color_space_s {
+ const gs_color_space_type _ds *type;
+ union {
+ gs_paint_cspace_params;
+ gs_pattern_params pattern;
+ } params;
+};
+/*extern_st(st_color_space);*/ /* in gxcspace.h */
+#define public_st_color_space() /* in gscolor.c */\
+ gs_public_st_composite(st_color_space, gs_color_space,\
+ "gs_color_space", color_space_enum_ptrs, color_space_reloc_ptrs)
+#define st_color_space_max_ptrs 2 /* 1 base + 1 indexed */
+
+/* ---------------- Procedures ---------------- */
+
+/* Get the index of a color space. */
+gs_color_space_index gs_color_space_get_index(P1(const gs_color_space *));
+
+/* Get the number of components in a color space. */
+int gs_color_space_num_components(P1(const gs_color_space *));
+
+/* Get the base space of an Indexed color space. */
+/* We have to redefine this because the VAX VMS C compiler (but not */
+/* the preprocessor!) limits identifiers to 31 characters. */
+#define gs_color_space_indexed_base_space(pcs)\
+ gs_color_space_indexed_base(pcs)
+const gs_color_space *
+ gs_color_space_indexed_base_space(P1(const gs_color_space *));
+
+#endif /* gscspace_INCLUDED */
diff --git a/pstoraster/gsdcolor.h b/pstoraster/gsdcolor.h
new file mode 100644
index 000000000..f9a4d7f72
--- /dev/null
+++ b/pstoraster/gsdcolor.h
@@ -0,0 +1,287 @@
+/* Copyright (C) 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gsdcolor.h */
+/* Device color representation for drivers */
+
+#ifndef gsdcolor_INCLUDED
+# define gsdcolor_INCLUDED
+
+#include "gxarith.h" /* for imod */
+#include "gxbitmap.h"
+#include "gxhttile.h"
+#include "gxcindex.h"
+
+#ifndef gx_device_color_DEFINED
+# define gx_device_color_DEFINED
+typedef struct gx_device_color_s gx_device_color;
+#endif
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * The definitions in the following section of the file are the only
+ * ones that should be used by read-only clients such as implementors
+ * of high-level driver functions.
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+/*
+ * A device color consists of a base color and an optional (tiled) mask.
+ * The base color may be a pure color, a binary halftone, or a colored
+ * bitmap (color halftone or colored Pattern). The mask is used for
+ * both colored and uncolored Patterns.
+ */
+
+/* Accessing a pure color. */
+#define gx_dc_is_pure(pdc)\
+ ((pdc)->type == gx_dc_type_pure)
+#define gx_dc_writes_pure(pdc, lop)\
+ (gx_dc_is_pure(pdc) && lop_no_S_is_T(lop))
+#define gx_dc_pure_color(pdc)\
+ ((pdc)->colors.pure)
+
+/* Accessing the phase of a halftone. */
+#define gx_dc_phase(pdc)\
+ ((pdc)->phase)
+
+/* Accessing a binary halftone. */
+#define gx_dc_is_binary_halftone(pdc)\
+ ((pdc)->type == gx_dc_type_ht_binary)
+#define gx_dc_binary_tile(pdc)\
+ (&(pdc)->colors.binary.b_tile->tiles)
+#define gx_dc_binary_color0(pdc)\
+ ((pdc)->colors.binary.color[0])
+#define gx_dc_binary_color1(pdc)\
+ ((pdc)->colors.binary.color[1])
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * The definitions in the following section of the file, plus the ones
+ * just above, are the only ones that should be used by clients that
+ * set as well as read device colors.
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#define color_is_set(pdc)\
+ ((pdc)->type != gx_dc_type_none)
+#define color_unset(pdc)\
+ ((pdc)->type = gx_dc_type_none)
+
+#define color_is_pure(pdc) gx_dc_is_pure(pdc)
+#define color_writes_pure(pdc, lop) gx_dc_writes_pure(pdc, lop)
+#define color_set_pure(pdc, color)\
+ ((pdc)->colors.pure = (color),\
+ (pdc)->type = gx_dc_type_pure)
+
+/* Set the phase to an offset from the tile origin. */
+#define color_set_phase(pdc, px, py)\
+ ((pdc)->phase.x = (px),\
+ (pdc)->phase.y = (py))
+/* Set the phase from the halftone phase in a graphics state. */
+#define color_set_phase_mod(pdc, px, py, tw, th)\
+ color_set_phase(pdc, imod(-(px), tw), imod(-(py), th))
+
+#define color_is_binary_halftone(pdc) gx_dc_is_binary_halftone(pdc)
+#define color_set_binary_halftone(pdc, ht, color0, color1, level)\
+ ((pdc)->colors.binary.b_ht = (ht),\
+ (pdc)->colors.binary.color[0] = (color0),\
+ (pdc)->colors.binary.color[1] = (color1),\
+ (pdc)->colors.binary.b_level = (level),\
+ (pdc)->type = gx_dc_type_ht_binary)
+#define color_set_binary_tile(pdc, color0, color1, tile)\
+ ((pdc)->colors.binary.b_ht = 0,\
+ (pdc)->colors.binary.color[0] = (color0),\
+ (pdc)->colors.binary.color[1] = (color1),\
+ (pdc)->colors.binary.b_tile = (tile),\
+ (pdc)->type = gx_dc_type_ht_binary)
+
+#define color_is_colored_halftone(pdc)\
+ ((pdc)->type == gx_dc_type_ht_colored)
+#define _color_set_c(pdc, i, b, l)\
+ ((pdc)->colors.colored.c_base[i] = (b),\
+ (pdc)->colors.colored.c_level[i] = (l))
+#define color_set_rgb_halftone(pdc, ht, br, lr, bg, lg, bb, lb, a)\
+ ((pdc)->colors.colored.c_ht = (ht),\
+ _color_set_c(pdc, 0, br, lr),\
+ _color_set_c(pdc, 1, bg, lg),\
+ _color_set_c(pdc, 2, bb, lb),\
+ (pdc)->colors.colored.alpha = (a),\
+ (pdc)->type = gx_dc_type_ht_colored)
+#define color_set_cmyk_halftone(pdc, ht, bc, lc, bm, lm, by, ly, bk, lk)\
+ ((pdc)->colors.colored.c_ht = (ht),\
+ _color_set_c(pdc, 0, bc, lc),\
+ _color_set_c(pdc, 1, bm, lm),\
+ _color_set_c(pdc, 2, by, ly),\
+ _color_set_c(pdc, 3, bk, lk),\
+ (pdc)->colors.colored.alpha = max_ushort,\
+ (pdc)->type = gx_dc_type_ht_colored)
+
+#define color_set_pattern(pdc, pid, pt)\
+ ((pdc)->id = (pid),\
+ (pdc)->colors.pattern.p_tile = (pt),\
+ (pdc)->type = gx_dc_type_pattern)
+#define color_set_null_pattern(pdc)\
+ color_set_pattern(pdc, gx_no_bitmap_id, (gx_color_tile *)0)
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * The remaining definitions are internal ones that are included in this
+ * file only because C's abstraction mechanisms aren't strong enough to
+ * allow us to keep them separate and still have in-line access to the
+ * commonly used members.
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+/* Define opaque types for objects referenced by device colors. */
+
+#ifndef gx_ht_tile_DEFINED
+# define gx_ht_tile_DEFINED
+typedef struct gx_ht_tile_s gx_ht_tile;
+#endif
+
+#ifndef gx_device_halftone_DEFINED
+# define gx_device_halftone_DEFINED
+typedef struct gx_device_halftone_s gx_device_halftone;
+#endif
+
+typedef struct gx_color_tile_s gx_color_tile;
+
+/*
+ * The device color in the graphics state is computed from client color
+ * specifications, and kept current through changes in transfer function,
+ * device, and (if relevant) halftone phase.
+ * (gx_set_dev_color sets the device color if needed.)
+ * For binary halftones (and eventually colored halftones as well),
+ * the bitmaps are only cached, so internal clients (the painting operators)
+ * must call gx_color_load to ensure that the bitmap is available.
+ * Device color elements set by gx_color_load are marked with @ below.
+ *
+ * Base colors are represented as follows:
+ *
+ * Pure color (gx_dc_pure):
+ * colors.pure = the color;
+ * Binary halftone (gx_dc_ht_binary):
+ * colors.binary.b_ht = the device halftone;
+ * colors.binary.color[0] = the color for 0s (darker);
+ * colors.binary.color[1] = the color for 1s (lighter);
+ * colors.binary.b_level = the number of pixels to lighten,
+ * 0 < halftone_level < P, the number of pixels in the tile;
+ * @ colors.binary.b_tile points to an entry in the binary
+ * tile cache.
+ * Colored halftone (gx_dc_ht_colored):
+ * colors.colored.c_ht = the device halftone;
+ * colors.colored.c_level[0..N-1] = the halftone levels,
+ * like b_level;
+ * colors.colored.c_base[0..N-1] = the base colors;
+ * N=3 for RGB devices, 4 for CMYK devices;
+ * 0 <= c_level[i] < P;
+ * 0 <= c_base[i] <= dither_rgb;
+ * colors.colored.alpha = the opacity.
+ * Colored pattern (gx_dc_pattern):
+ * (id and mask are also set, see below)
+ * @ colors.pattern.p_tile points to a gx_color_tile in
+ * the pattern cache, or is NULL for a null pattern.
+ *
+ * The phase element is used for all colors except pure ones. It holds
+ * the negative of the graphics state halftone phase, modulo the halftone
+ * tile or colored pattern size.
+ *
+ * The id and mask elements of a device color are only used for patterns:
+ * Non-pattern:
+ * id and mask are unused.
+ * Pattern:
+ * id gives the ID of the pattern (and its mask);
+ * @ mask points to a gx_color_tile in the pattern cache,
+ * or is NULL for a pattern that doesn't require a mask.
+ * (The 'bits' of the tile are not accessed.)
+ * For colored patterns requiring a mask, p_tile and mask
+ * point to the same cache entry.
+ * For masked colors, gx_set_dev_color replaces the type with a different
+ * type that applies the mask when painting. These types are not defined
+ * here, because they are only used in Level 2.
+ */
+
+/* A device color type is just a pointer to the procedures. */
+typedef struct gx_device_color_procs_s gx_device_color_procs;
+typedef const gx_device_color_procs _ds *gx_device_color_type;
+
+struct gx_device_color_s {
+ /* See the comment above for descriptions of the members. */
+ /* We use b_, c_, and p_ member names because */
+ /* some old compilers don't allow the same name to be used for */
+ /* two different structure members even when it's unambiguous. */
+ union _c {
+ gx_color_index pure;
+ struct _bin {
+ const gx_device_halftone *b_ht;
+ gx_color_index color[2];
+ uint b_level;
+ gx_ht_tile *b_tile;
+ } binary;
+ struct _col {
+ const gx_device_halftone *c_ht;
+ byte c_base[4];
+ uint c_level[4];
+ ushort /*gx_color_value*/ alpha;
+ } colored;
+ struct _pat {
+ gx_color_tile *p_tile;
+ } /*(colored)*/ pattern;
+ } colors;
+ gs_int_point phase;
+ gx_bitmap_id id;
+ gx_color_tile *mask;
+ /* We put the type last to preserve word alignment */
+ /* on platforms with short ints. */
+ gx_device_color_type type;
+};
+/*extern_st(st_device_color);*/ /* in gxdcolor.h */
+#define public_st_device_color() /* in gxcmap.c */\
+ gs_public_st_composite(st_device_color, gx_device_color, "gx_device_color",\
+ device_color_enum_ptrs, device_color_reloc_ptrs)
+#define st_device_color_max_ptrs 2
+
+/*
+ * Define the standard device color types.
+ * We define them here as pointers to the real types only because a few
+ * C compilers don't allow declaring externs with abstract struct types;
+ * we redefine them as macros in gxdcolor.h where the concrete type for
+ * gx_device_color_procs is available.
+ * We spell out the definition of gx_device_color type because some
+ * C compilers can't handle the typedef correctly.
+ */
+#ifndef gx_dc_type_none
+extern const gx_device_color_procs _ds *gx_dc_type_none; /* gxdcolor.c */
+#endif
+#ifndef gx_dc_type_pure
+extern const gx_device_color_procs _ds *gx_dc_type_pure; /* gxdcolor.c */
+#endif
+ /*
+ * We don't declare gx_dc_pattern here, so as not to create
+ * a spurious external reference in Level 1 systems.
+ */
+#ifndef gx_dc_type_pattern
+/*extern const gx_device_color_procs _ds *gx_dc_type_pattern;*/ /* gspcolor.c */
+#endif
+#ifndef gx_dc_type_ht_binary
+extern const gx_device_color_procs _ds *gx_dc_type_ht_binary; /* gxht.c */
+#endif
+#ifndef gx_dc_type_ht_colored
+extern const gx_device_color_procs _ds *gx_dc_type_ht_colored; /* gxcht.c */
+#endif
+
+#endif /* gsdcolor_INCLUDED */
diff --git a/pstoraster/gsdevice.c b/pstoraster/gsdevice.c
new file mode 100644
index 000000000..a5d694562
--- /dev/null
+++ b/pstoraster/gsdevice.c
@@ -0,0 +1,416 @@
+/*
+ Copyright 1993-1999 by Easy Software Products.
+ Copyright (C) 1989, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gsdevice.c */
+/* Device operators for Ghostscript library */
+#include "memory_.h" /* for memcpy */
+#include "gx.h"
+#include "gscdefs.h" /* for gs_lib_device_list */
+#include "gserrors.h"
+#include "gsstruct.h"
+#include "gspath.h" /* gs_initclip prototype */
+#include "gspaint.h" /* gs_erasepage prototype */
+#include "gsmatrix.h" /* for gscoord.h */
+#include "gscoord.h" /* for gs_initmatrix */
+#include "gzstate.h"
+#include "gxcmap.h"
+#include "gxdevice.h"
+#include "gxdevmem.h"
+
+/* Include the extern for the device list. */
+extern_gs_lib_device_list();
+
+/* Finalization for devices: close the device if it is open. */
+void
+gx_device_finalize(void *vptr)
+{ discard(gs_closedevice((gx_device *)vptr));
+}
+
+/* GC procedures */
+#define fdev ((gx_device_forward *)vptr)
+private ENUM_PTRS_BEGIN(device_forward_enum_ptrs) return 0;
+ case 0:
+ *pep = gx_device_enum_ptr(fdev->target);
+ break;
+ENUM_PTRS_END
+private RELOC_PTRS_BEGIN(device_forward_reloc_ptrs) {
+ fdev->target = gx_device_reloc_ptr(fdev->target, gcst);
+} RELOC_PTRS_END
+#undef fdev
+
+/* Structure descriptors. These must follow the procedures, because */
+/* we can't conveniently forward-declare the procedures. */
+/* (See gxdevice.h for details.) */
+public_st_device();
+public_st_device_forward();
+public_st_device_null();
+/* A fake descriptor for devices whose descriptor we can't find. */
+gs_private_st_complex_only(st_device_unknown, byte, "gx_device(unknown)",
+ 0, 0, 0, gx_device_finalize);
+
+/* GC utilities */
+/* Enumerate or relocate a device pointer for a client. */
+gx_device *
+gx_device_enum_ptr(gx_device *dev)
+{ if ( dev == 0 || dev->memory == 0 )
+ return 0;
+ return dev;
+}
+gx_device *
+gx_device_reloc_ptr(gx_device *dev, gc_state_t *gcst)
+{ if ( dev == 0 || dev->memory == 0 )
+ return dev;
+ return gs_reloc_struct_ptr(dev, gcst);
+}
+
+/* Set up the device procedures in the device structure. */
+/* Also copy old fields to new ones. */
+void
+gx_device_set_procs(gx_device *dev)
+{ if ( dev->static_procs != 0 ) /* 0 if already populated */
+ { dev->std_procs = *dev->static_procs;
+ dev->static_procs = 0;
+ }
+}
+
+/* Initialize a device just after allocation. */
+int
+gdev_initialize(gx_device *dev)
+{ *dev = *(gx_device *)&gs_null_device;
+ return 0;
+}
+
+/* Flush buffered output to the device */
+int
+gs_flushpage(gs_state *pgs)
+{ gx_device *dev = gs_currentdevice(pgs);
+ return (*dev_proc(dev, sync_output))(dev);
+}
+
+/* Make the device output the accumulated page description */
+int
+gs_copypage(gs_state *pgs)
+{ return gs_output_page(pgs, 1, 0);
+}
+int
+gs_output_page(gs_state *pgs, int num_copies, int flush)
+{ gx_device *dev = gs_currentdevice(pgs);
+ int code;
+
+ if ( dev->IgnoreNumCopies )
+ num_copies = 1;
+ code = (*dev_proc(dev, output_page))(dev, num_copies, flush);
+ if ( code >= 0 )
+ { dev->PageCount += num_copies;
+ if ( flush )
+ dev->ShowpageCount++;
+ }
+ return code;
+}
+
+/* Copy scan lines from an image device */
+int
+gs_copyscanlines(gx_device *dev, int start_y, byte *data, uint size,
+ int *plines_copied, uint *pbytes_copied)
+{ uint line_size = gx_device_raster(dev, 0);
+ uint count = size / line_size;
+ uint i;
+ byte *dest = data;
+ for ( i = 0; i < count; i++, dest += line_size )
+ { int code = (*dev_proc(dev, get_bits))(dev, start_y + i, dest, NULL);
+ if ( code < 0 )
+ { /* Might just be an overrun. */
+ if ( start_y + i == dev->height ) break;
+ return_error(code);
+ }
+ }
+ if ( plines_copied != NULL )
+ *plines_copied = i;
+ if ( pbytes_copied != NULL )
+ *pbytes_copied = i * line_size;
+ return 0;
+}
+
+/* Get the current device from the graphics state. */
+gx_device *
+gs_currentdevice(const gs_state *pgs)
+{ return pgs->device;
+}
+
+/* Get the name of a device. */
+const char *
+gs_devicename(const gx_device *dev)
+{ return dev->dname;
+}
+
+/* Get the initial matrix of a device. */
+void
+gs_deviceinitialmatrix(gx_device *dev, gs_matrix *pmat)
+{ fill_dev_proc(dev, get_initial_matrix, gx_default_get_initial_matrix);
+ (*dev_proc(dev, get_initial_matrix))(dev, pmat);
+}
+
+/* Get the N'th device from the known device list */
+const gx_device *
+gs_getdevice(int index)
+{ const gx_device **list;
+ int count = gs_lib_device_list(&list, NULL);
+
+ if ( index < 0 || index >= count )
+ return 0; /* index out of range */
+ return list[index];
+}
+
+/* Clone an existing device. */
+int
+gs_copydevice(gx_device **pnew_dev, const gx_device *dev, gs_memory_t *mem)
+{ gx_device *new_dev;
+ const gs_memory_struct_type_t *std = dev->stype;
+
+ /*
+ * Because command list devices have complicated internal pointer
+ * structures, we allocate all device instances as immovable.
+ */
+ if ( std == 0 )
+ { /*
+ * This is the statically allocated prototype. Find its
+ * structure descriptor, and fill it in if this is the first
+ * time we've needed it. (Right now we always fill it in,
+ * for simplicity.)
+ */
+ const gx_device **list;
+ gs_memory_struct_type_t *st;
+ int count = gs_lib_device_list(&list, &st);
+ int i;
+ bool forward = false;
+ const gx_device_procs *procs = dev->static_procs;
+
+ for ( i = 0; list[i] != dev; ++i )
+ if ( i == count )
+ { /* We can't find a structure descriptor for */
+ /* this device. Allocate it as bytes and */
+ /* hope for the best. */
+ std = &st_device_unknown;
+ new_dev = gs_alloc_struct_array_immovable(mem,
+ dev->params_size, gx_device, st,
+ "gs_copydevice(unknown)");
+ goto out;
+ }
+ st += i;
+ /*
+ * Try to figure out if this is a forwarding device.
+ * All printer devices, and no other devices, have
+ * a null fill_rectangle procedure; for other devices,
+ * we look for a likely forwarding procedure in the vector.
+ * The algorithm isn't foolproof, but it's all we've got.
+ */
+ if ( procs == 0 )
+ procs = &dev->std_procs;
+ if ( procs->fill_rectangle == 0 ||
+ procs->get_xfont_procs == gx_forward_get_xfont_procs
+ )
+ forward = true;
+ if ( forward )
+ *st = st_device_forward;
+ else
+ *st = st_device;
+ st->ssize = dev->params_size;
+ std = st;
+ }
+ new_dev = gs_alloc_struct_immovable(mem, gx_device, std,
+ "gs_copydevice");
+out: if ( new_dev == 0 )
+ return_error(gs_error_VMerror);
+ memcpy(new_dev, dev, dev->params_size);
+ new_dev->memory = mem;
+ new_dev->stype = std;
+ new_dev->is_open = false;
+ *pnew_dev = new_dev;
+ return 0;
+}
+
+/* Set the device in the graphics state */
+int
+gs_setdevice(gs_state *pgs, gx_device *dev)
+{ int code = gs_setdevice_no_erase(pgs, dev);
+ if ( code == 1 )
+ code = gs_erasepage(pgs);
+ return code;
+}
+int
+gs_setdevice_no_erase(gs_state *pgs, gx_device *dev)
+{ bool was_open = dev->is_open;
+ int code;
+ /* Initialize the device */
+ if ( !was_open )
+ { gx_device_fill_in_procs(dev);
+ if ( gs_device_is_memory(dev) )
+ { /* Set the target to the current device. */
+ gx_device *odev = gs_currentdevice_inline(pgs);
+ while ( odev != 0 && gs_device_is_memory(odev) )
+ odev = ((gx_device_memory *)odev)->target;
+ ((gx_device_memory *)dev)->target = odev;
+ }
+ code = (*dev_proc(dev, open_device))(dev);
+ if ( code < 0 ) return_error(code);
+ dev->is_open = true;
+ }
+ pgs->device = dev;
+ gx_set_cmap_procs(pgs);
+ pgs->ctm_default_set = false;
+ if ( (code = gs_initmatrix(pgs)) < 0 ||
+ (code = gs_initclip(pgs)) < 0
+ )
+ return code;
+ gx_unset_dev_color(pgs);
+ /* If we were in a charpath or a setcachedevice, */
+ /* we aren't any longer. */
+ pgs->in_cachedevice = 0;
+ pgs->in_charpath = 0;
+ return (was_open ? 0 : 1);
+}
+
+/* Make a null device. */
+void
+gs_make_null_device(gx_device_null *dev, gs_memory_t *mem)
+{ *dev = gs_null_device;
+ dev->memory = mem;
+}
+
+/* Select the null device. This is just a convenience. */
+void
+gs_nulldevice(gs_state *pgs)
+{ gs_setdevice(pgs, (gx_device *)&gs_null_device);
+}
+
+/* Close a device. The client is responsible for ensuring that */
+/* this device is not current in any graphics state. */
+int
+gs_closedevice(gx_device *dev)
+{ int code = 0;
+ if ( dev->is_open )
+ { code = (*dev_proc(dev, close_device))(dev);
+ if ( code < 0 ) return_error(code);
+ dev->is_open = false;
+ }
+ return code;
+}
+
+/* Install enough of a null device to suppress the page device check */
+/* during the execution of a restore/grestore/setgstate. */
+void
+gx_device_no_output(gs_state *pgs)
+{ pgs->device = (gx_device *)&gs_null_device;
+}
+
+/* Just set the device without reinitializing. */
+/* (For internal use only.) */
+void
+gx_set_device_only(gs_state *pgs, gx_device *dev)
+{ pgs->device = dev;
+}
+
+/* Compute the size of one scan line for a device, */
+/* with or without padding to a word boundary. */
+uint
+gx_device_raster(const gx_device *dev, bool pad)
+{ ulong bits = (ulong)dev->width * dev->color_info.depth;
+ return (pad ? bitmap_raster(bits) : (uint)((bits + 7) >> 3));
+}
+
+/* Adjust the resolution for devices that only have a fixed set of */
+/* geometries, so that the apparent size in inches remains constant. */
+/* If fit=1, the resolution is adjusted so that the entire image fits; */
+/* if fit=0, one dimension fits, but the other one is clipped. */
+int
+gx_device_adjust_resolution(gx_device *dev,
+ int actual_width, int actual_height, int fit)
+{ double width_ratio = (double)actual_width / dev->width ;
+ double height_ratio = (double)actual_height / dev->height ;
+ double ratio =
+ (fit ? min(width_ratio, height_ratio) :
+ max(width_ratio, height_ratio));
+ dev->x_pixels_per_inch *= ratio;
+ dev->y_pixels_per_inch *= ratio;
+ gx_device_set_width_height(dev, actual_width, actual_height);
+ return 0;
+}
+
+/* Set the HWMargins to values defined in inches. */
+/* If move_origin is true, also reset the Margins. */
+/* Note that this assumes a printer-type device (Y axis inverted). */
+void
+gx_device_set_margins(gx_device *dev, const float *margins /*[4]*/,
+ bool move_origin)
+{ int i;
+ for ( i = 0; i < 4; ++i )
+ dev->HWMargins[i] = margins[i] * 72.0;
+ if ( move_origin )
+ { dev->Margins[0] = -margins[0] * dev->MarginsHWResolution[0];
+ dev->Margins[1] = -margins[3] * dev->MarginsHWResolution[1];
+ }
+
+ dev->width = (dev->MediaSize[0] - dev->HWMargins[0] - dev->HWMargins[2]) *
+ dev->x_pixels_per_inch / 72.0 + 0.5;
+ dev->height = (dev->MediaSize[1] - dev->HWMargins[1] - dev->HWMargins[3]) *
+ dev->y_pixels_per_inch / 72.0 + 0.5;
+}
+
+/* Set the width and height, updating MediaSize to remain consistent. */
+void
+gx_device_set_width_height(gx_device *dev, int width, int height)
+{
+ dev->width = width - dev->x_pixels_per_inch *
+ (dev->HWMargins[0] + dev->HWMargins[2]) / 72.0;
+ dev->height = height - dev->y_pixels_per_inch *
+ (dev->HWMargins[1] + dev->HWMargins[3]) / 72.0;
+ dev->MediaSize[0] = width * 72.0 / dev->x_pixels_per_inch;
+ dev->MediaSize[1] = height * 72.0 / dev->y_pixels_per_inch;
+}
+
+/* Set the resolution, updating width and height to remain consistent. */
+void
+gx_device_set_resolution(gx_device *dev, floatp x_dpi, floatp y_dpi)
+{
+ dev->x_pixels_per_inch = x_dpi;
+ dev->y_pixels_per_inch = y_dpi;
+
+ dev->width = (dev->MediaSize[0] - dev->HWMargins[0] - dev->HWMargins[2]) *
+ dev->x_pixels_per_inch / 72.0 + 0.5;
+ dev->height = (dev->MediaSize[1] - dev->HWMargins[1] - dev->HWMargins[3]) *
+ dev->y_pixels_per_inch / 72.0 + 0.5;
+}
+
+/* Set the MediaSize, updating width and height to remain consistent. */
+void
+gx_device_set_media_size(gx_device *dev, floatp media_width, floatp media_height)
+{
+ dev->MediaSize[0] = media_width;
+ dev->MediaSize[1] = media_height;
+ dev->width = (dev->MediaSize[0] - dev->HWMargins[0] - dev->HWMargins[2]) *
+ dev->x_pixels_per_inch / 72.0 + 0.5;
+ dev->height = (dev->MediaSize[1] - dev->HWMargins[1] - dev->HWMargins[3]) *
+ dev->y_pixels_per_inch / 72.0 + 0.5;
+}
diff --git a/pstoraster/gsdevice.h b/pstoraster/gsdevice.h
new file mode 100644
index 000000000..f70753f60
--- /dev/null
+++ b/pstoraster/gsdevice.h
@@ -0,0 +1,86 @@
+/* Copyright (C) 1994, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gsdevice.h */
+/* Device and page control API */
+
+#ifndef gsdevice_INCLUDED
+# define gsdevice_INCLUDED
+
+#ifndef gx_device_DEFINED
+# define gx_device_DEFINED
+typedef struct gx_device_s gx_device;
+#endif
+
+#ifndef gx_device_memory_DEFINED
+# define gx_device_memory_DEFINED
+typedef struct gx_device_memory_s gx_device_memory;
+#endif
+
+#ifndef gs_param_list_DEFINED
+# define gs_param_list_DEFINED
+typedef struct gs_param_list_s gs_param_list;
+#endif
+
+#ifndef gs_state_DEFINED
+# define gs_state_DEFINED
+typedef struct gs_state_s gs_state;
+#endif
+
+int gs_flushpage(P1(gs_state *));
+int gs_copypage(P1(gs_state *));
+int gs_output_page(P3(gs_state *, int, int));
+int gs_copyscanlines(P6(gx_device *, int, byte *, uint, int *, uint *));
+const gx_device *gs_getdevice(P1(int));
+int gs_copydevice(P3(gx_device **, const gx_device *, gs_memory_t *));
+#define gs_makeimagedevice(pdev, pmat, w, h, colors, colors_size, mem)\
+ gs_makewordimagedevice(pdev, pmat, w, h, colors, colors_size, false, true, mem)
+int gs_makewordimagedevice(P9(gx_device **pnew_dev, const gs_matrix *pmat,
+ uint width, uint height,
+ const byte *colors, int num_colors,
+ bool word_oriented, bool page_device,
+ gs_memory_t *mem));
+#define gs_initialize_imagedevice(mdev, pmat, w, h, colors, colors_size, mem)\
+ gs_initialize_wordimagedevice(mdev, pmat, w, h, colors, color_size, false, true, mem)
+int gs_initialize_wordimagedevice(P9(gx_device_memory *new_dev,
+ const gs_matrix *pmat,
+ uint width, uint height,
+ const byte *colors, int colors_size,
+ bool word_oriented, bool page_device,
+ gs_memory_t *mem));
+void gs_nulldevice(P1(gs_state *));
+int gs_setdevice(P2(gs_state *, gx_device *));
+int gs_setdevice_no_erase(P2(gs_state *, gx_device *)); /* returns 1 */
+ /* if erasepage required */
+gx_device *gs_currentdevice(P1(const gs_state *));
+/* gzstate.h redefines the following: */
+#ifndef gs_currentdevice_inline
+# define gs_currentdevice_inline(pgs) gs_currentdevice(pgs)
+#endif
+const char *gs_devicename(P1(const gx_device *));
+void gs_deviceinitialmatrix(P2(gx_device *, gs_matrix *));
+int gs_getdeviceparams(P2(gx_device *, gs_param_list *));
+int gs_putdeviceparams(P2(gx_device *, gs_param_list *));
+int gs_closedevice(P1(gx_device *));
+
+#endif /* gsdevice_INCLUDED */
diff --git a/pstoraster/gsdevmem.c b/pstoraster/gsdevmem.c
new file mode 100644
index 000000000..6992e46b8
--- /dev/null
+++ b/pstoraster/gsdevmem.c
@@ -0,0 +1,221 @@
+/* Copyright (C) 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gsdevmem.c */
+/* Memory device creation for Ghostscript library */
+#include "math_.h" /* for fabs */
+#include "memory_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gxarith.h"
+#include "gxdevice.h"
+#include "gxdevmem.h"
+
+/* Make a memory (image) device. */
+/* If colors_size = -16, -24, or -32, this is a true-color device; */
+/* otherwise, colors_size is the size of the palette in bytes */
+/* (2^N for gray scale, 3*2^N for RGB color). */
+/* We separate device allocation and initialization at customer request. */
+int
+gs_initialize_wordimagedevice(gx_device_memory *new_dev, const gs_matrix *pmat,
+ uint width, uint height, const byte *colors, int colors_size,
+ bool word_oriented, bool page_device, gs_memory_t *mem)
+{ const gx_device_memory *proto_dev;
+ int palette_count = colors_size;
+ int num_components = 1;
+ int pcount;
+ int bits_per_pixel;
+ float x_pixels_per_unit, y_pixels_per_unit;
+ byte palette[256 * 3];
+ byte *dev_palette;
+ bool has_color;
+ switch ( colors_size )
+ {
+ case 3*2:
+ palette_count = 2; num_components = 3;
+ case 2:
+ bits_per_pixel = 1; break;
+ case 3*4:
+ palette_count = 4; num_components = 3;
+ case 4:
+ bits_per_pixel = 2; break;
+ case 3*16:
+ palette_count = 16; num_components = 3;
+ case 16:
+ bits_per_pixel = 4; break;
+ case 3*256:
+ palette_count = 256; num_components = 3;
+ case 256:
+ bits_per_pixel = 8; break;
+ case -16:
+ bits_per_pixel = 16; palette_count = 0; break;
+ case -24:
+ bits_per_pixel = 24; palette_count = 0; break;
+ case -32:
+ bits_per_pixel = 32; palette_count = 0; break;
+ default:
+ return_error(gs_error_rangecheck);
+ }
+ proto_dev = (word_oriented ?
+ gdev_mem_word_device_for_bits(bits_per_pixel) :
+ gdev_mem_device_for_bits(bits_per_pixel));
+ if ( proto_dev == 0 ) /* no suitable device */
+ return_error(gs_error_rangecheck);
+ pcount = palette_count * 3;
+ /* Check to make sure the palette contains white and black, */
+ /* and, if it has any colors, the six primaries. */
+ if ( bits_per_pixel <= 8 )
+ { const byte *p;
+ byte *q;
+ int primary_mask = 0;
+ int i;
+
+ has_color = false;
+ for ( i = 0, p = colors, q = palette;
+ i < palette_count; i++, q += 3
+ )
+ { int mask = 1;
+ switch ( num_components )
+ {
+ case 1: /* gray */
+ q[0] = q[1] = q[2] = *p++;
+ break;
+ default /* case 3 */: /* RGB */
+ q[0] = p[0], q[1] = p[1], q[2] = p[2];
+ p += 3;
+ }
+#define shift_mask(b,n)\
+ switch ( b ) { case 0xff: mask <<= n; case 0: break; default: mask = 0; }
+ shift_mask(q[0], 4);
+ shift_mask(q[1], 2);
+ shift_mask(q[2], 1);
+#undef shift_mask
+ primary_mask |= mask;
+ if ( q[0] != q[1] || q[0] != q[2] )
+ has_color = true;
+ }
+ switch ( primary_mask )
+ {
+ case 129: /* just black and white */
+ if ( has_color ) /* color but no primaries */
+ return_error(gs_error_rangecheck);
+ case 255: /* full color */
+ break;
+ default:
+ return_error(gs_error_rangecheck);
+ }
+ }
+ else
+ has_color = true;
+ /*
+ * The initial transformation matrix must map 1 user unit to
+ * 1/72". Let W and H be the width and height in pixels, and
+ * assume the initial matrix is of the form [A 0 0 B X Y].
+ * Then the size of the image in user units is (W/|A|,H/|B|),
+ * hence the size in inches is ((W/|A|)/72,(H/|B|)/72), so
+ * the number of pixels per inch is
+ * (W/((W/|A|)/72),H/((H/|B|)/72)), or (|A|*72,|B|*72).
+ * Similarly, if the initial matrix is [0 A B 0 X Y] for a 90
+ * or 270 degree rotation, the size of the image in user
+ * units is (W/|B|,H/|A|), so the pixels per inch are
+ * (|B|*72,|A|*72). We forbid non-orthogonal transformation
+ * matrices.
+ */
+ if ( is_fzero2(pmat->xy, pmat->yx) )
+ x_pixels_per_unit = pmat->xx, y_pixels_per_unit = pmat->yy;
+ else if ( is_fzero2(pmat->xx, pmat->yy) )
+ x_pixels_per_unit = pmat->yx, y_pixels_per_unit = pmat->xy;
+ else
+ return_error(gs_error_undefinedresult);
+ /* All checks done, allocate the device. */
+ if ( bits_per_pixel != 1 )
+ { dev_palette = gs_alloc_string(mem, pcount,
+ "gs_makeimagedevice(palette)");
+ if ( dev_palette == 0 )
+ return_error(gs_error_VMerror);
+ }
+ gs_make_mem_device(new_dev, proto_dev, mem,
+ (page_device ? 1 : -1), 0);
+ if ( !has_color )
+ { new_dev->color_info.num_components = 1;
+ new_dev->color_info.max_color = 0;
+ new_dev->color_info.dither_colors = 0;
+ }
+ if ( bits_per_pixel == 1 )
+ { /* Determine the polarity from the palette. */
+ /* This is somewhat bogus, but does the right thing */
+ /* in the only cases we care about. */
+ gdev_mem_mono_set_inverted(new_dev,
+ (palette[0] | palette[1] | palette[2]) != 0);
+ }
+ else
+ { new_dev->palette.size = pcount;
+ new_dev->palette.data = dev_palette;
+ memcpy(dev_palette, palette, pcount);
+ }
+ new_dev->initial_matrix = *pmat;
+ new_dev->MarginsHWResolution[0] = new_dev->HWResolution[0] =
+ fabs(x_pixels_per_unit) * 72;
+ new_dev->MarginsHWResolution[1] = new_dev->HWResolution[1] =
+ fabs(y_pixels_per_unit) * 72;
+ gx_device_set_width_height((gx_device *)new_dev, width, height);
+ /* Set the ImagingBBox so we get a correct clipping region. */
+ { gs_rect bbox;
+ bbox.p.x = 0;
+ bbox.p.y = 0;
+ bbox.q.x = width;
+ bbox.q.y = height;
+ gs_bbox_transform_inverse(&bbox, pmat, &bbox);
+ new_dev->ImagingBBox[0] = bbox.p.x;
+ new_dev->ImagingBBox[1] = bbox.p.y;
+ new_dev->ImagingBBox[2] = bbox.q.x;
+ new_dev->ImagingBBox[3] = bbox.q.y;
+ new_dev->ImagingBBox_set = true;
+ }
+ /* The bitmap will be allocated when the device is opened. */
+ new_dev->is_open = false;
+ new_dev->bitmap_memory = mem;
+ return 0;
+}
+
+int
+gs_makewordimagedevice(gx_device **pnew_dev, const gs_matrix *pmat,
+ uint width, uint height, const byte *colors, int num_colors,
+ bool word_oriented, bool page_device, gs_memory_t *mem)
+{ int code;
+ gx_device_memory *pnew =
+ gs_alloc_struct(mem, gx_device_memory, &st_device_memory,
+ "gs_makeimagedevice(device)");
+
+ if (pnew == 0)
+ return_error (gs_error_VMerror);
+ code = gs_initialize_wordimagedevice(pnew, pmat, width, height,
+ colors, num_colors, word_oriented,
+ page_device, mem);
+ if (code < 0)
+ { gs_free_object(mem, pnew, "gs_makeimagedevice(device)");
+ return code;
+ }
+ *pnew_dev = (gx_device *)pnew;
+ return 0;
+}
diff --git a/pstoraster/gsdparam.c b/pstoraster/gsdparam.c
new file mode 100644
index 000000000..ba6b76c1e
--- /dev/null
+++ b/pstoraster/gsdparam.c
@@ -0,0 +1,603 @@
+/*
+ Copyright 1993-1999 by Easy Software Products.
+ Copyright (C) 1993, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gsdparam.c */
+/* Default device parameters for Ghostscript library */
+#include "memory_.h" /* for memcpy */
+#include "string_.h" /* for strlen */
+#include "gx.h"
+#include "gserrors.h"
+#include "gsparam.h"
+#include "gxdevice.h"
+#include "gxfixed.h"
+
+/* Define whether we accept PageSize as a synonym for MediaSize. */
+/* This is for backward compatibility only. */
+/* MRS - This is for ADOBE POSTSCRIPT compatibility!!!! */
+#define PAGESIZE_IS_MEDIASIZE
+
+/* ================ Getting parameters ================ */
+
+/* Forward references */
+private bool param_HWColorMap(P2(gx_device *, byte *));
+
+/* Get the device parameters. */
+int
+gs_getdeviceparams(gx_device *dev, gs_param_list *plist)
+{ gx_device_set_procs(dev);
+ fill_dev_proc(dev, get_params, gx_default_get_params);
+ fill_dev_proc(dev, get_page_device, gx_default_get_page_device);
+ fill_dev_proc(dev, get_alpha_bits, gx_default_get_alpha_bits);
+ return (*dev_proc(dev, get_params))(dev, plist);
+}
+
+/* Standard ProcessColorModel values. */
+static const char *pcmsa[] = {
+ "", "DeviceGray", "", "DeviceRGB", "DeviceCMYK"
+};
+
+/* Get standard parameters. */
+int
+gx_default_get_params(gx_device *dev, gs_param_list *plist)
+{
+ int code;
+
+ /* Standard page device parameters: */
+
+ gs_param_string dns, pcms;
+ gs_param_float_array msa, ibba, hwra, ma;
+#define set_param_array(a, d, s)\
+ (a.data = d, a.size = s, a.persistent = false);
+
+ /* Non-standard parameters: */
+
+ int colors = dev->color_info.num_components;
+ int depth = dev->color_info.depth;
+ int GrayValues = dev->color_info.max_gray + 1;
+ int HWSize[2];
+ gs_param_int_array hwsa;
+ gs_param_float_array hwma, mhwra;
+
+ /* Fill in page device parameters. */
+
+ param_string_from_string(dns, dev->dname);
+ { const char *cms = pcmsa[colors];
+ /* We might have an uninitialized device with */
+ /* color_info.num_components = 0.... */
+ if ( *cms != 0 )
+ param_string_from_string(pcms, cms);
+ else
+ pcms.data = 0;
+ }
+ set_param_array(hwra, dev->HWResolution, 2);
+ set_param_array(msa, dev->MediaSize, 2);
+ set_param_array(ibba, dev->ImagingBBox, 4);
+ set_param_array(ma, dev->Margins, 2);
+
+ /* Fill in non-standard parameters. */
+
+ HWSize[0] = dev->width;
+ HWSize[1] = dev->height;
+ set_param_array(hwsa, HWSize, 2);
+ set_param_array(hwma, dev->HWMargins, 4);
+ set_param_array(mhwra, dev->MarginsHWResolution, 2);
+
+ /* Transmit the values. */
+
+ if (
+ /* Standard parameters */
+
+ (code = param_write_name(plist, "OutputDevice", &dns)) < 0 ||
+#ifdef PAGESIZE_IS_MEDIASIZE
+ (code = param_write_float_array(plist, "PageSize", &msa)) < 0 ||
+#endif
+ (code = (pcms.data == 0 ? 0 :
+ param_write_name(plist, "ProcessColorModel", &pcms))) < 0 ||
+ (code = param_write_float_array(plist, "HWResolution", &hwra)) < 0 ||
+ (code = (dev->ImagingBBox_set ?
+ param_write_float_array(plist, "ImagingBBox", &ibba) :
+ param_write_null(plist, "ImagingBBox"))) < 0 ||
+ (code = param_write_float_array(plist, "Margins", &ma)) < 0 ||
+
+ /* Non-standard parameters */
+
+ (code = param_write_int_array(plist, "HWSize", &hwsa)) < 0 ||
+ (code = param_write_float_array(plist, ".HWMargins", &hwma)) < 0 ||
+ (code = param_write_float_array(plist, ".MarginsHWResolution", &mhwra)) < 0 ||
+ (code = param_write_float_array(plist, ".MediaSize", &msa)) < 0 ||
+ (code = param_write_string(plist, "Name", &dns)) < 0 ||
+ (code = param_write_int(plist, "Colors", &colors)) < 0 ||
+ (code = param_write_int(plist, "BitsPerPixel", &depth)) < 0 ||
+ (code = param_write_int(plist, "GrayValues", &GrayValues)) < 0 ||
+ (code = param_write_long(plist, "PageCount", &dev->PageCount)) < 0 ||
+ (code = param_write_bool(plist, ".IgnoreNumCopies", &dev->IgnoreNumCopies)) < 0
+ )
+ return code;
+
+ /* Fill in color information. */
+
+ if ( colors > 1 )
+ { int RGBValues = dev->color_info.max_color + 1;
+ long ColorValues = 1L << depth;
+ if ( (code = param_write_int(plist, "RedValues", &RGBValues)) < 0 ||
+ (code = param_write_int(plist, "GreenValues", &RGBValues)) < 0 ||
+ (code = param_write_int(plist, "BlueValues", &RGBValues)) < 0 ||
+ (code = param_write_long(plist, "ColorValues", &ColorValues)) < 0
+ )
+ return code;
+ }
+
+ if ( param_requested(plist, "HWColorMap") )
+ { byte palette[3 << 8];
+ if ( param_HWColorMap(dev, palette) )
+ { gs_param_string hwcms;
+ hwcms.data = palette, hwcms.size = colors << depth,
+ hwcms.persistent = false;
+ if ( (code = param_write_string(plist, "HWColorMap", &hwcms)) < 0 )
+ return code;
+ }
+ }
+
+ { int tab = (*dev_proc(dev, get_alpha_bits))(dev, go_text);
+ int gab = (*dev_proc(dev, get_alpha_bits))(dev, go_graphics);
+
+ if ( (code = param_write_int(plist, "TextAlphaBits", &tab)) < 0 ||
+ (code = param_write_int(plist, "GraphicsAlphaBits", &gab)) < 0
+ )
+ return code;
+ }
+
+ return 0;
+}
+
+/* Get the color map for a device. Return true if there is one. */
+private bool
+param_HWColorMap(gx_device *dev, byte *palette /* 3 << 8 */)
+{ int depth = dev->color_info.depth;
+ int colors = dev->color_info.num_components;
+
+ if ( depth <= 8 && colors <= 3 )
+ { byte *p = palette;
+ gx_color_value rgb[3];
+ gx_color_index i;
+ fill_dev_proc(dev, map_color_rgb, gx_default_map_color_rgb);
+ for ( i = 0; (i >> depth) == 0; i++ )
+ { int j;
+ if ( (*dev_proc(dev, map_color_rgb))(dev, i, rgb) < 0 )
+ return false;
+ for ( j = 0; j < colors; j++ )
+ *p++ = gx_color_value_to_byte(rgb[j]);
+ }
+ return true;
+ }
+ return false;
+}
+
+/* ================ Putting parameters ================ */
+
+/* Forward references */
+private int param_MediaSize(P4(gs_param_list *, gs_param_name,
+ const float *, gs_param_float_array *));
+#if 0 /****** not used ******/
+private int param_check_bool(P4(gs_param_list *, gs_param_name, bool, bool));
+#endif /****** not used ******/
+private int param_check_long(P4(gs_param_list *, gs_param_name, long, bool));
+#define param_check_int(plist, pname, ival, defined)\
+ param_check_long(plist, pname, (long)(ival), defined)
+private int param_check_bytes(P5(gs_param_list *, gs_param_name, const byte *, uint, bool));
+#define param_check_string(plist, pname, str, defined)\
+ param_check_bytes(plist, pname, (const byte *)str, strlen(str), defined)
+
+/* Set the device parameters. */
+/* If the device was open and the put_params procedure closed it, */
+/* return 1; otherwise, return 0 or an error code as usual. */
+int
+gs_putdeviceparams(gx_device *dev, gs_param_list *plist)
+{ bool was_open = dev->is_open;
+ int code;
+
+ gx_device_set_procs(dev);
+ fill_dev_proc(dev, put_params, gx_default_put_params);
+ fill_dev_proc(dev, get_alpha_bits, gx_default_get_alpha_bits);
+ code = (*dev_proc(dev, put_params))(dev, plist);
+ return (code < 0 ? code : was_open && !dev->is_open ? 1 : code);
+}
+
+/* Set standard parameters. */
+/* Note that setting the size or resolution closes the device. */
+/* Window devices that don't want this to happen must temporarily */
+/* set is_open to false before calling gx_default_put_params, */
+/* and then taking appropriate action afterwards. */
+int
+gx_default_put_params(gx_device *dev, gs_param_list *plist)
+{ int ecode = 0;
+ int code;
+ gs_param_name param_name;
+ gs_param_float_array hwra;
+ gs_param_int_array hwsa;
+ gs_param_float_array msa;
+ gs_param_float_array ma;
+ gs_param_float_array hwma;
+ gs_param_float_array mhwra;
+
+ bool ignc = dev->IgnoreNumCopies;
+ gs_param_float_array ibba;
+ bool ibbnull = false;
+ int colors = dev->color_info.num_components;
+ int depth = dev->color_info.depth;
+ int GrayValues = dev->color_info.max_gray + 1;
+ int RGBValues = dev->color_info.max_color + 1;
+ long ColorValues = 1L << depth;
+ gs_param_string cms;
+
+#define BEGIN_ARRAY_PARAM(pread, pname, pa, psize, e)\
+ switch ( code = pread(plist, (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(plist, param_name, ecode);\
+ case 1:\
+ (pa).data = 0; /* mark as not filled */\
+ }
+
+ /*
+ * The HWResolution, HWSize, and MediaSize parameters interact in
+ * the following way:
+ * 1. Setting HWResolution recomputes HWSize from MediaSize.
+ * 2. Setting HWSize recomputes MediaSize from HWResolution.
+ * 3. Setting MediaSize recomputes HWSize from HWResolution.
+ * If more than one parameter is being set, we apply these rules
+ * in the order 1, 2, 3. This does the right thing in the most
+ * common case of setting more than one parameter, namely,
+ * setting both HWResolution and HWSize.
+ */
+
+ BEGIN_ARRAY_PARAM(param_read_float_array, "HWResolution", hwra, 2, hwre)
+ if ( hwra.data[0] <= 0 || hwra.data[1] <= 0 )
+ ecode = gs_note_error(gs_error_rangecheck);
+ else
+ break;
+ END_ARRAY_PARAM(hwra, hwre)
+
+ BEGIN_ARRAY_PARAM(param_read_int_array, "HWSize", hwsa, 2, hwsa)
+ /* We need a special check to handle the nullpage device, */
+ /* whose size is legitimately [0 0]. */
+ if ( (hwsa.data[0] <= 0 && hwsa.data[0] != dev->width) ||
+ (hwsa.data[1] <= 0 && hwsa.data[1] != dev->height)
+ )
+ ecode = gs_note_error(gs_error_rangecheck);
+#define max_coord (max_fixed / fixed_1)
+#if max_coord < max_int
+ else if ( hwsa.data[0] > max_coord || hwsa.data[1] > max_coord )
+ ecode = gs_note_error(gs_error_limitcheck);
+#endif
+#undef max_coord
+ else
+ break;
+ END_ARRAY_PARAM(hwsa, hwse)
+
+ { const float *res = (hwra.data == 0 ? dev->HWResolution : hwra.data);
+#ifdef PAGESIZE_IS_MEDIASIZE
+ const float *data;
+
+ /* .MediaSize takes precedence over PageSize, so */
+ /* we read PageSize first. */
+ code = param_MediaSize(plist, "PageSize", res, &msa);
+ if ( code < 0 )
+ ecode = code;
+ /* Prevent data from being set to 0 if PageSize is specified */
+ /* but .MediaSize is not. */
+ data = msa.data;
+ code = param_MediaSize(plist, ".MediaSize", res, &msa);
+ if ( code < 0 )
+ ecode = code;
+ else if ( msa.data == 0 )
+ msa.data = data;
+#else
+ code = param_MediaSize(plist, ".MediaSize", res, &msa);
+ if ( code < 0 )
+ ecode = code;
+#endif
+ }
+
+ BEGIN_ARRAY_PARAM(param_read_float_array, "Margins", ma, 2, me)
+ break;
+ END_ARRAY_PARAM(ma, me)
+
+ BEGIN_ARRAY_PARAM(param_read_float_array, ".HWMargins", hwma, 4, hwme)
+ break;
+ END_ARRAY_PARAM(hwma, hwme)
+
+ /* MarginsHWResolution cannot be changed, only checked. */
+ BEGIN_ARRAY_PARAM(param_read_float_array, ".MarginsHWResolution", mhwra, 2, mhwre)
+ if ( mhwra.data[0] != dev->MarginsHWResolution[0] ||
+ mhwra.data[1] != dev->MarginsHWResolution[1]
+ )
+ ecode = gs_note_error(gs_error_rangecheck);
+ else
+ break;
+ END_ARRAY_PARAM(mhwra, mhwre)
+
+ switch ( code = param_read_bool(plist, (param_name = ".IgnoreNumCopies"), &ignc) )
+ {
+ default:
+ ecode = code;
+ param_signal_error(plist, param_name, ecode);
+ case 0:
+ case 1:
+ break;
+ }
+
+ /* Ignore parameters that only have meaning for printers. */
+#define IGNORE_INT_PARAM(pname)\
+ { int igni;\
+ switch ( code = param_read_int(plist, (param_name = pname), &igni) )\
+ { default:\
+ ecode = code;\
+ param_signal_error(plist, param_name, ecode);\
+ case 0:\
+ case 1:\
+ break;\
+ }\
+ }
+ IGNORE_INT_PARAM("%MediaSource")
+ IGNORE_INT_PARAM("%MediaDestination")
+
+ switch ( code = param_read_float_array(plist, (param_name = "ImagingBBox"), &ibba) )
+ {
+ case 0:
+ if ( ibba.size != 4 ||
+ ibba.data[2] < ibba.data[0] || ibba.data[3] < ibba.data[1]
+ )
+ ecode = gs_note_error(gs_error_rangecheck);
+ else
+ break;
+ goto ibbe;
+ default:
+ if ( (code = param_read_null(plist, param_name)) == 0 )
+ { ibbnull = true;
+ ibba.data = 0;
+ break;
+ }
+ ecode = code; /* can't be 1 */
+ibbe: param_signal_error(plist, param_name, ecode);
+ case 1:
+ ibba.data = 0;
+ break;
+ }
+
+ /* Now check nominally read-only parameters. */
+ if ( (code = param_check_string(plist, "OutputDevice", dev->dname, true)) < 0 )
+ ecode = code;
+ if ( (code = param_check_string(plist, "ProcessColorModel", pcmsa[colors], colors != 0)) < 0 )
+ ecode = code;
+ if ( (code = param_check_string(plist, "Name", dev->dname, true)) < 0 )
+ ecode = code;
+ if ( (code = param_check_int(plist, "Colors", colors, true)) < 0 )
+ ecode = code;
+ if ( (code = param_check_int(plist, "BitsPerPixel", depth, true)) < 0 )
+ ecode = code;
+ if ( (code = param_check_int(plist, "GrayValues", GrayValues, true)) < 0 )
+ ecode = code;
+ if ( (code = param_check_long(plist, "PageCount", dev->PageCount, true)) < 0 )
+ ecode = code;
+ if ( (code = param_check_int(plist, "RedValues", RGBValues, colors > 1)) < 0 )
+ ecode = code;
+ if ( (code = param_check_int(plist, "GreenValues", RGBValues, colors > 1)) < 0 )
+ ecode = code;
+ if ( (code = param_check_int(plist, "BlueValues", RGBValues, colors > 1)) < 0 )
+ ecode = code;
+ if ( (code = param_check_long(plist, "ColorValues", ColorValues, colors > 1)) < 0 )
+ ecode = code;
+ if ( param_read_string(plist, "HWColorMap", &cms) != 1 )
+ { byte palette[3 << 8];
+ if ( param_HWColorMap(dev, palette) )
+ code = param_check_bytes(plist, "HWColorMap", palette,
+ colors << depth, true);
+ else
+ code = param_check_bytes(plist, "HWColorMap", 0, 0, false);
+ if ( code < 0 )
+ ecode = code;
+ }
+ if ( (code =
+ param_check_int(plist, "TextAlphaBits",
+ (*dev_proc(dev, get_alpha_bits))(dev, go_text),
+ true)) < 0
+ )
+ ecode = code;
+ if ( (code =
+ param_check_int(plist, "GraphicsAlphaBits",
+ (*dev_proc(dev, get_alpha_bits))(dev, go_graphics),
+ true)) < 0
+ )
+ ecode = code;
+
+ /* We must 'commit', in order to detect unknown parameters, */
+ /* even if there were errors. */
+ code = param_commit(plist);
+ if ( ecode < 0 )
+ return ecode;
+ if ( code < 0 )
+ return code;
+
+ /* Now actually make the changes. */
+ /* Changing resolution or page size requires closing the device, */
+ /* but changing margins or ImagingBBox does not. */
+ /* In order not to close and reopen the device unnecessarily, */
+ /* we check for replacing the values with the same ones. */
+
+ if ( hwra.data != 0 &&
+ (dev->HWResolution[0] != hwra.data[0] ||
+ dev->HWResolution[1] != hwra.data[1])
+ )
+ { if ( dev->is_open )
+ gs_closedevice(dev);
+ gx_device_set_resolution(dev, hwra.data[0], hwra.data[1]);
+ }
+ if ( hwsa.data != 0 &&
+ (dev->width != hwsa.data[0] ||
+ dev->height != hwsa.data[1])
+ )
+ { if ( dev->is_open )
+ gs_closedevice(dev);
+ gx_device_set_width_height(dev, hwsa.data[0], hwsa.data[1]);
+ }
+ if ( msa.data != 0 &&
+ (dev->MediaSize[0] != msa.data[0] ||
+ dev->MediaSize[1] != msa.data[1])
+ )
+ { if ( dev->is_open )
+ gs_closedevice(dev);
+ gx_device_set_page_size(dev, msa.data[0], msa.data[1]);
+ }
+ if ( ma.data != 0 )
+ { dev->Margins[0] = ma.data[0];
+ dev->Margins[1] = ma.data[1];
+ }
+ if ( hwma.data != 0 )
+ { dev->HWMargins[0] = hwma.data[0];
+ dev->HWMargins[1] = hwma.data[1];
+ dev->HWMargins[2] = hwma.data[2];
+ dev->HWMargins[3] = hwma.data[3];
+ }
+ dev->IgnoreNumCopies = ignc;
+ if ( ibba.data != 0 )
+ { dev->ImagingBBox[0] = ibba.data[0];
+ dev->ImagingBBox[1] = ibba.data[1];
+ dev->ImagingBBox[2] = ibba.data[2];
+ dev->ImagingBBox[3] = ibba.data[3];
+ dev->ImagingBBox_set = true;
+ }
+ else if ( ibbnull )
+ { dev->ImagingBBox_set = false;
+ }
+ return 0;
+}
+
+/* Read .MediaSize or, if supported as a synonym, PageSize. */
+private int
+param_MediaSize(gs_param_list *plist, gs_param_name pname,
+ const float *res, gs_param_float_array *pa)
+{ gs_param_name param_name;
+ int ecode = 0;
+ int code;
+
+ BEGIN_ARRAY_PARAM(param_read_float_array, pname, *pa, 2, mse)
+ float width_new = pa->data[0] * res[0] / 72;
+ float height_new = pa->data[1] * res[1] / 72;
+ if ( width_new < 0 || height_new < 0 )
+ ecode = gs_note_error(gs_error_rangecheck);
+#define max_coord (max_fixed / fixed_1)
+#if max_coord < max_int
+ else if ( width_new > max_coord || height_new > max_coord )
+ ecode = gs_note_error(gs_error_limitcheck);
+#endif
+#undef max_coord
+ else
+ break;
+ END_ARRAY_PARAM(*pa, mse)
+ return ecode;
+}
+
+#if 0 /****** not used ******/
+/* Check that a nominally read-only parameter is being set to */
+/* its existing value. */
+private int
+param_check_bool(gs_param_list *plist, gs_param_name pname, bool value,
+ bool defined)
+{ int code;
+ bool new_value;
+ switch ( code = param_read_bool(plist, pname, &new_value) )
+ {
+ case 0:
+ if ( defined && new_value == 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;
+}
+#endif /****** not used ******/
+private int
+param_check_long(gs_param_list *plist, gs_param_name pname, long value,
+ bool defined)
+{ int code;
+ long new_value;
+ switch ( code = param_read_long(plist, pname, &new_value) )
+ {
+ case 0:
+ if ( defined && new_value == 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;
+}
+private int
+param_check_bytes(gs_param_list *plist, gs_param_name pname, const byte *str,
+ uint size, bool defined)
+{ int code;
+ gs_param_string new_value;
+ switch ( code = param_read_string(plist, pname, &new_value) )
+ {
+ case 0:
+ if ( 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;
+}
diff --git a/pstoraster/gsdps1.c b/pstoraster/gsdps1.c
new file mode 100644
index 000000000..5a5bf9914
--- /dev/null
+++ b/pstoraster/gsdps1.c
@@ -0,0 +1,169 @@
+/* Copyright (C) 1991, 1992, 1994 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gsdps1.c */
+/* Display PostScript graphics additions for Ghostscript library */
+#include "math_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gsmatrix.h" /* for gscoord.h */
+#include "gscoord.h"
+#include "gspaint.h"
+#include "gxfixed.h"
+#include "gxmatrix.h"
+#include "gspath.h"
+#include "gspath2.h" /* defines interface */
+#include "gzpath.h"
+#include "gzstate.h"
+
+/*
+ * Define how much rounding slop setbbox should leave,
+ * in device coordinates. Because of rounding in transforming
+ * path coordinates to fixed point, the minimum realistic value is:
+ *
+ * #define box_rounding_slop_fixed (fixed_epsilon)
+ *
+ * But even this isn't enough to compensate for cumulative rounding error
+ * in rmoveto or rcurveto. Instead, we somewhat arbitrarily use:
+ */
+#define box_rounding_slop_fixed (fixed_epsilon * 3)
+
+/* ------ Graphics state ------ */
+
+/* Set the bounding box for the current path. */
+int
+gs_setbbox(gs_state *pgs, floatp llx, floatp lly, floatp urx, floatp ury)
+{ gs_rect ubox, dbox;
+ gs_fixed_rect obox, bbox;
+ gx_path *ppath = pgs->path;
+ int code;
+ if ( llx > urx || lly > ury )
+ return_error(gs_error_rangecheck);
+ /* Transform box to device coordinates. */
+ ubox.p.x = llx;
+ ubox.p.y = lly;
+ ubox.q.x = urx;
+ ubox.q.y = ury;
+ if ( (code = gs_bbox_transform(&ubox, &ctm_only(pgs), &dbox)) < 0 )
+ return code;
+ /* Round the corners in opposite directions. */
+ /* Because we can't predict the magnitude of the dbox values, */
+ /* we add/subtract the slop after fixing. */
+ bbox.p.x =
+ (fixed)floor(dbox.p.x * fixed_scale) - box_rounding_slop_fixed;
+ bbox.p.y =
+ (fixed)floor(dbox.p.y * fixed_scale) - box_rounding_slop_fixed;
+ bbox.q.x =
+ (fixed)ceil(dbox.q.x * fixed_scale) + box_rounding_slop_fixed;
+ bbox.q.y =
+ (fixed)ceil(dbox.q.y * fixed_scale) + box_rounding_slop_fixed;
+ if ( gx_path_bbox(ppath, &obox) >= 0 )
+ { /* Take the union of the bboxes. */
+ ppath->bbox.p.x = min(obox.p.x, bbox.p.x);
+ ppath->bbox.p.y = min(obox.p.y, bbox.p.y);
+ ppath->bbox.q.x = max(obox.q.x, bbox.q.x);
+ ppath->bbox.q.y = max(obox.q.y, bbox.q.y);
+ }
+ else /* empty path */
+ { /* Just set the bbox. */
+ ppath->bbox.p.x = bbox.p.x;
+ ppath->bbox.p.y = bbox.p.y;
+ ppath->bbox.q.x = bbox.q.x;
+ ppath->bbox.q.y = bbox.q.y;
+ }
+ ppath->bbox_set = 1;
+ return 0;
+}
+
+/* ------ Rectangles ------ */
+
+/* Append a list of rectangles to a path. */
+int
+gs_rectappend(gs_state *pgs, const gs_rect *pr, uint count)
+{ for ( ; count != 0; count--, pr++ )
+ { floatp px = pr->p.x, py = pr->p.y, qx = pr->q.x, qy = pr->q.y;
+ int code;
+ /* Ensure counter-clockwise drawing. */
+ if ( (qx >= px) != (qy >= py) )
+ qx = px, px = pr->q.x; /* swap x values */
+ if ( (code = gs_moveto(pgs, px, py)) < 0 ||
+ (code = gs_lineto(pgs, qx, py)) < 0 ||
+ (code = gs_lineto(pgs, qx, qy)) < 0 ||
+ (code = gs_lineto(pgs, px, qy)) < 0 ||
+ (code = gs_closepath(pgs)) < 0
+ )
+ return code;
+ }
+ return 0;
+}
+
+/* Clip to a list of rectangles. */
+int
+gs_rectclip(gs_state *pgs, const gs_rect *pr, uint count)
+{ int code;
+ gx_path old_path;
+ old_path = *pgs->path;
+ gx_path_reset(pgs->path);
+ if ( (code = gs_rectappend(pgs, pr, count)) < 0 ||
+ (code = gs_clip(pgs)) < 0
+ )
+ { gx_path_release(pgs->path);
+ *pgs->path = old_path;
+ return code;
+ }
+ gs_newpath(pgs);
+ gx_path_release(&old_path);
+ return 0;
+}
+
+/* Fill a list of rectangles. */
+/* (We could do this a lot more efficiently.) */
+int
+gs_rectfill(gs_state *pgs, const gs_rect *pr, uint count)
+{ int code;
+ if ( (code = gs_gsave(pgs)) < 0 ) return code;
+ if ( (code = gs_newpath(pgs)) < 0 ||
+ (code = gs_rectappend(pgs, pr, count)) < 0 ||
+ (code = gs_fill(pgs)) < 0
+ )
+ DO_NOTHING;
+ gs_grestore(pgs);
+ return code;
+}
+
+/* Stroke a list of rectangles. */
+/* (We could do this a lot more efficiently.) */
+int
+gs_rectstroke(gs_state *pgs, const gs_rect *pr, uint count,
+ const gs_matrix *pmat)
+{ int code;
+ if ( (code = gs_gsave(pgs)) < 0 ) return code;
+ if ( (code = gs_newpath(pgs)) < 0 ||
+ (code = gs_rectappend(pgs, pr, count)) < 0 ||
+ (pmat != NULL && (code = gs_concat(pgs, pmat)) < 0) ||
+ (code = gs_stroke(pgs)) < 0
+ )
+ DO_NOTHING;
+ gs_grestore(pgs);
+ return code;
+}
diff --git a/pstoraster/gserror.h b/pstoraster/gserror.h
new file mode 100644
index 000000000..b660714eb
--- /dev/null
+++ b/pstoraster/gserror.h
@@ -0,0 +1,33 @@
+/* Copyright (C) 1994 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gserror.h */
+/* Error return macros */
+
+#ifdef DEBUG
+int gs_log_error(P3(int, const char _ds *, int));
+#else
+# define gs_log_error(err, file, line) (err)
+#endif
+#define gs_note_error(err) gs_log_error(err, __FILE__, __LINE__)
+#define return_error(err) return gs_note_error(err)
diff --git a/pstoraster/gserrors.h b/pstoraster/gserrors.h
new file mode 100644
index 000000000..a6a206c1c
--- /dev/null
+++ b/pstoraster/gserrors.h
@@ -0,0 +1,48 @@
+/* Copyright (C) 1989, 1993 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gserrors.h */
+/* Error code definitions */
+
+/* A procedure that may return an error always returns */
+/* a non-negative value (zero, unless otherwise noted) for success, */
+/* or negative for failure. */
+/* We use ints rather than an enum to avoid a lot of casting. */
+
+#define gs_error_unknownerror (-1) /* unknown error */
+#define gs_error_interrupt (-6)
+#define gs_error_invalidaccess (-7)
+#define gs_error_invalidfileaccess (-9)
+#define gs_error_invalidfont (-10)
+#define gs_error_ioerror (-12)
+#define gs_error_limitcheck (-13)
+#define gs_error_nocurrentpoint (-14)
+#define gs_error_rangecheck (-15)
+#define gs_error_typecheck (-20)
+#define gs_error_undefined (-21)
+#define gs_error_undefinedfilename (-22)
+#define gs_error_undefinedresult (-23)
+#define gs_error_VMerror (-25)
+#define gs_error_unregistered (-29)
+
+#define gs_error_Fatal (-100)
diff --git a/pstoraster/gsexit.h b/pstoraster/gsexit.h
new file mode 100644
index 000000000..fa0a4ba8d
--- /dev/null
+++ b/pstoraster/gsexit.h
@@ -0,0 +1,39 @@
+/* Copyright (C) 1994 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gsexit.h */
+/* Declarations for exits */
+
+#ifndef gsexit_INCLUDED
+# define gsexit_INCLUDED
+
+void gs_exit_with_code(P2(int exit_status, int code));
+void gs_exit(P1(int exit_status));
+#define gs_abort() gs_exit(1)
+
+/* The only reason we export gs_exit_status is so that window systems */
+/* with alert boxes can know whether to pause before exiting if */
+/* the program terminates with an error. There must be a better way .... */
+extern int gs_exit_status;
+
+#endif /* gsexit_INCLUDED */
diff --git a/pstoraster/gsfont.c b/pstoraster/gsfont.c
new file mode 100644
index 000000000..b982ee975
--- /dev/null
+++ b/pstoraster/gsfont.c
@@ -0,0 +1,486 @@
+/* Copyright (C) 1989, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gsfont.c */
+/* Font operators for Ghostscript library */
+#include "gx.h"
+#include "memory_.h"
+#include "gserrors.h"
+#include "gsstruct.h"
+#include "gxfixed.h"
+#include "gxmatrix.h"
+#include "gzstate.h" /* must precede gxdevice */
+#include "gxdevice.h" /* must precede gxfont */
+#include "gschar.h"
+#include "gxfont.h"
+#include "gxfcache.h"
+
+/* Imported procedures */
+void gs_purge_font_from_char_caches(P2(gs_font_dir *, const gs_font *));
+
+/* Define the sizes of the various aspects of the font/character cache. */
+/*** Big memory machines ***/
+#define smax_LARGE 50 /* smax - # of scaled fonts */
+#define bmax_LARGE 500000 /* bmax - space for cached chars */
+#define mmax_LARGE 200 /* mmax - # of cached font/matrix pairs */
+#define cmax_LARGE 5000 /* cmax - # of cached chars */
+#define blimit_LARGE 2500 /* blimit/upper - max size of a single cached char */
+/*** Small memory machines ***/
+#define smax_SMALL 20 /* smax - # of scaled fonts */
+#define bmax_SMALL 25000 /* bmax - space for cached chars */
+#define mmax_SMALL 40 /* mmax - # of cached font/matrix pairs */
+#define cmax_SMALL 500 /* cmax - # of cached chars */
+#define blimit_SMALL 100 /* blimit/upper - max size of a single cached char */
+
+private_st_font_dir();
+private struct_proc_enum_ptrs(font_enum_ptrs);
+private struct_proc_reloc_ptrs(font_reloc_ptrs);
+public_st_gs_font();
+public_st_gs_font_base();
+private_st_gs_font_ptr();
+public_st_gs_font_ptr_element();
+
+/*
+ * Garbage collection of fonts poses some special problems. On the one
+ * hand, we need to keep track of all existing base (not scaled) fonts,
+ * using the next/prev list whose head is the orig_fonts member of the font
+ * directory; on the other hand, we want these to be "weak" pointers that
+ * don't keep fonts in existence if the fonts aren't referenced from
+ * anywhere else. We accomplish this as follows:
+ *
+ * We don't trace through gs_font_dir.orig_fonts or gs_font.{next,prev}
+ * during the mark phase of the GC.
+ *
+ * When we finalize a base gs_font, we unlink it from the list. (A
+ * gs_font is a base font iff its base member points to itself.)
+ *
+ * We *do* relocate the orig_fonts and next/prev pointers during the
+ * relocation phase of the GC. */
+
+/* Font directory GC procedures */
+#define dir ((gs_font_dir *)vptr)
+private ENUM_PTRS_BEGIN(font_dir_enum_ptrs)
+ { /* Enumerate pointers from cached characters to f/m pairs, */
+ /* and mark the cached character glyphs. */
+ /* See gxfcache.h for why we do this here. */
+ if ( index - st_font_dir_max_ptrs <= dir->ccache.table_mask )
+ { cached_char *cc =
+ dir->ccache.table[index - st_font_dir_max_ptrs];
+ if ( cc == 0 )
+ { ENUM_RETURN(0);
+ }
+ (*dir->ccache.mark_glyph)(cc->code);
+ ENUM_RETURN(cc_pair(cc) - cc->pair_index);
+ }
+ }
+ return 0;
+#define e1(i,elt) ENUM_PTR(i,gs_font_dir,elt);
+ font_dir_do_ptrs(e1)
+#undef e1
+ENUM_PTRS_END
+private RELOC_PTRS_BEGIN(font_dir_reloc_ptrs) ;
+ /* Relocate the pointers from cached characters to f/m pairs. */
+ /* See gxfcache.h for why we do this here. */
+ { int chi;
+ for ( chi = dir->ccache.table_mask; chi >= 0; --chi )
+ { cached_char *cc = dir->ccache.table[chi];
+ if ( cc != 0 )
+ cc_set_pair_only(cc,
+ (cached_fm_pair *)
+ gs_reloc_struct_ptr(cc_pair(cc) -
+ cc->pair_index, gcst) +
+ cc->pair_index);
+ }
+ }
+ /* We have to relocate the cached characters before we */
+ /* relocate dir->ccache.table! */
+ RELOC_PTR(gs_font_dir, orig_fonts);
+#define r1(i,elt) RELOC_PTR(gs_font_dir, elt);
+ font_dir_do_ptrs(r1)
+#undef r1
+RELOC_PTRS_END
+#undef dir
+
+/* GC procedures for fonts */
+#define pfont ((gs_font *)vptr)
+/* When we finalize a base font, we unlink it from the orig_fonts list. */
+/* See above for more information. */
+void
+gs_font_finalize(void *vptr)
+{ if ( pfont->base == pfont )
+ { gs_font *next = pfont->next;
+ gs_font *prev = pfont->prev;
+ if_debug3('u', "[u]unlinking font 0x%lx, prev=0x%lx, next=0x%lx\n",
+ (ulong)pfont, (ulong)prev, (ulong)next);
+ /* gs_purge_font may have unlinked this font already: */
+ /* don't unlink it twice. */
+ if ( next != 0 && next->prev == pfont )
+ next->prev = prev;
+ if ( prev != 0 )
+ { if ( prev->next == pfont )
+ prev->next = next;
+ }
+ else if ( pfont->dir->orig_fonts == pfont )
+ pfont->dir->orig_fonts = next;
+ }
+}
+private ENUM_PTRS_BEGIN(font_enum_ptrs) return 0;
+ /* We don't enumerate next or prev of base fonts. */
+ /* See above for details. */
+ case 0:
+ ENUM_RETURN((pfont->base == pfont ? 0 : pfont->next));
+ case 1:
+ ENUM_RETURN((pfont->base == pfont ? 0 : pfont->prev));
+ ENUM_PTR(2, gs_font, dir);
+ ENUM_PTR(3, gs_font, base);
+ ENUM_PTR(4, gs_font, client_data);
+ENUM_PTRS_END
+private RELOC_PTRS_BEGIN(font_reloc_ptrs) ;
+ /* We *do* always relocate next and prev. */
+ /* Again, see above for details. */
+ RELOC_PTR(gs_font, next);
+ RELOC_PTR(gs_font, prev);
+ RELOC_PTR(gs_font, dir);
+ RELOC_PTR(gs_font, base);
+ RELOC_PTR(gs_font, client_data);
+RELOC_PTRS_END
+#undef pfont
+
+/* Allocate a font directory */
+private bool
+cc_no_mark_glyph(gs_glyph glyph)
+{ return false;
+}
+gs_font_dir *
+gs_font_dir_alloc(gs_memory_t *mem)
+{ gs_font_dir *pdir = 0;
+#if !arch_small_memory
+# ifdef DEBUG
+ if ( !gs_debug_c('.') )
+# endif
+ { /* Try allocating a very large cache. */
+ /* If this fails, allocate a small one. */
+ pdir = gs_font_dir_alloc_limits(mem,
+ smax_LARGE, bmax_LARGE, mmax_LARGE,
+ cmax_LARGE, blimit_LARGE);
+ }
+ if ( pdir == 0 )
+#endif
+ pdir = gs_font_dir_alloc_limits(mem,
+ smax_SMALL, bmax_SMALL, mmax_SMALL,
+ cmax_SMALL, blimit_SMALL);
+ if ( pdir == 0 )
+ return 0;
+ pdir->ccache.mark_glyph = cc_no_mark_glyph;
+ return pdir;
+}
+gs_font_dir *
+gs_font_dir_alloc_limits(gs_memory_t *mem,
+ uint smax, uint bmax, uint mmax, uint cmax, uint upper)
+{ register gs_font_dir *pdir =
+ gs_alloc_struct(mem, gs_font_dir, &st_font_dir,
+ "font_dir_alloc(dir)");
+ int code;
+ if ( pdir == 0 )
+ return 0;
+ code = gx_char_cache_alloc(mem, pdir, bmax, mmax, cmax, upper);
+ if ( code < 0 )
+ { gs_free_object(mem, pdir, "font_dir_alloc(dir)");
+ return 0;
+ }
+ pdir->orig_fonts = 0;
+ pdir->scaled_fonts = 0;
+ pdir->ssize = 0;
+ pdir->smax = smax;
+ return pdir;
+}
+
+/* Macro for linking an element at the head of a chain */
+#define link_first(first, elt)\
+ if ( (elt->next = first) != NULL ) first->prev = elt;\
+ elt->prev = 0;\
+ first = elt
+
+/* definefont */
+/* Use this only for original (unscaled) fonts! */
+/* Note that it expects pfont->procs.define_font to be set already. */
+int
+gs_definefont(gs_font_dir *pdir, gs_font *pfont)
+{ int code;
+ pfont->dir = pdir;
+ pfont->base = pfont;
+ code = (*pfont->procs.define_font)(pdir, pfont);
+ if ( code < 0 )
+ { /* Make sure we don't try to finalize this font. */
+ pfont->base = 0;
+ return code;
+ }
+ link_first(pdir->orig_fonts, pfont);
+ if_debug2('m', "[m]defining font 0x%lx, next=0x%lx\n",
+ (ulong)pfont, (ulong)pfont->next);
+ return 0;
+}
+/* Default (vacuous) definefont handler. */
+int
+gs_no_define_font(gs_font_dir *pdir, gs_font *pfont)
+{ return 0;
+}
+
+/* scalefont */
+int
+gs_scalefont(gs_font_dir *pdir, const gs_font *pfont, floatp scale,
+ gs_font **ppfont)
+{ gs_matrix mat;
+ gs_make_scaling(scale, scale, &mat);
+ return gs_makefont(pdir, pfont, &mat, ppfont);
+}
+
+/* makefont */
+int
+gs_makefont(gs_font_dir *pdir, const gs_font *pfont,
+ const gs_matrix *pmat, gs_font **ppfont)
+{
+#define pbfont ((const gs_font_base *)pfont)
+ int code;
+ gs_font *prev = 0;
+ gs_font *pf_out = pdir->scaled_fonts;
+ gs_memory_t *mem = pfont->memory;
+ gs_matrix newmat;
+ bool can_cache;
+
+ if ( (code = gs_matrix_multiply(&pfont->FontMatrix, pmat, &newmat)) < 0 )
+ return code;
+ /* Check for the font already being in the scaled font cache. */
+ /* Only attempt to share fonts if the current font has */
+ /* a valid UniqueID or XUID. */
+#ifdef DEBUG
+if ( gs_debug_c('m') )
+ { if ( pfont->FontType == ft_composite )
+ dprintf("[m]composite");
+ else if ( uid_is_UniqueID(&pbfont->UID) )
+ dprintf1("[m]UniqueID=%ld", pbfont->UID.id);
+ else if ( uid_is_XUID(&pbfont->UID) )
+ dprintf1("[m]XUID(%u)", (uint)(-pbfont->UID.id));
+ else
+ dprintf("[m]no UID");
+ dprintf7(", FontType=%d,\n[m] new FontMatrix=[%g %g %g %g %g %g]\n",
+ pfont->FontType,
+ pmat->xx, pmat->xy, pmat->yx, pmat->yy,
+ pmat->tx, pmat->ty);
+ }
+#endif
+ /* The UID of a composite font is of no value in caching.... */
+ if ( pfont->FontType != ft_composite &&
+ uid_is_valid(&pbfont->UID)
+ )
+ { for ( ; pf_out != 0; prev = pf_out, pf_out = pf_out->next )
+ if ( pf_out->FontType == pfont->FontType &&
+ pf_out->base == pfont->base &&
+ uid_equal(&((gs_font_base *)pf_out)->UID, &pbfont->UID) &&
+ pf_out->FontMatrix.xx == newmat.xx &&
+ pf_out->FontMatrix.xy == newmat.xy &&
+ pf_out->FontMatrix.yx == newmat.yx &&
+ pf_out->FontMatrix.yy == newmat.yy &&
+ pf_out->FontMatrix.tx == newmat.tx &&
+ pf_out->FontMatrix.ty == newmat.ty
+ )
+ { *ppfont = pf_out;
+ if_debug1('m', "[m]found font=0x%lx\n", (ulong)pf_out);
+ return 0;
+ }
+ can_cache = true;
+ }
+ else
+ can_cache = false;
+ pf_out = gs_alloc_struct(mem, gs_font, gs_object_type(mem, pfont),
+ "gs_makefont");
+ if ( !pf_out )
+ return_error(gs_error_VMerror);
+ memcpy(pf_out, pfont, gs_object_size(mem, pfont));
+ pf_out->FontMatrix = newmat;
+ pf_out->client_data = 0;
+ pf_out->dir = pdir;
+ pf_out->base = pfont->base;
+ *ppfont = pf_out;
+ code = (*pf_out->procs.make_font)(pdir, pfont, pmat, ppfont);
+ if ( code < 0 )
+ return code;
+ if ( can_cache )
+ { if ( pdir->ssize == pdir->smax )
+ { /* Must discard a cached scaled font. */
+ /* prev points to the last (oldest) font. */
+ if_debug1('m', "[m]discarding font 0x%lx\n",
+ (ulong)prev);
+ prev->prev->next = 0;
+ if ( prev->FontType != ft_composite )
+ { if_debug1('m', "[m]discarding UID 0x%lx\n",
+ (ulong)((gs_font_base *)prev)->
+ UID.xvalues);
+ uid_free(&((gs_font_base *)prev)->UID,
+ prev->memory,
+ "gs_makefont(discarding)");
+ uid_set_invalid(&((gs_font_base *)prev)->UID);
+ }
+ }
+ else
+ pdir->ssize++;
+ link_first(pdir->scaled_fonts, pf_out);
+ }
+ if_debug2('m', "[m]new font=0x%lx can_cache=%s\n",
+ (ulong)*ppfont, (can_cache ? "true" : "false"));
+ return 1;
+#undef pbfont
+}
+/* Default (vacuous) makefont handler. */
+int
+gs_no_make_font(gs_font_dir *pdir, const gs_font *pfont,
+ const gs_matrix *pmat, gs_font **ppfont)
+{ return 0;
+}
+/* Makefont handler for base fonts, which must copy the XUID. */
+int
+gs_base_make_font(gs_font_dir *pdir, const gs_font *pfont,
+ const gs_matrix *pmat, gs_font **ppfont)
+{
+#define pbfont ((gs_font_base *)*ppfont)
+ if ( uid_is_XUID(&pbfont->UID) )
+ { uint xsize = uid_XUID_size(&pbfont->UID);
+ long *xvalues = (long *)
+ gs_alloc_byte_array(pbfont->memory, xsize, sizeof(long),
+ "gs_base_make_font(XUID)");
+ if ( xvalues == 0 )
+ return_error(gs_error_VMerror);
+ memcpy(xvalues, uid_XUID_values(&pbfont->UID),
+ xsize * sizeof(long));
+ pbfont->UID.xvalues = xvalues;
+ }
+#undef pbfont
+ return 0;
+}
+
+/* setfont */
+int
+gs_setfont(gs_state *pgs, gs_font *pfont)
+{ pgs->font = pgs->root_font = pfont;
+ pgs->char_tm_valid = false;
+ return 0;
+}
+
+/* currentfont */
+gs_font *
+gs_currentfont(const gs_state *pgs)
+{ return pgs->font;
+}
+
+/* rootfont */
+gs_font *
+gs_rootfont(const gs_state *pgs)
+{ return pgs->root_font;
+}
+
+/* cachestatus */
+void
+gs_cachestatus(register const gs_font_dir *pdir, register uint pstat[7])
+{ pstat[0] = pdir->ccache.bsize;
+ pstat[1] = pdir->ccache.bmax;
+ pstat[2] = pdir->fmcache.msize;
+ pstat[3] = pdir->fmcache.mmax;
+ pstat[4] = pdir->ccache.csize;
+ pstat[5] = pdir->ccache.cmax;
+ pstat[6] = pdir->ccache.upper;
+}
+
+/* setcacheparams */
+int
+gs_setcachesize(gs_font_dir *pdir, uint size)
+{ /* This doesn't delete anything from the cache yet. */
+ pdir->ccache.bmax = size;
+ return 0;
+}
+int
+gs_setcachelower(gs_font_dir *pdir, uint size)
+{ pdir->ccache.lower = size;
+ return 0;
+}
+int
+gs_setcacheupper(gs_font_dir *pdir, uint size)
+{ pdir->ccache.upper = size;
+ return 0;
+}
+
+/* currentcacheparams */
+uint
+gs_currentcachesize(const gs_font_dir *pdir)
+{ return pdir->ccache.bmax;
+}
+uint
+gs_currentcachelower(const gs_font_dir *pdir)
+{ return pdir->ccache.lower;
+}
+uint
+gs_currentcacheupper(const gs_font_dir *pdir)
+{ return pdir->ccache.upper;
+}
+
+/* Purge a font from all font- and character-related tables. */
+/* This is only used by restore (and, someday, the GC). */
+void
+gs_purge_font(gs_font *pfont)
+{ gs_font_dir *pdir = pfont->dir;
+ gs_font *pf;
+
+ /* Remove the font from its list (orig_fonts or scaled_fonts). */
+ gs_font *prev = pfont->prev;
+ gs_font *next = pfont->next;
+ if ( next != 0 )
+ next->prev = prev, pfont->next = 0;
+ if ( prev != 0 )
+ prev->next = next, pfont->prev = 0;
+ else if ( pdir->orig_fonts == pfont )
+ pdir->orig_fonts = next;
+ else if ( pdir->scaled_fonts == pfont )
+ pdir->scaled_fonts = next;
+ else
+ { /* Shouldn't happen! */
+ lprintf1("purged font 0x%lx not found\n", (ulong)pfont);
+ }
+ if ( pfont->base != pfont )
+ { /* This is a cached scaled font. */
+ pdir->ssize--;
+ }
+
+ /* Purge the font from the scaled font cache. */
+ for ( pf = pdir->scaled_fonts; pf != 0; )
+ { if ( pf->base == pfont )
+ { gs_purge_font(pf);
+ pf = pdir->scaled_fonts; /* start over */
+ }
+ else
+ pf = pf->next;
+ }
+
+ /* Purge the font from the font/matrix pair cache, */
+ /* including all cached characters rendered with that font. */
+ gs_purge_font_from_char_caches(pdir, pfont);
+
+}
diff --git a/pstoraster/gsfont.h b/pstoraster/gsfont.h
new file mode 100644
index 000000000..14d8e9379
--- /dev/null
+++ b/pstoraster/gsfont.h
@@ -0,0 +1,70 @@
+/* Copyright (C) 1989, 1993, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gsfont.h */
+/* Client interface to font operations */
+/* Requires gsmatrix.h */
+
+/* A 'font directory' object (to avoid making fonts global). */
+/* 'directory' is something of a misnomer: this structure */
+/* just keeps track of the defined fonts, and the scaled font and */
+/* rendered character caches. */
+#ifndef gs_font_dir_DEFINED
+# define gs_font_dir_DEFINED
+typedef struct gs_font_dir_s gs_font_dir;
+#endif
+
+/* Font objects */
+#ifndef gs_font_DEFINED
+# define gs_font_DEFINED
+typedef struct gs_font_s gs_font;
+#endif
+
+/* Initialization */
+/* These procedures return 0 if they fail. */
+gs_font_dir *gs_font_dir_alloc(P1(gs_memory_t *));
+gs_font_dir *gs_font_dir_alloc_limits(P6(gs_memory_t *,
+ uint /*smax*/, uint /*bmax*/, uint /*mmax*/,
+ uint /*cmax*/, uint /*upper*/));
+
+/* Font manipulations */
+/* Use gs_definefont only with original (unscaled) fonts! */
+int gs_definefont(P2(gs_font_dir *, gs_font *));
+/* gs_scalefont and gs_makefont return 0 if the scaled font */
+/* was already in the cache, 1 if a new font was created. */
+int gs_scalefont(P4(gs_font_dir *, const gs_font *, floatp, gs_font **));
+int gs_makefont(P4(gs_font_dir *, const gs_font *, const gs_matrix *, gs_font **));
+int gs_setfont(P2(gs_state *, gs_font *));
+gs_font * gs_currentfont(P1(const gs_state *));
+gs_font * gs_rootfont(P1(const gs_state *));
+void gs_purge_font(P1(gs_font *));
+
+/* Font cache parameter operations */
+void gs_cachestatus(P2(const gs_font_dir *, uint [7]));
+#define gs_setcachelimit(pdir,limit) gs_setcacheupper(pdir,limit)
+uint gs_currentcachesize(P1(const gs_font_dir *));
+int gs_setcachesize(P2(gs_font_dir *, uint));
+uint gs_currentcachelower(P1(const gs_font_dir *));
+int gs_setcachelower(P2(gs_font_dir *, uint));
+uint gs_currentcacheupper(P1(const gs_font_dir *));
+int gs_setcacheupper(P2(gs_font_dir *, uint));
diff --git a/pstoraster/gsfont0.c b/pstoraster/gsfont0.c
new file mode 100644
index 000000000..cc801403e
--- /dev/null
+++ b/pstoraster/gsfont0.c
@@ -0,0 +1,115 @@
+/* Copyright (C) 1994, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gsfont0.c */
+/* Composite font operations for Ghostscript library */
+#include "memory_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gsstruct.h"
+#include "gxfixed.h"
+#include "gsmatrix.h"
+#include "gxdevice.h"
+#include "gxdevmem.h"
+#include "gxchar.h"
+#include "gxfcache.h" /* gs_font_dir */
+#include "gxfont.h"
+#include "gxfont0.h"
+
+/* Structure descriptor */
+private struct_proc_enum_ptrs(font_type0_enum_ptrs);
+private struct_proc_reloc_ptrs(font_type0_reloc_ptrs);
+public_st_gs_font_type0();
+#define pfont ((gs_font_type0 *)vptr)
+private ENUM_PTRS_BEGIN(font_type0_enum_ptrs) ENUM_PREFIX(st_gs_font, gs_type0_data_max_ptrs);
+ ENUM_PTR(0, gs_font_type0, data.Encoding);
+ ENUM_PTR(1, gs_font_type0, data.FDepVector);
+ case 2:
+ if ( pfont->data.FMapType == fmap_SubsVector )
+ { *pep = (void *)&pfont->data.SubsVector;
+ return ptr_const_string_type;
+ }
+ else
+ { *pep = 0;
+ break;
+ }
+ENUM_PTRS_END
+private RELOC_PTRS_BEGIN(font_type0_reloc_ptrs) RELOC_PREFIX(st_gs_font);
+ RELOC_PTR(gs_font_type0, data.Encoding);
+ RELOC_PTR(gs_font_type0, data.FDepVector);
+ if ( pfont->data.FMapType == fmap_SubsVector )
+ RELOC_CONST_STRING_PTR(gs_font_type0, data.SubsVector);
+RELOC_PTRS_END
+#undef pfont
+
+/* Adjust a composite font by concatenating a given matrix */
+/* to the FontMatrix of all descendant composite fonts. */
+private int
+gs_type0_adjust_matrix(gs_font_dir *pdir, gs_font_type0 *pfont,
+ const gs_matrix *pmat)
+{ gs_font **pdep = pfont->data.FDepVector;
+ uint fdep_size = pfont->data.fdep_size;
+ gs_font **ptdep;
+ uint i;
+ /* Check for any descendant composite fonts. */
+ for ( i = 0; i < fdep_size; i++ )
+ if ( pdep[i]->FontType == ft_composite )
+ break;
+ if ( i == fdep_size )
+ return 0;
+ ptdep = gs_alloc_struct_array(pfont->memory, fdep_size, gs_font *,
+ &st_gs_font_ptr_element,
+ "gs_type0_adjust_font(FDepVector)");
+ if ( ptdep == 0 )
+ return_error(gs_error_VMerror);
+ memcpy(ptdep, pdep, sizeof(gs_font *) * fdep_size);
+ for ( ; i < fdep_size; i++ )
+ if ( pdep[i]->FontType == ft_composite )
+ { int code = gs_makefont(pdir, pdep[i], pmat, &ptdep[i]);
+ if ( code < 0 )
+ return code;
+ }
+ pfont->data.FDepVector = ptdep;
+ return 0;
+}
+
+/* Finish defining a composite font, */
+/* by adjusting its descendants' FontMatrices. */
+int
+gs_type0_define_font(gs_font_dir *pdir, gs_font *pfont)
+{ const gs_matrix *pmat = &pfont->FontMatrix;
+ /* Check for the identity matrix, which is common in root fonts. */
+ if ( pmat->xx == 1.0 && pmat->yy == 1.0 &&
+ pmat->xy == 0.0 && pmat->yx == 0.0 &&
+ pmat->tx == 0.0 && pmat->ty == 0.0
+ )
+ return 0;
+ return gs_type0_adjust_matrix(pdir, (gs_font_type0 *)pfont, pmat);
+}
+
+/* Finish scaling a composite font similarly. */
+int
+gs_type0_make_font(gs_font_dir *pdir, const gs_font *pfont,
+ const gs_matrix *pmat, gs_font **ppfont)
+{ return gs_type0_adjust_matrix(pdir, (gs_font_type0 *)*ppfont, pmat);
+}
diff --git a/pstoraster/gshsb.c b/pstoraster/gshsb.c
new file mode 100644
index 000000000..57e28f894
--- /dev/null
+++ b/pstoraster/gshsb.c
@@ -0,0 +1,138 @@
+/* Copyright (C) 1994 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gshsb.c */
+/* HSB color operators for Ghostscript library */
+#include "gx.h"
+#include "gscolor.h"
+#include "gshsb.h" /* interface definition */
+#include "gxfrac.h"
+
+/* Forward references */
+private void color_hsb_to_rgb(P4(floatp h, floatp s, floatp b, float rgb[3]));
+private void color_rgb_to_hsb(P4(floatp r, floatp g, floatp b, float hsb[3]));
+
+/* Force a parameter into the range [0.0..1.0]. */
+#define force_unit(p) (p < 0.0 ? 0.0 : p > 1.0 ? 1.0 : p)
+
+/* sethsbcolor */
+int
+gs_sethsbcolor(gs_state *pgs, floatp h, floatp s, floatp b)
+{ float rgb[3];
+ color_hsb_to_rgb(force_unit(h), force_unit(s), force_unit(b), rgb);
+ return gs_setrgbcolor(pgs, rgb[0], rgb[1], rgb[2]);
+}
+
+/* currenthsbcolor */
+int
+gs_currenthsbcolor(const gs_state *pgs, float pr3[3])
+{ float rgb[3];
+ gs_currentrgbcolor(pgs, rgb);
+ color_rgb_to_hsb(rgb[0], rgb[1], rgb[2], pr3);
+ return 0;
+}
+
+/* ------ Internal routines ------ */
+
+/* Note: the color model conversion algorithms are taken from */
+/* Rogers, Procedural Elements for Computer Graphics, pp. 401-403. */
+
+/* Convert RGB to HSB. */
+private void
+color_rgb_to_hsb(floatp r, floatp g, floatp b, float hsb[3])
+{ frac red = float2frac(r), green = float2frac(g), blue = float2frac(b);
+#define rhue hsb[0]
+#define rsat hsb[1]
+#define rbri hsb[2]
+ if ( red == green && green == blue )
+ { rhue = 0; /* arbitrary */
+ rsat = 0;
+ rbri = r; /* pick any one */
+ }
+ else
+ { /* Convert rgb to hsb */
+ frac V, Temp;
+ long diff, H;
+ V = (red > green ? red : green);
+ if ( blue > V ) V = blue;
+ Temp = (red > green ? green : red);
+ if ( blue < Temp ) Temp = blue;
+ diff = V - Temp;
+ if ( V == red )
+ H = (green - blue) * frac_1_long / diff;
+ else if ( V == green )
+ H = (blue - red) * frac_1_long / diff + 2 * frac_1_long;
+ else /* V == blue */
+ H = (red - green) * frac_1_long / diff + 4 * frac_1_long;
+ if ( H < 0 ) H += 6 * frac_1_long;
+ rhue = H / (frac_1 * 6.0);
+ rsat = diff / (float)V;
+ rbri = frac2float(V);
+ }
+#undef rhue
+#undef rsat
+#undef rbri
+}
+
+/* Convert HSB to RGB. */
+private void
+color_hsb_to_rgb(floatp hue, floatp saturation, floatp brightness, float rgb[3])
+{ if ( saturation == 0 )
+ { rgb[0] = rgb[1] = rgb[2] = brightness;
+ }
+ else
+ { /* Convert hsb to rgb. */
+ /* We rely on the fact that the product of two */
+ /* fracs fits into an unsigned long. */
+ floatp h6 = hue * 6;
+ ulong V = float2frac(brightness); /* force arithmetic to long */
+ frac S = float2frac(saturation);
+ int I = (int)h6;
+ ulong F = float2frac(h6 - I); /* ditto */
+ /* M = V*(1-S), N = V*(1-S*F), K = V*(1-S*(1-F)) = M-N+V */
+ frac M = V * (frac_1_long - S) / frac_1_long;
+ frac N = V * (frac_1_long - S * F / frac_1_long) / frac_1_long;
+ frac K = M - N + V;
+ frac R, G, B;
+ switch ( I )
+ {
+ default: R = V; G = K; B = M; break;
+ case 1: R = N; G = V; B = M; break;
+ case 2: R = M; G = V; B = K; break;
+ case 3: R = M; G = N; B = V; break;
+ case 4: R = K; G = M; B = V; break;
+ case 5: R = V; G = M; B = N; break;
+ }
+ rgb[0] = frac2float(R);
+ rgb[1] = frac2float(G);
+ rgb[2] = frac2float(B);
+#ifdef DEBUG
+if ( gs_debug_c('c') )
+{ dprintf7("[c]hsb(%g,%g,%g)->VSFI(%ld,%d,%ld,%d)->\n",
+ hue, saturation, brightness, V, S, F, I);
+ dprintf6(" RGB(%d,%d,%d)->rgb(%g,%g,%g)\n",
+ R, G, B, rgb[0], rgb[1], rgb[2]);
+}
+#endif
+ }
+}
diff --git a/pstoraster/gshsb.h b/pstoraster/gshsb.h
new file mode 100644
index 000000000..4d361398e
--- /dev/null
+++ b/pstoraster/gshsb.h
@@ -0,0 +1,28 @@
+/* Copyright (C) 1994 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gshsb.h */
+/* Client interface to HSB color routines */
+
+int gs_sethsbcolor(P4(gs_state *, floatp, floatp, floatp)),
+ gs_currenthsbcolor(P2(const gs_state *, float [3]));
diff --git a/pstoraster/gsht.c b/pstoraster/gsht.c
new file mode 100644
index 000000000..b5c5b2fb6
--- /dev/null
+++ b/pstoraster/gsht.c
@@ -0,0 +1,427 @@
+/* Copyright (C) 1989, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gsht.c */
+/* setscreen operator for Ghostscript library */
+#include "memory_.h"
+#include <stdlib.h> /* for qsort */
+#include "gx.h"
+#include "gserrors.h"
+#include "gsstruct.h"
+#include "gxarith.h" /* for igcd */
+#include "gzstate.h"
+#include "gxdevice.h" /* for gzht.h */
+#include "gzht.h"
+
+/* Forward declarations */
+int gx_ht_process_screen(P4(gs_screen_enum *, gs_state *,
+ gs_screen_halftone *, bool)); /* exported for gsht1.c */
+void gx_set_effective_transfer(P1(gs_state *));
+
+/* Structure types */
+public_st_ht_order();
+public_st_halftone();
+public_st_device_halftone();
+
+/* GC procedures */
+
+#define hptr ((gs_halftone *)vptr)
+
+private ENUM_PTRS_BEGIN(halftone_enum_ptrs) return 0;
+ case 0:
+ switch ( hptr->type )
+ {
+ case ht_type_threshold:
+ ENUM_RETURN_CONST_STRING_PTR(gs_halftone, params.threshold.thresholds);
+ case ht_type_multiple:
+ case ht_type_multiple_colorscreen:
+ *pep = hptr->params.multiple.components; break;
+ default:
+ return 0;
+ }
+ break;
+ENUM_PTRS_END
+
+private RELOC_PTRS_BEGIN(halftone_reloc_ptrs) {
+ switch ( hptr->type )
+ {
+ case ht_type_threshold:
+ RELOC_CONST_STRING_PTR(gs_halftone, params.threshold.thresholds);
+ break;
+ case ht_type_multiple:
+ case ht_type_multiple_colorscreen:
+ RELOC_PTR(gs_halftone, params.multiple.components);
+ break;
+ case ht_type_none:
+ case ht_type_screen:
+ case ht_type_colorscreen:
+ case ht_type_spot:
+ break;
+ }
+} RELOC_PTRS_END
+
+#undef hptr
+
+/* setscreen */
+int
+gs_setscreen(gs_state *pgs, gs_screen_halftone *phsp)
+{ gs_screen_enum senum;
+ int code = gx_ht_process_screen(&senum, pgs, phsp,
+ gs_currentaccuratescreens());
+ if ( code < 0 )
+ return code;
+ return gs_screen_install(&senum);
+}
+
+/* currentscreen */
+int
+gs_currentscreen(const gs_state *pgs, gs_screen_halftone *phsp)
+{ switch ( pgs->halftone->type )
+ {
+ case ht_type_screen:
+ *phsp = pgs->halftone->params.screen;
+ return 0;
+ case ht_type_colorscreen:
+ *phsp = pgs->halftone->params.colorscreen.screens.colored.gray;
+ return 0;
+ default:
+ return_error(gs_error_undefined);
+ }
+}
+
+/* .currentscreenlevels */
+int
+gs_currentscreenlevels(const gs_state *pgs)
+{ return pgs->dev_ht->order.num_levels;
+}
+
+/* sethalftonephase */
+int
+gs_sethalftonephase(gs_state *pgs, int x, int y)
+{ pgs->ht_phase.x = x;
+ pgs->ht_phase.y = y;
+ gx_unset_dev_color(pgs);
+ return 0;
+}
+
+/* currenthalftonephase */
+int
+gs_currenthalftonephase(const gs_state *pgs, gs_int_point *pphase)
+{ *pphase = pgs->ht_phase;
+ return 0;
+}
+
+/* currenthalftone */
+int
+gs_currenthalftone(gs_state *pgs, gs_halftone *pht)
+{ *pht = *pgs->halftone;
+ return 0;
+}
+
+/* ------ Internal routines ------ */
+
+/* Process one screen plane. */
+int
+gx_ht_process_screen(gs_screen_enum *penum, gs_state *pgs,
+ gs_screen_halftone *phsp, bool accurate)
+{ gs_point pt;
+ int code = gs_screen_init_accurate(penum, pgs, phsp, accurate);
+ if ( code < 0 ) return code;
+ while ( (code = gs_screen_currentpoint(penum, &pt)) == 0 )
+ if ( (code = gs_screen_next(penum, (*phsp->spot_function)(pt.x, pt.y))) < 0 )
+ return code;
+ return 0;
+}
+
+/* Allocate and initialize the contents of a halftone order. */
+int
+gx_ht_alloc_order(gx_ht_order *porder, uint width, uint height,
+ uint strip_shift, uint num_levels, gs_memory_t *mem)
+{ uint size = width * height;
+ gx_ht_order order;
+
+ order = *porder;
+ order.width = width;
+ order.height = height;
+ order.raster = bitmap_raster(width);
+ order.shift = strip_shift;
+ order.orig_height = order.height;
+ order.orig_shift = order.shift;
+ order.full_height = ht_order_full_height(&order);
+ order.multiple = 1;
+ order.num_levels = num_levels;
+ order.num_bits = size;
+ order.levels =
+ (uint *)gs_alloc_byte_array(mem, num_levels, sizeof(uint),
+ "ht order(levels)");
+ order.bits =
+ (gx_ht_bit *)gs_alloc_byte_array(mem, size, sizeof(gx_ht_bit),
+ "ht order(bits)");
+ if ( order.levels == 0 || order.bits == 0 )
+ { gs_free_object(mem, order.bits, "ht order(bits)");
+ gs_free_object(mem, order.levels, "ht order(levels)");
+ return_error(gs_error_VMerror);
+ }
+ order.cache = 0;
+ order.transfer = 0;
+ *porder = order;
+ return 0;
+}
+
+/* Compare keys ("masks", actually sample values) for qsort. */
+private int
+compare_samples(const void *p1, const void *p2)
+{ ht_sample_t m1 = ((const gx_ht_bit *)p1)->mask;
+ ht_sample_t m2 = ((const gx_ht_bit *)p2)->mask;
+ return (m1 < m2 ? -1 : m1 > m2 ? 1 : 0);
+}
+/* Sort the halftone order by sample value. */
+void
+gx_sort_ht_order(gx_ht_bit *recs, uint N)
+{ int i;
+ /* Tag each sample with its index, for sorting. */
+ for ( i = 0; i < N; i++ )
+ recs[i].offset = i;
+ qsort((void *)recs, N, sizeof(*recs), compare_samples);
+#ifdef DEBUG
+if ( gs_debug_c('H') )
+ { uint i;
+ dprintf("[H]Sorted samples:\n");
+ for ( i = 0; i < N; i++ )
+ dprintf3("%5u: %5u: %u\n",
+ i, recs[i].offset, recs[i].mask);
+ }
+#endif
+}
+
+/*
+ * Construct the halftone order from a sampled spot function. Only width x
+ * strip samples have been filled in; we must replicate the resulting sorted
+ * order vertically, shifting it by shift each time. See gxdht.h regarding
+ * the invariants that must be restored.
+ */
+void
+gx_ht_construct_spot_order(gx_ht_order *porder)
+{ uint width = porder->width;
+ uint height = porder->orig_height;
+ uint num_levels = porder->num_levels; /* = width x strip */
+ uint strip = num_levels / width;
+ gx_ht_bit *bits = porder->bits;
+ uint *levels = porder->levels;
+ uint shift = porder->orig_shift;
+ uint full_height = porder->full_height;
+ uint num_bits = porder->num_bits;
+ uint copies = num_bits / (width * strip);
+ gx_ht_bit *bp = bits + num_bits - 1;
+ uint i;
+
+ gx_sort_ht_order(bits, num_levels);
+ if_debug5('h',
+ "[h]spot order: num_levels=%u w=%u h=%u strip=%u shift=%u\n",
+ num_levels, width, height, strip, shift);
+ /* Fill in the levels array, replicating the bits vertically */
+ /* if needed. */
+ for ( i = num_levels; i > 0; )
+ { uint offset = bits[--i].offset;
+ uint x = offset % width;
+ uint hy = offset - x;
+ uint k;
+
+ levels[i] = i * copies;
+ for ( k = 0; k < copies;
+ k++, bp--, hy += num_levels, x = (x + width - shift) % width
+ )
+ bp->offset = hy + x;
+ }
+ /* If we have a complete halftone, restore the invariant. */
+ if ( num_bits == width * full_height )
+ { porder->height = full_height;
+ porder->shift = 0;
+ }
+ gx_ht_construct_bits(porder);
+}
+
+/* Construct offset/masks from the whitening order. */
+/* porder->bits[i].offset contains the index of the bit position */
+/* that is i'th in the whitening order. */
+void
+gx_ht_construct_bits(gx_ht_order *porder)
+{ uint width = porder->width;
+ uint size = porder->num_bits;
+ gx_ht_bit *bits = porder->bits;
+ uint i;
+ gx_ht_bit *phb;
+ byte *pb;
+ uint padding = porder->raster * 8 - width;
+
+ for ( i = 0, phb = bits; i < size; i++, phb++ )
+ { int pix = phb->offset;
+ ht_mask_t mask;
+ pix += pix / width * padding;
+ phb->offset = (pix >> 3) & -sizeof(mask);
+ mask = (ht_mask_t)1 << (~pix & (ht_mask_bits - 1));
+ /* Replicate the mask bits. */
+ pix = ht_mask_bits - width;
+ while ( (pix -= width) >= 0 )
+ mask |= mask >> width;
+ /* Store the mask, reversing bytes if necessary. */
+ phb->mask = 0;
+ for ( pb = (byte *)&phb->mask + (sizeof(mask) - 1);
+ mask != 0;
+ mask >>= 8, pb--
+ )
+ *pb = (byte)mask;
+ }
+#ifdef DEBUG
+if ( gs_debug_c('H') )
+ { dprintf1("[H]Halftone order bits 0x%lx:\n", (ulong)bits);
+ for ( i = 0, phb = bits; i < size; i++, phb++ )
+ dprintf3("%4d: %u:0x%lx\n", i, phb->offset,
+ (ulong)phb->mask);
+ }
+#endif
+}
+
+/* Install a new halftone in the graphics state. */
+int
+gx_ht_install(gs_state *pgs, const gs_halftone *pht,
+ const gx_device_halftone *pdht)
+{ gx_device_halftone *pgdht = pgs->dev_ht;
+ if ( (ulong)pdht->order.raster * (pdht->order.num_bits /
+ pdht->order.width) > pgs->ht_cache->bits_size
+ )
+ return_error(gs_error_limitcheck);
+ *pgs->halftone = *pht;
+ *pgdht = *pdht;
+ /* Clear the cache, to avoid confusion in case the address of */
+ /* a new order vector matches that of a (deallocated) old one. */
+ gx_ht_clear_cache(pgs->ht_cache);
+ /* Set the color_indices according to the device color_info. */
+ /* Also compute the LCM of the primary color cell sizes. */
+ /* Note that for strip halftones, the "cell size" is the */
+ /* theoretical fully expanded size with shift = 0. */
+ if ( pdht->components != 0 )
+ { static const gs_ht_separation_name dcnames[5][4] =
+ { { gs_ht_separation_Default }, /* not used */
+ { gs_ht_separation_Default, gs_ht_separation_Default,
+ gs_ht_separation_Default, gs_ht_separation_Gray
+ },
+ { gs_ht_separation_Default }, /* not used */
+ { gs_ht_separation_Red, gs_ht_separation_Green,
+ gs_ht_separation_Blue, gs_ht_separation_Default
+ },
+ { gs_ht_separation_Cyan, gs_ht_separation_Magenta,
+ gs_ht_separation_Yellow, gs_ht_separation_Black
+ }
+ };
+ static const gs_ht_separation_name cscnames[4] =
+ { gs_ht_separation_Red, gs_ht_separation_Green,
+ gs_ht_separation_Blue, gs_ht_separation_Default
+ };
+ int num_comps = gs_currentdevice_inline(pgs)->color_info.
+ num_components;
+ const gs_ht_separation_name _ds *cnames = dcnames[num_comps];
+ int lcm_width = 1, lcm_height = 1;
+ uint i;
+
+ /* Halftones set by setcolorscreen, and (we think) */
+ /* Type 2 and Type 4 halftones, are supposed to work */
+ /* for both RGB and CMYK, so we need a special check here. */
+ if ( num_comps == 4 &&
+ (pht->type == ht_type_colorscreen ||
+ pht->type == ht_type_multiple_colorscreen)
+ )
+ cnames = cscnames;
+ if_debug4('h', "[h]dcnames=%lu,%lu,%lu,%lu\n",
+ (ulong)cnames[0], (ulong)cnames[1],
+ (ulong)cnames[2], (ulong)cnames[3]);
+ memset(pgdht->color_indices, 0, sizeof(pdht->color_indices));
+ for ( i = 0; i < pdht->num_comp; i++ )
+ { const gx_ht_order_component *pcomp =
+ &pdht->components[i];
+ int j;
+ if_debug2('h', "[h]cname[%d]=%lu\n",
+ i, (ulong)pcomp->cname);
+ for ( j = 0; j < 4; j++ )
+ { if ( pcomp->cname == cnames[j] )
+ { if_debug2('h', "[h]color_indices[%d]=%d\n",
+ j, i);
+ pgdht->color_indices[j] = i;
+ }
+ }
+ }
+ /* Now do a second pass to compute the LCM. */
+ /* We have to do it this way in case some entry in */
+ /* color_indices is still 0. */
+ for ( i = 0; i < 4; ++i )
+ { const gx_ht_order_component *pcomp =
+ &pdht->components[pgdht->color_indices[i]];
+ uint cw = pcomp->corder.width;
+ uint ch = pcomp->corder.full_height;
+ int dw = lcm_width / igcd(lcm_width, cw);
+ int dh = lcm_height / igcd(lcm_height, ch);
+ lcm_width = (cw > max_int / dw ? max_int : cw * dw);
+ lcm_height = (ch > max_int / dh ? max_int : ch * dh);
+ }
+ pgdht->lcm_width = lcm_width;
+ pgdht->lcm_height = lcm_height;
+ }
+ else
+ { /* Only one component. */
+ pgdht->lcm_width = pgdht->order.width;
+ pgdht->lcm_height = pgdht->order.full_height;
+ }
+ if_debug2('h', "[h]LCM=(%d,%d)\n",
+ pgdht->lcm_width, pgdht->lcm_height);
+ gx_set_effective_transfer(pgs);
+ gx_unset_dev_color(pgs);
+ return 0;
+}
+
+/* Reestablish the effective transfer functions, taking into account */
+/* any overrides from halftone dictionaries. */
+void
+gx_set_effective_transfer(gs_state *pgs)
+{ const gx_device_halftone *pdht = pgs->dev_ht;
+ pgs->effective_transfer = pgs->set_transfer; /* default */
+ if ( pdht->components == 0 )
+ { /* Check for transfer function override in single halftone */
+ gx_transfer_map *pmap = pdht->order.transfer;
+ if ( pmap != 0 )
+ pgs->effective_transfer.indexed[0] =
+ pgs->effective_transfer.indexed[1] =
+ pgs->effective_transfer.indexed[2] =
+ pgs->effective_transfer.indexed[3] = pmap;
+ }
+ else
+ { /* Check in all 4 standard separations */
+ int i;
+ for ( i = 0; i < 4; ++i )
+ { gx_transfer_map *pmap =
+ pdht->components[pdht->color_indices[i]].corder.
+ transfer;
+ if ( pmap != 0 )
+ pgs->effective_transfer.indexed[i] = pmap;
+ }
+ }
+}
diff --git a/pstoraster/gsht.h b/pstoraster/gsht.h
new file mode 100644
index 000000000..2746fce7b
--- /dev/null
+++ b/pstoraster/gsht.h
@@ -0,0 +1,81 @@
+/* Copyright (C) 1993, 1994 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gsht.h */
+/* Public interface to halftone functionality */
+
+#ifndef gsht_INCLUDED
+# define gsht_INCLUDED
+
+/* Client definition of (Type 1) halftones */
+typedef struct gs_screen_halftone_s {
+ float frequency;
+ float angle;
+ float (*spot_function)(P2(floatp, floatp));
+ /* setscreen or sethalftone sets these: */
+ /* (a Level 2 feature, but we include them in Level 1) */
+ float actual_frequency;
+ float actual_angle;
+} gs_screen_halftone;
+#define st_screen_halftone_max_ptrs 0
+
+/* Client definition of color (Type 2) halftones */
+typedef struct gs_colorscreen_halftone_s {
+ union _css {
+ gs_screen_halftone indexed[4];
+ struct _csc {
+ gs_screen_halftone red, green, blue, gray;
+ } colored;
+ } screens;
+} gs_colorscreen_halftone;
+#define st_colorscreen_halftone_max_ptrs 0
+
+/* Opaque definition of general halftones */
+#ifndef gs_halftone_DEFINED
+# define gs_halftone_DEFINED
+typedef struct gs_halftone_s gs_halftone;
+#endif
+
+/* Procedural interface */
+int gs_setscreen(P2(gs_state *, gs_screen_halftone *));
+int gs_currentscreen(P2(const gs_state *, gs_screen_halftone *));
+int gs_currentscreenlevels(P1(const gs_state *));
+
+/*
+ * Enumeration-style definition of a single screen. The client must:
+ * - probably, call gs_screen_enum_alloc;
+ * - call gs_screen_init;
+ * - in a loop,
+ * - call gs_screen_currentpoint; if it returns 1, exit;
+ * - call gs_screen_next;
+ * - if desired, call gs_screen_install to install the screen.
+ */
+typedef struct gs_screen_enum_s gs_screen_enum;
+gs_screen_enum *gs_screen_enum_alloc(P2(gs_memory_t *, client_name_t));
+int gs_screen_init(P3(gs_screen_enum *, gs_state *,
+ gs_screen_halftone *));
+int gs_screen_currentpoint(P2(gs_screen_enum *, gs_point *));
+int gs_screen_next(P2(gs_screen_enum *, floatp));
+int gs_screen_install(P1(gs_screen_enum *));
+
+#endif /* gsht_INCLUDED */
diff --git a/pstoraster/gsht1.c b/pstoraster/gsht1.c
new file mode 100644
index 000000000..97269456c
--- /dev/null
+++ b/pstoraster/gsht1.c
@@ -0,0 +1,346 @@
+/* Copyright (C) 1994, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gsht1.c */
+/* Extended halftone operators for Ghostscript library */
+#include "memory_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gsstruct.h"
+#include "gsutil.h" /* for gs_next_ids */
+#include "gzstate.h"
+#include "gxdevice.h" /* for gzht.h */
+#include "gzht.h"
+
+/* Define the size of the halftone tile cache. */
+#define max_tile_bytes_LARGE 4096
+#define max_tile_bytes_SMALL 512
+#if arch_small_memory
+# define max_tile_cache_bytes max_tile_bytes_SMALL
+#else
+# define max_tile_cache_bytes\
+ (gs_if_debug_c('.') ? max_tile_bytes_SMALL : max_tile_bytes_LARGE)
+#endif
+
+/* Imports from gsht.c */
+int gx_ht_process_screen(P4(gs_screen_enum *, gs_state *,
+ gs_screen_halftone *, bool));
+
+/* Imports from gscolor.c */
+void load_transfer_map(P3(gs_state *, gx_transfer_map *, floatp));
+
+/* Forward declarations */
+private int process_spot(P3(gx_ht_order *, gs_state *,
+ gs_spot_halftone *));
+private int process_threshold(P3(gx_ht_order *, gs_state *,
+ gs_threshold_halftone *));
+
+/* Structure types */
+public_st_halftone_component();
+public_st_ht_component_element();
+private_st_ht_order_component();
+private_st_ht_order_comp_element();
+
+/* GC procedures */
+
+#define hptr ((gs_halftone_component *)vptr)
+
+private ENUM_PTRS_BEGIN(halftone_component_enum_ptrs) return 0;
+ case 0:
+ if ( hptr->type != ht_type_threshold )
+ return 0;
+ ENUM_RETURN_CONST_STRING_PTR(gs_halftone_component, params.threshold.thresholds);
+} }
+
+private RELOC_PTRS_BEGIN(halftone_component_reloc_ptrs) {
+ if ( hptr->type == ht_type_threshold )
+ RELOC_CONST_STRING_PTR(gs_halftone_component, params.threshold.thresholds);
+} RELOC_PTRS_END
+
+#undef hptr
+
+/* setcolorscreen */
+int
+gs_setcolorscreen(gs_state *pgs, gs_colorscreen_halftone *pht)
+{ gs_halftone ht;
+ ht.type = ht_type_colorscreen;
+ ht.params.colorscreen = *pht;
+ return gs_sethalftone(pgs, &ht);
+}
+
+/* currentcolorscreen */
+int
+gs_currentcolorscreen(gs_state *pgs, gs_colorscreen_halftone *pht)
+{ int code;
+ switch ( pgs->halftone->type )
+ {
+ case ht_type_colorscreen:
+ *pht = pgs->halftone->params.colorscreen;
+ return 0;
+ default:
+ code = gs_currentscreen(pgs, &pht->screens.colored.gray);
+ if ( code < 0 )
+ return code;
+ pht->screens.colored.red = pht->screens.colored.gray;
+ pht->screens.colored.green = pht->screens.colored.gray;
+ pht->screens.colored.blue = pht->screens.colored.gray;
+ return 0;
+ }
+}
+
+/* Set the halftone in the graphics state. */
+int
+gs_sethalftone(gs_state *pgs, gs_halftone *pht)
+{ gx_device_halftone dev_ht;
+ int code = gs_sethalftone_prepare(pgs, pht, &dev_ht);
+ if ( code < 0 )
+ return code;
+ return gx_ht_install(pgs, pht, &dev_ht);
+}
+/* Prepare the halftone, but don't install it. */
+int
+gs_sethalftone_prepare(gs_state *pgs, gs_halftone *pht,
+ gx_device_halftone *pdht)
+{ gs_memory_t *mem = pgs->memory;
+ gx_ht_order_component *pocs = 0;
+ int code = 0;
+ switch ( pht->type )
+ {
+ case ht_type_colorscreen:
+ { gs_screen_halftone *phc =
+ pht->params.colorscreen.screens.indexed;
+ static const gs_ht_separation_name cnames[4] =
+ { gs_ht_separation_Default, gs_ht_separation_Red,
+ gs_ht_separation_Green, gs_ht_separation_Blue
+ };
+ static const int cindex[4] = { 3, 0, 1, 2 };
+ int i;
+ pocs = gs_alloc_struct_array(mem, 4,
+ gx_ht_order_component,
+ &st_ht_order_component_element,
+ "gs_sethalftone");
+ if ( pocs == 0 )
+ return_error(gs_error_VMerror);
+ for ( i = 0; i < 4; i++ )
+ { gs_screen_enum senum;
+ int ci = cindex[i];
+ gx_ht_order_component *poc = &pocs[i];
+ code = gx_ht_process_screen(&senum, pgs, &phc[ci],
+ gs_currentaccuratescreens());
+ if ( code < 0 )
+ break;
+#define sorder senum.order
+ poc->corder = sorder;
+ poc->cname = cnames[i];
+ if ( i == 0 ) /* Gray = Default */
+ pdht->order = sorder;
+ else
+ { uint tile_bytes =
+ sorder.raster *
+ (sorder.num_bits / sorder.width);
+ uint num_tiles =
+ max_tile_cache_bytes / tile_bytes + 1;
+ gx_ht_cache *pcache =
+ gx_ht_alloc_cache(mem, num_tiles,
+ tile_bytes * num_tiles);
+ if ( pcache == 0 )
+ { code = gs_note_error(gs_error_VMerror);
+ break;
+ }
+ poc->corder.cache = pcache;
+ gx_ht_init_cache(pcache, &poc->corder);
+ }
+#undef sorder
+ }
+ if ( code < 0 )
+ break;
+ pdht->components = pocs;
+ pdht->num_comp = 4;
+ } break;
+ case ht_type_spot:
+ code = process_spot(&pdht->order, pgs, &pht->params.spot);
+ if ( code < 0 )
+ return code;
+ pdht->components = 0;
+ break;
+ case ht_type_threshold:
+ code = process_threshold(&pdht->order, pgs,
+ &pht->params.threshold);
+ if ( code < 0 )
+ return code;
+ pdht->components = 0;
+ break;
+ case ht_type_multiple:
+ case ht_type_multiple_colorscreen:
+ { uint count = pht->params.multiple.num_comp;
+ bool have_Default = false;
+ uint i;
+ gs_halftone_component *phc = pht->params.multiple.components;
+ gx_ht_order_component *poc_next;
+ pocs = gs_alloc_struct_array(mem, count,
+ gx_ht_order_component,
+ &st_ht_order_component_element,
+ "gs_sethalftone");
+ if ( pocs == 0 )
+ return_error(gs_error_VMerror);
+ poc_next = pocs + 1;
+ for ( i = 0; i < count; i++, phc++ )
+ { gx_ht_order_component *poc;
+ if ( phc->cname == gs_ht_separation_Default )
+ { if ( have_Default )
+ { /* Duplicate Default */
+ code = gs_note_error(gs_error_rangecheck);
+ break;
+ }
+ poc = pocs;
+ have_Default = true;
+ }
+ else if ( i == count - 1 && !have_Default )
+ { /* No Default */
+ code = gs_note_error(gs_error_rangecheck);
+ break;
+ }
+ else
+ poc = poc_next++;
+ poc->cname = phc->cname;
+ switch ( phc->type )
+ {
+ case ht_type_spot:
+ code = process_spot(&poc->corder, pgs,
+ &phc->params.spot);
+ break;
+ case ht_type_threshold:
+ code = process_threshold(&poc->corder, pgs,
+ &phc->params.threshold);
+ break;
+ default:
+ code = gs_note_error(gs_error_rangecheck);
+ break;
+ }
+ if ( code < 0 )
+ break;
+ if ( poc != pocs )
+ { gx_ht_cache *pcache =
+ gx_ht_alloc_cache(mem, 1,
+ poc->corder.raster *
+ (poc->corder.num_bits /
+ poc->corder.width));
+ if ( pcache == 0 )
+ { code = gs_note_error(gs_error_VMerror);
+ break;
+ }
+ poc->corder.cache = pcache;
+ gx_ht_init_cache(pcache, &poc->corder);
+ }
+ }
+ if ( code < 0 )
+ break;
+ pdht->order = pocs[0].corder; /* Default */
+ if ( count == 1 )
+ { /* We have only a Default; */
+ /* we don't need components. */
+ gs_free_object(mem, pocs, "gs_sethalftone");
+ pdht->components = 0;
+ }
+ else
+ { pdht->components = pocs;
+ pdht->num_comp = count;
+ }
+ } break;
+ default:
+ return_error(gs_error_rangecheck);
+ }
+ if ( code < 0 )
+ gs_free_object(mem, pocs, "gs_sethalftone");
+ return code;
+}
+
+/* ------ Internal routines ------ */
+
+/* Process a transfer function override, if any. */
+private int
+process_transfer(gx_ht_order *porder, gs_state *pgs,
+ gs_mapping_proc tproc)
+{ gx_transfer_map *pmap;
+ if ( tproc == 0 )
+ return 0;
+ pmap = gs_alloc_struct(pgs->memory, gx_transfer_map, &st_transfer_map,
+ "process_transfer");
+ if ( pmap == 0 )
+ return_error(gs_error_VMerror);
+ pmap->proc = tproc;
+ pmap->id = gs_next_ids(1);
+ load_transfer_map(pgs, pmap, 0.0);
+ porder->transfer = pmap;
+ return 0;
+}
+
+/* Process a spot plane. */
+private int
+process_spot(gx_ht_order *porder, gs_state *pgs,
+ gs_spot_halftone *phsp)
+{ gs_screen_enum senum;
+ int code = gx_ht_process_screen(&senum, pgs, &phsp->screen,
+ phsp->accurate_screens);
+ if ( code < 0 )
+ return code;
+ *porder = senum.order;
+ return process_transfer(porder, pgs, phsp->transfer);
+}
+
+/* Process a threshold plane. */
+private int
+process_threshold(gx_ht_order *porder, gs_state *pgs,
+ gs_threshold_halftone *phtp)
+{ int code = gx_ht_alloc_order(porder, phtp->width, phtp->height,
+ 0, 256, pgs->memory);
+ if ( code < 0 )
+ return code;
+ gx_ht_construct_threshold_order(porder, phtp->thresholds.data);
+ return process_transfer(porder, pgs, phtp->transfer);
+}
+
+/* Construct the halftone order from a threshold array. */
+void
+gx_ht_construct_threshold_order(gx_ht_order *porder, const byte *thresholds)
+{ uint size = porder->num_bits;
+ uint *levels = porder->levels;
+ gx_ht_bit *bits = porder->bits;
+ uint i, j;
+ for ( i = 0; i < size; i++ )
+ bits[i].mask = max(1, thresholds[i]);
+ gx_sort_ht_order(bits, size);
+ /* We want to set levels[j] to the lowest value of i */
+ /* such that bits[i].mask > j. */
+ for ( i = 0, j = 0; i < size; i++ )
+ { if ( bits[i].mask != j )
+ { if_debug3('h', "[h]levels[%u..%u] = %u\n",
+ j, (uint)bits[i].mask, i);
+ while ( j < bits[i].mask )
+ levels[j++] = i;
+ }
+ }
+ while ( j < 256 )
+ levels[j++] = size;
+ gx_ht_construct_bits(porder);
+}
diff --git a/pstoraster/gsht1.h b/pstoraster/gsht1.h
new file mode 100644
index 000000000..d16a6041d
--- /dev/null
+++ b/pstoraster/gsht1.h
@@ -0,0 +1,42 @@
+/* Copyright (C) 1994 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gsht1.h */
+/* Extended public interface to halftones */
+
+#ifndef gsht1_INCLUDED
+# define gsht1_INCLUDED
+
+#include "gsht.h"
+
+/* Procedural interface */
+int gs_setcolorscreen(P2(gs_state *, gs_colorscreen_halftone *));
+int gs_currentcolorscreen(P2(gs_state *, gs_colorscreen_halftone *));
+
+/* We include sethalftone here, even though it is a Level 2 feature, */
+/* because it turns out to be convenient to define setcolorscreen */
+/* using sethalftone. */
+int gs_sethalftone(P2(gs_state *, gs_halftone *));
+int gs_currenthalftone(P2(gs_state *, gs_halftone *));
+
+#endif /* gsht1_INCLUDED */
diff --git a/pstoraster/gshtscr.c b/pstoraster/gshtscr.c
new file mode 100644
index 000000000..303b75531
--- /dev/null
+++ b/pstoraster/gshtscr.c
@@ -0,0 +1,503 @@
+/* Copyright (C) 1993, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gshtscr.c */
+/* Screen (Type 1) halftone processing for Ghostscript library */
+#include "math_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gsstruct.h"
+#include "gxarith.h"
+#include "gzstate.h"
+#include "gxdevice.h" /* for gzht.h */
+#include "gzht.h"
+
+/* Structure descriptors */
+private_st_gs_screen_enum();
+
+/* GC procedures */
+#define eptr ((gs_screen_enum *)vptr)
+
+private ENUM_PTRS_BEGIN(screen_enum_enum_ptrs) {
+ if ( index < 2 + st_ht_order_max_ptrs )
+ { gs_ptr_type_t ret = (*st_ht_order.enum_ptrs)(&eptr->order, sizeof(eptr->order), index-2, pep);
+ if ( ret == 0 ) /* don't stop early */
+ ret = ptr_struct_type, *pep = 0;
+ return ret;
+ }
+ return (*st_halftone.enum_ptrs)(&eptr->halftone, sizeof(eptr->halftone), index-(2+st_ht_order_max_ptrs), pep);
+ }
+ ENUM_PTR(0, gs_screen_enum, pgs);
+ENUM_PTRS_END
+
+private RELOC_PTRS_BEGIN(screen_enum_reloc_ptrs) {
+ RELOC_PTR(gs_screen_enum, pgs);
+ (*st_halftone.reloc_ptrs)(&eptr->halftone, sizeof(gs_halftone), gcst);
+ (*st_ht_order.reloc_ptrs)(&eptr->order, sizeof(gx_ht_order), gcst);
+} RELOC_PTRS_END
+
+#undef eptr
+
+/* Define the default value of AccurateScreens that affects */
+/* setscreen and setcolorscreen. */
+private bool screen_accurate_screens = false;
+
+/* Default AccurateScreens control */
+void
+gs_setaccuratescreens(bool accurate)
+{ screen_accurate_screens = accurate;
+}
+bool
+gs_currentaccuratescreens(void)
+{ return screen_accurate_screens;
+}
+
+/*
+ * We construct a halftone tile as a super-cell consisting of multiple
+ * copies of a multi-cell whose corners lie on integral coordinates and
+ * which in turn is a square array of basic cells whose corners lie on
+ * rational coordinates.
+ *
+ * Let T be the aspect ratio (ratio of physical pixel height to physical
+ * pixel width), which is abs(xx/yy) for portrait devices and abs(yx/xy) for
+ * landscape devices. We characterize the basic cell by four rational
+ * numbers U(') = M(')/R and V(') = N(')/R where R is positive, at least one
+ * of U and V (and the corresponding one of V' and U') is non-zero, and U'
+ * is approximately U/T and V' is approximately V*T; these numbers define
+ * the vertices of the basic cell at device space coordinates (0,0), (U,V),
+ * (U-V',U'+V), and (-V',U'); then the multi-cell is defined similarly by
+ * M(') and N('). R > 1 is only possible if AccurateScreens is true; if
+ * AccurateScreens is false, multi-cells and basic cells are the same. From
+ * these definitions, the basic cell has an area of B = U*U' + V*V' = (M*M'
+ * + N*N') / R^2 pixels, and the multi-cell has an area of C = B * R^2 =
+ * M*M' + N*N' pixels.
+ *
+ * If the coefficients of the default device transformation matrix are xx,
+ * xy, yx, and yy, then U and V are related to the frequency F and the angle
+ * A by:
+ * P = 72 / F;
+ * U = P * (xx * cos(A) + yx * sin(A));
+ * V = P * (xy * cos(A) + yy * sin(A)).
+ *
+ * We can tile the plane with any rectangular super-cell that consists of
+ * repetitions of the multi-cell and whose corners coincide with multi-cell
+ * coordinates (0,0). To determine the number of repetitions, we must find
+ * the smallest (absolute value) coordinates where the corners of
+ * multi-cells lie on the coordinate axes. We observe that for any integers
+ * i, j such that i*N - j*M' = 0, a multi-cell corner lies on the X axis at
+ * W = i*M + j*N'; similarly, if i'*M - j'*N' = 0, a corner lies on the Y
+ * axis at W' = i'*N + j'*M'. We can compute the values of i and j that
+ * give the minimum value of W by
+ * D = gcd(abs(M'), abs(N)), i = M'/D, j = N/D, W = C / D
+ * and similarly
+ * D' = gcd(abs(M), abs(N')), i' = N'/D', j' = M/D', W' = C / D'
+ * Then the super-cell occupies Z = W * W' pixels, consisting of Z / C
+ * multi-cells or Z / B basic cells. The trick in all this is to find
+ * values of F and A that aren't too far from the requested ones, and that
+ * yield a manageably small value for Z.
+ *
+ * Note that the super-cell only has to be so large because we want to use
+ * it directly to tile the plane. In fact, we can decompose it into W' / D
+ * horizontal strips of width W and height D, shifted horizontally with
+ * respect to each other by S pixels, where we compute S by finding h and k
+ * such that h*N - k*M' = D and then S = h*M + k*N'. The routines here only
+ * generate a single strip of samples, and let gx_ht_construct_spot_order
+ * construct the rest. If W' is large, we actually keep only one strip, and
+ * let the strip_tile_rectangle routines do the shifting at rendering time.
+ *
+ * **** NOTE: the current algorithms assume T = 1, U' = U, V' = V.
+ * We are in the process of fixing this.
+ */
+
+/* Forward references */
+private void pick_cell_size(P7(gs_screen_halftone *ph,
+ const gs_matrix *pmat, long max_size, bool accurate,
+ gs_int_point *pMN, int *pR, gs_int_point *ptile));
+
+/* Allocate a screen enumerator. */
+gs_screen_enum *
+gs_screen_enum_alloc(gs_memory_t *mem, client_name_t cname)
+{ return gs_alloc_struct(mem, gs_screen_enum, &st_gs_screen_enum, cname);
+}
+
+/* Set up for halftone sampling. */
+int
+gs_screen_init(gs_screen_enum *penum, gs_state *pgs,
+ gs_screen_halftone *phsp)
+{ return gs_screen_init_accurate(penum, pgs, phsp,
+ screen_accurate_screens);
+}
+int
+gs_screen_init_accurate(gs_screen_enum *penum, gs_state *pgs,
+ gs_screen_halftone *phsp, bool accurate)
+{ int code = gs_screen_order_init(&penum->order, pgs, phsp, accurate);
+ if ( code < 0 )
+ return code;
+ return gs_screen_enum_init(penum, &penum->order, pgs, phsp);
+}
+
+/* Allocate and initialize a spot screen. */
+/* This is the first half of gs_screen_init_accurate. */
+int
+gs_screen_order_init(gx_ht_order *porder, const gs_state *pgs,
+ gs_screen_halftone *phsp, bool accurate)
+{ gs_matrix imat;
+ long max_size;
+ gs_int_point MN, tile;
+ uint shift;
+ int code;
+ int R, D;
+ uint wd;
+
+ if ( phsp->frequency < 0.1 )
+ return_error(gs_error_rangecheck);
+ gs_deviceinitialmatrix(gs_currentdevice(pgs), &imat);
+ max_size = (accurate ? max_ushort : 512);
+ pick_cell_size(phsp, &imat, max_size, accurate, &MN, &R, &tile);
+#define M MN.x
+#define N MN.y
+#define W tile.x
+ D = igcd(M, N);
+ wd = W / D;
+ { /* Unfortunately, we don't know any closed formula for */
+ /* computing the shift, so we do it by enumeration. */
+ uint K;
+ uint vk = (N < 0 ? N + W : N) / D;
+
+ for ( K = 0; K < wd; K++ )
+ { if ( (K * vk) % wd == 1 )
+ break;
+ }
+ shift = ((((M < 0 ? M + W : M) / D) * K) % wd) * D;
+ if_debug2('h', "[h]strip=%d shift=%d\n", D, shift);
+ /* We just computed what amounts to a right shift; */
+ /* what we want is a left shift. */
+ if ( shift != 0 )
+ shift = W - shift;
+ }
+ /* NOTE: patching the next line to 'false' forces all screens */
+ /* to be strip halftones, even if they are small. */
+ if ( tile.y <= max_size / bitmap_raster(tile.x) )
+ { /*
+ * Allocate an order for the entire tile, but only sample one
+ * strip. Note that this causes the order parameters to be
+ * self-inconsistent until gx_ht_construct_spot_order fixes them
+ * up: see gxdht.h for more information.
+ */
+ code = gx_ht_alloc_order(porder, tile.x, tile.y, 0,
+ W * D, pgs->memory);
+ porder->height = porder->orig_height = D;
+ porder->shift = porder->orig_shift = shift;
+ }
+ else
+ { /* Just allocate the order for a single strip. */
+ code = gx_ht_alloc_order(porder, tile.x, D, shift,
+ W * D, pgs->memory);
+ }
+ if ( code < 0 )
+ return code;
+ porder->multiple = R;
+#undef M
+#undef N
+#undef W
+ return 0;
+}
+
+/*
+ * The following routine looks for "good" values of U and V
+ * in a simple-minded way, according to the following algorithm:
+ * We compute trial values u and v from the original values of F and A.
+ * Normally these will not be integers. We then examine the 4 pairs of
+ * integers obtained by rounding each of u and v independently up or down,
+ * and pick the pair U, V that yields the smallest value of W. If no pair
+ * yields an acceptably small W, we divide both u and v by 2 and try again.
+ * Then we run the equations backward to obtain the actual F and A.
+ * This is fairly easy given that we require either xx = yy = 0 or
+ * xy = yx = 0. In the former case, we have
+ * U = (72 / F * xx) * cos(A);
+ * V = (72 / F * yy) * sin(A);
+ * from which immediately
+ * A = arctan((V / yy) / (U / xx)),
+ * or equivalently
+ * A = arctan((V * xx) / (U * yy)).
+ * We can then obtain F as
+ * F = (72 * xx / U) * cos(A),
+ * or equivalently
+ * F = (72 * yy / V) * sin(A).
+ * For landscape devices, we replace xx by yx, yy by xy, and interchange
+ * sin and cos, resulting in
+ * A = arctan((U * xy) / (V * yx))
+ * and
+ * F = (72 * yx / U) * sin(A)
+ * or
+ * F = (72 * xy / V) * cos(A).
+ */
+/* ph->frequency and ph->angle are input parameters; */
+/* the routine sets ph->actual_frequency and ph->actual_angle. */
+private void
+pick_cell_size(gs_screen_halftone *ph, const gs_matrix *pmat, long max_size,
+ bool accurate, gs_int_point *pMN, int *pR, gs_int_point *ptile)
+{ bool landscape = (pmat->xy != 0.0 || pmat->yx != 0.0);
+ /* Account for a possibly reflected coordinate system. */
+ /* See gxstroke.c for the algorithm. */
+ bool reflected =
+ (landscape ? pmat->xy * pmat->yx > pmat->xx * pmat->yy :
+ (pmat->xx < 0) != (pmat->yy < 0));
+ int reflection = (reflected ? -1 : 1);
+ int rotation =
+ (landscape ? (pmat->yx < 0 ? 90 : -90) : pmat->xx < 0 ? 180 : 0);
+ const double f0 = ph->frequency, a0 = ph->angle;
+ gs_matrix rmat;
+ gs_point t;
+ double u0, v0;
+ int m, n, r, rt = 1, w;
+ double f = 0, a = 0;
+ double f_best = max_int, a_best = 360, e_best = 1000;
+ long w_size = max_size;
+ bool better;
+
+ /*
+ * We need to find a vector in device space whose length is
+ * 1 inch / ph->frequency and whose angle is ph->angle.
+ * Because device pixels may not be square, we can't simply
+ * map the length to device space and then rotate it;
+ * instead, since we know that user space is uniform in X and Y,
+ * we calculate the correct angle in user space before rotation.
+ */
+
+ /* Compute trial values of u and v. */
+
+ gs_make_rotation(a0 * reflection + rotation, &rmat);
+ gs_distance_transform(72.0 / f0, 0.0, &rmat, &t);
+ gs_distance_transform(t.x, t.y, pmat, &t);
+ if_debug9('h', "[h]Requested: f=%g a=%g mat=[%g %g %g %g] max_size=%ld =>\n u=%g v=%g\n",
+ ph->frequency, ph->angle,
+ pmat->xx, pmat->xy, pmat->yx, pmat->yy, max_size,
+ t.x, t.y);
+
+ /* Adjust u and v to reasonable values. */
+
+ u0 = t.x;
+ v0 = t.y;
+ if ( u0 == 0 && v0 == 0 )
+ u0 = v0 = 1;
+ while ( fabs(u0) + fabs(v0) < 4 )
+ u0 *= 2, v0 *= 2;
+try_size:
+ better = false;
+ { int m0 = (int)floor(u0 * rt + 0.0001);
+ int n0 = (int)floor(v0 * rt + 0.0001);
+ int mt, nt;
+
+ for ( mt = m0 + 1; mt >= m0; mt-- )
+ for ( nt = n0 + 1; nt >= n0; nt-- )
+ { int d = igcd(mt, nt);
+ long mtd = mt / d * (long)mt, ntd = nt / d * (long)nt;
+ long wt = any_abs(mtd) + any_abs(ntd);
+ long raster;
+ long wt_size;
+ double fr, ar, ft, at, f_diff, a_diff, f_err, a_err;
+
+ if_debug3('h', "[h]trying m=%d, n=%d, r=%d\n", mt, nt, rt);
+ if ( wt >= max_short )
+ continue;
+ /* Check the strip size, not the full tile size, */
+ /* against max_size. */
+ raster = bitmap_raster(wt);
+ if ( raster > max_size / d || raster > max_long / wt )
+ continue;
+ wt_size = raster * wt;
+
+ /* Compute the corresponding values of F and A. */
+
+ if ( landscape )
+ ar = atan2(mt * pmat->xy, nt * pmat->yx),
+ fr = 72.0 * (mt == 0 ? pmat->xy / nt * cos(ar) :
+ pmat->yx / mt * sin(ar));
+ else
+ ar = atan2(nt * pmat->xx, mt * pmat->yy),
+ fr = 72.0 * (mt == 0 ? pmat->yy / nt * sin(ar) :
+ pmat->xx / mt * cos(ar));
+ ft = fabs(fr) * rt;
+ /****** FOLLOWING IS WRONG IF NON-SQUARE PIXELS ******/
+ /* Normalize the angle to the requested quadrant. */
+ at = (ar * radians_to_degrees - rotation) * reflection;
+ at -= floor(at / 180.0) * 180.0;
+ at += floor(a0 / 180.0) * 180.0;
+ f_diff = fabs(ft - f0);
+ a_diff = fabs(at - a0);
+ f_err = f_diff / fabs(f0);
+ a_err = (a0 == 0 ? a_diff : a_diff / fabs(a0));
+
+ if_debug5('h', " ==> d=%d, wt=%ld, wt_size=%ld, f=%g, a=%g\n",
+ d, wt, bitmap_raster(wt) * wt, ft, at);
+
+ /*
+ * If AccurateScreens is true, minimize angle and frequency
+ * error within the permitted maximum super-cell size;
+ * if AccurateScreens is false, minimize cell size.
+ */
+
+ if ( accurate )
+ { /* Minimize percentage error. */
+ double err = f_err * a_err;
+ if ( err > e_best )
+ continue;
+ e_best = err;
+ }
+ else
+ { /* Minimize cell size. */
+ if ( wt_size >= w_size )
+ continue;
+ }
+ m = mt, n = nt, r = rt, w = wt, f = ft, a = at;
+ w_size = wt_size, f_best = f_diff, a_best = a_diff;
+ better = true;
+ if_debug3('h', "*** w_size=%ld, f_best=%g, a_best=%g\n",
+ w_size, f_best, a_best);
+ if ( f_err <= 0.01 && a_err <= 0.01 )
+ goto done;
+ }
+ }
+ if ( better )
+ { /* If we want accurate screens, continue till we fail. */
+ if ( accurate )
+ { ++rt;
+ goto try_size;
+ }
+ }
+ else
+ { /*
+ * We couldn't find an acceptable M and N. If R > 1, quit;
+ * if R = 1, shrink the cell and try again.
+ */
+ if ( rt == 1 )
+ { u0 /= 2;
+ v0 /= 2;
+ goto try_size;
+ }
+ }
+
+ /* Deliver the results. */
+done:
+ if_debug6('h', "[h]Chosen: f=%g a=%g m=%d n=%d r=%d w=%d\n",
+ f, a, m, n, r, w);
+ pMN->x = m, pMN->y = n;
+ *pR = r;
+ ptile->x = w, ptile->y = w;
+ ph->actual_frequency = f;
+ ph->actual_angle = a;
+}
+
+/* Prepare to sample a spot screen. */
+/* This is the second half of gs_screen_init_accurate. */
+int
+gs_screen_enum_init(gs_screen_enum *penum, const gx_ht_order *porder,
+ gs_state *pgs, gs_screen_halftone *phsp)
+{ uint W = porder->width;
+ uint D = porder->num_levels / W;
+
+ penum->pgs = pgs; /* ensure clean for GC */
+ penum->order = *porder;
+ penum->halftone.type = ht_type_screen;
+ penum->halftone.params.screen = *phsp;
+ penum->x = penum->y = 0;
+ penum->strip = D;
+ penum->shift = porder->shift;
+ /* The transformation matrix must include normalization to the */
+ /* interval (-1..1), and rotation by the negative of the angle. */
+ /****** WRONG IF NON-SQUARE PIXELS ******/
+ { float xscale = 2.0 / sqrt((double)W * D) * porder->multiple;
+ float yscale = xscale;
+ gs_make_rotation(-phsp->actual_angle, &penum->mat);
+ penum->mat.xx *= xscale, penum->mat.xy *= xscale;
+ penum->mat.yx *= yscale, penum->mat.yy *= yscale;
+ penum->mat.tx = -1.0;
+ penum->mat.ty = -1.0;
+ if_debug7('h', "[h]Screen: (%dx%d)/%d [%f %f %f %f]\n",
+ porder->width, porder->height, porder->multiple,
+ penum->mat.xx, penum->mat.xy,
+ penum->mat.yx, penum->mat.yy);
+ }
+ return 0;
+}
+
+/* Report current point for sampling */
+int
+gs_screen_currentpoint(gs_screen_enum *penum, gs_point *ppt)
+{ gs_point pt;
+ int code;
+ if ( penum->y >= penum->strip ) /* all done */
+ { gx_ht_construct_spot_order(&penum->order);
+ return 1;
+ }
+ /* We displace the sampled coordinates very slightly */
+ /* in order to reduce the likely number of points */
+ /* for which the spot function returns the same value. */
+ if ( (code = gs_point_transform(penum->x + 0.501, penum->y + 0.498, &penum->mat, &pt)) < 0 )
+ return code;
+ if ( pt.x < -1.0 )
+ pt.x += ((int)(-ceil(pt.x)) + 1) & ~1;
+ else if ( pt.x >= 1.0 )
+ pt.x -= ((int)pt.x + 1) & ~1;
+ if ( pt.y < -1.0 )
+ pt.y += ((int)(-ceil(pt.y)) + 1) & ~1;
+ else if ( pt.y >= 1.0 )
+ pt.y -= ((int)pt.y + 1) & ~1;
+ *ppt = pt;
+ return 0;
+}
+
+/* Record next halftone sample */
+int
+gs_screen_next(gs_screen_enum *penum, floatp value)
+{ ht_sample_t sample;
+ int width = penum->order.width;
+ if ( value < -1.0 || value > 1.0 )
+ return_error(gs_error_rangecheck);
+ /* The following statement was split into two */
+ /* to work around a bug in the Siemens C compiler. */
+ sample = (ht_sample_t)(value * max_ht_sample);
+ sample += max_ht_sample; /* convert from signed to biased */
+#ifdef DEBUG
+if ( gs_debug_c('H') )
+ { gs_point pt;
+ gs_screen_currentpoint(penum, &pt);
+ dprintf6("[H]sample x=%d y=%d (%f,%f): %f -> %u\n",
+ penum->x, penum->y, pt.x, pt.y, value, sample);
+ }
+#endif
+ penum->order.bits[penum->y * width + penum->x].mask = sample;
+ if ( ++(penum->x) >= width )
+ penum->x = 0, ++(penum->y);
+ return 0;
+}
+
+/* Install a fully constructed screen in the gstate. */
+int
+gs_screen_install(gs_screen_enum *penum)
+{ gx_device_halftone dev_ht;
+ dev_ht.order = penum->order;
+ dev_ht.components = 0;
+ return gx_ht_install(penum->pgs, &penum->halftone, &dev_ht);
+}
diff --git a/pstoraster/gsimage.c b/pstoraster/gsimage.c
new file mode 100644
index 000000000..7d1bc834b
--- /dev/null
+++ b/pstoraster/gsimage.c
@@ -0,0 +1,312 @@
+/* Copyright (C) 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gsimage.c */
+/* Image setup procedures for Ghostscript library */
+#include "memory_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gsstruct.h"
+#include "gscspace.h"
+#include "gsmatrix.h" /* for gsiparam.h */
+#include "gsimage.h"
+#include "gxarith.h" /* for igcd */
+#include "gxdevice.h"
+#include "gzstate.h"
+
+/* Define the enumeration state for this interface layer. */
+/*typedef struct gs_image_enum_s gs_image_enum;*/ /* in gsimage.h */
+struct gs_image_enum_s {
+ /* The following are set at initialization time. */
+ gs_memory_t *memory;
+ gx_device *dev;
+ void *info; /* driver bookkeeping structure */
+ int num_components;
+ bool multi;
+ int num_planes;
+ int width, height;
+ int bpp; /* bits per pixel (per plane, if multi) */
+ int bytes_mod; /* minimum # of bytes for an integral # of pixels */
+ uint raster; /* bytes per row (per plane), no padding */
+ /* The following are updated dynamically. */
+ int plane_index; /* index of next plane of data */
+ int x, y;
+ uint pos; /* byte position within the scan line */
+ gs_const_string planes[4];
+ byte saved[4][6]; /* partial bytes_mod bytes */
+ int num_saved; /* # of saved bytes (per plane) */
+ bool error;
+};
+gs_private_st_composite(st_gs_image_enum, gs_image_enum, "gs_image_enum",
+ gs_image_enum_enum_ptrs, gs_image_enum_reloc_ptrs);
+#define gs_image_enum_num_ptrs 2
+
+/* GC procedures */
+#define eptr ((gs_image_enum *)vptr)
+private ENUM_PTRS_BEGIN(gs_image_enum_enum_ptrs) {
+ /* Enumerate the data planes. */
+ index -= gs_image_enum_num_ptrs;
+ if ( index < eptr->plane_index )
+ { *pep = (void *)&eptr->planes[index];
+ return ptr_string_type;
+ }
+ return 0;
+ }
+ ENUM_PTR(0, gs_image_enum, dev);
+ ENUM_PTR(1, gs_image_enum, info);
+ENUM_PTRS_END
+private RELOC_PTRS_BEGIN(gs_image_enum_reloc_ptrs) {
+ int i;
+ RELOC_PTR(gs_image_enum, dev);
+ RELOC_PTR(gs_image_enum, info);
+ for ( i = 0; i < eptr->plane_index; i++ )
+ RELOC_CONST_STRING_PTR(gs_image_enum, planes[i]);
+} RELOC_PTRS_END
+#undef eptr
+
+/* Allocate an image enumerator. */
+gs_image_enum *
+gs_image_enum_alloc(gs_memory_t *mem, client_name_t cname)
+{ gs_image_enum *pie =
+ gs_alloc_struct(mem, gs_image_enum, &st_gs_image_enum, cname);
+
+ if ( pie != 0 )
+ { int i;
+ pie->memory = mem;
+ pie->info = 0;
+ /* Clean pointers for GC. */
+ pie->dev = 0;
+ for ( i = 0; i < countof(pie->planes); ++i )
+ pie->planes[i].data = 0, pie->planes[i].size = 0;
+ }
+ return pie;
+}
+
+/* Start processing an image. */
+int
+gs_image_init(gs_image_enum *pie, const gs_image_t *pim, bool multi,
+ gs_state *pgs)
+{ gx_device *dev = gs_currentdevice_inline(pgs);
+ gs_image_t image;
+ ulong samples_per_row = pim->Width;
+ int code;
+
+ if ( pim->Width == 0 || pim->Height == 0 )
+ return 1;
+ image = *pim;
+ if ( image.ImageMask )
+ { image.ColorSpace = NULL;
+ if ( pgs->in_cachedevice <= 1 )
+ image.adjust = false;
+ pie->num_components = pie->num_planes = 1;
+ }
+ else
+ { if ( pgs->in_cachedevice )
+ return_error(gs_error_undefined);
+ if ( image.ColorSpace == NULL )
+ image.ColorSpace = gs_color_space_DeviceGray();
+ pie->num_components =
+ gs_color_space_num_components(image.ColorSpace);
+ if ( multi )
+ pie->num_planes = pie->num_components;
+ else
+ { pie->num_planes = 1;
+ samples_per_row *= pie->num_components;
+ }
+ }
+ if ( image.ImageMask | image.CombineWithColor )
+ gx_set_dev_color(pgs);
+ code = (*dev_proc(dev, begin_image))
+ (dev, (const gs_imager_state *)pgs, &image,
+ (multi ? gs_image_format_component_planar : gs_image_format_chunky),
+ (gs_image_shape_t)(gs_image_shape_rows | gs_image_shape_split_row),
+ pgs->dev_color, pgs->clip_path, pie->memory, &pie->info);
+ if ( code < 0 )
+ return code;
+ pie->dev = dev;
+ pie->multi = multi;
+ pie->bpp =
+ image.BitsPerComponent * pie->num_components / pie->num_planes;
+ pie->width = image.Width;
+ pie->height = image.Height;
+ pie->bytes_mod = pie->bpp / igcd(pie->bpp, 8);
+ pie->raster = (samples_per_row * image.BitsPerComponent + 7) >> 3;
+ /* Initialize the dynamic part of the state. */
+ pie->plane_index = 0;
+ pie->x = pie->y = 0;
+ pie->pos = 0;
+ pie->num_saved = 0;
+ pie->error = false;
+ return 0;
+}
+
+/*
+ * Return the number of bytes of data per row
+ * (per plane, if MultipleDataSources is true).
+ */
+uint
+gs_image_bytes_per_row(const gs_image_enum *pie)
+{ return pie->raster;
+}
+
+/* Process the next piece of an image. */
+private int near
+copy_planes(gx_device *dev, gs_image_enum *pie, const byte **planes, int w, int h)
+{ int code = (*dev_proc(dev, image_data))(dev, pie->info, planes,
+ pie->raster, pie->x, pie->y,
+ w, h);
+ if ( code < 0 )
+ pie->error = true;
+ return code;
+}
+int
+gs_image_next(gs_image_enum *pie, const byte *dbytes, uint dsize,
+ uint *pused)
+{ gx_device *dev = pie->dev;
+ uint left;
+ const byte *planes[4];
+ int i;
+ int code;
+
+ /*
+ * Handle the following differences between gs_image_next and
+ * the device image_data procedure:
+ *
+ * - image_data requires an array of planes; gs_image_next
+ * expects planes in successive calls.
+ *
+ * - image_data requires that each call pass an integral
+ * number of pixels, and not cross scan line boundaries
+ * unless all scan lines have the same amount of data;
+ * gs_image_next allows arbitrary amounts of data.
+ */
+ if ( pie->plane_index != 0 )
+ if ( dsize != pie->planes[0].size )
+ return_error(gs_error_rangecheck);
+ pie->planes[pie->plane_index].data = dbytes;
+ pie->planes[pie->plane_index].size = dsize;
+ if ( ++(pie->plane_index) != pie->num_planes )
+ return 0;
+ /* We have a full set of planes. */
+ for ( i = 0; i < pie->num_planes; ++i )
+ planes[i] = pie->planes[i].data;
+ left = dsize;
+ /*
+ * We might have some left-over bytes from the previous set of planes.
+ * Check for this now.
+ */
+ if ( pie->num_saved != 0 )
+ { int copy =
+ min(pie->bytes_mod, pie->raster - pie->pos) - pie->num_saved;
+ if ( dsize >= copy )
+ { int w = pie->bytes_mod * 8 / pie->bpp;
+ const byte *mod_planes[4];
+
+ for ( i = 0; i < pie->num_planes; ++i )
+ { mod_planes[i] = pie->saved[i];
+ memcpy(pie->saved[i] + pie->num_saved, planes[i], copy);
+ planes[i] += copy;
+ }
+ pie->num_saved = 0;
+ left -= copy;
+ if ( pie->x + w >= pie->width )
+ { w = pie->width - pie->x;
+ code = copy_planes(dev, pie, mod_planes, w, 1);
+ if ( code < 0 )
+ return code;
+ pie->x = 0;
+ pie->y++;
+ pie->pos = 0;
+ if ( pie->y == pie->height )
+ { *pused = dsize - left;
+ return 1;
+ }
+ }
+ else
+ { code = copy_planes(dev, pie, mod_planes, w, 1);
+ if ( code < 0 )
+ return code;
+ pie->x += w;
+ pie->pos += pie->bytes_mod;
+ }
+ }
+ }
+ /*
+ * Pass data by rows.
+ */
+ { uint row_left;
+ while ( left >= (row_left = pie->raster - pie->pos) )
+ { int w = pie->width - pie->x;
+ int h = (pie->x == 0 ? left / row_left : 1);
+
+ if ( h > pie->height - pie->y )
+ h = pie->height - pie->y;
+ code = copy_planes(dev, pie, planes, w, h);
+ if ( code < 0 )
+ return code;
+ row_left *= h;
+ for ( i = 0; i < pie->num_planes; ++i )
+ planes[i] += row_left;
+ left -= row_left;
+ pie->x = 0;
+ pie->y += h;
+ pie->pos = 0;
+ if ( pie->y == pie->height )
+ { *pused = dsize - left;
+ return 1;
+ }
+ }
+ if ( (row_left = left - left % pie->bytes_mod) != 0 )
+ { int w = row_left * 8 / pie->bpp;
+
+ code = copy_planes(dev, pie, planes, w, 1);
+ if ( code < 0 )
+ return code;
+ for ( i = 0; i < pie->num_planes; ++i )
+ planes[i] += row_left;
+ left -= row_left;
+ pie->x += w;
+ pie->pos += row_left;
+ }
+ }
+ /*
+ * Save any left-over bytes.
+ */
+ if ( left != 0 )
+ { for ( i = 0; i < pie->num_planes; ++i )
+ memcpy(pie->saved[i] + pie->num_saved, planes[i], left);
+ pie->num_saved += left;
+ }
+ pie->plane_index = 0;
+ *pused = dsize;
+ return 0;
+}
+
+/* Clean up after processing an image. */
+void
+gs_image_cleanup(gs_image_enum *pie)
+{ gx_device *dev = pie->dev;
+
+ (*dev_proc(dev, end_image))(dev, pie->info, !pie->error);
+ /* Don't free the local enumerator -- the client does that. */
+}
diff --git a/pstoraster/gsimage.h b/pstoraster/gsimage.h
new file mode 100644
index 000000000..27f862087
--- /dev/null
+++ b/pstoraster/gsimage.h
@@ -0,0 +1,50 @@
+/* Copyright (C) 1992, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gsimage.h */
+/* Client interface to image painting */
+/* Requires gsstate.h */
+#include "gsiparam.h"
+
+/*
+ * The image painting interface uses an enumeration style:
+ * the client initializes an enumerator, then supplies data incrementally.
+ */
+typedef struct gs_image_enum_s gs_image_enum;
+gs_image_enum *gs_image_enum_alloc(P2(gs_memory_t *, client_name_t));
+/*
+ * image_init returns 1 for an empty image, 0 normally, <0 on error.
+ * Note that image_init serves for both image and imagemask,
+ * depending on the value of ImageMask in the image structure.
+ */
+int gs_image_init(P4(gs_image_enum *penum, const gs_image_t *pim,
+ bool MultipleDataSources, gs_state *pgs));
+int gs_image_next(P4(gs_image_enum *penum, const byte *dbytes,
+ uint dsize, uint *pused));
+/*
+ * Return the number of bytes of data per row
+ * (per plane, if MultipleDataSources is true).
+ */
+uint gs_image_bytes_per_row(P1(const gs_image_enum *penum));
+/* Clean up after processing an image. */
+void gs_image_cleanup(P1(gs_image_enum *penum));
diff --git a/pstoraster/gsimpath.c b/pstoraster/gsimpath.c
new file mode 100644
index 000000000..2752c5d53
--- /dev/null
+++ b/pstoraster/gsimpath.c
@@ -0,0 +1,188 @@
+/* Copyright (C) 1989, 1992 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gsimpath.c */
+/* Image to outline conversion for Ghostscript library */
+#include "gx.h"
+#include "gserrors.h"
+#include "gsmatrix.h"
+#include "gsstate.h"
+#include "gspath.h"
+
+/* Define the state of the conversion process. */
+typedef struct {
+ /* The following are set at the beginning of the conversion. */
+ gs_state *pgs;
+ const byte *data; /* image data */
+ int width, height, raster;
+ /* The following are updated dynamically. */
+ int dx, dy; /* X/Y increment of current run */
+ int count; /* # of steps in current run */
+} status;
+
+/* Define the scaling for the path tracer. */
+/* It must be even. */
+#define outline_scale 4
+/* Define the length of the short strokes for turning corners. */
+#define step 1
+
+/* Forward declarations */
+private int near get_pixel(P3(const status *, int, int));
+private int near trace_from(P4(status *, int, int, int));
+private int near add_dxdy(P4(status *, int, int, int));
+#define add_deltas(s, dx, dy, n)\
+ if ( (code = add_dxdy(s, dx, dy, n)) < 0 ) return code
+/* Append an outline derived from an image to the current path. */
+int
+gs_imagepath(gs_state *pgs, int width, int height, const byte *data)
+{ status stat;
+ status *out = &stat;
+ int code, x, y;
+ /* Initialize the state. */
+ stat.pgs = pgs;
+ stat.data = data;
+ stat.width = width;
+ stat.height = height;
+ stat.raster = (width + 7) / 8;
+ /* Trace the cells to form an outline. The trace goes in clockwise */
+ /* order, always starting by going west along a bottom edge. */
+ for ( y = height - 1; y >= 0; y-- )
+ for ( x = width - 1; x >= 0; x-- )
+ { if ( get_pixel(out, x, y) && !get_pixel(out, x, y - 1) &&
+ (!get_pixel(out, x + 1, y) || get_pixel(out, x + 1, y - 1)) &&
+ !trace_from(out, x, y, 1)
+ )
+ { /* Found a starting point */
+ stat.count = 0;
+ stat.dx = stat.dy = 0;
+ if ( (code = trace_from(out, x, y, 0)) < 0 )
+ return code;
+ add_deltas(out, 0, 0, 1); /* force out last segment */
+ if ( (code = gs_closepath(pgs)) < 0 )
+ return code;
+ }
+ }
+ return 0;
+}
+
+/* Get a pixel from the data. Return 0 if outside the image. */
+private int near
+get_pixel(register const status *out, int x, int y)
+{ if ( x < 0 || x >= out->width || y < 0 || y >= out->height )
+ return 0;
+ return (out->data[y * out->raster + (x >> 3)] >> (~x & 7)) & 1;
+}
+
+/* Trace a path. If detect is true, don't draw, just return 1 if we ever */
+/* encounter a starting point whose x,y follows that of the initial point */
+/* in x-then-y scan order; if detect is false, actually draw the outline. */
+private int near
+trace_from(register status *out, int x0, int y0, int detect)
+{ int x = x0, y = y0;
+ int dx = -1, dy = 0; /* initially going west */
+ int part = 0; /* how far along edge we are; */
+ /* initialized only to pacify gcc */
+ int code;
+ if ( !detect )
+ { part = (get_pixel(out, x + 1, y - 1) ?
+ outline_scale - step : step);
+ code = gs_moveto(out->pgs,
+ x + 1 - part / (float)outline_scale,
+ (float)y);
+ if ( code < 0 ) return code;
+ }
+ while ( 1 )
+ { /* Relative to the current direction, */
+ /* -dy,dx is at +90 degrees (counter-clockwise); */
+ /* tx,ty is at +45 degrees; */
+ /* ty,-tx is at -45 degrees (clockwise); */
+ /* dy,-dx is at -90 degrees. */
+ int tx = dx - dy, ty = dy + dx;
+ if ( get_pixel(out, x + tx, y + ty) )
+ { /* Cell at 45 degrees is full, */
+ /* go counter-clockwise. */
+ if ( !detect )
+ { /* If this is a 90 degree corner set at a */
+ /* 45 degree angle, avoid backtracking. */
+ if ( out->dx == ty && out->dy == -tx )
+ {
+#define half_scale (outline_scale / 2 - step)
+ out->count -= half_scale;
+ add_deltas(out, tx, ty, outline_scale / 2);
+#undef half_scale
+ }
+ else
+ { add_deltas(out, dx, dy, step - part);
+ add_deltas(out, tx, ty, outline_scale - step);
+ }
+ part = outline_scale - step;
+ }
+ x += tx, y += ty;
+ dx = -dy, dy += tx;
+ }
+ else if ( !get_pixel(out, x + dx, y + dy) )
+ { /* Cell straight ahead is empty, go clockwise. */
+ if ( !detect )
+ { add_deltas(out, dx, dy, outline_scale - step - part);
+ add_deltas(out, ty, -tx, step);
+ part = step;
+ }
+ dx = dy, dy -= ty;
+ }
+ else
+ { /* Neither of the above, go in same direction. */
+ if ( !detect )
+ { add_deltas(out, dx, dy, outline_scale);
+ }
+ x += dx, y += dy;
+ }
+ if ( dx == -step && dy == 0 && !(tx == -step && ty == -step) )
+ { /* We just turned a corner and are going west, */
+ /* so the previous pixel is a starting point pixel. */
+ if ( x == x0 && y == y0 ) return 0;
+ if ( detect && (y > y0 || (y == y0 && x > x0)) )
+ return 1;
+ }
+ }
+}
+
+/* Add a (dx, dy) pair to the path being formed. */
+/* Accumulate successive segments in the same direction. */
+private int near
+add_dxdy(register status *out, int dx, int dy, int count)
+{ if ( count != 0 )
+ { if ( dx == out->dx && dy == out->dy )
+ out->count += count;
+ else
+ { if ( out->count != 0 )
+ { int code = gs_rlineto(out->pgs,
+ out->dx * out->count / (float)outline_scale,
+ out->dy * out->count / (float)outline_scale);
+ if ( code < 0 ) return code;
+ }
+ out->dx = dx, out->dy = dy;
+ out->count = count;
+ }
+ }
+ return 0;
+}
diff --git a/pstoraster/gsinit.c b/pstoraster/gsinit.c
new file mode 100644
index 000000000..ebe826297
--- /dev/null
+++ b/pstoraster/gsinit.c
@@ -0,0 +1,74 @@
+/* Copyright (C) 1989, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gsinit.c */
+/* Initialization for the imager */
+#include "stdio_.h"
+#include "memory_.h"
+#include "gdebug.h"
+#include "gscdefs.h"
+#include "gsmemory.h"
+#include "gp.h"
+#include "gslib.h" /* interface definition */
+
+/* Imported from gsmisc.c */
+extern FILE *gs_debug_out;
+
+/* Imported from gsmemory.c */
+void gs_malloc_init(P0());
+void gs_malloc_release(P0());
+
+/* Configuration information from gconfig.c. */
+extern_gx_init_table();
+
+/* Initialization to be done before anything else. */
+void
+gs_lib_init(FILE *debug_out)
+{ gs_lib_init0(debug_out);
+ gs_lib_init1(&gs_memory_default);
+}
+void
+gs_lib_init0(FILE *debug_out)
+{ gs_debug_out = debug_out;
+ gs_malloc_init();
+ /* Reset debugging flags */
+ memset(gs_debug, 0, 128);
+ gs_log_errors = 0;
+}
+void
+gs_lib_init1(gs_memory_t *mem)
+{ /* Run configuration-specific initialization procedures. */
+ { void (**ipp)(P1(gs_memory_t *));
+ for ( ipp = gx_init_table; *ipp != 0; ++ipp )
+ (**ipp)(mem);
+ }
+}
+
+/* Clean up after execution. */
+void
+gs_lib_finit(int exit_status, int code)
+{ fflush(stderr); /* in case of error exit */
+ /* Do platform-specific cleanup. */
+ gp_exit(exit_status, code);
+ gs_malloc_release();
+}
diff --git a/pstoraster/gsio.h b/pstoraster/gsio.h
new file mode 100644
index 000000000..f89716962
--- /dev/null
+++ b/pstoraster/gsio.h
@@ -0,0 +1,65 @@
+/* Copyright (C) 1989, 1990, 1993, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gsio.h */
+/* stdio redirection */
+
+#ifndef gsio_INCLUDED
+# define gsio_INCLUDED
+
+/* The library and interpreter never use stdin/out/err directly. */
+extern FILE *gs_stdin, *gs_stdout, *gs_stderr;
+
+/* Redefine all the relevant stdio functions to use the above. */
+/* Some functions we make illegal, rather than redefining them. */
+#undef stdin
+#define stdin gs_stdin
+#undef stdout
+#define stdout gs_stdout
+#undef stderr
+#define stderr gs_stderr
+#undef fgetchar
+#define fgetchar() fgetc(stdin)
+#undef fputchar
+#define fputchar(c) fputc(c, stdout)
+#undef getchar
+#define getchar() getc(stdin)
+#undef gets
+#define gets Function._gets_.unavailable
+/* We should do something about perror, but since many Unix systems */
+/* don't provide the strerror function, we can't. (No Aladdin-maintained */
+/* code uses perror.) */
+#undef printf
+#define printf Function._printf_.unavailable
+#undef putchar
+#define putchar(c) fputc(c, stdout)
+#undef puts
+#define puts(s) (fputs(s, stdout), putchar('\n'))
+#undef scanf
+#define scanf Function._scanf_.unavailable
+#undef vprintf
+#define vprintf Function._vprintf_.unavailable
+#undef vscanf
+#define vscanf Function._vscanf_.unavailable
+
+#endif /* gsio_INCLUDED */
diff --git a/pstoraster/gsiodev.c b/pstoraster/gsiodev.c
new file mode 100644
index 000000000..d02d57129
--- /dev/null
+++ b/pstoraster/gsiodev.c
@@ -0,0 +1,270 @@
+/* Copyright (C) 1993, 1994, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gsiodev.c */
+/* IODevice implementation for Ghostscript */
+#include "errno_.h"
+#include "string_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gp.h"
+#include "gsparam.h"
+#include "gxiodev.h"
+
+/* Import the IODevice table from gconfig.c. */
+extern gx_io_device *gx_io_device_table[];
+extern uint gx_io_device_table_count;
+
+/* Define the OS (%os%) device. */
+iodev_proc_fopen(iodev_os_fopen);
+iodev_proc_fclose(iodev_os_fclose);
+private iodev_proc_delete_file(os_delete);
+private iodev_proc_rename_file(os_rename);
+private iodev_proc_file_status(os_status);
+private iodev_proc_enumerate_files(os_enumerate);
+private iodev_proc_get_params(os_get_params);
+gx_io_device gs_iodev_os = {
+ "%os%", "FileSystem",
+ { iodev_no_init, iodev_no_open_device,
+ NULL /*iodev_os_open_file*/, iodev_os_fopen, iodev_os_fclose,
+ os_delete, os_rename, os_status,
+ os_enumerate, gp_enumerate_files_next, gp_enumerate_files_close,
+ os_get_params, iodev_no_put_params
+ }
+};
+
+/* ------ Initialization ------ */
+
+void
+gs_iodev_init(gs_memory_t *mem)
+{ /* Run the one-time initialization of each IODevice. */
+ gx_io_device **piodev = &gx_io_device_table[0];
+ for ( ; *piodev; piodev++ )
+ ((*piodev)->procs.init)(*piodev, mem);
+}
+
+/* ------ Default (unimplemented) IODevice procedures ------ */
+
+int
+iodev_no_init(gx_io_device *iodev, gs_memory_t *mem)
+{ return 0;
+}
+
+int
+iodev_no_open_device(gx_io_device *iodev, const char *access, stream **ps,
+ gs_memory_t *mem)
+{ return_error(gs_error_invalidfileaccess);
+}
+
+int
+iodev_no_open_file(gx_io_device *iodev, const char *fname, uint namelen,
+ const char *access, stream **ps, gs_memory_t *mem)
+{ return_error(gs_error_invalidfileaccess);
+}
+
+int
+iodev_no_fopen(gx_io_device *iodev, const char *fname, const char *access,
+ FILE **pfile, char *rfname, uint rnamelen)
+{ return_error(gs_error_invalidfileaccess);
+}
+
+int
+iodev_no_fclose(gx_io_device *iodev, FILE *file)
+{ return_error(gs_error_ioerror);
+}
+
+int
+iodev_no_delete_file(gx_io_device *iodev, const char *fname)
+{ return_error(gs_error_invalidfileaccess);
+}
+
+int
+iodev_no_rename_file(gx_io_device *iodev, const char *from, const char *to)
+{ return_error(gs_error_invalidfileaccess);
+}
+
+int
+iodev_no_file_status(gx_io_device *iodev, const char *fname, struct stat *pstat)
+{ return_error(gs_error_undefinedfilename);
+}
+
+file_enum *
+iodev_no_enumerate_files(gx_io_device *iodev, const char *pat, uint patlen,
+ gs_memory_t *memory)
+{ return NULL;
+}
+
+int
+iodev_no_get_params(gx_io_device *iodev, gs_param_list *plist)
+{ return 0;
+}
+
+int
+iodev_no_put_params(gx_io_device *iodev, gs_param_list *plist)
+{ return param_commit(plist);
+}
+
+/* ------ %os% ------ */
+
+/* The fopen routine is exported for %null. */
+int
+iodev_os_fopen(gx_io_device *iodev, const char *fname, const char *access,
+ FILE **pfile, char *rfname, uint rnamelen)
+{ errno = 0;
+ *pfile = gp_fopen(fname, access);
+ if ( *pfile == NULL )
+ return_error(gs_fopen_errno_to_code(errno));
+ if ( rfname != NULL )
+ strcpy(rfname, fname);
+ return 0;
+}
+
+/* The fclose routine is exported for %null. */
+int
+iodev_os_fclose(gx_io_device *iodev, FILE *file)
+{ fclose(file);
+ return 0;
+}
+
+private int
+os_delete(gx_io_device *iodev, const char *fname)
+{ return (unlink(fname) == 0 ? 0 : gs_error_ioerror);
+}
+
+private int
+os_rename(gx_io_device *iodev, const char *from, const char *to)
+{ return (rename(from, to) == 0 ? 0 : gs_error_ioerror);
+}
+
+private int
+os_status(gx_io_device *iodev, const char *fname, struct stat *pstat)
+{ /* The RS/6000 prototype for stat doesn't include const, */
+ /* so we have to explicitly remove the const modifier. */
+ return (stat((char *)fname, pstat) < 0 ? gs_error_undefinedfilename : 0);
+}
+
+private file_enum *
+os_enumerate(gx_io_device *iodev, const char *pat, uint patlen,
+ gs_memory_t *mem)
+{ return gp_enumerate_files_init(pat, patlen, mem);
+}
+
+private int
+os_get_params(gx_io_device *iodev, gs_param_list *plist)
+{ /* We aren't going to implement *all* of the Adobe parameters.... */
+ int code;
+ bool btrue = true;
+
+ if ( (code = param_write_bool(plist, "HasNames", &btrue)) < 0 )
+ return code;
+ return 0;
+}
+
+/* ------ Utilities ------ */
+
+/* Get the N'th IODevice from the known device table. */
+gx_io_device *
+gs_getiodevice(int index)
+{ if ( index < 0 || index >= gx_io_device_table_count )
+ return 0; /* index out of range */
+ return gx_io_device_table[index];
+}
+
+/* Look up an IODevice name. */
+/* The name may be either %device or %device%. */
+gx_io_device *
+gs_findiodevice(const byte *str, uint len)
+{ gx_io_device **pftab;
+ if ( len > 1 && str[len - 1] == '%' )
+ len--;
+ for ( pftab = gx_io_device_table; *pftab != NULL; pftab++ )
+ { const char *dname = (*pftab)->dname;
+ if ( strlen(dname) == len + 1 && !memcmp(str, dname, len) )
+ return *pftab;
+ }
+ return 0;
+}
+
+/* ------ Accessors ------ */
+
+/* Get IODevice parameters. */
+int
+gs_getdevparams(gx_io_device *iodev, gs_param_list *plist)
+{ /* All IODevices have the Type parameter. */
+ gs_param_string ts;
+ int code;
+
+ param_string_from_string(ts, iodev->dtype);
+ code = param_write_name(plist, "Type", &ts);
+ if ( code < 0 )
+ return code;
+ return (*iodev->procs.get_params)(iodev, plist);
+}
+
+/* Put IODevice parameters. */
+int
+gs_putdevparams(gx_io_device *iodev, gs_param_list *plist)
+{ return (*iodev->procs.put_params)(iodev, plist);
+}
+
+/* Convert an OS error number to a PostScript error */
+/* if opening a file fails. */
+int
+gs_fopen_errno_to_code(int eno)
+{ /* Different OSs vary widely in their error codes. */
+ /* We try to cover as many variations as we know about. */
+ switch ( eno )
+ {
+#ifdef ENOENT
+ case ENOENT:
+ return_error(gs_error_undefinedfilename);
+#endif
+#ifdef ENOFILE
+# ifndef ENOENT
+# define ENOENT ENOFILE
+# endif
+# if ENOFILE != ENOENT
+ case ENOFILE:
+ return_error(gs_error_undefinedfilename);
+# endif
+#endif
+#ifdef ENAMETOOLONG
+ case ENAMETOOLONG:
+ return_error(gs_error_undefinedfilename);
+#endif
+#ifdef EACCES
+ case EACCES:
+ return_error(gs_error_invalidfileaccess);
+#endif
+#ifdef EMFILE
+ case EMFILE:
+ return_error(gs_error_limitcheck);
+#endif
+#ifdef ENFILE
+ case ENFILE:
+ return_error(gs_error_limitcheck);
+#endif
+ default:
+ return_error(gs_error_ioerror);
+ }
+}
diff --git a/pstoraster/gsiparam.h b/pstoraster/gsiparam.h
new file mode 100644
index 000000000..a57b9d141
--- /dev/null
+++ b/pstoraster/gsiparam.h
@@ -0,0 +1,238 @@
+/* Copyright (C) 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gsiparam.h */
+/* Image parameter definition */
+/* Requires gsmatrix.h */
+
+#ifndef gsiparam_INCLUDED
+# define gsiparam_INCLUDED
+
+/* ---------------- Image parameters ---------------- */
+
+/* Define an opaque type for a color space. */
+#ifndef gs_color_space_DEFINED
+# define gs_color_space_DEFINED
+typedef struct gs_color_space_s gs_color_space;
+#endif
+
+/*
+ * Define the structure for specifying image data. It follows closely
+ * the discussion on pp. 219-223 of the PostScript Language Reference Manual,
+ * Second Edition, with the following exceptions:
+ *
+ * ColorSpace and ImageMask are added members from PDF.
+ *
+ * MultipleDataSources is not a member of this structure,
+ * but an argument to gs_image_init.
+ *
+ * adjust and CombineWithColor are not PostScript or PDF standard
+ * (see the RasterOp section of language.doc for a discussion of
+ * CombineWithColor).
+ */
+typedef enum {
+ /* Single plane, chunky pixels. */
+ gs_image_format_chunky = 0,
+ /* num_components planes, chunky components. */
+ gs_image_format_component_planar = 1,
+ /* BitsPerComponent * num_components planes, 1 bit per plane */
+ /****** NOT SUPPORTED YET, DO NOT USE ******/
+ gs_image_format_bit_planar = 2
+} gs_image_format_t;
+
+typedef struct gs_image_s {
+ /*
+ * Define the width of source image in pixels.
+ */
+ int Width;
+ /*
+ * Define the height of source image in pixels.
+ */
+ int Height;
+ /*
+ * Define the transformation from user space to image space.
+ */
+ gs_matrix ImageMatrix;
+ /*
+ * Define B, the number of bits per pixel component.
+ * Currently this must be 1 for masks.
+ */
+ int BitsPerComponent;
+ /*
+ * Define the source color space (must be NULL for masks).
+ */
+ const gs_color_space *ColorSpace;
+ /*
+ * Define the linear remapping of the input values.
+ * For the I'th pixel component, we start by treating
+ * the B bits of component data as a fraction F between
+ * 0 and 1; the actual component value is then
+ * Decode[I*2] + F * (Decode[I*2+1] - Decode[I*2]).
+ * For masks, only the first two entries are used;
+ * they must be 1,0 for write-0s masks, 0,1 for write-1s.
+ */
+ float Decode[8];
+ /*
+ * Define whether to smooth the image.
+ */
+ bool Interpolate;
+ /*
+ * Define whether this is a mask or a solid image.
+ */
+ bool ImageMask;
+ /***
+ *** The following are not PostScript standard.
+ ***/
+ /*
+ * Define whether to expand each destination pixel, to make
+ * masked characters look better (only used for masks).
+ */
+ bool adjust;
+ /*
+ * Define whether to use the drawing color as the
+ * "texture" for RasterOp. For more information,
+ * see the discussion of RasterOp in language.doc.
+ */
+ bool CombineWithColor;
+} gs_image_t;
+
+/*
+ * Define procedures for initializing a gs_image_t to default values.
+ * For masks, write_1s = false paints 0s, write_1s = true paints 1s.
+ * This is consistent with the "polarity" operand of the PostScript
+ * imagemask operator.
+ */
+void
+ gs_image_t_init_gray(P1(gs_image_t *pim)),
+ gs_image_t_init_color(P1(gs_image_t *pim)),/* general color, initially RGB */
+ gs_image_t_init_mask(P2(gs_image_t *pim, bool write_1s));
+
+/* ---------------- Driver interface for images ---------------- */
+
+/*
+ * When we call the driver image_data procedure, we pass values X, Y, W,
+ * and H to specify the rectangle of source data. The default is to pass
+ * the entire image in a single call, in which case X = Y = 0,
+ * W = Width, H = Height. However, banding may require multiple calls,
+ * each call passing only a subset of the image data; also,
+ * some environments may not pass full rows or the full height.
+ * We define some flags that define what kind of subset may be passed.
+ * These flags may be or'ed together. For example:
+ *
+ * Normal PostScript images will have flags = 2+16
+ * Normal PCL-5 images will have flags = 2+16+64
+ * Banded unrotated images will have flags = 1+2+16
+ * Banded rotated images will have flags = 1+2+4+8+16+64
+ *
+ * Add 32 to any of the above for very long rows.
+ */
+typedef enum {
+ /* We may skip some rows at the top (beginning), */
+ /* i.e., the first Y value may not be zero. */
+ gs_image_shape_clip_top = 1,
+ /* We may skip some rows at the bottom (end), */
+ /* i.e., the last Y+H value may not equal Height. */
+ gs_image_shape_clip_bottom = 2,
+ /* We may skip some data on the left side, */
+ /* i.e., some X value may not be zero. */
+ gs_image_shape_clip_left = 4,
+ /* We may skip some data on the right side, */
+ /* i.e., some X+W value may not equal Width. */
+ gs_image_shape_clip_right = 8,
+ /* We may pass rows of image in more than one call, */
+ /* i.e., Y may not have the same value on all calls. */
+ gs_image_shape_rows = 16,
+ /* We may pass a single row in pieces, */
+ /* i.e., there may be multiple calls with the same Y. */
+ gs_image_shape_split_row = 32,
+ /* Different rows may have different widths, */
+ /* i.e., X or X+W may not have the same value on all calls. */
+ gs_image_shape_varying_width = 64
+} gs_image_shape_t;
+
+/****** REMAINDER OF FILE UNDER CONSTRUCTION. PROCEED AT YOUR OWN RISK. ******/
+
+#if 0 /****************************************************************/
+
+/* Define the color space of the image data. */
+typedef enum {
+ gs_color_space_index_DeviceGray = 0, /* 1 */
+ gs_color_space_index_DeviceRGB, /* 3 */
+ gs_color_space_index_DeviceCMYK, /* 4 */
+ gs_color_space_index_CIEDEFG, /* 4 */
+ gs_color_space_index_CIEDEF, /* 3 */
+ gs_color_space_index_CIEABC, /* 3 */
+ gs_color_space_index_CIEA /* 1 */
+} gs_image_color_space_index;
+
+/*
+ * Define an opaque structure type for referring to the internal structures
+ * that define color rendering information (transfer function, black
+ * generation, undercolor removal, CIE rendering dictionary, halftoning).
+ */
+typedef struct gx_color_rendering_info_s gx_color_rendering_info;
+
+/*
+ * Define a structure for defining the color space of an image.
+ * ****** MUST RECONCILE THIS WITH gs_color_space. ******
+ */
+typedef
+ struct {
+ /* Define the index of the base color space. */
+ gs_color_space_index base_index;
+ /* Provide the parameters of a CIE space, if required. */
+ void *base_data;
+ /* If this is an indexed space, palette.data points to */
+ /* the palette, consisting of N bytes per entry where */
+ /* N is the number of color space components; if this is */
+ /* not an indexed space, palette.data = 0. */
+ gs_const_string palette;
+ }
+gx_image_color_space;
+
+/* ---------------- Procedures ---------------- */
+
+/****** MOVE pcri TO IMAGER STATE ******/
+
+/* ---------------- Services ---------------- */
+
+/*
+In order to make the driver's life easier, we provide the following callback
+procedure:
+*/
+
+int gx_map_image_color(P5(gx_device *dev,
+ const gs_image_t *pim,
+ const gx_color_rendering_info *pcri,
+ const uint components[4],
+ gx_drawing_color *pdcolor));
+
+/*
+Map a source color to a drawing color. The components are simply the pixel
+component values from the input data, i.e., 1 to 4 B-bit numbers from the
+source data. Return 0 if the operation succeeded, or a negative error code.
+*/
+
+#endif /****************************************************************/
+
+#endif /* gsiparam_INCLUDED */
diff --git a/pstoraster/gslib.h b/pstoraster/gslib.h
new file mode 100644
index 000000000..65adfdc28
--- /dev/null
+++ b/pstoraster/gslib.h
@@ -0,0 +1,39 @@
+/* Copyright (C) 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gslib.h */
+/* Top-level API functions for imaging library */
+/* Requires stdio.h, gsmemory.h */
+
+/*
+ * Initialize the library. gs_lib_init does all of the initialization,
+ * using the C heap for initial allocation; if a client wants the library to
+ * use a different default allocator during initialization, it should call
+ * gs_lib_init0 and then gs_lib_init1.
+ */
+void gs_lib_init(P1(FILE *debug_out));
+void gs_lib_init0(P1(FILE *debug_out));
+void gs_lib_init1(P1(gs_memory_t *));
+
+/* Clean up after execution. */
+void gs_lib_finit(P2(int exit_status, int code));
diff --git a/pstoraster/gsline.c b/pstoraster/gsline.c
new file mode 100644
index 000000000..2cb14117c
--- /dev/null
+++ b/pstoraster/gsline.c
@@ -0,0 +1,234 @@
+/* Copyright (C) 1989, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gsline.c */
+/* Line parameter operators for Ghostscript library */
+#include "math_.h"
+#include "memory_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gxfixed.h" /* ditto */
+#include "gxmatrix.h" /* for gzstate */
+#include "gzstate.h"
+#include "gsline.h" /* for prototypes */
+#include "gzline.h"
+
+/* ------ Device-independent parameters ------ */
+
+#define pgs_lp gs_currentlineparams_inline(pgs)
+
+/* setlinewidth */
+int
+gs_setlinewidth(gs_state *pgs, floatp width)
+{ gx_set_line_width(pgs_lp, width);
+ return 0;
+}
+
+/* currentlinewidth */
+float
+gs_currentlinewidth(const gs_state *pgs)
+{ return gx_current_line_width(pgs_lp);
+}
+
+/* setlinecap */
+int
+gs_setlinecap(gs_state *pgs, gs_line_cap cap)
+{ pgs_lp->cap = cap;
+ return 0;
+}
+
+/* currentlinecap */
+gs_line_cap
+gs_currentlinecap(const gs_state *pgs)
+{ return pgs_lp->cap;
+}
+
+/* setlinejoin */
+int
+gs_setlinejoin(gs_state *pgs, gs_line_join join)
+{ pgs_lp->join = join;
+ return 0;
+}
+
+/* currentlinejoin */
+gs_line_join
+gs_currentlinejoin(const gs_state *pgs)
+{ return pgs_lp->join;
+}
+
+/* setmiterlimit */
+int
+gx_set_miter_limit(gx_line_params *plp, floatp limit)
+{ if ( limit < 1.0 )
+ return_error(gs_error_rangecheck);
+ plp->miter_limit = limit;
+ /*
+ * Compute the miter check value. The supplied miter limit is an
+ * upper bound on 1/sin(phi/2); we convert this to a lower bound on
+ * tan(phi). Note that if phi > pi/2, this is negative. We use the
+ * half-angle and angle-sum formulas here to avoid the trig functions.
+ * We also need a special check for phi/2 close to pi/4.
+ * Some C compilers can't handle this as a conditional expression....
+ */
+ { double limit_squared = limit * limit;
+ if ( limit_squared < 2.0001 && limit_squared > 1.9999 )
+ plp->miter_check = 1.0e6;
+ else
+ plp->miter_check =
+ sqrt(limit_squared - 1) * 2 / (limit_squared - 2);
+ }
+ return 0;
+}
+int
+gs_setmiterlimit(gs_state *pgs, floatp limit)
+{ return gx_set_miter_limit(pgs_lp, limit);
+}
+
+/* currentmiterlimit */
+float
+gs_currentmiterlimit(const gs_state *pgs)
+{ return pgs_lp->miter_limit;
+}
+
+/* setdash */
+int
+gx_set_dash(gx_dash_params *dash, const float *pattern, uint length,
+ floatp offset, gs_memory_t *mem)
+{ uint n = length;
+ const float *dfrom = pattern;
+ bool ink = true;
+ int index = 0;
+ float pattern_length = 0.0;
+ float dist_left;
+ float *ppat = dash->pattern;
+
+ /* Check the dash pattern. */
+ while ( n-- )
+ { float elt = *dfrom++;
+ if ( elt < 0 )
+ return_error(gs_error_rangecheck);
+ pattern_length += elt;
+ }
+ if ( length == 0 ) /* empty pattern */
+ { dist_left = 0.0;
+ if ( mem )
+ ppat = 0;
+ }
+ else
+ { if ( pattern_length == 0 )
+ return_error(gs_error_rangecheck);
+ /* Compute the initial index, ink_on, and distance left */
+ /* in the pattern, according to the offset. */
+#define f_mod(a, b) ((a) - floor((a) / (b)) * (b))
+ if ( length & 1 )
+ { /* Odd and even repetitions of the pattern */
+ /* have opposite ink values! */
+ float length2 = pattern_length * 2;
+ dist_left = f_mod(offset, length2);
+ if ( dist_left >= pattern_length )
+ dist_left -= pattern_length, ink = !ink;
+ }
+ else
+ dist_left = f_mod(offset, pattern_length);
+ while ( (dist_left -= pattern[index]) >= 0 &&
+ (dist_left > 0 || pattern[index] != 0)
+ )
+ ink = !ink, index++;
+ if ( mem )
+ { ppat = (float *)gs_alloc_bytes(mem,
+ length * sizeof(float),
+ "dash pattern");
+ if ( ppat == 0 )
+ return_error(gs_error_VMerror);
+ }
+ memcpy(ppat, pattern, length * sizeof(float));
+ }
+ dash->pattern = ppat;
+ dash->pattern_size = length;
+ dash->offset = offset;
+ dash->init_ink_on = ink;
+ dash->init_index = index;
+ dash->init_dist_left = -dist_left;
+ return 0;
+}
+int
+gs_setdash(gs_state *pgs, const float *pattern, uint length, floatp offset)
+{ return gx_set_dash(&pgs_lp->dash, pattern, length, offset,
+ pgs->memory);
+}
+
+/* currentdash */
+uint
+gs_currentdash_length(const gs_state *pgs)
+{ return pgs_lp->dash.pattern_size;
+}
+const float *
+gs_currentdash_pattern(const gs_state *pgs)
+{ return pgs_lp->dash.pattern;
+}
+float
+gs_currentdash_offset(const gs_state *pgs)
+{ return pgs_lp->dash.offset;
+}
+
+/* Internal accessor for line parameters */
+const gx_line_params *
+gs_currentlineparams(const gs_imager_state *pis)
+{ return gs_currentlineparams_inline(pis);
+}
+
+/* ------ Device-dependent parameters ------ */
+
+/* setflat */
+int
+gs_imager_setflat(gs_imager_state *pis, floatp flat)
+{ if ( flat <= 0.2 )
+ flat = 0.2;
+ else if ( flat > 100 )
+ flat = 100;
+ pis->flatness = flat;
+ return 0;
+}
+int
+gs_setflat(gs_state *pgs, floatp flat)
+{ return gs_imager_setflat((gs_imager_state *)pgs, flat);
+}
+
+/* currentflat */
+float
+gs_currentflat(const gs_state *pgs)
+{ return pgs->flatness;
+}
+
+/* setstrokeadjust */
+int
+gs_setstrokeadjust(gs_state *pgs, bool stroke_adjust)
+{ pgs->stroke_adjust = stroke_adjust;
+ return 0;
+}
+
+/* currentstrokeadjust */
+bool
+gs_currentstrokeadjust(const gs_state *pgs)
+{ return pgs->stroke_adjust;
+}
diff --git a/pstoraster/gsline.h b/pstoraster/gsline.h
new file mode 100644
index 000000000..e64ead373
--- /dev/null
+++ b/pstoraster/gsline.h
@@ -0,0 +1,60 @@
+/* Copyright (C) 1994, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gsline.h */
+/* Line parameter and quality definitions */
+
+#ifndef gsline_INCLUDED
+# define gsline_INCLUDED
+
+#include "gslparam.h"
+
+/* Procedures */
+int gs_setlinewidth(P2(gs_state *, floatp));
+float gs_currentlinewidth(P1(const gs_state *));
+int gs_setlinecap(P2(gs_state *, gs_line_cap));
+gs_line_cap
+ gs_currentlinecap(P1(const gs_state *));
+int gs_setlinejoin(P2(gs_state *, gs_line_join));
+gs_line_join
+ gs_currentlinejoin(P1(const gs_state *));
+int gs_setmiterlimit(P2(gs_state *, floatp));
+float gs_currentmiterlimit(P1(const gs_state *));
+int gs_setdash(P4(gs_state *, const float *, uint, floatp));
+uint gs_currentdash_length(P1(const gs_state *));
+const float *
+ gs_currentdash_pattern(P1(const gs_state *));
+float gs_currentdash_offset(P1(const gs_state *));
+int gs_setflat(P2(gs_state *, floatp));
+float gs_currentflat(P1(const gs_state *));
+int gs_setstrokeadjust(P2(gs_state *, bool));
+bool gs_currentstrokeadjust(P1(const gs_state *));
+
+/* Imager-level procedures */
+#ifndef gs_imager_state_DEFINED
+# define gs_imager_state_DEFINED
+typedef struct gs_imager_state_s gs_imager_state;
+#endif
+int gs_imager_setflat(P2(gs_imager_state *, floatp));
+
+#endif /* gsline_INCLUDED */
diff --git a/pstoraster/gslparam.h b/pstoraster/gslparam.h
new file mode 100644
index 000000000..5458ee5bd
--- /dev/null
+++ b/pstoraster/gslparam.h
@@ -0,0 +1,47 @@
+/* Copyright (C) 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gslparam.h */
+/* Line parameter definitions */
+
+#ifndef gslparam_INCLUDED
+# define gslparam_INCLUDED
+
+/* Line cap values */
+typedef enum {
+ gs_cap_butt = 0,
+ gs_cap_round = 1,
+ gs_cap_square = 2,
+ gs_cap_triangle = 3 /* not supported by PostScript */
+} gs_line_cap;
+
+/* Line join values */
+typedef enum {
+ gs_join_miter = 0,
+ gs_join_round = 1,
+ gs_join_bevel = 2,
+ gs_join_none = 3, /* not supported by PostScript */
+ gs_join_triangle = 4 /* not supported by PostScript */
+} gs_line_join;
+
+#endif /* gslparam_INCLUDED */
diff --git a/pstoraster/gsmatrix.c b/pstoraster/gsmatrix.c
new file mode 100644
index 000000000..9404cb875
--- /dev/null
+++ b/pstoraster/gsmatrix.c
@@ -0,0 +1,418 @@
+/* Copyright (C) 1989, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gsmatrix.c */
+/* Matrix operators for Ghostscript library */
+#include "math_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gxfarith.h"
+#include "gxfixed.h"
+#include "gxmatrix.h"
+
+/* The identity matrix */
+private gs_matrix gs_identity_matrix =
+ { identity_matrix_body };
+
+/* ------ Matrix creation ------ */
+
+/* Create an identity matrix */
+void
+gs_make_identity(gs_matrix *pmat)
+{ *pmat = gs_identity_matrix;
+}
+
+/* Create a translation matrix */
+int
+gs_make_translation(floatp dx, floatp dy, gs_matrix *pmat)
+{ *pmat = gs_identity_matrix;
+ pmat->tx = dx;
+ pmat->ty = dy;
+ return 0;
+}
+
+/* Create a scaling matrix */
+int
+gs_make_scaling(floatp sx, floatp sy, gs_matrix *pmat)
+{ *pmat = gs_identity_matrix;
+ pmat->xx = sx;
+ pmat->yy = sy;
+ return 0;
+}
+
+/* Create a rotation matrix. */
+/* The angle is in degrees. */
+int
+gs_make_rotation(floatp ang, gs_matrix *pmat)
+{ gs_sincos_t sincos;
+ gs_sincos_degrees(ang, &sincos);
+ pmat->yy = pmat->xx = sincos.cos;
+ pmat->xy = sincos.sin;
+ pmat->yx = -sincos.sin;
+ pmat->tx = pmat->ty = 0.0;
+ return 0;
+}
+
+/* ------ Matrix arithmetic ------ */
+
+/* Multiply two matrices. We should check for floating exceptions, */
+/* but for the moment it's just too awkward. */
+/* Since this is used heavily, we check for shortcuts. */
+int
+gs_matrix_multiply(const gs_matrix *pm1, const gs_matrix *pm2, gs_matrix *pmr)
+{ double xx1 = pm1->xx, yy1 = pm1->yy;
+ double tx1 = pm1->tx, ty1 = pm1->ty;
+ double xx2 = pm2->xx, yy2 = pm2->yy;
+ double xy2 = pm2->xy, yx2 = pm2->yx;
+ if ( !is_skewed(pm1) )
+ { pmr->tx = tx1 * xx2 + pm2->tx;
+ pmr->ty = ty1 * yy2 + pm2->ty;
+ if ( is_fzero(xy2) )
+ pmr->xy = 0;
+ else
+ pmr->xy = xx1 * xy2,
+ pmr->ty += tx1 * xy2;
+ pmr->xx = xx1 * xx2;
+ if ( is_fzero(yx2) )
+ pmr->yx = 0;
+ else
+ pmr->yx = yy1 * yx2,
+ pmr->tx += ty1 * yx2;
+ pmr->yy = yy1 * yy2;
+ }
+ else
+ { double xy1 = pm1->xy, yx1 = pm1->yx;
+ pmr->xx = xx1 * xx2 + xy1 * yx2;
+ pmr->xy = xx1 * xy2 + xy1 * yy2;
+ pmr->yy = yx1 * xy2 + yy1 * yy2;
+ pmr->yx = yx1 * xx2 + yy1 * yx2;
+ pmr->tx = tx1 * xx2 + ty1 * yx2 + pm2->tx;
+ pmr->ty = tx1 * xy2 + ty1 * yy2 + pm2->ty;
+ }
+ return 0;
+}
+
+/* Invert a matrix. Return gs_error_undefinedresult if not invertible. */
+int
+gs_matrix_invert(const gs_matrix *pm, gs_matrix *pmr)
+{ /* We have to be careful about fetch/store order, */
+ /* because pm might be the same as pmr. */
+ if ( !is_skewed(pm) )
+ { if ( is_fzero(pm->xx) || is_fzero(pm->yy) )
+ return_error(gs_error_undefinedresult);
+ pmr->tx = - (pmr->xx = 1.0 / pm->xx) * pm->tx;
+ pmr->xy = 0.0;
+ pmr->yx = 0.0;
+ pmr->ty = - (pmr->yy = 1.0 / pm->yy) * pm->ty;
+ }
+ else
+ { double det = pm->xx * pm->yy - pm->xy * pm->yx;
+ double mxx = pm->xx, mtx = pm->tx;
+ if ( det == 0 )
+ return_error(gs_error_undefinedresult);
+ pmr->xx = pm->yy / det;
+ pmr->xy = - pm->xy / det;
+ pmr->yx = - pm->yx / det;
+ pmr->yy = mxx / det; /* xx is already changed */
+ pmr->tx = - (mtx * pmr->xx + pm->ty * pmr->yx);
+ pmr->ty = - (mtx * pmr->xy + pm->ty * pmr->yy); /* tx ditto */
+ }
+ return 0;
+}
+
+/* Translate a matrix, possibly in place. */
+int
+gs_matrix_translate(const gs_matrix *pm, floatp dx, floatp dy, gs_matrix *pmr)
+{ gs_point trans;
+ int code = gs_distance_transform(dx, dy, pm, &trans);
+ if ( code < 0 )
+ return code;
+ if ( pmr != pm )
+ *pmr = *pm;
+ pmr->tx += trans.x;
+ pmr->ty += trans.y;
+ return 0;
+}
+
+/* Scale a matrix, possibly in place. */
+int
+gs_matrix_scale(const gs_matrix *pm, floatp sx, floatp sy, gs_matrix *pmr)
+{ pmr->xx = pm->xx * sx;
+ pmr->xy = pm->xy * sx;
+ pmr->yx = pm->yx * sy;
+ pmr->yy = pm->yy * sy;
+ if ( pmr != pm )
+ { pmr->tx = pm->tx;
+ pmr->ty = pm->ty;
+ }
+ return 0;
+}
+
+/* Rotate a matrix, possibly in place. The angle is in degrees. */
+int
+gs_matrix_rotate(const gs_matrix *pm, floatp ang, gs_matrix *pmr)
+{ double mxx, mxy;
+ gs_sincos_t sincos;
+ gs_sincos_degrees(ang, &sincos);
+ mxx = pm->xx, mxy = pm->xy;
+ pmr->xx = sincos.cos * mxx + sincos.sin * pm->yx;
+ pmr->xy = sincos.cos * mxy + sincos.sin * pm->yy;
+ pmr->yx = sincos.cos * pm->yx - sincos.sin * mxx;
+ pmr->yy = sincos.cos * pm->yy - sincos.sin * mxy;
+ if ( pmr != pm )
+ { pmr->tx = pm->tx;
+ pmr->ty = pm->ty;
+ }
+ return 0;
+}
+
+/* ------ Coordinate transformations (floating point) ------ */
+
+/* Note that all the transformation routines take separate */
+/* x and y arguments, but return their result in a point. */
+
+/* Transform a point. */
+int
+gs_point_transform(floatp x, floatp y, const gs_matrix *pmat,
+ gs_point *ppt)
+{ ppt->x = x * pmat->xx + pmat->tx;
+ ppt->y = y * pmat->yy + pmat->ty;
+ if ( !is_fzero(pmat->yx) )
+ ppt->x += y * pmat->yx;
+ if ( !is_fzero(pmat->xy) )
+ ppt->y += x * pmat->xy;
+ return 0;
+}
+
+/* Inverse-transform a point. */
+/* Return gs_error_undefinedresult if the matrix is not invertible. */
+int
+gs_point_transform_inverse(floatp x, floatp y, const gs_matrix *pmat,
+ gs_point *ppt)
+{ if ( !is_skewed(pmat) )
+ { if ( is_fzero(pmat->xx) || is_fzero(pmat->yy) )
+ return_error(gs_error_undefinedresult);
+ ppt->x = (x - pmat->tx) / pmat->xx;
+ ppt->y = (y - pmat->ty) / pmat->yy;
+ return 0;
+ }
+ else
+ { /* There are faster ways to do this, */
+ /* but we won't implement one unless we have to. */
+ gs_matrix imat;
+ int code = gs_matrix_invert(pmat, &imat);
+ if ( code < 0 )
+ return code;
+ return gs_point_transform(x, y, &imat, ppt);
+ }
+}
+
+/* Transform a distance. */
+int
+gs_distance_transform(floatp dx, floatp dy, const gs_matrix *pmat,
+ gs_point *pdpt)
+{ pdpt->x = dx * pmat->xx;
+ pdpt->y = dy * pmat->yy;
+ if ( !is_fzero(pmat->yx) )
+ pdpt->x += dy * pmat->yx;
+ if ( !is_fzero(pmat->xy) )
+ pdpt->y += dx * pmat->xy;
+ return 0;
+}
+
+/* Inverse-transform a distance. */
+/* Return gs_error_undefinedresult if the matrix is not invertible. */
+int
+gs_distance_transform_inverse(floatp dx, floatp dy,
+ const gs_matrix *pmat, gs_point *pdpt)
+{ if ( !is_skewed(pmat) )
+ { if ( is_fzero(pmat->xx) || is_fzero(pmat->yy) )
+ return_error(gs_error_undefinedresult);
+ pdpt->x = dx / pmat->xx;
+ pdpt->y = dy / pmat->yy;
+ }
+ else
+ { double det = pmat->xx * pmat->yy - pmat->xy * pmat->yx;
+ if ( det == 0 )
+ return_error(gs_error_undefinedresult);
+ pdpt->x = (dx * pmat->yy - dy * pmat->yx) / det;
+ pdpt->y = (dy * pmat->xx - dx * pmat->xy) / det;
+ }
+ return 0;
+}
+
+/* Compute the bounding box of 4 points. */
+int
+gs_points_bbox(const gs_point pts[4], gs_rect *pbox)
+{
+#define assign_min_max(vmin, vmax, v0, v1)\
+ if ( v0 < v1 ) vmin = v0, vmax = v1; else vmin = v1, vmax = v0
+#define assign_min_max_4(vmin, vmax, v0, v1, v2, v3)\
+ { double min01, max01, min23, max23;\
+ assign_min_max(min01, max01, v0, v1);\
+ assign_min_max(min23, max23, v2, v3);\
+ vmin = min(min01, min23);\
+ vmax = max(max01, max23);\
+ }
+ assign_min_max_4(pbox->p.x, pbox->q.x,
+ pts[0].x, pts[1].x, pts[2].x, pts[3].x);
+ assign_min_max_4(pbox->p.y, pbox->q.y,
+ pts[0].y, pts[1].y, pts[2].y, pts[3].y);
+#undef assign_min_max
+#undef assign_min_max_4
+ return 0;
+}
+
+/* Transform or inverse-transform a bounding box. */
+/* Return gs_error_undefinedresult if the matrix is not invertible. */
+private int
+bbox_transform_either_only(const gs_rect *pbox_in, const gs_matrix *pmat,
+ gs_point pts[4],
+ int (*point_xform)(P4(floatp, floatp, const gs_matrix *, gs_point *)))
+{ int code;
+ if ( (code = (*point_xform)(pbox_in->p.x, pbox_in->p.y, pmat, &pts[0])) < 0 ||
+ (code = (*point_xform)(pbox_in->p.x, pbox_in->q.y, pmat, &pts[1])) < 0 ||
+ (code = (*point_xform)(pbox_in->q.x, pbox_in->p.y, pmat, &pts[2])) < 0 ||
+ (code = (*point_xform)(pbox_in->q.x, pbox_in->q.y, pmat, &pts[3])) < 0
+ )
+ DO_NOTHING;
+ return code;
+}
+
+private int
+bbox_transform_either(const gs_rect *pbox_in, const gs_matrix *pmat,
+ gs_rect *pbox_out,
+ int (*point_xform)(P4(floatp, floatp, const gs_matrix *, gs_point *)))
+{ int code;
+ /*
+ * In principle, we could transform only one point and two
+ * distance vectors; however, because of rounding, we will only
+ * get fully consistent results if we transform all 4 points.
+ * We must compute the max and min after transforming,
+ * since a rotation may be involved.
+ */
+ gs_point pts[4];
+ if ( (code = bbox_transform_either_only(pbox_in, pmat, pts, point_xform)) < 0 )
+ return code;
+ return gs_points_bbox(pts, pbox_out);
+}
+int
+gs_bbox_transform(const gs_rect *pbox_in, const gs_matrix *pmat,
+ gs_rect *pbox_out)
+{ return bbox_transform_either(pbox_in, pmat, pbox_out,
+ gs_point_transform);
+}
+int
+gs_bbox_transform_only(const gs_rect *pbox_in, const gs_matrix *pmat,
+ gs_point points[4])
+{ return bbox_transform_either_only(pbox_in, pmat, points,
+ gs_point_transform);
+}
+int
+gs_bbox_transform_inverse(const gs_rect *pbox_in, const gs_matrix *pmat,
+ gs_rect *pbox_out)
+{ return bbox_transform_either(pbox_in, pmat, pbox_out,
+ gs_point_transform_inverse);
+}
+
+/* ------ Coordinate transformations (to fixed point) ------ */
+
+#define f_fits_in_fixed(f) f_fits_in_bits(f, fixed_int_bits)
+
+/* Transform a point with a fixed-point result. */
+int
+gs_point_transform2fixed(const gs_matrix_fixed *pmat,
+ floatp x, floatp y, gs_fixed_point *ppt)
+{ fixed px, py, t;
+ double dtemp;
+ int code;
+ if ( !pmat->txy_fixed_valid )
+ { /* The translation is out of range. Do the */
+ /* computation in floating point, and convert to */
+ /* fixed at the end. */
+ gs_point fpt;
+ gs_point_transform(x, y, (const gs_matrix *)pmat, &fpt);
+ if ( !(f_fits_in_fixed(fpt.x) && f_fits_in_fixed(fpt.y)) )
+ return_error(gs_error_limitcheck);
+ ppt->x = float2fixed(fpt.x);
+ ppt->y = float2fixed(fpt.y);
+ return 0;
+ }
+ if ( !is_fzero(pmat->xy) )
+ { /* Hope for 90 degree rotation */
+ if ( (code = set_dfmul2fixed_vars(px, y, pmat->yx, dtemp)) < 0 ||
+ (code = set_dfmul2fixed_vars(py, x, pmat->xy, dtemp)) < 0
+ )
+ return code;
+ if ( !is_fzero(pmat->xx) )
+ { if ( (code = set_dfmul2fixed_vars(t, x, pmat->xx, dtemp)) < 0 )
+ return code;
+ px += t; /* should check for overflow */
+ }
+ if ( !is_fzero(pmat->yy) )
+ { if ( (code = set_dfmul2fixed_vars(t, y, pmat->yy, dtemp)) < 0 )
+ return code;
+ py += t; /* should check for overflow */
+ }
+ }
+ else
+ { if ( (code = set_dfmul2fixed_vars(px, x, pmat->xx, dtemp)) < 0 ||
+ (code = set_dfmul2fixed_vars(py, y, pmat->yy, dtemp)) < 0
+ )
+ return code;
+ if ( !is_fzero(pmat->yx) )
+ { if ( (code = set_dfmul2fixed_vars(t, y, pmat->yx, dtemp)) < 0 )
+ return code;
+ px += t; /* should check for overflow */
+ }
+ }
+ ppt->x = px + pmat->tx_fixed; /* should check for overflow */
+ ppt->y = py + pmat->ty_fixed; /* should check for overflow */
+ return 0;
+}
+
+/* Transform a distance with a fixed-point result. */
+int
+gs_distance_transform2fixed(const gs_matrix_fixed *pmat,
+ floatp dx, floatp dy, gs_fixed_point *ppt)
+{ fixed px, py, t;
+ double dtemp;
+ int code;
+ if ( (code = set_dfmul2fixed_vars(px, dx, pmat->xx, dtemp)) < 0 ||
+ (code = set_dfmul2fixed_vars(py, dy, pmat->yy, dtemp)) < 0
+ )
+ return code;
+ if ( !is_fzero(pmat->yx) )
+ { if ( (code = set_dfmul2fixed_vars(t, dy, pmat->yx, dtemp)) < 0 )
+ return code;
+ px += t; /* should check for overflow */
+ }
+ if ( !is_fzero(pmat->xy) )
+ { if ( (code = set_dfmul2fixed_vars(t, dx, pmat->xy, dtemp)) < 0 )
+ return code;
+ py += t; /* should check for overflow */
+ }
+ ppt->x = px;
+ ppt->y = py;
+ return 0;
+}
diff --git a/pstoraster/gsmatrix.h b/pstoraster/gsmatrix.h
new file mode 100644
index 000000000..14c056216
--- /dev/null
+++ b/pstoraster/gsmatrix.h
@@ -0,0 +1,75 @@
+/* Copyright (C) 1989, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gsmatrix.h */
+/* Definition of matrices and client interface to matrix routines */
+
+#ifndef gsmatrix_INCLUDED
+# define gsmatrix_INCLUDED
+
+/* See p. 65 of the PostScript manual for the semantics of */
+/* transformation matrices. */
+
+/* Structure for a transformation matrix. */
+#define _matrix_body\
+ float xx, xy, yx, yy, tx, ty
+struct gs_matrix_s {
+ _matrix_body;
+};
+#ifndef gs_matrix_DEFINED
+# define gs_matrix_DEFINED
+typedef struct gs_matrix_s gs_matrix;
+#endif
+/* Macro for initializing constant matrices */
+#define constant_matrix_body(xx, xy, yx, yy, tx, ty)\
+ (float)(xx), (float)(xy), (float)(yx),\
+ (float)(yy), (float)(tx), (float)(ty)
+
+/* The identity matrix (for structure initialization) */
+#define identity_matrix_body\
+ constant_matrix_body(1, 0, 0, 1, 0, 0)
+
+/* Matrix creation */
+void gs_make_identity(P1(gs_matrix *));
+int gs_make_translation(P3(floatp, floatp, gs_matrix *)),
+ gs_make_scaling(P3(floatp, floatp, gs_matrix *)),
+ gs_make_rotation(P2(floatp, gs_matrix *));
+
+/* Matrix arithmetic */
+int gs_matrix_multiply(P3(const gs_matrix *, const gs_matrix *, gs_matrix *)),
+ gs_matrix_invert(P2(const gs_matrix *, gs_matrix *)),
+ gs_matrix_translate(P4(const gs_matrix *, floatp, floatp, gs_matrix *)),
+ gs_matrix_scale(P4(const gs_matrix *, floatp, floatp, gs_matrix *)),
+ gs_matrix_rotate(P3(const gs_matrix *, floatp, gs_matrix *));
+
+/* Coordinate transformation */
+int gs_point_transform(P4(floatp, floatp, const gs_matrix *, gs_point *)),
+ gs_point_transform_inverse(P4(floatp, floatp, const gs_matrix *, gs_point *)),
+ gs_distance_transform(P4(floatp, floatp, const gs_matrix *, gs_point *)),
+ gs_distance_transform_inverse(P4(floatp, floatp, const gs_matrix *, gs_point *)),
+ gs_points_bbox(P2(const gs_point [4], gs_rect *)),
+ gs_bbox_transform_only(P3(const gs_rect *, const gs_matrix *, gs_point [4])),
+ gs_bbox_transform(P3(const gs_rect *, const gs_matrix *, gs_rect *)),
+ gs_bbox_transform_inverse(P3(const gs_rect *, const gs_matrix *, gs_rect *));
+
+#endif /* gsmatrix_INCLUDED */
diff --git a/pstoraster/gsmdebug.h b/pstoraster/gsmdebug.h
new file mode 100644
index 000000000..a49e80d6d
--- /dev/null
+++ b/pstoraster/gsmdebug.h
@@ -0,0 +1,50 @@
+/* Copyright (C) 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gsmdebug.h */
+/* Debugging definitions for memory manager */
+/* Requires gdebug.h (for gs_debug) */
+
+/* Define the fill patterns used for debugging the allocator. */
+extern byte
+ gs_alloc_fill_alloc, /* allocated but not initialized */
+ gs_alloc_fill_block, /* locally allocated block */
+ gs_alloc_fill_collected, /* garbage collected */
+ gs_alloc_fill_deleted, /* locally deleted block */
+ gs_alloc_fill_free; /* freed */
+
+/* Define an alias for a specialized debugging flag */
+/* that used to be a separate variable. */
+#define gs_alloc_debug gs_debug['@']
+
+/* Conditionally fill unoccupied blocks with a pattern. */
+extern void gs_alloc_memset(P3(void *, int/*byte*/, ulong));
+#ifdef DEBUG
+/* The following peculiar syntax avoids incorrect capture of an 'else'. */
+# define gs_alloc_fill(ptr, fill, len)\
+ do { if ( gs_alloc_debug ) gs_alloc_memset(ptr, fill, (ulong)(len));\
+ } while ( 0 )
+#else
+# define gs_alloc_fill(ptr, fill, len)\
+ DO_NOTHING
+#endif
diff --git a/pstoraster/gsmemory.c b/pstoraster/gsmemory.c
new file mode 100644
index 000000000..a9e3d28d5
--- /dev/null
+++ b/pstoraster/gsmemory.c
@@ -0,0 +1,392 @@
+/*
+ Copyright 1993-1999 by Easy Software Products.
+ Copyright (C) 1993, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gsmemory.c */
+/* Generic allocator support for Ghostscript library */
+#include "gx.h"
+#include "malloc_.h"
+#include "memory_.h"
+#include "gsmdebug.h"
+#include "gsrefct.h" /* to check prototype */
+#include "gsstruct.h" /* ditto */
+
+/* Define the fill patterns for unallocated memory. */
+byte gs_alloc_fill_alloc = 0xa1;
+byte gs_alloc_fill_block = 0xb1;
+byte gs_alloc_fill_collected = 0xc1;
+byte gs_alloc_fill_deleted = 0xd1;
+byte gs_alloc_fill_free = 0xf1;
+
+/* A 'structure' type descriptor for free blocks. */
+gs_public_st_simple(st_free, byte, "(free)");
+
+/* The 'structure' type descriptor for bytes. */
+gs_public_st_simple(st_bytes, byte, "bytes");
+
+/* Fill an unoccupied block with a pattern. */
+/* Note that the block size may be too large for a single memset. */
+void
+gs_alloc_memset(void *ptr, int/*byte*/ fill, ulong lsize)
+{ ulong msize = lsize;
+ char *p = ptr;
+ int isize;
+
+ for ( ; msize; msize -= isize, p += isize )
+ { isize = min(msize, max_int);
+ memset(p, fill, isize);
+ }
+}
+
+/* ------ Heap allocator ------ */
+
+/*
+ * An implementation of Ghostscript's memory manager interface
+ * that works directly with the C heap. We keep track of all allocated
+ * blocks so we can free them at cleanup time.
+ */
+private gs_memory_proc_alloc_bytes(gs_heap_alloc_bytes);
+private gs_memory_proc_alloc_struct(gs_heap_alloc_struct);
+private gs_memory_proc_alloc_byte_array(gs_heap_alloc_byte_array);
+private gs_memory_proc_alloc_struct_array(gs_heap_alloc_struct_array);
+private gs_memory_proc_resize_object(gs_heap_resize_object);
+private gs_memory_proc_object_size(gs_heap_object_size);
+private gs_memory_proc_object_type(gs_heap_object_type);
+private gs_memory_proc_free_object(gs_heap_free_object);
+private gs_memory_proc_alloc_string(gs_heap_alloc_string);
+private gs_memory_proc_resize_string(gs_heap_resize_string);
+private gs_memory_proc_free_string(gs_heap_free_string);
+private gs_memory_proc_register_root(gs_heap_register_root);
+private gs_memory_proc_unregister_root(gs_heap_unregister_root);
+private gs_memory_proc_status(gs_heap_status);
+private gs_memory_proc_enable_free(gs_heap_enable_free);
+gs_memory_t gs_memory_default = {
+ { gs_heap_alloc_bytes,
+ gs_heap_alloc_bytes,
+ gs_heap_alloc_struct,
+ gs_heap_alloc_struct,
+ gs_heap_alloc_byte_array,
+ gs_heap_alloc_byte_array,
+ gs_heap_alloc_struct_array,
+ gs_heap_alloc_struct_array,
+ gs_heap_resize_object,
+ gs_heap_object_size,
+ gs_heap_object_type,
+ gs_heap_free_object,
+ gs_heap_alloc_string,
+ gs_heap_alloc_string,
+ gs_heap_resize_string,
+ gs_heap_free_string,
+ gs_heap_register_root,
+ gs_heap_unregister_root,
+ gs_heap_status,
+ gs_heap_enable_free
+ }
+};
+/* We must make sure that malloc_blocks leave the block aligned. */
+typedef struct malloc_block_s malloc_block;
+#define malloc_block_data\
+ malloc_block *next;\
+ malloc_block *prev;\
+ uint size;\
+ gs_memory_type_ptr_t type;\
+ client_name_t cname
+struct malloc_block_data_s { malloc_block_data; };
+struct malloc_block_s {
+ malloc_block_data;
+/* ANSI C does not allow zero-size arrays, so we need the following */
+/* unnecessary and wasteful workaround: */
+#define _npad (8 - (sizeof(struct malloc_block_data_s) & 7))
+ byte _pad[_npad]; /* pad to double */
+#undef _npad
+};
+
+private malloc_block *malloc_list;
+private long malloc_used;
+
+/* Initialize the malloc heap. */
+private long heap_available(P0());
+void
+gs_malloc_init(void)
+{ malloc_list = 0;
+ malloc_used = 0;
+}
+/* Estimate the amount of available memory by probing with mallocs. */
+/* We may under-estimate by a lot, but that's better than winding up with */
+/* a seriously inflated address space. */
+/* This is quite a hack! */
+#define max_malloc_probes 20
+#define malloc_probe_size 64000
+private long
+heap_available(void)
+{ long avail = 0;
+ void *probes[max_malloc_probes];
+ uint n;
+ for ( n = 0; n < max_malloc_probes; n++ )
+ { if ( (probes[n] = malloc(malloc_probe_size)) == 0 )
+ break;
+ if_debug2('a', "[a]heap_available probe[%d]=0x%lx\n",
+ n, (ulong)probes[n]);
+ avail += malloc_probe_size;
+ }
+ while ( n )
+ free(probes[--n]);
+ return avail;
+}
+
+/* Allocate various kinds of blocks. */
+private byte *
+gs_heap_alloc_bytes(gs_memory_t *mem, uint size, client_name_t cname)
+{ byte *ptr;
+ const char *msg = "";
+ /**** MRS - 64-bit align all data structures!!!!!!!!!!! ****/
+ size = (size + 7) & ~7;
+
+ if ( size > max_uint - sizeof(malloc_block)
+ )
+ { /* Can't represent the size in a uint! */
+ msg = "too large for size_t";
+ ptr = 0;
+ }
+ else
+ { ptr = (byte *)malloc(size + sizeof(malloc_block));
+ if ( ptr == 0 )
+ msg = "failed";
+ else
+ { malloc_block *bp = (malloc_block *)ptr;
+ if ( malloc_list )
+ malloc_list->prev = bp;
+ bp->next = malloc_list;
+ bp->prev = 0;
+ bp->size = size;
+ bp->type = &st_bytes;
+ bp->cname = cname;
+ malloc_list = bp;
+ msg = "OK";
+ ptr = (byte *)(bp + 1);
+ gs_alloc_fill(ptr, gs_alloc_fill_alloc, size);
+ malloc_used += size + sizeof(malloc_block);
+ }
+ }
+ if ( gs_debug_c('a') || !*msg )
+ dprintf4("[a]gs_malloc(%s)(%u) = 0x%lx: %s\n",
+ client_name_string(cname),
+ size, (ulong)ptr, msg);
+ return ptr;
+}
+private void *
+gs_heap_alloc_struct(gs_memory_t *mem, gs_memory_type_ptr_t pstype,
+ client_name_t cname)
+{ void *ptr = gs_heap_alloc_bytes(mem, gs_struct_type_size(pstype), cname);
+ if ( ptr == 0 )
+ return 0;
+ ((malloc_block *)ptr)[-1].type = pstype;
+ return ptr;
+}
+private byte *
+gs_heap_alloc_byte_array(gs_memory_t *mem, uint num_elements, uint elt_size,
+ client_name_t cname)
+{ ulong lsize = (ulong)num_elements * elt_size;
+ if ( lsize != (uint)lsize )
+ return 0;
+ return gs_heap_alloc_bytes(mem, (uint)lsize, cname);
+}
+private void *
+gs_heap_alloc_struct_array(gs_memory_t *mem, uint num_elements,
+ gs_memory_type_ptr_t pstype, client_name_t cname)
+{ void *ptr = gs_heap_alloc_byte_array(mem, num_elements, gs_struct_type_size(pstype), cname);
+ if ( ptr == 0 )
+ return 0;
+ ((malloc_block *)ptr)[-1].type = pstype;
+ return ptr;
+}
+private void *
+gs_heap_resize_object(gs_memory_t *mem, void *obj, uint new_num_elements,
+ client_name_t cname)
+{ malloc_block *ptr = (malloc_block *)obj - 1;
+ gs_memory_type_ptr_t pstype = ptr->type;
+ uint old_size = gs_object_size(mem, obj) + sizeof(malloc_block);
+ uint new_size = gs_struct_type_size(pstype) * new_num_elements +
+ sizeof(malloc_block);
+ malloc_block *new_ptr = (malloc_block *)gs_realloc(ptr, old_size, new_size);
+
+ if ( new_ptr == 0 )
+ return 0;
+ if ( new_ptr->prev )
+ new_ptr->prev->next = new_ptr;
+ else
+ malloc_list = new_ptr;
+ if ( new_ptr->next )
+ new_ptr->next->prev = new_ptr;
+ new_ptr->size = new_size - sizeof(malloc_block);
+ malloc_used -= old_size;
+ malloc_used += new_size;
+ if ( new_size > old_size )
+ gs_alloc_fill((byte *)new_ptr + old_size,
+ gs_alloc_fill_alloc, new_size - old_size);
+ return new_ptr + 1;
+}
+private uint
+gs_heap_object_size(gs_memory_t *mem, const void *ptr)
+{ return ((const malloc_block *)ptr)[-1].size;
+}
+private gs_memory_type_ptr_t
+gs_heap_object_type(gs_memory_t *mem, const void *ptr)
+{ return ((const malloc_block *)ptr)[-1].type;
+}
+private void
+gs_heap_free_object(gs_memory_t *mem, void *ptr, client_name_t cname)
+{ malloc_block *bp = malloc_list;
+ if ( gs_debug_c('a') )
+ dprintf3("[a]gs_free(%s) 0x%lx(%u)\n",
+ client_name_string(cname), (ulong)ptr,
+ (ptr == 0 ? 0 : ((malloc_block *)ptr)[-1].size));
+ if ( ptr == 0 )
+ return;
+ if ( ptr == bp + 1 )
+ { malloc_list = bp->next;
+ malloc_used -= bp->size + sizeof(malloc_block);
+ gs_alloc_fill(bp + 1, gs_alloc_fill_free, bp->size);
+ free(bp);
+ }
+ else
+ { malloc_block *np;
+ for ( ; (np = bp->next) != 0; bp = np )
+ { if ( ptr == np + 1 )
+ { bp->next = np->next;
+ malloc_used -= np->size + sizeof(malloc_block);
+ gs_alloc_fill(np + 1, gs_alloc_fill_free, np->size);
+ free(np);
+ return;
+ }
+ }
+ lprintf2("%s: free 0x%lx not found!\n",
+ client_name_string(cname), (ulong)ptr);
+ free((char *)((malloc_block *)ptr - 1));
+ }
+}
+private byte *
+gs_heap_alloc_string(gs_memory_t *mem, uint nbytes, client_name_t cname)
+{ return gs_heap_alloc_bytes(mem, nbytes, cname);
+}
+private byte *
+gs_heap_resize_string(gs_memory_t *mem, byte *data, uint old_num, uint new_num,
+ client_name_t cname)
+{ if ( gs_heap_object_type(mem, data) != &st_bytes )
+ { lprintf2("%s: resizing non-string 0x%lx!\n",
+ client_name_string(cname), (ulong)data);
+ }
+ return gs_heap_resize_object(mem, data, new_num, cname);
+}
+private void
+gs_heap_free_string(gs_memory_t *mem, byte *data, uint nbytes,
+ client_name_t cname)
+{ /****** SHOULD CHECK SIZE IF DEBUGGING ******/
+ gs_heap_free_object(mem, data, cname);
+}
+private void
+gs_heap_register_root(gs_memory_t *mem, gs_gc_root_t *rp, gs_ptr_type_t ptype,
+ void **up, client_name_t cname)
+{
+}
+private void
+gs_heap_unregister_root(gs_memory_t *mem, gs_gc_root_t *rp,
+ client_name_t cname)
+{
+}
+private void
+gs_heap_status(gs_memory_t *mem, gs_memory_status_t *pstat)
+{ pstat->allocated = malloc_used + heap_available();
+ pstat->used = malloc_used;
+}
+private void
+gs_heap_enable_free(gs_memory_t *mem, bool enable)
+{ if ( enable )
+ mem->procs.free_object = gs_heap_free_object,
+ mem->procs.free_string = gs_heap_free_string;
+ else
+ mem->procs.free_object = gs_ignore_free_object,
+ mem->procs.free_string = gs_ignore_free_string;
+}
+
+/* Release all malloc'ed blocks. */
+void
+gs_malloc_release(void)
+{ malloc_block *bp = malloc_list;
+ malloc_block *np;
+ for ( ; bp != 0; bp = np )
+ { np = bp->next;
+ if ( gs_debug_c('a') )
+ dprintf3("[a]gs_malloc_release(%s) 0x%lx(%u)\n",
+ client_name_string(bp->cname), (ulong)(bp + 1),
+ bp->size);
+ gs_alloc_fill(bp + 1, gs_alloc_fill_free, bp->size);
+ free(bp);
+ }
+ malloc_list = 0;
+ malloc_used = 0;
+}
+
+/* ------ Other memory management ------ */
+
+/* No-op freeing procedures */
+void
+gs_ignore_free_object(gs_memory_t *mem, void *data, client_name_t cname)
+{
+}
+void
+gs_ignore_free_string(gs_memory_t *mem, byte *data, uint nbytes,
+ client_name_t cname)
+{
+}
+
+/* No-op pointer enumeration procedure */
+gs_ptr_type_t
+gs_no_struct_enum_ptrs(void *vptr, uint size, uint index, void **pep)
+{ return 0;
+}
+
+/* No-op pointer relocation procedure */
+void
+gs_no_struct_reloc_ptrs(void *vptr, uint size, gc_state_t *gcst)
+{
+}
+
+/* Get the size of a structure from the descriptor. */
+uint
+gs_struct_type_size(gs_memory_type_ptr_t pstype)
+{ return pstype->ssize;
+}
+
+/* Get the name of a structure from the descriptor. */
+struct_name_t
+gs_struct_type_name(gs_memory_type_ptr_t pstype)
+{ return pstype->sname;
+}
+
+/* Normal freeing routine for reference-counted structures. */
+void
+rc_free_struct_only(gs_memory_t *mem, void *data, client_name_t cname)
+{ gs_free_object(mem, data, cname);
+}
diff --git a/pstoraster/gsmemory.h b/pstoraster/gsmemory.h
new file mode 100644
index 000000000..2f0c575bc
--- /dev/null
+++ b/pstoraster/gsmemory.h
@@ -0,0 +1,355 @@
+/* Copyright (C) 1993, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gsmemory.h */
+/* Client interface for memory allocation */
+
+/*
+ * The allocator knows about two basic kinds of memory: objects, which are
+ * aligned and cannot have pointers to their interior, and strings, which are
+ * not aligned and which can have interior references.
+ *
+ * The standard allocator includes a garbage collector. The garbage
+ * collector normally may move objects, relocating pointers to them;
+ * however, objects may be declared immovable at the time of allocation.
+ * Clients must not attempt to resize immovable objects, and must not create
+ * references to substrings of immovable strings.
+ */
+
+#ifndef gsmemory_INCLUDED
+# define gsmemory_INCLUDED
+
+/*
+ * Define the (opaque) type for a structure descriptor. */
+typedef struct gs_memory_struct_type_s gs_memory_struct_type_t;
+typedef const gs_memory_struct_type_t *gs_memory_type_ptr_t;
+
+ /* Accessors for structure types. */
+
+typedef client_name_t struct_name_t;
+
+/* Get the size of a structure from the descriptor. */
+uint gs_struct_type_size(P1(gs_memory_type_ptr_t));
+
+/* Get the name of a structure from the descriptor. */
+struct_name_t gs_struct_type_name(P1(gs_memory_type_ptr_t));
+#define gs_struct_type_name_string(styp)\
+ ((const char *)gs_struct_type_name(styp))
+
+/* An opaque type for the garbage collector state. */
+/* We need this because it is passed to pointer implementation procedures. */
+typedef struct gc_state_s gc_state_t;
+
+/*
+ * A pointer type defines how to mark the referent of the pointer.
+ * We define it here so that we can define ptr_struct_type,
+ * which is needed so we can define gs_register_struct_root.
+ */
+typedef struct gs_ptr_procs_s {
+
+ /* Unmark the referent of a pointer. */
+
+#define ptr_proc_unmark(proc)\
+ void proc(P2(void *, gc_state_t *))
+ ptr_proc_unmark((*unmark));
+
+ /* Mark the referent of a pointer. */
+ /* Return true iff it was unmarked before. */
+
+#define ptr_proc_mark(proc)\
+ bool proc(P2(void *, gc_state_t *))
+ ptr_proc_mark((*mark));
+
+ /* Relocate a pointer. */
+ /* Note that the argument is const, but the */
+ /* return value is not: this shifts the compiler */
+ /* 'discarding const' warning from the call sites */
+ /* (the reloc_ptr routines) to the implementations. */
+
+#define ptr_proc_reloc(proc, typ)\
+ typ *proc(P2(const typ *, gc_state_t *))
+ ptr_proc_reloc((*reloc), void);
+
+} gs_ptr_procs_t;
+typedef const gs_ptr_procs_t _ds *gs_ptr_type_t;
+
+/* Define the pointer type for ordinary structure pointers. */
+extern const gs_ptr_procs_t ptr_struct_procs;
+#define ptr_struct_type (&ptr_struct_procs)
+
+/* Define the pointer types for a pointer to a gs_[const_]string. */
+extern const gs_ptr_procs_t ptr_string_procs;
+#define ptr_string_type (&ptr_string_procs)
+extern const gs_ptr_procs_t ptr_const_string_procs;
+#define ptr_const_string_type (&ptr_const_string_procs)
+
+/* Register a structure root. */
+#define gs_register_struct_root(mem, root, pp, cname)\
+ gs_register_root(mem, root, ptr_struct_type, pp, cname)
+
+/*
+ * Define the type for a GC root.
+ */
+typedef struct gs_gc_root_s gs_gc_root_t;
+struct gs_gc_root_s {
+ gs_gc_root_t *next;
+ gs_ptr_type_t ptype;
+ void **p;
+};
+
+/* Print a root debugging message. */
+#define if_debug_root(c, msg, rp)\
+ if_debug4(c, "%s 0x%lx: 0x%lx -> 0x%lx\n",\
+ msg, (ulong)(rp), (ulong)(rp)->p, (ulong)*(rp)->p)
+
+/* Define the type for memory manager statistics. */
+typedef struct gs_memory_status_s {
+ /*
+ * "Allocated" space is the total amount of space acquired from
+ * the parent of the memory manager. It includes space used for
+ * allocated data, space available for allocation, and overhead.
+ */
+ ulong allocated;
+ /*
+ * "Used" space is the amount of space used by allocated data
+ * plus overhead.
+ */
+ ulong used;
+} gs_memory_status_t;
+
+/*
+ * Define the memory manager procedural interface.
+ */
+struct gs_memory_s;
+typedef struct gs_memory_s gs_memory_t;
+typedef struct gs_memory_procs_s {
+
+ /*
+ * Allocate bytes. The bytes are always aligned maximally
+ * if the processor requires alignment.
+ */
+
+#define gs_memory_proc_alloc_bytes(proc)\
+ byte *proc(P3(gs_memory_t *mem, uint nbytes, client_name_t cname))
+#define gs_alloc_bytes(mem, nbytes, cname)\
+ (*(mem)->procs.alloc_bytes)(mem, nbytes, cname)
+ gs_memory_proc_alloc_bytes((*alloc_bytes));
+#define gs_alloc_bytes_immovable(mem, nbytes, cname)\
+ (*(mem)->procs.alloc_bytes_immovable)(mem, nbytes, cname)
+ gs_memory_proc_alloc_bytes((*alloc_bytes_immovable));
+
+ /*
+ * Allocate a structure.
+ */
+
+#define gs_memory_proc_alloc_struct(proc)\
+ void *proc(P3(gs_memory_t *mem, gs_memory_type_ptr_t pstype,\
+ client_name_t cname))
+#define gs_alloc_struct(mem, typ, pstype, cname)\
+ (typ *)(*(mem)->procs.alloc_struct)(mem, pstype, cname)
+ gs_memory_proc_alloc_struct((*alloc_struct));
+#define gs_alloc_struct_immovable(mem, typ, pstype, cname)\
+ (typ *)(*(mem)->procs.alloc_struct_immovable)(mem, pstype, cname)
+ gs_memory_proc_alloc_struct((*alloc_struct_immovable));
+
+ /*
+ * Allocate an array of bytes.
+ */
+
+#define gs_memory_proc_alloc_byte_array(proc)\
+ byte *proc(P4(gs_memory_t *mem, uint num_elements, uint elt_size,\
+ client_name_t cname))
+#define gs_alloc_byte_array(mem, nelts, esize, cname)\
+ (*(mem)->procs.alloc_byte_array)(mem, nelts, esize, cname)
+ gs_memory_proc_alloc_byte_array((*alloc_byte_array));
+#define gs_alloc_byte_array_immovable(mem, nelts, esize, cname)\
+ (*(mem)->procs.alloc_byte_array_immovable)(mem, nelts, esize, cname)
+ gs_memory_proc_alloc_byte_array((*alloc_byte_array_immovable));
+
+ /*
+ * Allocate an array of structures.
+ */
+
+#define gs_memory_proc_alloc_struct_array(proc)\
+ void *proc(P4(gs_memory_t *mem, uint num_elements,\
+ gs_memory_type_ptr_t pstype, client_name_t cname))
+#define gs_alloc_struct_array(mem, nelts, typ, pstype, cname)\
+ (typ *)(*(mem)->procs.alloc_struct_array)(mem, nelts, pstype, cname)
+ gs_memory_proc_alloc_struct_array((*alloc_struct_array));
+#define gs_alloc_struct_array_immovable(mem, nelts, typ, pstype, cname)\
+ (typ *)(*(mem)->procs.alloc_struct_array_immovable)(mem, nelts, pstype, cname)
+ gs_memory_proc_alloc_struct_array((*alloc_struct_array_immovable));
+
+ /*
+ * Resize an object to a new number of elements, considering
+ * it as an array of bytes or structures. The new size may
+ * be either larger or smaller than the old.
+ */
+
+#define gs_memory_proc_resize_object(proc)\
+ void *proc(P4(gs_memory_t *mem, void *obj, uint new_num_elements,\
+ client_name_t cname))
+#define gs_resize_object(mem, obj, newn, cname)\
+ (*(mem)->procs.resize_object)(mem, obj, newn, cname)
+ gs_memory_proc_resize_object((*resize_object));
+
+ /*
+ * Get the size of an object (anything except a string).
+ */
+
+#define gs_memory_proc_object_size(proc)\
+ uint proc(P2(gs_memory_t *mem, const void *obj))
+#define gs_object_size(mem, obj)\
+ (*(mem)->procs.object_size)(mem, obj)
+ gs_memory_proc_object_size((*object_size));
+
+ /*
+ * Get the type of an object (anything except a string).
+ * The value returned for byte objects is useful only for
+ * printing.
+ */
+
+#define gs_memory_proc_object_type(proc)\
+ gs_memory_type_ptr_t proc(P2(gs_memory_t *mem, const void *obj))
+#define gs_object_type(mem, obj)\
+ (*(mem)->procs.object_type)(mem, obj)
+ gs_memory_proc_object_type((*object_type));
+
+ /*
+ * Free an object (anything except a string).
+ * Note: data == 0 must be allowed, and must be a no-op.
+ */
+
+#define gs_memory_proc_free_object(proc)\
+ void proc(P3(gs_memory_t *mem, void *data, client_name_t cname))
+#define gs_free_object(mem, data, cname)\
+ (*(mem)->procs.free_object)(mem, data, cname)
+ gs_memory_proc_free_object((*free_object));
+
+ /*
+ * Allocate a string (unaligned bytes).
+ */
+
+#define gs_memory_proc_alloc_string(proc)\
+ byte *proc(P3(gs_memory_t *mem, uint nbytes, client_name_t cname))
+#define gs_alloc_string(mem, nbytes, cname)\
+ (*(mem)->procs.alloc_string)(mem, nbytes, cname)
+ gs_memory_proc_alloc_string((*alloc_string));
+#define gs_alloc_string_immovable(mem, nbytes, cname)\
+ (*(mem)->procs.alloc_string_immovable)(mem, nbytes, cname)
+ gs_memory_proc_alloc_string((*alloc_string_immovable));
+
+ /*
+ * Resize a string.
+ */
+
+#define gs_memory_proc_resize_string(proc)\
+ byte *proc(P5(gs_memory_t *mem, byte *data, uint old_num, uint new_num,\
+ client_name_t cname))
+#define gs_resize_string(mem, data, oldn, newn, cname)\
+ (*(mem)->procs.resize_string)(mem, data, oldn, newn, cname)
+ gs_memory_proc_resize_string((*resize_string));
+
+ /*
+ * Free a string.
+ */
+
+#define gs_memory_proc_free_string(proc)\
+ void proc(P4(gs_memory_t *mem, byte *data, uint nbytes,\
+ client_name_t cname))
+#define gs_free_string(mem, data, nbytes, cname)\
+ (*(mem)->procs.free_string)(mem, data, nbytes, cname)
+ gs_memory_proc_free_string((*free_string));
+
+ /*
+ * Register a root for the garbage collector.
+ */
+
+#define gs_memory_proc_register_root(proc)\
+ void proc(P5(gs_memory_t *mem, gs_gc_root_t *root, gs_ptr_type_t ptype,\
+ void **pp, client_name_t cname))
+#define gs_register_root(mem, root, ptype, pp, cname)\
+ (*(mem)->procs.register_root)(mem, root, ptype, pp, cname)
+ gs_memory_proc_register_root((*register_root));
+
+ /*
+ * Unregister a root.
+ */
+
+#define gs_memory_proc_unregister_root(proc)\
+ void proc(P3(gs_memory_t *mem, gs_gc_root_t *root, client_name_t cname))
+#define gs_unregister_root(mem, root, cname)\
+ (*(mem)->procs.unregister_root)(mem, root, cname)
+ gs_memory_proc_unregister_root((*unregister_root));
+
+ /*
+ * Report status (assigned, used).
+ */
+
+#define gs_memory_proc_status(proc)\
+ void proc(P2(gs_memory_t *mem, gs_memory_status_t *status))
+#define gs_memory_status(mem, pst)\
+ (*(mem)->procs.status)(mem, pst)
+ gs_memory_proc_status((*status));
+
+ /*
+ * Enable or disable the freeing operations: when disabled,
+ * these operations return normally but do nothing. The
+ * garbage collector and the PostScript interpreter
+ * 'restore' operator need to temporarily disable the
+ * freeing functions of (an) allocator(s) while running
+ * finalization procedures.
+ */
+
+#define gs_memory_proc_enable_free(proc)\
+ void proc(P2(gs_memory_t *mem, bool enable))
+#define gs_enable_free(mem, enable)\
+ (*(mem)->procs.enable_free)(mem, enable)
+ gs_memory_proc_enable_free((*enable_free));
+
+} gs_memory_procs_t;
+
+/* Define no-op freeing procedures for use by enable_free. */
+gs_memory_proc_free_object(gs_ignore_free_object);
+gs_memory_proc_free_string(gs_ignore_free_string);
+
+/*
+ * An allocator instance. Subclasses may have state as well.
+ */
+#define gs_memory_common\
+ gs_memory_procs_t procs
+struct gs_memory_s {
+ gs_memory_common;
+};
+
+/*
+ * gs_malloc and gs_free are historical artifacts, but we still need
+ * a memory manager instance that allocates directly from the C heap.
+ */
+extern gs_memory_t gs_memory_default;
+#define gs_malloc(nelts, esize, cname)\
+ (void *)gs_alloc_byte_array(&gs_memory_default, nelts, esize, cname)
+#define gs_free(data, nelts, esize, cname)\
+ gs_free_object(&gs_memory_default, data, cname)
+
+#endif /* gsmemory_INCLUDED */
diff --git a/pstoraster/gsmisc.c b/pstoraster/gsmisc.c
new file mode 100644
index 000000000..098dc5e55
--- /dev/null
+++ b/pstoraster/gsmisc.c
@@ -0,0 +1,649 @@
+/*
+ Copyright 1993-1999 by Easy Software Products.
+ Copyright (C) 1989, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gsmisc.c */
+/* Miscellaneous utilities for Ghostscript library */
+#include "malloc_.h"
+#include "math_.h"
+#include "memory_.h"
+#include "gx.h"
+#include "gpcheck.h" /* for gs_return_check_interrupt */
+#include "gserrors.h"
+#include "gconfigv.h" /* for USE_ASM */
+#include "gxfarith.h"
+#include "gxfixed.h"
+
+/* Define private replacements for stdin, stdout, and stderr. */
+FILE *gs_stdin, *gs_stdout, *gs_stderr;
+/* Ghostscript writes debugging output to gs_debug_out. */
+/* We define gs_debug and gs_debug_out even if DEBUG isn't defined, */
+/* so that we can compile individual modules with DEBUG set. */
+char gs_debug[128];
+FILE *gs_debug_out;
+
+/* Define eprintf_program_name and lprintf_file_and_line as procedures */
+/* so one can set breakpoints on them. */
+void
+eprintf_program_name(FILE *f, const char *program_name)
+{ fprintf(f, "%s: ", program_name);
+}
+void
+lprintf_file_and_line(FILE *f, const char *file, int line)
+{ fprintf(f, "%s(%d): ", file, line);
+}
+
+/* Log an error return. We always include this, in case other */
+/* modules were compiled with DEBUG set. */
+#undef gs_log_error /* in case DEBUG isn't set */
+int
+gs_log_error(int err, const char _ds *file, int line)
+{ if ( gs_log_errors )
+ { if ( file == NULL )
+ dprintf1("Returning error %d.\n", err);
+ else
+ dprintf3("%s(%d): Returning error %d.\n",
+ (const char *)file, line, err);
+ }
+ return err;
+}
+
+/* Check for interrupts before a return. */
+int
+gs_return_check_interrupt(int code)
+{ if ( code < 0 )
+ return code;
+ { int icode = gp_check_interrupts();
+ return (icode == 0 ? 0 :
+ gs_note_error((icode > 0 ? gs_error_interrupt : icode)));
+ }
+}
+
+/* ------ Substitutes for missing C library functions ------ */
+
+#ifdef memory__need_memmove /* see memory_.h */
+/* Copy bytes like memcpy, guaranteed to handle overlap correctly. */
+/* ANSI C defines the returned value as being the src argument, */
+/* but with the const restriction removed! */
+void *
+gs_memmove(void *dest, const void *src, size_t len)
+{ if ( !len )
+ return (void *)src;
+#define bdest ((byte *)dest)
+#define bsrc ((const byte *)src)
+ /* We use len-1 for comparisons because adding len */
+ /* might produce an offset overflow on segmented systems. */
+ if ( ptr_le(bdest, bsrc) )
+ { register byte *end = bdest + (len - 1);
+ if ( ptr_le(bsrc, end) )
+ { /* Source overlaps destination from above. */
+ register const byte *from = bsrc;
+ register byte *to = bdest;
+ for ( ; ; )
+ { *to = *from;
+ if ( to >= end ) /* faster than = */
+ return (void *)src;
+ to++; from++;
+ }
+ }
+ }
+ else
+ { register const byte *from = bsrc + (len - 1);
+ if ( ptr_le(bdest, from) )
+ { /* Source overlaps destination from below. */
+ register const byte *end = bsrc;
+ register byte *to = bdest + (len - 1);
+ for ( ; ; )
+ { *to = *from;
+ if ( from <= end ) /* faster than = */
+ return (void *)src;
+ to--; from--;
+ }
+ }
+ }
+#undef bdest
+#undef bsrc
+ /* No overlap, it's safe to use memcpy. */
+ memcpy(dest, src, len);
+ return (void *)src;
+}
+#endif
+
+#ifdef memory__need_memchr /* see memory_.h */
+/* ch should obviously be char rather than int, */
+/* but the ANSI standard declaration uses int. */
+const char *
+gs_memchr(const char *ptr, int ch, size_t len)
+{ if ( len > 0 )
+ { register const char *p = ptr;
+ register uint count = len;
+ do { if ( *p == (char)ch ) return p; p++; } while ( --count );
+ }
+ return 0;
+}
+#endif
+
+#ifdef memory__need_memset /* see memory_.h */
+/* ch should obviously be char rather than int, */
+/* but the ANSI standard declaration uses int. */
+void *
+gs_memset(void *dest, register int ch, size_t len)
+{ if ( ch == 0 )
+ bzero(dest, len);
+ else if ( len > 0 )
+ { register char *p = dest;
+ register uint count = len;
+ do { *p++ = (char)ch; } while ( --count );
+ }
+ return dest;
+}
+#endif
+
+#ifdef malloc__need_realloc /* see malloc_.h */
+/* Some systems have non-working implementations of realloc. */
+void *
+gs_realloc(void *old_ptr, size_t old_size, size_t new_size)
+{ void *new_ptr;
+ /**** MRS - 64-bit align all data structures!!!!!!!!!!! ****/
+ new_size = (new_size + 7) & ~7;
+ if ( new_size )
+ { new_ptr = malloc(new_size);
+ if ( new_ptr == NULL )
+ return NULL;
+ }
+ else
+ new_ptr = NULL;
+ /* We have to pass in the old size, since we have no way to */
+ /* determine it otherwise. */
+ if ( old_ptr != NULL )
+ { if ( new_ptr != NULL )
+ memcpy(new_ptr, old_ptr, min(old_size, new_size));
+ free(old_ptr);
+ }
+ return new_ptr;
+}
+#endif
+
+/* ------ Debugging support ------ */
+
+/* Dump a region of memory. */
+void
+debug_dump_bytes(const byte *from, const byte *to, const char *msg)
+{ const byte *p = from;
+ if ( from < to && msg )
+ dprintf1("%s:\n", msg);
+ while ( p != to )
+ { const byte *q = min(p + 16, to);
+ dprintf1("0x%lx:", (ulong)p);
+ while ( p != q ) dprintf1(" %02x", *p++);
+ dputc('\n');
+ }
+}
+
+/* Dump a bitmap. */
+void
+debug_dump_bitmap(const byte *bits, uint raster, uint height, const char *msg)
+{ uint y;
+ const byte *data = bits;
+ for ( y = 0; y < height; ++y, data += raster )
+ debug_dump_bytes(data, data + raster, (y == 0 ? msg : NULL));
+}
+
+/* Print a string. */
+void
+debug_print_string(const byte *chrs, uint len)
+{ uint i;
+ for ( i = 0; i < len; i++ )
+ dputc(chrs[i]);
+ fflush(dstderr);
+}
+
+/* ------ Arithmetic ------ */
+
+/* Compute M modulo N. Requires N > 0; guarantees 0 <= imod(M,N) < N, */
+/* regardless of the whims of the % operator for negative operands. */
+int
+imod(int m, int n)
+{ if ( n <= 0 )
+ return 0; /* sanity check */
+ if ( m >= 0 )
+ return m % n;
+ { int r = -m % n;
+ return (r == 0 ? 0 : n - r);
+ }
+}
+
+/* Compute the GCD of two integers. */
+int
+igcd(int x, int y)
+{ int c = x, d = y;
+ if ( c < 0 ) c = -c;
+ if ( d < 0 ) d = -d;
+ while ( c != 0 && d != 0 )
+ if ( c > d ) c %= d;
+ else d %= c;
+ return d + c; /* at most one is non-zero */
+}
+
+#if defined(set_fmul2fixed_vars) && !USE_ASM
+
+/*
+ * Floating multiply with fixed result, for avoiding floating point in
+ * common coordinate transformations. Assumes IEEE representation,
+ * 16-bit short, 32-bit long. Optimized for the case where the first
+ * operand has no more than 16 mantissa bits, e.g., where it is a user space
+ * coordinate (which are often integers).
+ *
+ * The assembly language version of this code is actually faster than
+ * the FPU, if the code is compiled with FPU_TYPE=0 (which requires taking
+ * a trap on every FPU operation). If there is no FPU, the assembly
+ * language version of this code is over 10 times as fast as the emulated FPU.
+ */
+/* Some of the following code has been tweaked for the Borland 16-bit */
+/* compiler. The tweaks do not change the algorithms. */
+#if arch_ints_are_short && !defined(FOR80386)
+# define SHORT_ARITH
+#endif
+int
+set_fmul2fixed_(fixed *pr, long /*float*/ a, long /*float*/ b)
+{
+#ifdef SHORT_ARITH
+# define long_rsh8_ushort(x)\
+ (((ushort)(x) >> 8) | ((ushort)((ulong)(x) >> 16) << 8))
+# define utemp ushort
+#else
+# define long_rsh8_ushort(x) ((ushort)((x) >> 8))
+# define utemp ulong
+#endif
+ /* utemp may be either ushort or ulong. This is OK because */
+ /* we only use ma and mb in multiplications involving */
+ /* a long or ulong operand. */
+ utemp ma = long_rsh8_ushort(a) | 0x8000;
+ utemp mb = long_rsh8_ushort(b) | 0x8000;
+ int e = 260 + _fixed_shift - ((
+ (((uint)((ulong)a >> 16)) & 0x7f80) +
+ (((uint)((ulong)b >> 16)) & 0x7f80)
+ ) >> 7);
+ ulong p1 = ma * (b & 0xff);
+ ulong p = (ulong)ma * mb;
+#define p_bits (sizeof(p) * 8)
+
+ if ( (byte)a ) /* >16 mantissa bits */
+ { ulong p2 = (a & 0xff) * mb;
+ p += ((((uint)(byte)a * (uint)(byte)b) >> 8) + p1 + p2) >> 8;
+ }
+ else
+ p += p1 >> 8;
+ if ( (uint)e < p_bits ) /* e = -1 is possible */
+ p >>= e;
+ else if ( e >= p_bits ) /* also detects a=0 or b=0 */
+ { *pr = fixed_0;
+ return 0;
+ }
+ else if ( e >= -(p_bits - 1) || p >= 1L << (p_bits - 1 + e) )
+ return_error(gs_error_limitcheck);
+ else
+ p <<= -e;
+ *pr = ((a ^ b) < 0 ? -p : p);
+ return 0;
+}
+int
+set_dfmul2fixed_(fixed *pr, ulong /*double lo*/ xalo, long /*float*/ b, long /*double hi*/ xahi)
+{
+#ifdef SHORT_ARITH
+# define long_lsh3(x) ((((x) << 1) << 1) << 1)
+# define long_rsh(x,ng16) ((uint)((x) >> 16) >> (ng16 - 16))
+#else
+# define long_lsh3(x) ((x) << 3)
+# define long_rsh(x,ng16) ((x) >> ng16)
+#endif
+ return set_fmul2fixed_(pr,
+ (xahi & 0xc0000000) +
+ (long_lsh3(xahi) & 0x3ffffff8) +
+ long_rsh(xalo, 29),
+ b);
+}
+
+#endif
+
+#ifdef set_float2fixed_vars
+
+/*
+ * Convert from floating point to fixed point with scaling.
+ * These are efficient algorithms for FPU-less machines.
+ */
+#define fbits_float 23
+#define fbits_double 20
+int
+set_float2fixed_(fixed *pr, long /*float*/ vf, int frac_bits)
+{ fixed mantissa;
+ int shift;
+
+ if ( !(vf & 0x7f800000) )
+ { *pr = fixed_0;
+ return 0;
+ }
+ mantissa = (fixed)((vf & 0x7fffff) | 0x800000);
+ shift = ((vf >> 23) & 255) - (127 + 23) + frac_bits;
+ if ( shift >= 0 )
+ { if ( shift > sizeof(fixed) * 8 - 24 )
+ return_error(gs_error_limitcheck);
+ if ( vf < 0 )
+ mantissa = -mantissa;
+ *pr = (fixed)(mantissa << shift);
+ }
+ else
+ *pr = (shift < -24 ? fixed_0 :
+ vf < 0 ? -(fixed)(mantissa >> -shift) : /* truncate */
+ (fixed)(mantissa >> -shift));
+ return 0;
+}
+int
+set_double2fixed_(fixed *pr, ulong /*double lo*/ lo,
+ long /*double hi*/ hi, int frac_bits)
+{ fixed mantissa;
+ int shift;
+
+ if ( !(hi & 0x7ff00000) )
+ { *pr = fixed_0;
+ return 0;
+ }
+ /* We only use 31 bits of mantissa even if sizeof(long) > 4. */
+ mantissa = (fixed)(((hi & 0xfffff) << 10) | (lo >> 22) | 0x40000000);
+ shift = ((hi >> 20) & 2047) - (1023 + 30) + frac_bits;
+ if ( shift > 0 )
+ return_error(gs_error_limitcheck);
+ *pr = (shift < -30 ? fixed_0 :
+ hi < 0 ? -(fixed)(mantissa >> -shift) : /* truncate */
+ (fixed)(mantissa >> -shift));
+ return 0;
+}
+static const byte f2f_shifts[] =
+ { 4, 3, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 };
+#define f2f(x, v, f, fbits)\
+ if ( x < 0 )\
+ f = 0xc0000000 + ((31-fbits-1) << fbits), v = -x;\
+ else\
+ f = 0x40000000 + ((31-fbits-1) << fbits), v = x;\
+ if ( v < 0x4000 )\
+ v <<= 14, f -= 14 << fbits;\
+ if ( v < 0x400000 )\
+ v <<= 8, f -= 8 << fbits;\
+ if ( v < 0x4000000 )\
+ v <<= 4, f -= 4 << fbits;\
+ { int shift = f2f_shifts[v >> 27];\
+ v <<= shift, f -= shift << fbits;\
+ }
+long
+fixed2float_(fixed x, int frac_bits)
+{ fixed v;
+ long f;
+
+ if ( x == 0 )
+ return 0;
+ f2f(x, v, f, fbits_float);
+ return f + ((v + 0x40) >> 7);
+}
+void
+set_fixed2double_(double *pd, fixed x, int frac_bits)
+{ fixed v;
+ long f;
+
+ if ( x == 0 )
+ { ((long *)pd)[1 - arch_is_big_endian] = 0;
+ ((ulong *)pd)[arch_is_big_endian] = 0;
+ }
+ else
+ { f2f(x, v, f, fbits_double);
+ ((long *)pd)[1 - arch_is_big_endian] = f + (v >> 11);
+ ((ulong *)pd)[arch_is_big_endian] = (ulong)(v << 21);
+ }
+}
+
+#endif
+
+/* Trace calls on sqrt when debugging. */
+#undef sqrt
+extern double sqrt(P1(double));
+double
+gs_sqrt(double x, const char *file, int line)
+{ if ( gs_debug_c('~') )
+ { fprintf(stdout, "[~]sqrt(%g) at %s:%d\n",
+ x, (const char *)file, line);
+ fflush(stdout);
+ }
+ return sqrt(x);
+}
+
+/*
+ * Define sine and cosine functions that take angles in degrees rather than
+ * radians, and that are implemented efficiently on machines with slow
+ * (or no) floating point.
+ */
+#if USE_FPU < 0 /****** maybe should be <= 0 ? ******/
+
+#define sin0 0.00000000000000000
+#define sin1 0.01745240643728351
+#define sin2 0.03489949670250097
+#define sin3 0.05233595624294383
+#define sin4 0.06975647374412530
+#define sin5 0.08715574274765817
+#define sin6 0.10452846326765346
+#define sin7 0.12186934340514748
+#define sin8 0.13917310096006544
+#define sin9 0.15643446504023087
+#define sin10 0.17364817766693033
+#define sin11 0.19080899537654480
+#define sin12 0.20791169081775931
+#define sin13 0.22495105434386498
+#define sin14 0.24192189559966773
+#define sin15 0.25881904510252074
+#define sin16 0.27563735581699916
+#define sin17 0.29237170472273671
+#define sin18 0.30901699437494740
+#define sin19 0.32556815445715670
+#define sin20 0.34202014332566871
+#define sin21 0.35836794954530027
+#define sin22 0.37460659341591201
+#define sin23 0.39073112848927377
+#define sin24 0.40673664307580015
+#define sin25 0.42261826174069944
+#define sin26 0.43837114678907740
+#define sin27 0.45399049973954675
+#define sin28 0.46947156278589081
+#define sin29 0.48480962024633706
+#define sin30 0.50000000000000000
+#define sin31 0.51503807491005416
+#define sin32 0.52991926423320490
+#define sin33 0.54463903501502708
+#define sin34 0.55919290347074679
+#define sin35 0.57357643635104605
+#define sin36 0.58778525229247314
+#define sin37 0.60181502315204827
+#define sin38 0.61566147532565829
+#define sin39 0.62932039104983739
+#define sin40 0.64278760968653925
+#define sin41 0.65605902899050728
+#define sin42 0.66913060635885824
+#define sin43 0.68199836006249848
+#define sin44 0.69465837045899725
+#define sin45 0.70710678118654746
+#define sin46 0.71933980033865108
+#define sin47 0.73135370161917046
+#define sin48 0.74314482547739413
+#define sin49 0.75470958022277201
+#define sin50 0.76604444311897801
+#define sin51 0.77714596145697090
+#define sin52 0.78801075360672190
+#define sin53 0.79863551004729283
+#define sin54 0.80901699437494745
+#define sin55 0.81915204428899180
+#define sin56 0.82903757255504174
+#define sin57 0.83867056794542394
+#define sin58 0.84804809615642596
+#define sin59 0.85716730070211222
+#define sin60 0.86602540378443860
+#define sin61 0.87461970713939574
+#define sin62 0.88294759285892688
+#define sin63 0.89100652418836779
+#define sin64 0.89879404629916704
+#define sin65 0.90630778703664994
+#define sin66 0.91354545764260087
+#define sin67 0.92050485345244037
+#define sin68 0.92718385456678731
+#define sin69 0.93358042649720174
+#define sin70 0.93969262078590832
+#define sin71 0.94551857559931674
+#define sin72 0.95105651629515353
+#define sin73 0.95630475596303544
+#define sin74 0.96126169593831889
+#define sin75 0.96592582628906831
+#define sin76 0.97029572627599647
+#define sin77 0.97437006478523525
+#define sin78 0.97814760073380558
+#define sin79 0.98162718344766398
+#define sin80 0.98480775301220802
+#define sin81 0.98768834059513777
+#define sin82 0.99026806874157036
+#define sin83 0.99254615164132198
+#define sin84 0.99452189536827329
+#define sin85 0.99619469809174555
+#define sin86 0.99756405025982420
+#define sin87 0.99862953475457383
+#define sin88 0.99939082701909576
+#define sin89 0.99984769515639127
+#define sin90 1.00000000000000000
+
+private const double sin_table[361] = {
+ sin0,
+ sin1, sin2, sin3, sin4, sin5, sin6, sin7, sin8, sin9, sin10,
+ sin11, sin12, sin13, sin14, sin15, sin16, sin17, sin18, sin19, sin20,
+ sin21, sin22, sin23, sin24, sin25, sin26, sin27, sin28, sin29, sin30,
+ sin31, sin32, sin33, sin34, sin35, sin36, sin37, sin38, sin39, sin40,
+ sin41, sin42, sin43, sin44, sin45, sin46, sin47, sin48, sin49, sin50,
+ sin51, sin52, sin53, sin54, sin55, sin56, sin57, sin58, sin59, sin60,
+ sin61, sin62, sin63, sin64, sin65, sin66, sin67, sin68, sin69, sin70,
+ sin71, sin72, sin73, sin74, sin75, sin76, sin77, sin78, sin79, sin80,
+ sin81, sin82, sin83, sin84, sin85, sin86, sin87, sin88, sin89, sin90,
+ sin89, sin88, sin87, sin86, sin85, sin84, sin83, sin82, sin81, sin80,
+ sin79, sin78, sin77, sin76, sin75, sin74, sin73, sin72, sin71, sin70,
+ sin69, sin68, sin67, sin66, sin65, sin64, sin63, sin62, sin61, sin60,
+ sin59, sin58, sin57, sin56, sin55, sin54, sin53, sin52, sin51, sin50,
+ sin49, sin48, sin47, sin46, sin45, sin44, sin43, sin42, sin41, sin40,
+ sin39, sin38, sin37, sin36, sin35, sin34, sin33, sin32, sin31, sin30,
+ sin29, sin28, sin27, sin26, sin25, sin24, sin23, sin22, sin21, sin20,
+ sin19, sin18, sin17, sin16, sin15, sin14, sin13, sin12, sin11, sin10,
+ sin9, sin8, sin7, sin6, sin5, sin4, sin3, sin2, sin1, sin0,
+ -sin1, -sin2, -sin3, -sin4, -sin5, -sin6, -sin7, -sin8, -sin9, -sin10,
+-sin11, -sin12, -sin13, -sin14, -sin15, -sin16, -sin17, -sin18, -sin19, -sin20,
+-sin21, -sin22, -sin23, -sin24, -sin25, -sin26, -sin27, -sin28, -sin29, -sin30,
+-sin31, -sin32, -sin33, -sin34, -sin35, -sin36, -sin37, -sin38, -sin39, -sin40,
+-sin41, -sin42, -sin43, -sin44, -sin45, -sin46, -sin47, -sin48, -sin49, -sin50,
+-sin51, -sin52, -sin53, -sin54, -sin55, -sin56, -sin57, -sin58, -sin59, -sin60,
+-sin61, -sin62, -sin63, -sin64, -sin65, -sin66, -sin67, -sin68, -sin69, -sin70,
+-sin71, -sin72, -sin73, -sin74, -sin75, -sin76, -sin77, -sin78, -sin79, -sin80,
+-sin81, -sin82, -sin83, -sin84, -sin85, -sin86, -sin87, -sin88, -sin89, -sin90,
+-sin89, -sin88, -sin87, -sin86, -sin85, -sin84, -sin83, -sin82, -sin81, -sin80,
+-sin79, -sin78, -sin77, -sin76, -sin75, -sin74, -sin73, -sin72, -sin71, -sin70,
+-sin69, -sin68, -sin67, -sin66, -sin65, -sin64, -sin63, -sin62, -sin61, -sin60,
+-sin59, -sin58, -sin57, -sin56, -sin55, -sin54, -sin53, -sin52, -sin51, -sin50,
+-sin49, -sin48, -sin47, -sin46, -sin45, -sin44, -sin43, -sin42, -sin41, -sin40,
+-sin39, -sin38, -sin37, -sin36, -sin35, -sin34, -sin33, -sin32, -sin31, -sin30,
+-sin29, -sin28, -sin27, -sin26, -sin25, -sin24, -sin23, -sin22, -sin21, -sin20,
+-sin19, -sin18, -sin17, -sin16, -sin15, -sin14, -sin13, -sin12, -sin11, -sin10,
+ -sin9, -sin8, -sin7, -sin6, -sin5, -sin4, -sin3, -sin2, -sin1, -sin0
+};
+
+double
+gs_sin_degrees(double ang)
+{ int ipart;
+ if ( is_fneg(ang) )
+ ang = 180 - ang;
+ ipart = (int)ang;
+ if ( ipart >= 360 )
+ { int arem = ipart % 360;
+ ang -= (ipart - arem);
+ ipart = arem;
+ }
+ return
+ (ang == ipart ? sin_table[ipart] :
+ sin_table[ipart] + (sin_table[ipart+1] - sin_table[ipart]) *
+ (ang - ipart));
+}
+
+double
+gs_cos_degrees(double ang)
+{ int ipart;
+ if ( is_fneg(ang) )
+ ang = 90 - ang;
+ else
+ ang += 90;
+ ipart = (int)ang;
+ if ( ipart >= 360 )
+ { int arem = ipart % 360;
+ ang -= (ipart - arem);
+ ipart = arem;
+ }
+ return
+ (ang == ipart ? sin_table[ipart] :
+ sin_table[ipart] + (sin_table[ipart+1] - sin_table[ipart]) *
+ (ang - ipart));
+}
+
+void
+gs_sincos_degrees(double ang, gs_sincos_t *psincos)
+{ psincos->sin = gs_sin_degrees(ang);
+ psincos->cos = gs_cos_degrees(ang);
+}
+
+#else /* we have floating point */
+
+double
+gs_sin_degrees(double ang)
+{ return sin(ang * (M_PI / 180));
+}
+
+double
+gs_cos_degrees(double ang)
+{ return cos(ang * (M_PI / 180));
+}
+
+void
+gs_sincos_degrees(double ang, gs_sincos_t *psincos)
+{ int quads;
+ if ( ang >= -360 && ang <= 360 &&
+ ang == (quads = (int)ang / 90) * 90
+ )
+ { static const int isincos[5] = { 0, 1, 0, -1, 0 };
+ quads &= 3;
+ psincos->sin = isincos[quads];
+ psincos->cos = isincos[quads + 1];
+ }
+ else
+ { psincos->sin = gs_sin_degrees(ang);
+ psincos->cos = gs_cos_degrees(ang);
+ }
+}
+
+#endif /* USE_FPU */
diff --git a/pstoraster/gspaint.c b/pstoraster/gspaint.c
new file mode 100644
index 000000000..4692eba5b
--- /dev/null
+++ b/pstoraster/gspaint.c
@@ -0,0 +1,292 @@
+/* Copyright (C) 1989, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gspaint.c */
+/* Painting procedures for Ghostscript library */
+#include "math_.h" /* for fabs */
+#include "gx.h"
+#include "gpcheck.h"
+#include "gserrors.h"
+#include "gsropt.h" /* for gxpaint.h */
+#include "gxfixed.h"
+#include "gxmatrix.h" /* for gs_state */
+#include "gspaint.h"
+#include "gspath.h"
+#include "gzpath.h"
+#include "gxpaint.h"
+#include "gzstate.h"
+#include "gxdevice.h"
+#include "gxdevmem.h"
+#include "gxcpath.h"
+
+/* Define the nominal size for alpha buffers. */
+#define abuf_nominal_SMALL 500
+#define abuf_nominal_LARGE 2000
+#if arch_small_memory
+# define abuf_nominal abuf_nominal_SMALL
+#else
+# define abuf_nominal\
+ (gs_if_debug_c('.') ? abuf_nominal_SMALL : abuf_nominal_LARGE)
+#endif
+
+/* Erase the page */
+int
+gs_erasepage(gs_state *pgs)
+{ /* We can't just fill with device white; we must take the */
+ /* transfer function into account. */
+ int code;
+ if ( (code = gs_gsave(pgs)) < 0 )
+ return code;
+ if ( (code = gs_setgray(pgs, 1.0)) >= 0 )
+ { /* Fill the page directly, ignoring clipping. */
+ code = gs_fillpage(pgs);
+ }
+ gs_grestore(pgs);
+ return code;
+}
+
+/* Fill the page with the current color. */
+int
+gs_fillpage(gs_state *pgs)
+{ gx_device *dev;
+ int code;
+ gs_logical_operation_t save_lop;
+
+ gx_set_dev_color(pgs);
+ dev = gs_currentdevice(pgs);
+ /* Fill the page directly, ignoring clipping. */
+ /* Use the default RasterOp. */
+ save_lop = pgs->log_op;
+ gs_init_rop(pgs);
+ code = gx_fill_rectangle(0, 0, dev->width, dev->height,
+ pgs->dev_color, pgs);
+ pgs->log_op = save_lop;
+ if ( code < 0 )
+ return code;
+ return (*dev_proc(dev, sync_output))(dev);
+}
+
+/*
+ * Determine the number of bits of alpha buffer for a stroke or fill.
+ * We should do alpha buffering iff this value is >1.
+ */
+private int near
+alpha_buffer_bits(gs_state *pgs)
+{ gx_device *dev;
+
+ if ( !color_is_pure(pgs->dev_color) )
+ return 0;
+ dev = gs_currentdevice_inline(pgs);
+ if ( gs_device_is_abuf(dev) )
+ { /* We're already writing into an alpha buffer. */
+ return 0;
+ }
+ return (*dev_proc(dev, get_alpha_bits))(dev, go_graphics);
+}
+/*
+ * Set up an alpha buffer for a stroke or fill operation. Return 0
+ * if no buffer could be allocated, 1 if a buffer was installed,
+ * or the usual negative error code.
+ *
+ * The fill/stroke code sets up a clipping device if needed; however,
+ * since we scale up all the path coordinates, we either need to scale up
+ * the clipping region, or do clipping after, rather than before,
+ * alpha buffering. Either of these is a little inconvenient, but
+ * the former is less inconvenient.
+ */
+private int near
+alpha_buffer_init(gs_state *pgs, fixed extra_x, fixed extra_y, int alpha_bits)
+{ gx_device *dev = gs_currentdevice_inline(pgs);
+ int log2_alpha_bits;
+ gs_fixed_rect bbox;
+ gs_int_rect ibox;
+ uint width, raster, band_space;
+ uint height;
+ gs_log2_scale_point log2_scale;
+ gs_memory_t *mem;
+ gx_device_memory *mdev;
+
+ log2_alpha_bits = alpha_bits >> 1; /* works for 1,2,4 */
+ log2_scale.x = log2_scale.y = log2_alpha_bits;
+ gx_path_bbox(pgs->path, &bbox);
+ ibox.p.x = fixed2int(bbox.p.x - extra_x) - 1;
+ ibox.p.y = fixed2int(bbox.p.y - extra_y) - 1;
+ ibox.q.x = fixed2int_ceiling(bbox.q.x + extra_x) + 1;
+ ibox.q.y = fixed2int_ceiling(bbox.q.y + extra_y) + 1;
+ width = (ibox.q.x - ibox.p.x) << log2_scale.x;
+ raster = bitmap_raster(width);
+ band_space = raster << log2_scale.y;
+ height = (abuf_nominal / band_space) << log2_scale.y;
+ if ( height == 0 )
+ height = 1 << log2_scale.y;
+ mem = pgs->memory;
+ mdev = gs_alloc_struct(mem, gx_device_memory, &st_device_memory,
+ "alpha_buffer_init");
+ if ( mdev == 0 )
+ return 0; /* if no room, don't buffer */
+ gs_make_mem_abuf_device(mdev, mem, dev, &log2_scale,
+ alpha_bits, ibox.p.x << log2_scale.x);
+ mdev->width = width;
+ mdev->height = height;
+ mdev->bitmap_memory = mem;
+ if ( (*dev_proc(mdev, open_device))((gx_device *)mdev) < 0 )
+ { /* No room for bits, punt. */
+ gs_free_object(mem, mdev, "alpha_buffer_init");
+ return 0;
+ }
+ gx_set_device_only(pgs, (gx_device *)mdev);
+ gx_path_scale_exp2(pgs->path, log2_scale.x, log2_scale.y);
+ gx_cpath_scale_exp2(pgs->clip_path, log2_scale.x, log2_scale.y);
+ return 1;
+}
+
+/* Release an alpha buffer. */
+private void near
+alpha_buffer_release(gs_state *pgs, bool newpath)
+{ gx_device_memory *mdev =
+ (gx_device_memory *)gs_currentdevice_inline(pgs);
+ gx_device *target = mdev->target;
+
+ (*dev_proc(mdev, close_device))((gx_device *)mdev);
+ gs_free_object(mdev->memory, mdev, "alpha_buffer_release");
+ gx_set_device_only(pgs, target);
+ gx_cpath_scale_exp2(pgs->clip_path,
+ -mdev->log2_scale.x, -mdev->log2_scale.y);
+ if ( !(newpath && !pgs->path->shares_segments) )
+ gx_path_scale_exp2(pgs->path,
+ -mdev->log2_scale.x, -mdev->log2_scale.y);
+}
+
+/* Fill the current path using a specified rule. */
+private int near
+fill_with_rule(gs_state *pgs, int rule)
+{ int code;
+ /* If we're inside a charpath, just merge the current path */
+ /* into the parent's path. */
+ if ( pgs->in_charpath )
+ code = gx_path_add_char_path(pgs->show_gstate->path, pgs->path,
+ (gs_char_path_mode)pgs->in_charpath);
+ else
+ { int abits, acode;
+
+ gx_set_dev_color(pgs);
+ code = gx_color_load(pgs->dev_color, pgs);
+ if ( code < 0 )
+ return code;
+ abits = alpha_buffer_bits(pgs);
+ if ( abits > 1 )
+ { acode = alpha_buffer_init(pgs, pgs->fill_adjust.x,
+ pgs->fill_adjust.y, abits);
+ if ( acode < 0 )
+ return acode;
+ }
+ else
+ acode = 0;
+ code = gx_fill_path(pgs->path, pgs->dev_color, pgs, rule,
+ pgs->fill_adjust.x, pgs->fill_adjust.y);
+ if ( acode > 0 )
+ alpha_buffer_release(pgs, code >= 0);
+ if ( code >= 0 )
+ gs_newpath(pgs);
+
+ }
+ return code;
+}
+/* Fill using the winding number rule */
+int
+gs_fill(gs_state *pgs)
+{ return fill_with_rule(pgs, gx_rule_winding_number);
+}
+/* Fill using the even/odd rule */
+int
+gs_eofill(gs_state *pgs)
+{ return fill_with_rule(pgs, gx_rule_even_odd);
+}
+
+/* Stroke the current path */
+int
+gs_stroke(gs_state *pgs)
+{ int code;
+ /* If we're inside a charpath, just merge the current path */
+ /* into the parent's path. */
+ if ( pgs->in_charpath )
+ code = gx_path_add_char_path(pgs->show_gstate->path, pgs->path,
+ (gs_char_path_mode)pgs->in_charpath);
+ else
+ { int abits, acode;
+ float orig_width;
+
+ gx_set_dev_color(pgs);
+ code = gx_color_load(pgs->dev_color, pgs);
+ if ( code < 0 )
+ return code;
+ abits = alpha_buffer_bits(pgs);
+ if ( abits > 1 )
+ { /* Expand the bounding box by the line width. */
+ /* This is expensive to compute, so we only do it */
+ /* if we know we're going to buffer. */
+ float xxyy = fabs(pgs->ctm.xx) + fabs(pgs->ctm.yy);
+ float xyyx = fabs(pgs->ctm.xy) + fabs(pgs->ctm.yx);
+ float new_width =
+ (orig_width = gs_currentlinewidth(pgs)) *
+ (1 << (abits / 2));
+ fixed extra_adjust =
+ float2fixed(max(xxyy, xyyx) * new_width / 2);
+
+ /* Scale up the line width. */
+ if ( extra_adjust < fixed_1 )
+ extra_adjust = fixed_1;
+ acode = alpha_buffer_init(pgs,
+ pgs->fill_adjust.x + extra_adjust,
+ pgs->fill_adjust.y + extra_adjust,
+ abits);
+ if ( acode < 0 )
+ return acode;
+ gs_setlinewidth(pgs, new_width);
+ }
+ else
+ acode = 0;
+ code = gx_stroke_fill(pgs->path, pgs);
+ if ( acode > 0 )
+ alpha_buffer_release(pgs, code >= 0);
+ if ( code >= 0 )
+ gs_newpath(pgs);
+ if ( abits > 1 )
+ gs_setlinewidth(pgs, orig_width);
+ }
+ return code;
+}
+
+/* Compute the stroked outline of the current path */
+int
+gs_strokepath(gs_state *pgs)
+{ gx_path spath;
+ int code;
+ gx_path_init(&spath, pgs->memory);
+ code = gx_stroke_add(pgs->path, &spath, pgs);
+ if ( code < 0 )
+ return code;
+ gx_path_release(pgs->path);
+ *pgs->path = spath;
+ return 0;
+}
diff --git a/pstoraster/gspaint.h b/pstoraster/gspaint.h
new file mode 100644
index 000000000..ea1d84ef1
--- /dev/null
+++ b/pstoraster/gspaint.h
@@ -0,0 +1,36 @@
+/* Copyright (C) 1989, 1992, 1993 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gspaint.h */
+/* Client interface to painting functions */
+/* Requires gsstate.h */
+
+/* Painting */
+int gs_erasepage(P1(gs_state *)),
+ gs_fillpage(P1(gs_state *)),
+ gs_fill(P1(gs_state *)),
+ gs_eofill(P1(gs_state *)),
+ gs_stroke(P1(gs_state *));
+
+/* Image tracing */
+int gs_imagepath(P4(gs_state *, int, int, const byte *));
diff --git a/pstoraster/gsparam.c b/pstoraster/gsparam.c
new file mode 100644
index 000000000..5257577cf
--- /dev/null
+++ b/pstoraster/gsparam.c
@@ -0,0 +1,374 @@
+/* Copyright (C) 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gsparam.c */
+/* Default implementation of parameter lists */
+#include "memory_.h"
+#include "string_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gsparam.h"
+#include "gsstruct.h"
+
+/* Forward references */
+typedef union c_param_value_s c_param_value;
+/*typedef struct gs_c_param_s gs_c_param;*/ /* in gsparam.h */
+
+/* Define the GC type for a parameter list. */
+private_st_c_param_list();
+
+/* Define a union type for parameter values. */
+union c_param_value_s {
+ bool b;
+ long l;
+ float f;
+ gs_param_string s;
+ gs_param_int_array ia;
+ gs_param_float_array fa;
+ gs_param_string_array sa;
+ gs_c_param_list d;
+};
+
+/*
+ * Define the parameter list element types.
+ * We would like to define:
+ * #define cpt(n, t) ((sizeof(t) << 4) + n)
+ * #define cpt_size(cpt) ((cpt) >> 4)
+ * but a bug in the Borland C++ 3.1 compiler causes the enumerated type
+ * never to get defined if we do this. Instead, we use an ordinary
+ * enumeration, and get the sizes from a table.
+ */
+typedef enum {
+ cpt_null, cpt_bool, cpt_long, cpt_float,
+ cpt_string, cpt_int_array,
+ cpt_float_array, cpt_string_array,
+ cpt_dict
+} c_param_type;
+private const byte c_param_type_sizes[] = {
+ 0, sizeof(bool), sizeof(long), sizeof(float),
+ sizeof(gs_param_string), sizeof(gs_param_int_array),
+ sizeof(gs_param_float_array), sizeof(gs_param_string_array),
+ sizeof(gs_c_param_list)
+};
+#define cpt_size(cpt) (c_param_type_sizes[(int)cpt])
+
+/* Define a parameter list element. */
+struct gs_c_param_s {
+ gs_c_param *next;
+ gs_param_name key;
+ c_param_value value;
+ c_param_type type;
+};
+/* Parameter values aren't really simple, */
+/* but since parameter lists are transient, it doesn't matter. */
+gs_private_st_ptrs1(st_c_param, gs_c_param, "gs_c_param",
+ c_param_enum_ptrs, c_param_reloc_ptrs, next);
+
+/* ================ Utilities ================ */
+
+#define cplist ((gs_c_param_list *)plist)
+
+/* ================ Writing parameters to a list ================ */
+
+private param_proc_xmit_null(c_param_write_null);
+private param_proc_xmit_bool(c_param_write_bool);
+private param_proc_xmit_int(c_param_write_int);
+private param_proc_xmit_long(c_param_write_long);
+private param_proc_xmit_float(c_param_write_float);
+private param_proc_xmit_string(c_param_write_string);
+private param_proc_xmit_int_array(c_param_write_int_array);
+private param_proc_xmit_float_array(c_param_write_float_array);
+private param_proc_xmit_string_array(c_param_write_string_array);
+private param_proc_begin_xmit_dict(c_param_begin_write_dict);
+private param_proc_end_xmit_dict(c_param_end_write_dict);
+private param_proc_requested(c_param_requested);
+private const gs_param_list_procs c_write_procs = {
+ c_param_write_null,
+ c_param_write_bool,
+ c_param_write_int,
+ c_param_write_long,
+ c_param_write_float,
+ c_param_write_string,
+ c_param_write_string, /* name = string */
+ c_param_write_int_array,
+ c_param_write_float_array,
+ c_param_write_string_array,
+ c_param_write_string_array, /* name = string */
+ c_param_begin_write_dict,
+ c_param_end_write_dict,
+ c_param_requested
+};
+
+/* Initialize a list for writing. */
+void
+gs_c_param_list_write(gs_c_param_list *plist, gs_memory_t *mem)
+{ plist->procs = &c_write_procs;
+ plist->memory = mem;
+ plist->head = 0;
+ plist->count = 0;
+}
+
+/* Release a list. */
+void
+gs_c_param_list_release(gs_c_param_list *plist)
+{ gs_c_param *pparam;
+ while ( (pparam = plist->head) != 0 )
+ { gs_c_param *next = pparam->next;
+ if ( pparam->type == cpt_dict )
+ gs_c_param_list_release(&pparam->value.d);
+ gs_free_object(plist->memory, pparam, "gs_c_param_list_release");
+ plist->head = next;
+ plist->count--;
+ }
+}
+
+/* Generic routine for writing a parameter to a list. */
+private int
+c_param_write(gs_c_param_list *plist, gs_param_name pkey, void *pvalue,
+ c_param_type type)
+{ gs_c_param *pparam =
+ gs_alloc_struct(plist->memory, gs_c_param, &st_c_param,
+ "c_param_write");
+ if ( pparam == 0 )
+ return_error(gs_error_VMerror);
+ pparam->next = plist->head;
+ pparam->key = pkey;
+ memcpy(&pparam->value, pvalue, cpt_size(type));
+ pparam->type = type;
+ plist->head = pparam;
+ plist->count++;
+ return 0;
+}
+
+/* Individual writing routines. */
+#define cpw(pvalue, type)\
+ c_param_write(cplist, pkey, pvalue, type)
+private int
+c_param_write_null(gs_param_list *plist, gs_param_name pkey)
+{ return cpw(NULL, cpt_null);
+}
+private int
+c_param_write_bool(gs_param_list *plist, gs_param_name pkey, bool *pvalue)
+{ return cpw(pvalue, cpt_bool);
+}
+private int
+c_param_write_int(gs_param_list *plist, gs_param_name pkey, int *pvalue)
+{ long l = *pvalue;
+ return cpw(&l, cpt_long);
+}
+private int
+c_param_write_long(gs_param_list *plist, gs_param_name pkey, long *pvalue)
+{ return cpw(pvalue, cpt_long);
+}
+private int
+c_param_write_float(gs_param_list *plist, gs_param_name pkey, float *pvalue)
+{ return cpw(pvalue, cpt_float);
+}
+private int
+c_param_write_string(gs_param_list *plist, gs_param_name pkey,
+ gs_param_string *pvalue)
+{ return cpw(pvalue, cpt_string);
+}
+private int
+c_param_write_int_array(gs_param_list *plist, gs_param_name pkey,
+ gs_param_int_array *pvalue)
+{ return cpw(pvalue, cpt_int_array);
+}
+private int
+c_param_write_float_array(gs_param_list *plist, gs_param_name pkey,
+ gs_param_float_array *pvalue)
+{ return cpw(pvalue, cpt_float_array);
+}
+private int
+c_param_write_string_array(gs_param_list *plist, gs_param_name pkey,
+ gs_param_string_array *pvalue)
+{ return cpw(pvalue, cpt_string_array);
+}
+private int
+c_param_begin_write_dict(gs_param_list *plist, gs_param_name pkey,
+ gs_param_dict *pvalue, bool int_keys)
+{ gs_c_param_list *dlist =
+ gs_alloc_struct(cplist->memory, gs_c_param_list, &st_c_param_list,
+ "c_param_begin_write_dict");
+ if ( dlist == 0 )
+ return_error(gs_error_VMerror);
+ gs_c_param_list_write(dlist, cplist->memory);
+ pvalue->list = (gs_param_list *)dlist;
+ return 0;
+}
+private int
+c_param_end_write_dict(gs_param_list *plist, gs_param_name pkey,
+ gs_param_dict *pvalue)
+{ return cpw(pvalue->list, cpt_dict);
+}
+
+/* Other procedures */
+private bool
+c_param_requested(const gs_param_list *plist, gs_param_name pkey)
+{ return true;
+}
+
+/* ================ Reading from a list to parameters ================ */
+
+private param_proc_xmit_null(c_param_read_null);
+private param_proc_xmit_bool(c_param_read_bool);
+private param_proc_xmit_int(c_param_read_int);
+private param_proc_xmit_long(c_param_read_long);
+private param_proc_xmit_float(c_param_read_float);
+private param_proc_xmit_string(c_param_read_string);
+private param_proc_xmit_int_array(c_param_read_int_array);
+private param_proc_xmit_float_array(c_param_read_float_array);
+private param_proc_xmit_string_array(c_param_read_string_array);
+private param_proc_begin_xmit_dict(c_param_begin_read_dict);
+private param_proc_end_xmit_dict(c_param_end_read_dict);
+private param_proc_get_policy(c_param_read_get_policy);
+private param_proc_signal_error(c_param_read_signal_error);
+private param_proc_commit(c_param_read_commit);
+private const gs_param_list_procs c_read_procs = {
+ c_param_read_null,
+ c_param_read_bool,
+ c_param_read_int,
+ c_param_read_long,
+ c_param_read_float,
+ c_param_read_string,
+ c_param_read_string, /* name = string */
+ c_param_read_int_array,
+ c_param_read_float_array,
+ c_param_read_string_array,
+ c_param_read_string_array, /* name = string */
+ c_param_begin_read_dict,
+ c_param_end_read_dict,
+ NULL, /* requested */
+ c_param_read_get_policy,
+ c_param_read_signal_error,
+ c_param_read_commit
+};
+
+/* Switch a list from writing to reading. */
+void
+gs_c_param_list_read(gs_c_param_list *plist)
+{ plist->procs = &c_read_procs;
+}
+
+/* Generic routine for reading a parameter from a list. */
+private gs_c_param *
+c_param_find(gs_c_param_list *plist, gs_param_name pkey)
+{ gs_c_param *pparam = plist->head;
+ for ( ; pparam != 0; pparam = pparam->next )
+ if ( !strcmp(pparam->key, pkey) )
+ return pparam;
+ return 0;
+}
+private int
+c_param_read(gs_c_param_list *plist, gs_param_name pkey, void *pvalue,
+ c_param_type type)
+{ gs_c_param *pparam = c_param_find(plist, pkey);
+ if ( pparam == 0 )
+ return 1;
+ if ( pparam->type != type )
+ return_error(gs_error_typecheck);
+ memcpy(pvalue, &pparam->value, cpt_size(type));
+ return 0;
+}
+
+/* Individual reading routines. */
+#define cpr(pvalue, type)\
+ c_param_read(cplist, pkey, pvalue, type)
+private int
+c_param_read_null(gs_param_list *plist, gs_param_name pkey)
+{ return cpr(NULL, cpt_null);
+}
+private int
+c_param_read_bool(gs_param_list *plist, gs_param_name pkey, bool *pvalue)
+{ return cpr(pvalue, cpt_bool);
+}
+private int
+c_param_read_int(gs_param_list *plist, gs_param_name pkey, int *pvalue)
+{ long l;
+ int code = cpr(&l, cpt_long);
+ if ( code == 0 )
+ { if ( l < min_int || l > max_int )
+ return_error(gs_error_rangecheck);
+ }
+ return code;
+}
+private int
+c_param_read_long(gs_param_list *plist, gs_param_name pkey, long *pvalue)
+{ return cpr(pvalue, cpt_long);
+}
+private int
+c_param_read_float(gs_param_list *plist, gs_param_name pkey, float *pvalue)
+{ return cpr(pvalue, cpt_float);
+}
+private int
+c_param_read_string(gs_param_list *plist, gs_param_name pkey,
+ gs_param_string *pvalue)
+{ return cpr(pvalue, cpt_string);
+}
+private int
+c_param_read_int_array(gs_param_list *plist, gs_param_name pkey,
+ gs_param_int_array *pvalue)
+{ return cpr(pvalue, cpt_int_array);
+}
+private int
+c_param_read_float_array(gs_param_list *plist, gs_param_name pkey,
+ gs_param_float_array *pvalue)
+{ return cpr(pvalue, cpt_float_array);
+}
+private int
+c_param_read_string_array(gs_param_list *plist, gs_param_name pkey,
+ gs_param_string_array *pvalue)
+{ return cpr(pvalue, cpt_string_array);
+}
+private int
+c_param_begin_read_dict(gs_param_list *plist, gs_param_name pkey,
+ gs_param_dict *pvalue, bool int_keys)
+{ gs_c_param *pparam = c_param_find(cplist, pkey);
+ if ( pparam == 0 )
+ return 1;
+ if ( pparam->type != cpt_dict )
+ return_error(gs_error_typecheck);
+ gs_c_param_list_read(&pparam->value.d);
+ pvalue->list = (gs_param_list *)&pparam->value.d;
+ pvalue->size = pparam->value.d.count;
+ return 0;
+}
+private int
+c_param_end_read_dict(gs_param_list *plist, gs_param_name pkey,
+ gs_param_dict *pvalue)
+{ return 0;
+}
+
+/* Other procedures */
+private int
+c_param_read_get_policy(gs_param_list *plist, gs_param_name pkey)
+{ return gs_param_policy_ignore;
+}
+private int
+c_param_read_signal_error(gs_param_list *plist, gs_param_name pkey, int code)
+{ return code;
+}
+private int
+c_param_read_commit(gs_param_list *plist)
+{ return 0;
+}
diff --git a/pstoraster/gsparam.h b/pstoraster/gsparam.h
new file mode 100644
index 000000000..50903fe2d
--- /dev/null
+++ b/pstoraster/gsparam.h
@@ -0,0 +1,349 @@
+/* Copyright (C) 1993, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gsparam.h */
+/* Client interface to parameter dictionaries */
+
+#ifndef gsparam_INCLUDED
+# define gsparam_INCLUDED
+
+/*
+ * Several interfaces use parameter dictionaries to communicate sets of
+ * (key, value) pairs from a client to an object with complex state.
+ * (Several of these correspond directly to similar interfaces in the
+ * PostScript language.) This file defines the API for parameter dictionaries.
+ */
+
+#ifndef gs_param_list_DEFINED
+# define gs_param_list_DEFINED
+typedef struct gs_param_list_s gs_param_list;
+#endif
+typedef const char *gs_param_name;
+
+/*
+ * Define a structure for representing a variable-size value
+ * (string/name, integer array, or floating point array).
+ * The size is the number of elements, not the size in bytes.
+ * A value is persistent if it is defined as static const,
+ * or if it is allocated in garbage-collectable space and never freed.
+ */
+
+#define _param_array_struct(sname,etype)\
+ struct sname { const etype *data; uint size; bool persistent; }
+typedef _param_array_struct(gs_param_string_s, byte) gs_param_string;
+typedef _param_array_struct(gs_param_int_array_s, int) gs_param_int_array;
+typedef _param_array_struct(gs_param_float_array_s, float) gs_param_float_array;
+typedef _param_array_struct(gs_param_string_array_s, gs_param_string) gs_param_string_array;
+
+#define param_string_from_string(ps, str)\
+ (ps).data = (const byte *)(str), (ps).size = strlen((const char *)(ps).data),\
+ (ps).persistent = true
+
+/* Define the type for dictionary and mixed-type-array values. */
+typedef struct gs_param_collection_s {
+ gs_param_list *list;
+ uint size;
+} gs_param_collection;
+typedef gs_param_collection gs_param_dict;
+typedef gs_param_collection gs_param_array;
+
+/* Define the 'policies' for handling out-of-range parameter values. */
+/* This is not an enum, because some parameters may recognize other values. */
+#define gs_param_policy_signal_error 0
+#define gs_param_policy_ignore 1
+#define gs_param_policy_consult_user 2
+
+/*
+ * Define the object procedures. Note that the same interface is used
+ * both for getting and for setting parameter values. (This is a bit
+ * of a hack, and we might change it someday.) The procedures return
+ * as follows:
+ * - 'reading' procedures ('put' operations from the client's viewpoint)
+ * return 1 for a missing parameter, 0 for a valid parameter, <0 on error.
+ * - 'writing' procedures ('get' operations from the client's viewpoint)
+ * return 0 or 1 if successful, <0 on error.
+ */
+
+/*
+ * Transmitting variable-size objects requires some extra care.
+ * - When writing an array, string, name, or dictionary, the
+ * implementation (not the client) sets all the fields of the value.
+ * - When reading an array, string, or name, the client must set
+ * all the fields of the value.
+ * - When reading a dictionary, the client must set the size field
+ * before calling begin_write_dict; the implementation of begin_write_dict
+ * allocates the list.
+ */
+
+/*
+ * Setting parameters must use a "two-phase commit" policy. Specifically,
+ * any put_params procedure must observe the following discipline:
+
+ 1. For each parameter known to the device, ask the parameter list if
+there is a new value, and if so, make all necessary validity checks. If any
+check fails, call param_signal_error for that parameter, but continue to
+check further parameters. Normally, this step should not alter the state of
+the device; however, if the device allows changing any parameters that are
+read-only by default (for example, BitsPerPixel or ProcessColorModel), or if
+it replaces the default put_params behavior for any parameter (for example,
+if it handles MediaSize or Resolution itself to forestall the normal closing
+of the device when these are set), step 1 of put_params must change the
+parameters in the device state, and step 2 must undo the changes if
+returning an error.
+
+ 2. Call the "superclass" put_params routine. For printer devices,
+this is gdev_prn_put_params; for other devices, it is gx_default_put_params.
+Note that this must be done even if errors were detected in step 1. If this
+routine returns an error code, or if step 1 detected an error, undo any
+changes that step 1 made in the device state, and return the error code.
+
+ 3. Install the new parameter values in the device. If necessary,
+close the device first; a higher-level routine (gs_putdeviceparams) will
+reopen the device if necessary.
+
+ */
+
+typedef struct gs_param_list_procs_s {
+
+ /* Transmit a null value. */
+ /* Note that this is the only 'transmit' operation */
+ /* that does not actually pass any data. */
+
+#define param_proc_xmit_null(proc)\
+ int proc(P2(gs_param_list *, gs_param_name))
+ param_proc_xmit_null((*xmit_null));
+#define param_read_null(plist, pkey)\
+ (*(plist)->procs->xmit_null)(plist, pkey)
+#define param_write_null(plist, pkey)\
+ (*(plist)->procs->xmit_null)(plist, pkey)
+
+ /* Transmit a Boolean value. */
+
+#define param_proc_xmit_bool(proc)\
+ int proc(P3(gs_param_list *, gs_param_name, bool *))
+ param_proc_xmit_bool((*xmit_bool));
+#define param_read_bool(plist, pkey, pvalue)\
+ (*(plist)->procs->xmit_bool)(plist, pkey, pvalue)
+#define param_write_bool(plist, pkey, pvalue)\
+ (*(plist)->procs->xmit_bool)(plist, pkey, pvalue)
+
+ /* Transmit an integer value. */
+
+#define param_proc_xmit_int(proc)\
+ int proc(P3(gs_param_list *, gs_param_name, int *))
+ param_proc_xmit_int((*xmit_int));
+#define param_read_int(plist, pkey, pvalue)\
+ (*(plist)->procs->xmit_int)(plist, pkey, pvalue)
+#define param_write_int(plist, pkey, pvalue)\
+ (*(plist)->procs->xmit_int)(plist, pkey, pvalue)
+
+ /* Transmit a long value. */
+
+#define param_proc_xmit_long(proc)\
+ int proc(P3(gs_param_list *, gs_param_name, long *))
+ param_proc_xmit_long((*xmit_long));
+#define param_read_long(plist, pkey, pvalue)\
+ (*(plist)->procs->xmit_long)(plist, pkey, pvalue)
+#define param_write_long(plist, pkey, pvalue)\
+ (*(plist)->procs->xmit_long)(plist, pkey, pvalue)
+
+ /* Transmit a float value. */
+
+#define param_proc_xmit_float(proc)\
+ int proc(P3(gs_param_list *, gs_param_name, float *))
+ param_proc_xmit_float((*xmit_float));
+#define param_read_float(plist, pkey, pvalue)\
+ (*(plist)->procs->xmit_float)(plist, pkey, pvalue)
+#define param_write_float(plist, pkey, pvalue)\
+ (*(plist)->procs->xmit_float)(plist, pkey, pvalue)
+
+ /* Transmit a string value. */
+
+#define param_proc_xmit_string(proc)\
+ int proc(P3(gs_param_list *, gs_param_name, gs_param_string *))
+ param_proc_xmit_string((*xmit_string));
+#define param_read_string(plist, pkey, pvalue)\
+ (*(plist)->procs->xmit_string)(plist, pkey, pvalue)
+#define param_write_string(plist, pkey, pvalue)\
+ (*(plist)->procs->xmit_string)(plist, pkey, pvalue)
+
+ /* Transmit a name value. */
+
+#define param_proc_xmit_name(proc)\
+ int proc(P3(gs_param_list *, gs_param_name, gs_param_string *))
+ param_proc_xmit_name((*xmit_name));
+#define param_read_name(plist, pkey, pvalue)\
+ (*(plist)->procs->xmit_name)(plist, pkey, pvalue)
+#define param_write_name(plist, pkey, pvalue)\
+ (*(plist)->procs->xmit_name)(plist, pkey, pvalue)
+
+ /* Transmit an integer array value. */
+
+#define param_proc_xmit_int_array(proc)\
+ int proc(P3(gs_param_list *, gs_param_name, gs_param_int_array *))
+ param_proc_xmit_int_array((*xmit_int_array));
+#define param_read_int_array(plist, pkey, pvalue)\
+ (*(plist)->procs->xmit_int_array)(plist, pkey, pvalue)
+#define param_write_int_array(plist, pkey, pvalue)\
+ (*(plist)->procs->xmit_int_array)(plist, pkey, pvalue)
+
+ /* Transmit a float array value. */
+
+#define param_proc_xmit_float_array(proc)\
+ int proc(P3(gs_param_list *, gs_param_name, gs_param_float_array *))
+ param_proc_xmit_float_array((*xmit_float_array));
+#define param_read_float_array(plist, pkey, pvalue)\
+ (*(plist)->procs->xmit_float_array)(plist, pkey, pvalue)
+#define param_write_float_array(plist, pkey, pvalue)\
+ (*(plist)->procs->xmit_float_array)(plist, pkey, pvalue)
+
+ /* Transmit a string array value. */
+
+#define param_proc_xmit_string_array(proc)\
+ int proc(P3(gs_param_list *, gs_param_name, gs_param_string_array *))
+ param_proc_xmit_string_array((*xmit_string_array));
+#define param_read_string_array(plist, pkey, pvalue)\
+ (*(plist)->procs->xmit_string_array)(plist, pkey, pvalue)
+#define param_write_string_array(plist, pkey, pvalue)\
+ (*(plist)->procs->xmit_string_array)(plist, pkey, pvalue)
+
+ /* Transmit a name array value. */
+
+#define param_proc_xmit_name_array(proc)\
+ int proc(P3(gs_param_list *, gs_param_name, gs_param_string_array *))
+ param_proc_xmit_name_array((*xmit_name_array));
+#define param_read_name_array(plist, pkey, pvalue)\
+ (*(plist)->procs->xmit_name_array)(plist, pkey, pvalue)
+#define param_write_name_array(plist, pkey, pvalue)\
+ (*(plist)->procs->xmit_name_array)(plist, pkey, pvalue)
+
+ /* Start transmitting a dictionary value. */
+ /* If int_keys is true, the keys are actually integers */
+ /* (although still presented as strings). */
+
+#define param_proc_begin_xmit_dict(proc)\
+ int proc(P4(gs_param_list *, gs_param_name, gs_param_dict *, bool))
+ param_proc_begin_xmit_dict((*begin_xmit_dict));
+#define param_begin_read_dict(plist, pkey, pvalue, int_keys)\
+ (*(plist)->procs->begin_xmit_dict)(plist, pkey, pvalue, int_keys)
+#define param_begin_write_dict(plist, pkey, pvalue, int_keys)\
+ (*(plist)->procs->begin_xmit_dict)(plist, pkey, pvalue, int_keys)
+
+ /* Finish transmitting a dictionary value. */
+
+#define param_proc_end_xmit_dict(proc)\
+ int proc(P3(gs_param_list *, gs_param_name, gs_param_dict *))
+ param_proc_end_xmit_dict((*end_xmit_dict));
+#define param_end_read_dict(plist, pkey, pvalue)\
+ (*(plist)->procs->end_xmit_dict)(plist, pkey, pvalue)
+#define param_end_write_dict(plist, pkey, pvalue)\
+ (*(plist)->procs->end_xmit_dict)(plist, pkey, pvalue)
+
+ /* Determine whether a given key has been requested. */
+ /* (Only used when writing.) */
+
+#define param_proc_requested(proc)\
+ bool proc(P2(const gs_param_list *, gs_param_name))
+ param_proc_requested((*requested));
+#define param_requested(plist, pkey)\
+ (*(plist)->procs->requested)(plist, pkey)
+
+ /* Get the 'policy' associated with an out-of-range parameter value. */
+ /* (Only used when reading.) */
+
+#define param_proc_get_policy(proc)\
+ int proc(P2(gs_param_list *, gs_param_name))
+ param_proc_get_policy((*get_policy));
+#define param_get_policy(plist, pkey)\
+ (*(plist)->procs->get_policy)(plist, pkey)
+
+ /*
+ * Signal an error. (Only used when reading.)
+ * The procedure may return a different error code,
+ * or may return 0 indicating that the error is to be ignored.
+ */
+
+#define param_proc_signal_error(proc)\
+ int proc(P3(gs_param_list *, gs_param_name, int))
+ param_proc_signal_error((*signal_error));
+#define param_signal_error(plist, pkey, code)\
+ (*(plist)->procs->signal_error)(plist, pkey, code)
+#define param_return_error(plist, pkey, code)\
+ return_error(param_signal_error(plist, pkey, code))
+
+ /*
+ * "Commit" a set of changes. (Only used when reading.)
+ * This is called at the end of the first phase.
+ */
+
+#define param_proc_commit(proc)\
+ int proc(P1(gs_param_list *))
+ param_proc_commit((*commit));
+#define param_commit(plist)\
+ (*(plist)->procs->commit)(plist)
+
+} gs_param_list_procs;
+
+/* Define an abstract parameter dictionary. Implementations are */
+/* concrete subclasses. */
+#define gs_param_list_common\
+ const gs_param_list_procs _ds *procs
+struct gs_param_list_s {
+ gs_param_list_common;
+};
+
+/*
+ * Define a default implementation, intended to be usable easily
+ * from C code. The intended usage pattern is:
+ gs_c_param_list list;
+ [... other code here ...]
+ gs_c_param_list_write(&list, mem);
+ [As many as needed:]
+ code = param_write_XXX(&list, "ParamName", &param_value);
+ [Check code for <0]
+ gs_c_param_list_read(&list);
+ code = gs_putdeviceparams(dev, &list);
+ gs_c_param_list_release(&list);
+ [Check code for <0]
+ if ( code == 1 )
+ { code = (*dev_proc(dev, open_device))(dev);
+ [Check code for <0]
+ }
+ */
+
+typedef struct gs_c_param_s gs_c_param; /* opaque here */
+typedef struct gs_c_param_list_s {
+ gs_param_list_common;
+ gs_memory_t *memory;
+ gs_c_param *head;
+ uint count;
+} gs_c_param_list;
+#define private_st_c_param_list() /* in gsparam.c */\
+ gs_private_st_ptrs1(st_c_param_list, gs_c_param_list, "c_param_list",\
+ c_param_list_enum_ptrs, c_param_list_reloc_ptrs, head)
+/* Clients normally allocate the gs_c_param_list on the stack. */
+void gs_c_param_list_write(P2(gs_c_param_list *, gs_memory_t *));
+void gs_c_param_list_read(P1(gs_c_param_list *)); /* switch to reading */
+void gs_c_param_list_release(P1(gs_c_param_list *));
+
+#endif /* gsparam_INCLUDED */
diff --git a/pstoraster/gspath.c b/pstoraster/gspath.c
new file mode 100644
index 000000000..9cf05ac80
--- /dev/null
+++ b/pstoraster/gspath.c
@@ -0,0 +1,259 @@
+/* Copyright (C) 1989, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gspath.c */
+/* Basic path routines for Ghostscript library */
+#include "gx.h"
+#include "gserrors.h"
+#include "gxfixed.h"
+#include "gxmatrix.h"
+#include "gscoord.h" /* requires gsmatrix.h */
+#include "gzstate.h"
+#include "gzpath.h"
+#include "gxdevice.h" /* for gxcpath.h */
+#include "gzcpath.h"
+
+/* ------ Miscellaneous ------ */
+
+int
+gs_newpath(gs_state *pgs)
+{ gx_path_release(pgs->path);
+ gx_path_init(pgs->path, pgs->memory);
+ return 0;
+}
+
+int
+gs_closepath(gs_state *pgs)
+{ return gx_path_close_subpath(pgs->path);
+}
+
+int
+gs_upmergepath(gs_state *pgs)
+{ return gx_path_add_path(pgs->saved->path, pgs->path);
+}
+
+/* ------ Points and lines ------ */
+
+int
+gs_currentpoint(const gs_state *pgs, gs_point *ppt)
+{ gs_fixed_point pt;
+ int code = gx_path_current_point(pgs->path, &pt);
+ if ( code < 0 ) return code;
+ return gs_itransform((gs_state *)pgs,
+ fixed2float(pt.x), fixed2float(pt.y), ppt);
+}
+
+int
+gs_moveto(gs_state *pgs, floatp x, floatp y)
+{ int code;
+ gs_fixed_point pt;
+ if ( (code = gs_point_transform2fixed(&pgs->ctm, x, y, &pt)) >= 0 )
+ code = gx_path_add_point(pgs->path, pt.x, pt.y);
+ return code;
+}
+
+int
+gs_rmoveto(gs_state *pgs, floatp x, floatp y)
+{ int code;
+ gs_fixed_point dpt;
+ if ( (code = gs_distance_transform2fixed(&pgs->ctm, x, y, &dpt)) >= 0 )
+ code = gx_path_add_relative_point(pgs->path, dpt.x, dpt.y);
+ return code;
+}
+
+int
+gs_lineto(gs_state *pgs, floatp x, floatp y)
+{ int code;
+ gs_fixed_point pt;
+ if ( (code = gs_point_transform2fixed(&pgs->ctm, x, y, &pt)) >= 0 )
+ code = gx_path_add_line(pgs->path, pt.x, pt.y);
+ return code;
+}
+
+int
+gs_rlineto(gs_state *pgs, floatp x, floatp y)
+{ gs_fixed_point cpt, dpt;
+ int code = gx_path_current_point(pgs->path, &cpt);
+ if ( code < 0 ) return code;
+ if ( (code = gs_distance_transform2fixed(&pgs->ctm, x, y, &dpt)) >= 0 )
+ code = gx_path_add_line(pgs->path, cpt.x + dpt.x, cpt.y + dpt.y);
+ return code;
+}
+
+/* ------ Curves ------ */
+
+int
+gs_curveto(gs_state *pgs,
+ floatp x1, floatp y1, floatp x2, floatp y2, floatp x3, floatp y3)
+{ gs_fixed_point p1, p2, p3;
+ int code;
+ if ( (code = gs_point_transform2fixed(&pgs->ctm, x1, y1, &p1)) < 0 ||
+ (code = gs_point_transform2fixed(&pgs->ctm, x2, y2, &p2)) < 0 ||
+ (code = gs_point_transform2fixed(&pgs->ctm, x3, y3, &p3)) < 0
+ ) return code;
+ return gx_path_add_curve(pgs->path,
+ p1.x, p1.y, p2.x, p2.y, p3.x, p3.y);
+}
+
+int
+gs_rcurveto(gs_state *pgs,
+ floatp dx1, floatp dy1, floatp dx2, floatp dy2, floatp dx3, floatp dy3)
+{ gs_fixed_point pt, p1, p2, p3;
+ int code = gx_path_current_point(pgs->path, &pt);
+ if ( code < 0 ) return code;
+ if ( (code = gs_distance_transform2fixed(&pgs->ctm, dx1, dy1, &p1)) < 0 ||
+ (code = gs_distance_transform2fixed(&pgs->ctm, dx2, dy2, &p2)) < 0 ||
+ (code = gs_distance_transform2fixed(&pgs->ctm, dx3, dy3, &p3)) < 0
+ ) return code;
+ return gx_path_add_curve(pgs->path,
+ pt.x + p1.x, pt.y + p1.y,
+ pt.x + p2.x, pt.y + p2.y,
+ pt.x + p3.x, pt.y + p3.y);
+}
+
+/* ------ Clipping ------ */
+
+/* Forward references */
+private int common_clip(P2(gs_state *, int));
+private int set_clip_path(P3(gs_state *, gx_clip_path *, int));
+
+int
+gs_clippath(gs_state *pgs)
+{ gx_path path;
+ int code = gx_cpath_path(pgs->clip_path, &path);
+ if ( code < 0 ) return code;
+ return gx_path_copy(&path, pgs->path, 1);
+}
+
+int
+gs_initclip(gs_state *pgs)
+{ register gx_device *dev = gs_currentdevice(pgs);
+ gs_rect bbox;
+ gs_fixed_rect box;
+ gs_matrix imat;
+
+ if ( dev->ImagingBBox_set )
+ { /* Use the ImagingBBox, relative to default user space. */
+ gs_defaultmatrix(pgs, &imat);
+ bbox.p.x = dev->ImagingBBox[0];
+ bbox.p.y = dev->ImagingBBox[1];
+ bbox.q.x = dev->ImagingBBox[2];
+ bbox.q.y = dev->ImagingBBox[3];
+ }
+ else
+ { /* Use the MediaSize indented by the HWMargins, */
+ /* relative to unrotated user space adjusted by */
+ /* the Margins. (We suspect this isn't quite right, */
+ /* but the whole issue of "margins" is such a mess that */
+ /* we don't think we can do any better.) */
+ (*dev_proc(dev, get_initial_matrix))(dev, &imat);
+ /* Adjust for the Margins. */
+ imat.tx += dev->Margins[0] * dev->HWResolution[0] /
+ dev->MarginsHWResolution[0];
+ imat.ty += dev->Margins[1] * dev->HWResolution[1] /
+ dev->MarginsHWResolution[1];
+ bbox.p.x = dev->HWMargins[0];
+ bbox.p.y = dev->HWMargins[1];
+ bbox.q.x = dev->MediaSize[0] - dev->HWMargins[2];
+ bbox.q.y = dev->MediaSize[1] - dev->HWMargins[3];
+ }
+ gs_bbox_transform(&bbox, &imat, &bbox);
+ /* Round the clipping box so that it doesn't get ceilinged. */
+ box.p.x = fixed_rounded(float2fixed(bbox.p.x));
+ box.p.y = fixed_rounded(float2fixed(bbox.p.y));
+ box.q.x = fixed_rounded(float2fixed(bbox.q.x));
+ box.q.y = fixed_rounded(float2fixed(bbox.q.y));
+ return gx_clip_to_rectangle(pgs, &box);
+}
+
+int
+gs_clip(gs_state *pgs)
+{ return common_clip(pgs, gx_rule_winding_number);
+}
+
+int
+gs_eoclip(gs_state *pgs)
+{ return common_clip(pgs, gx_rule_even_odd);
+}
+
+private int
+common_clip(gs_state *pgs, int rule)
+{ gx_path fpath;
+ int code = gx_path_flatten(pgs->path, &fpath, pgs->flatness);
+ if ( code < 0 ) return code;
+ code = gx_cpath_intersect(pgs, pgs->clip_path, &fpath, rule);
+ if ( code != 1 ) gx_path_release(&fpath);
+ if ( code < 0 ) return code;
+ return set_clip_path(pgs, pgs->clip_path, rule);
+}
+
+int
+gs_setclipoutside(gs_state *pgs, bool outside)
+{ return gx_cpath_set_outside(pgs->clip_path, outside);
+}
+
+bool
+gs_currentclipoutside(const gs_state *pgs)
+{ return gx_cpath_is_outside(pgs->clip_path);
+}
+
+/* Establish a rectangle as the clipping path. */
+/* Used by initclip and by the character and Pattern cache logic. */
+int
+gx_clip_to_rectangle(gs_state *pgs, gs_fixed_rect *pbox)
+{ gx_clip_path cpath;
+ int code = gx_cpath_from_rectangle(&cpath, pbox, pgs->memory);
+ if ( code < 0 ) return code;
+ gx_cpath_release(pgs->clip_path);
+ return set_clip_path(pgs, &cpath, gx_rule_winding_number);
+}
+
+/* Set the clipping path to the current path, without intersecting. */
+/* Currently only used by the insideness testing operators, */
+/* but might be used by viewclip eventually. */
+/* The algorithm is very inefficient; we'll improve it later if needed. */
+int
+gx_clip_to_path(gs_state *pgs)
+{ gs_fixed_rect bbox;
+ int code;
+ if ( (code = gx_path_bbox(pgs->path, &bbox)) < 0 ||
+ (code = gx_clip_to_rectangle(pgs, &bbox)) < 0
+ )
+ return code;
+ return gs_clip(pgs);
+}
+
+/* Set the clipping path (internal). */
+private int
+set_clip_path(gs_state *pgs, gx_clip_path *pcpath, int rule)
+{ *pgs->clip_path = *pcpath;
+ pgs->clip_rule = rule;
+#ifdef DEBUG
+if ( gs_debug_c('P') )
+ { extern void gx_cpath_print(P1(const gx_clip_path *));
+ dprintf("[P]Clipping path:\n"),
+ gx_cpath_print(pcpath);
+ }
+#endif
+ return 0;
+}
diff --git a/pstoraster/gspath.h b/pstoraster/gspath.h
new file mode 100644
index 000000000..a26a71246
--- /dev/null
+++ b/pstoraster/gspath.h
@@ -0,0 +1,77 @@
+/* Copyright (C) 1989, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gspath.h */
+/* Client interface to path manipulation facilities */
+/* Requires gsstate.h */
+#include "gspenum.h"
+
+/* Path constructors */
+int gs_newpath(P1(gs_state *)),
+ gs_moveto(P3(gs_state *, floatp, floatp)),
+ gs_rmoveto(P3(gs_state *, floatp, floatp)),
+ gs_lineto(P3(gs_state *, floatp, floatp)),
+ gs_rlineto(P3(gs_state *, floatp, floatp)),
+ gs_arc(P6(gs_state *, floatp, floatp, floatp, floatp, floatp)),
+ gs_arcn(P6(gs_state *, floatp, floatp, floatp, floatp, floatp)),
+/*
+ * Because of an obscure bug in the IBM RS/6000 compiler, one (but not both)
+ * bool argument(s) for gs_arc_add must come before the floatp arguments.
+ */
+ gs_arc_add(P8(gs_state *, bool, floatp, floatp, floatp, floatp, floatp, bool)),
+ gs_arcto(P7(gs_state *, floatp, floatp, floatp, floatp, floatp, float [4])),
+ gs_curveto(P7(gs_state *, floatp, floatp, floatp, floatp, floatp, floatp)),
+ gs_rcurveto(P7(gs_state *, floatp, floatp, floatp, floatp, floatp, floatp)),
+ gs_closepath(P1(gs_state *));
+
+/* Add the current path to the path in the previous graphics state. */
+int gs_upmergepath(P1(gs_state *));
+
+/* Path accessors and transformers */
+int gs_currentpoint(P2(const gs_state *, gs_point *)),
+ gs_upathbbox(P3(gs_state *, gs_rect *, bool)),
+ gs_dashpath(P1(gs_state *)),
+ gs_flattenpath(P1(gs_state *)),
+ gs_reversepath(P1(gs_state *)),
+ gs_strokepath(P1(gs_state *));
+/* The extra argument for gs_upathbbox controls whether to include */
+/* a trailing moveto in the bounding box. */
+#define gs_pathbbox(pgs, prect)\
+ gs_upathbbox(pgs, prect, false)
+
+/* Path enumeration */
+
+/* This interface makes a copy of the path. */
+gs_path_enum *
+ gs_path_enum_alloc(P2(gs_memory_t *, client_name_t));
+int gs_path_enum_init(P2(gs_path_enum *, const gs_state *));
+int gs_path_enum_next(P2(gs_path_enum *, gs_point [3])); /* 0 when done */
+void gs_path_enum_cleanup(P1(gs_path_enum *));
+
+/* Clipping */
+int gs_clippath(P1(gs_state *)),
+ gs_initclip(P1(gs_state *)),
+ gs_clip(P1(gs_state *)),
+ gs_eoclip(P1(gs_state *));
+int gs_setclipoutside(P2(gs_state *, bool));
+bool gs_currentclipoutside(P1(const gs_state *));
diff --git a/pstoraster/gspath1.c b/pstoraster/gspath1.c
new file mode 100644
index 000000000..72448d2bd
--- /dev/null
+++ b/pstoraster/gspath1.c
@@ -0,0 +1,371 @@
+/* Copyright (C) 1989, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gspath1.c */
+/* Additional PostScript Level 1 path routines for Ghostscript library */
+#include "math_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gsstruct.h"
+#include "gxfixed.h"
+#include "gxfarith.h"
+#include "gxmatrix.h"
+#include "gzstate.h"
+#include "gspath.h"
+#include "gzpath.h"
+#include "gscoord.h" /* gs_itransform prototype */
+
+/* ------ Arcs ------ */
+
+/* Conversion parameters */
+#define degrees_to_radians (M_PI / 180.0)
+
+/* Forward declarations */
+/*
+ * Because of an obscure bug in the IBM RS/6000 compiler, the int argument
+ * for arc_add must come before the floatp arguments.
+ */
+typedef enum {
+ arc_nothing,
+ arc_moveto,
+ arc_lineto
+} arc_action;
+private int arc_add(P9(gs_state *, arc_action,
+ floatp, floatp, floatp, floatp, floatp, floatp, floatp));
+
+int
+gs_arc(gs_state *pgs,
+ floatp xc, floatp yc, floatp r, floatp ang1, floatp ang2)
+{ return gs_arc_add(pgs, false, xc, yc, r, ang1, ang2, true);
+}
+
+int
+gs_arcn(gs_state *pgs,
+ floatp xc, floatp yc, floatp r, floatp ang1, floatp ang2)
+{ return gs_arc_add(pgs, true, xc, yc, r, ang1, ang2, true);
+}
+
+int
+gs_arc_add(gs_state *pgs, bool clockwise, floatp axc, floatp ayc, floatp arad,
+ floatp aang1, floatp aang2, bool add_line)
+{ float ar = arad;
+ fixed ang1 = float2fixed(aang1), ang2 = float2fixed(aang2), adiff;
+ float ang1r; /* reduced angle */
+ gs_sincos_t sincos;
+ float x0, y0, sin0, cos0;
+ float x3r, y3r;
+ arc_action action = (add_line ? arc_lineto : arc_moveto);
+ int code;
+
+#define fixed_90 int2fixed(90)
+#define fixed_180 int2fixed(180)
+#define fixed_360 int2fixed(360)
+ if ( ar < 0 )
+ { ang1 += fixed_180;
+ ang2 += fixed_180;
+ ar = - ar;
+ }
+ ang1r = fixed2float(ang1 % fixed_360);
+ gs_sincos_degrees(ang1r, &sincos);
+ sin0 = ar * sincos.sin, cos0 = ar * sincos.cos;
+ x0 = axc + cos0, y0 = ayc + sin0;
+ if ( clockwise )
+ { /* Quadrant reduction */
+ while ( ang1 < ang2 ) ang2 -= fixed_360;
+ while ( (adiff = ang2 - ang1) < -fixed_90 )
+ { float w = sin0; sin0 = -cos0; cos0 = w;
+ x3r = axc + cos0, y3r = ayc + sin0;
+ code = arc_add(pgs, action, ar, x0, y0, x3r, y3r,
+ (x0 + cos0),
+ (y0 + sin0));
+ if ( code < 0 ) return code;
+ x0 = x3r, y0 = y3r;
+ ang1 -= fixed_90;
+ action = arc_nothing;
+ }
+ }
+ else
+ { /* Quadrant reduction */
+ while ( ang2 < ang1 ) ang2 += fixed_360;
+ while ( (adiff = ang2 - ang1) > fixed_90 )
+ { float w = cos0; cos0 = -sin0; sin0 = w;
+ x3r = axc + cos0, y3r = ayc + sin0;
+ code = arc_add(pgs, action, ar, x0, y0, x3r, y3r,
+ (x0 + cos0),
+ (y0 + sin0));
+ if ( code < 0 ) return code;
+ x0 = x3r, y0 = y3r;
+ ang1 += fixed_90;
+ action = arc_nothing;
+ }
+ }
+ /* Compute the intersection of the tangents. */
+ /* We define xt and yt as separate variables to work around */
+ /* a floating point bug in one of the SPARC compilers. */
+ /* We know that -fixed_90 <= adiff <= fixed_90. */
+ { double trad =
+ tan(fixed2float(adiff) * (degrees_to_radians / 2));
+ double xt = x0 - trad * sin0, yt = y0 + trad * cos0;
+ gs_sincos_degrees(fixed2float(ang2), &sincos);
+ code = arc_add(pgs, action, ar, x0, y0,
+ (axc + ar * sincos.cos),
+ (ayc + ar * sincos.sin),
+ xt, yt);
+ }
+ return code;
+}
+
+int
+gs_arcto(gs_state *pgs,
+ floatp ax1, floatp ay1, floatp ax2, floatp ay2, floatp arad, float retxy[4])
+{ float xt0, yt0, xt2, yt2;
+ gs_point up0;
+#define ax0 up0.x
+#define ay0 up0.y
+ int code;
+ if ( arad < 0 )
+ return_error(gs_error_undefinedresult);
+ /* Transform the current point back into user coordinates */
+ if ( (code = gs_currentpoint(pgs, &up0)) < 0 ) return code;
+ { /* Now we have to compute the tangent points. */
+ /* Basically, the idea is to compute the tangent */
+ /* of the bisector by using tan(x+y) and tan(z/2) */
+ /* formulas, without ever using any trig. */
+ float dx0 = ax0 - ax1, dy0 = ay0 - ay1;
+ float dx2 = ax2 - ax1, dy2 = ay2 - ay1;
+ /* Compute the squared lengths from p1 to p0 and p2. */
+ double sql0 = dx0 * dx0 + dy0 * dy0;
+ double sql2 = dx2 * dx2 + dy2 * dy2;
+ /* Compute the distance from p1 to the tangent points. */
+ /* This is the only hairy part. */
+ double num = dy0 * dx2 - dy2 * dx0;
+ double denom = sqrt(sql0 * sql2) - (dx0 * dx2 + dy0 * dy2);
+ /* Check for collinear points. */
+ if ( fabs(num) < 1.0e-6 || fabs(denom) < 1.0e-6 )
+ { gs_fixed_point pt;
+ code = gs_point_transform2fixed(&pgs->ctm, ax1, ay1, &pt);
+ if ( code >= 0 ) code = gx_path_add_line(pgs->path, pt.x, pt.y);
+ xt0 = xt2 = ax1;
+ yt0 = yt2 = ay1;
+ }
+ else /* not collinear */
+ { double dist = fabs(arad * num / denom);
+ double l0 = dist / sqrt(sql0), l2 = dist / sqrt(sql2);
+ xt0 = ax1 + dx0 * l0;
+ yt0 = ay1 + dy0 * l0;
+ xt2 = ax1 + dx2 * l2;
+ yt2 = ay1 + dy2 * l2;
+ code = arc_add(pgs, arc_lineto, arad, xt0, yt0, xt2, yt2, ax1, ay1);
+ }
+ }
+ if ( retxy != 0 )
+ { retxy[0] = xt0;
+ retxy[1] = yt0;
+ retxy[2] = xt2;
+ retxy[3] = yt2;
+ }
+ return code;
+}
+
+/* Internal routine for adding an arc to the path. */
+private int
+arc_add(gs_state *pgs, arc_action action,
+ floatp r, floatp x0, floatp y0, floatp x3, floatp y3, floatp xt, floatp yt)
+{ gx_path *path = pgs->path;
+ floatp dx = xt - x0, dy = yt - y0;
+ double dist = dx * dx + dy * dy;
+ double r2 = r * r;
+ floatp fraction;
+ gs_fixed_point p0, p3, pt, cpt;
+ int code;
+ /* Compute the fraction coefficient for the curve. */
+ /* See gx_path_add_partial_arc for details. */
+ if ( dist >= r2 * 1.0e8 ) /* almost zero radius; */
+ /* the >= catches dist == r == 0 */
+ fraction = 0.0;
+ else
+ fraction = (4.0/3.0) / (1 + sqrt(1 + dist / r2));
+ if_debug8('r',
+ "[r]Arc f=%f p0=(%f,%f) pt=(%f,%f) p3=(%f,%f) action=%d\n",
+ fraction, x0, y0, xt, yt, x3, y3, (int)action);
+ if ( (code = gs_point_transform2fixed(&pgs->ctm, x0, y0, &p0)) < 0 ||
+ (code = gs_point_transform2fixed(&pgs->ctm, x3, y3, &p3)) < 0 ||
+ (code = gs_point_transform2fixed(&pgs->ctm, xt, yt, &pt)) < 0 ||
+ (code =
+ (action == arc_nothing ? 0 :
+ action == arc_lineto &&
+ gx_path_current_point(path, &cpt) >= 0 ?
+ gx_path_add_line(path, p0.x, p0.y) :
+ /* action == arc_moveto */
+ gx_path_add_point(path, p0.x, p0.y))) < 0
+ )
+ return code;
+ return gx_path_add_partial_arc(path, p3.x, p3.y, pt.x, pt.y, fraction);
+}
+
+/* ------ Path transformers ------ */
+
+int
+gs_dashpath(gs_state *pgs)
+{ gx_path fpath;
+ int code;
+
+ if ( gs_currentdash_length == 0 )
+ return 0; /* no dash pattern */
+ code = gs_flattenpath(pgs);
+ if ( code < 0 )
+ return code;
+ code = gx_path_expand_dashes(pgs->path, &fpath,
+ (const gs_imager_state *)pgs);
+ if ( code < 0 )
+ return code;
+ gx_path_release(pgs->path);
+ *pgs->path = fpath;
+ return 0;
+}
+
+int
+gs_flattenpath(gs_state *pgs)
+{ gx_path fpath;
+ int code;
+ if ( !pgs->path->curve_count ) return 0; /* no curves */
+ code = gx_path_flatten(pgs->path, &fpath, pgs->flatness);
+ if ( code < 0 ) return code;
+ gx_path_release(pgs->path);
+ *pgs->path = fpath;
+ return 0;
+}
+
+int
+gs_reversepath(gs_state *pgs)
+{ gx_path rpath;
+ int code = gx_path_copy_reversed(pgs->path, &rpath, 1);
+ if ( code < 0 ) return code;
+ gx_path_release(pgs->path);
+ *pgs->path = rpath;
+ return 0;
+}
+
+/* ------ Accessors ------ */
+
+int
+gs_upathbbox(gs_state *pgs, gs_rect *pbox, bool include_moveto)
+{ gs_fixed_rect fbox; /* box in device coordinates */
+ gs_rect dbox;
+ int code = gx_path_bbox(pgs->path, &fbox);
+
+ if ( code < 0 )
+ return code;
+ /* If the path ends with a moveto and include_moveto is true, */
+ /* include the moveto in the bounding box. */
+ if ( pgs->path->subpath_open < 0 && include_moveto )
+ { gs_fixed_point pt;
+ gx_path_current_point_inline(pgs->path, &pt);
+ if ( pt.x < fbox.p.x )
+ fbox.p.x = pt.x;
+ if ( pt.y < fbox.p.y )
+ fbox.p.y = pt.y;
+ if ( pt.x > fbox.q.x )
+ fbox.q.x = pt.x;
+ if ( pt.y > fbox.q.y )
+ fbox.q.y = pt.y;
+ }
+ /* Transform the result back to user coordinates. */
+ dbox.p.x = fixed2float(fbox.p.x);
+ dbox.p.y = fixed2float(fbox.p.y);
+ dbox.q.x = fixed2float(fbox.q.x);
+ dbox.q.y = fixed2float(fbox.q.y);
+ return gs_bbox_transform_inverse(&dbox, &ctm_only(pgs), pbox);
+}
+
+/* ------ Enumerators ------ */
+
+/* Start enumerating a path */
+int
+gs_path_enum_init(gs_path_enum *penum, const gs_state *pgs)
+{ gx_path *copied_path = gx_path_alloc(pgs->memory, "gs_path_enum_init");
+ int code;
+
+ if ( copied_path == 0 )
+ return_error(gs_error_VMerror);
+ code = gx_path_copy(pgs->path, copied_path, 1);
+ if ( code < 0 )
+ { gs_free_object(pgs->memory, copied_path, "gs_path_enum_init");
+ return code;
+ }
+ gx_path_enum_init(penum, copied_path);
+ penum->pgs = pgs;
+ penum->copied_path = copied_path;
+ return 0;
+}
+
+/* Enumerate the next element of a path. */
+/* If the path is finished, return 0; */
+/* otherwise, return the element type. */
+int
+gs_path_enum_next(gs_path_enum *penum, gs_point ppts[3])
+{ gs_fixed_point fpts[3];
+ gs_state *pgs = (gs_state *)penum->pgs; /* discard const */
+ int pe_op = gx_path_enum_next(penum, fpts);
+ int code;
+
+ switch ( pe_op )
+ {
+ case 0: /* all done */
+ case gs_pe_closepath:
+ break;
+ case gs_pe_curveto:
+ if ( (code = gs_itransform(pgs,
+ fixed2float(fpts[1].x),
+ fixed2float(fpts[1].y),
+ &ppts[1])) < 0 ||
+ (code = gs_itransform(pgs,
+ fixed2float(fpts[2].x),
+ fixed2float(fpts[2].y),
+ &ppts[2])) < 0 )
+ return code;
+ case gs_pe_moveto:
+ case gs_pe_lineto:
+ if ( (code = gs_itransform(pgs,
+ fixed2float(fpts[0].x),
+ fixed2float(fpts[0].y),
+ &ppts[0])) < 0 )
+ return code;
+ default: /* error */
+ break;
+ }
+ return pe_op;
+}
+
+/* Clean up after a pathforall. */
+void
+gs_path_enum_cleanup(gs_path_enum *penum)
+{ if ( penum->copied_path != 0 ) /* don't do it twice ... */
+ /* shouldn't be needed! */
+ { gx_path_release(penum->copied_path);
+ gs_free_object(penum->pgs->memory, penum->copied_path,
+ "gs_path_enum_cleanup");
+ penum->path = 0;
+ penum->copied_path = 0;
+ }
+}
diff --git a/pstoraster/gspath2.h b/pstoraster/gspath2.h
new file mode 100644
index 000000000..717b437be
--- /dev/null
+++ b/pstoraster/gspath2.h
@@ -0,0 +1,35 @@
+/* Copyright (C) 1994 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gspath2.h */
+/* Client interface to Level 2 path facilities */
+/* Requires gsmatrix.h */
+
+/* Miscellaneous */
+int gs_setbbox(P5(gs_state *, floatp, floatp, floatp, floatp));
+
+/* Rectangles */
+int gs_rectappend(P3(gs_state *, const gs_rect *, uint));
+int gs_rectclip(P3(gs_state *, const gs_rect *, uint));
+int gs_rectfill(P3(gs_state *, const gs_rect *, uint));
+int gs_rectstroke(P4(gs_state *, const gs_rect *, uint, const gs_matrix *));
diff --git a/pstoraster/gspcolor.c b/pstoraster/gspcolor.c
new file mode 100644
index 000000000..cba66ef03
--- /dev/null
+++ b/pstoraster/gspcolor.c
@@ -0,0 +1,743 @@
+/* Copyright (C) 1993, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gspcolor.c */
+/* Pattern color operators and procedures for Ghostscript library */
+#include "math_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gsstruct.h"
+#include "gsutil.h" /* for gs_next_ids */
+#include "gxarith.h"
+#include "gxfixed.h"
+#include "gxmatrix.h"
+#include "gxcoord.h" /* for gs_concat, gx_tr'_to_fixed */
+#include "gxcspace.h" /* for gscolor2.h */
+#include "gxcolor2.h"
+#include "gxdcolor.h"
+#include "gxdevice.h"
+#include "gxdevmem.h"
+#include "gxclip2.h"
+#include "gspath.h"
+#include "gxpath.h"
+#include "gxpcolor.h"
+#include "gzstate.h"
+#include "gsimage.h"
+
+private_st_client_pattern();
+public_st_pattern_instance();
+
+/* Define the Pattern color space. */
+extern cs_proc_remap_color(gx_remap_Pattern);
+private cs_proc_install_cspace(gx_install_Pattern);
+private cs_proc_adjust_cspace_count(gx_adjust_cspace_Pattern);
+private cs_proc_init_color(gx_init_Pattern);
+private cs_proc_adjust_color_count(gx_adjust_color_Pattern);
+private struct_proc_enum_ptrs(gx_enum_ptrs_Pattern);
+private struct_proc_reloc_ptrs(gx_reloc_ptrs_Pattern);
+const gs_color_space_type
+ gs_color_space_type_Pattern =
+ { gs_color_space_index_Pattern, -1, false,
+ gx_init_Pattern, gx_no_concrete_space,
+ gx_no_concretize_color, NULL,
+ gx_remap_Pattern, gx_install_Pattern,
+ gx_adjust_cspace_Pattern, gx_adjust_color_Pattern,
+ gx_enum_ptrs_Pattern, gx_reloc_ptrs_Pattern
+ };
+
+/* makepattern */
+private int compute_inst_matrix(P3(gs_pattern_instance *pinst,
+ const gs_state *saved,
+ gs_rect *pbbox));
+int
+gs_makepattern(gs_client_color *pcc, const gs_client_pattern *pcp,
+ const gs_matrix *pmat, gs_state *pgs, gs_memory_t *mem)
+{ gs_memory_t *save_mem = pgs->memory;
+ gs_pattern_instance inst;
+ gs_pattern_instance *pinst;
+ gs_state *saved;
+ gs_rect bbox;
+ gs_fixed_rect cbox;
+ int code;
+
+ if ( mem == 0 )
+ mem = save_mem;
+ rc_alloc_struct_1(pinst, gs_pattern_instance, &st_pattern_instance,
+ mem, return_error(gs_error_VMerror),
+ "gs_makepattern");
+ inst.rc = pinst->rc;
+ pgs->memory = mem;
+ saved = gs_gstate(pgs);
+ pgs->memory = save_mem;
+ if ( saved == 0 )
+ return_error(gs_error_VMerror);
+ gs_concat(saved, pmat);
+ gs_newpath(saved);
+ inst.template = *pcp;
+ inst.saved = saved;
+ code = compute_inst_matrix(&inst, saved, &bbox);
+ if ( code < 0 )
+ { gs_state_free(saved);
+ return code;
+ }
+#define mat inst.matrix
+ if_debug6('t', "[t]matrix=[%g %g %g %g %g %g]\n",
+ mat.xx, mat.xy, mat.yx, mat.yy, mat.tx, mat.ty);
+ /* Check for singular tiling matrix. */
+ if ( fabs(mat.xx * mat.yy - mat.xy * mat.yx) < 1.0e-6 )
+ { gs_state_free(saved);
+ return_error(gs_error_rangecheck);
+ }
+ if_debug8('t', "[t]bbox=(%g,%g),(%g,%g) ibbox=(%g,%g),(%g,%g)\n",
+ bbox.p.x, bbox.p.y, bbox.q.x, bbox.q.y,
+ inst.bbox.p.x, inst.bbox.p.y, inst.bbox.q.x, inst.bbox.q.y);
+ { float bbw = bbox.q.x - bbox.p.x;
+ float bbh = bbox.q.y - bbox.p.y;
+ /* If the step and the size agree to within 1/2 pixel, */
+ /* make them the same. */
+ inst.size.x = (int)(bbw + 0.8); /* 0.8 is arbitrary */
+ inst.size.y = (int)(bbh + 0.8);
+ if ( mat.xy == 0 && mat.yx == 0 &&
+ fabs(fabs(mat.xx) - bbw) < 0.5 &&
+ fabs(fabs(mat.yy) - bbh) < 0.5
+ )
+ { gs_scale(saved, fabs(inst.size.x / mat.xx),
+ fabs(inst.size.y / mat.yy));
+ code = compute_inst_matrix(&inst, saved, &bbox);
+ if ( code < 0 )
+ { gs_state_free(saved);
+ return code;
+ }
+ if_debug2('t',
+ "[t]adjusted XStep & YStep to size=(%d,%d)\n",
+ inst.size.x, inst.size.y);
+ }
+ }
+ inst.offset.x = bbox.p.x - mat.tx;
+ inst.offset.y = bbox.p.y - mat.ty;
+ gx_translate_to_fixed(saved, -float2fixed(inst.offset.x),
+ -float2fixed(inst.offset.y));
+#undef mat
+ cbox.p.x = fixed_0;
+ cbox.p.y = fixed_0;
+ cbox.q.x = int2fixed(inst.size.x);
+ cbox.q.y = int2fixed(inst.size.y);
+ code = gx_clip_to_rectangle(saved, &cbox);
+ if ( code < 0 )
+ { gs_state_free(saved);
+ return code;
+ }
+ inst.id = gs_next_ids(1);
+ *pinst = inst;
+ pcc->pattern = pinst;
+ return 0;
+#undef mat
+}
+/* Compute the instance matrix and bounding box from the step values */
+/* and the saved matrix. */
+private int
+compute_inst_matrix(gs_pattern_instance *pinst, const gs_state *saved,
+ gs_rect *pbbox)
+{ int code;
+ pinst->matrix.xx = pinst->template.XStep * saved->ctm.xx;
+ pinst->matrix.xy = pinst->template.XStep * saved->ctm.xy;
+ pinst->matrix.yx = pinst->template.YStep * saved->ctm.yx;
+ pinst->matrix.yy = pinst->template.YStep * saved->ctm.yy;
+ pinst->matrix.tx = saved->ctm.tx;
+ pinst->matrix.ty = saved->ctm.ty;
+ if ( (code = gs_bbox_transform(&pinst->template.BBox, &ctm_only(saved),
+ pbbox)) < 0 ||
+ (code = gs_bbox_transform_inverse(pbbox, &pinst->matrix,
+ &pinst->bbox)) < 0
+ )
+ return code;
+ return 0;
+}
+
+/* setpattern */
+int
+gs_setpattern(gs_state *pgs, const gs_client_color *pcc)
+{ int code = gs_setpatternspace(pgs);
+ if ( code < 0 )
+ return code;
+ return gs_setcolor(pgs, pcc);
+}
+
+/* setpatternspace */
+/* This does all the work of setpattern except for the final setcolor. */
+int
+gs_setpatternspace(gs_state *pgs)
+{ int code = 0;
+ if ( pgs->color_space->type->index != gs_color_space_index_Pattern )
+ { gs_color_space cs;
+ cs.params.pattern.base_space =
+ *(gs_paint_color_space *)pgs->color_space;
+ cs.params.pattern.has_base_space = true;
+ cs.type = &gs_color_space_type_Pattern;
+ code = gs_setcolorspace(pgs, &cs);
+ }
+ return code;
+}
+
+/* getpattern */
+/* This is only intended for the benefit of pattern PaintProcs. */
+const gs_client_pattern *
+gs_getpattern(const gs_client_color *pcc)
+{ return &pcc->pattern->template;
+}
+
+/* makebitmappattern */
+private int bitmap_PaintProc(P2(const gs_client_color *, gs_state *));
+int
+gs_makebitmappattern(gs_client_color *pcc, const gx_tile_bitmap *tile,
+ bool mask, gs_state *pgs, gs_memory_t *mem)
+{ gs_client_pattern pat;
+ gs_matrix mat, smat;
+
+ if ( tile->raster != bitmap_raster(tile->rep_width) )
+ return_error(gs_error_rangecheck);
+ uid_set_UniqueID(&pat.uid, gs_next_ids(1));
+ pat.PaintType = (mask ? 2 : 1);
+ pat.TilingType = 1;
+ pat.BBox.p.x = 0;
+ pat.BBox.p.y = 0;
+ pat.BBox.q.x = tile->rep_width;
+ pat.BBox.q.y = tile->rep_height;
+ pat.XStep = tile->rep_width;
+ pat.YStep = tile->rep_height;
+ pat.PaintProc = bitmap_PaintProc;
+ pat.client_data = tile->data;
+ gs_make_identity(&mat);
+ gs_currentmatrix(pgs, &smat);
+ if ( smat.yy > 0 )
+ mat.yy = -1;
+ gs_setmatrix(pgs, &mat);
+ gs_makepattern(pcc, &pat, &mat, pgs, mem);
+ gs_setmatrix(pgs, &smat);
+ return 0;
+}
+private int
+bitmap_PaintProc(const gs_client_color *pcolor, gs_state *pgs)
+{ gs_image_enum *pen = gs_image_enum_alloc(gs_state_memory(pgs), "tppp");
+ const gs_client_pattern *ppat = gs_getpattern(pcolor);
+ const byte *dp = ppat->client_data;
+ gs_image_t image;
+ int n;
+ uint nbytes, raster, used;
+
+ if ( ppat->PaintType == 2 )
+ gs_image_t_init_mask(&image, true);
+ else
+ { gs_image_t_init_gray(&image);
+ image.Decode[0] = 1.0;
+ image.Decode[1] = 0.0;
+ }
+ image.Width = (int)ppat->XStep;
+ image.Height = (int)ppat->YStep;
+ raster = bitmap_raster(image.Width);
+ nbytes = (image.Width + 7) >> 3;
+ gs_image_init(pen, &image, false, pgs);
+ for ( n = image.Height; n > 0; dp += raster, --n )
+ gs_image_next(pen, dp, nbytes, &used);
+ gs_image_cleanup(pen);
+ gs_free_object(gs_state_memory(pgs), pen, "tppp");
+ return 0;
+}
+
+/* ------ Color space implementation ------ */
+
+/* Pattern device color types. */
+/* We need a masked analogue of each of the non-pattern types, */
+/* to handle uncolored patterns. */
+/* We use 'masked_fill_rect' instead of 'masked_fill_rectangle' */
+/* in order to limit identifier lengths to 32 characters. */
+private dev_color_proc_load(gx_dc_pattern_load);
+private dev_color_proc_fill_rectangle(gx_dc_pattern_fill_rectangle);
+private struct_proc_enum_ptrs(dc_pattern_enum_ptrs);
+private struct_proc_reloc_ptrs(dc_pattern_reloc_ptrs);
+private dev_color_proc_load(gx_dc_pure_masked_load);
+private dev_color_proc_fill_rectangle(gx_dc_pure_masked_fill_rect);
+private struct_proc_enum_ptrs(dc_masked_enum_ptrs);
+private struct_proc_reloc_ptrs(dc_masked_reloc_ptrs);
+private dev_color_proc_load(gx_dc_binary_masked_load);
+private dev_color_proc_fill_rectangle(gx_dc_binary_masked_fill_rect);
+private struct_proc_enum_ptrs(dc_binary_masked_enum_ptrs);
+private struct_proc_reloc_ptrs(dc_binary_masked_reloc_ptrs);
+private dev_color_proc_load(gx_dc_colored_masked_load);
+private dev_color_proc_fill_rectangle(gx_dc_colored_masked_fill_rect);
+/* The device color types are exported for gxpcmap.c. */
+const gx_device_color_procs
+ gx_dc_pattern =
+ { gx_dc_pattern_load, gx_dc_pattern_fill_rectangle,
+ dc_pattern_enum_ptrs, dc_pattern_reloc_ptrs
+ },
+ gx_dc_pure_masked =
+ { gx_dc_pure_masked_load, gx_dc_pure_masked_fill_rect,
+ dc_masked_enum_ptrs, dc_masked_reloc_ptrs
+ },
+ gx_dc_binary_masked =
+ { gx_dc_binary_masked_load, gx_dc_binary_masked_fill_rect,
+ dc_binary_masked_enum_ptrs, dc_binary_masked_reloc_ptrs
+ },
+ gx_dc_colored_masked =
+ { gx_dc_colored_masked_load, gx_dc_colored_masked_fill_rect,
+ dc_masked_enum_ptrs, dc_masked_reloc_ptrs
+ };
+#undef gx_dc_type_pattern
+const gx_device_color_procs _ds *gx_dc_type_pattern = &gx_dc_pattern;
+#define gx_dc_type_pattern (&gx_dc_pattern)
+/* GC procedures */
+#define cptr ((gx_device_color *)vptr)
+private ENUM_PTRS_BEGIN(dc_pattern_enum_ptrs) {
+ return dc_masked_enum_ptrs(vptr, size, index - 1, pep);
+ }
+ case 0:
+ { gx_color_tile *tile = cptr->colors.pattern.p_tile;
+ ENUM_RETURN((tile == 0 ? tile : tile - tile->index));
+ }
+ENUM_PTRS_END
+private RELOC_PTRS_BEGIN(dc_pattern_reloc_ptrs) {
+ gx_color_tile *tile = cptr->colors.pattern.p_tile;
+ if ( tile != 0 )
+ { uint index = tile->index;
+ RELOC_TYPED_OFFSET_PTR(gx_device_color, colors.pattern.p_tile, index);
+ }
+ dc_masked_reloc_ptrs(vptr, size, gcst);
+} RELOC_PTRS_END
+private ENUM_PTRS_BEGIN(dc_masked_enum_ptrs) return 0;
+ case 0:
+ { gx_color_tile *mask = cptr->mask;
+ ENUM_RETURN((mask == 0 ? mask : mask - mask->index));
+ }
+ENUM_PTRS_END
+private RELOC_PTRS_BEGIN(dc_masked_reloc_ptrs) {
+ gx_color_tile *mask = cptr->mask;
+ if ( mask != 0 )
+ { uint index = mask->index;
+ RELOC_TYPED_OFFSET_PTR(gx_device_color, mask, index);
+ }
+} RELOC_PTRS_END
+private ENUM_PTRS_BEGIN(dc_binary_masked_enum_ptrs) {
+ return (*gx_dc_procs_ht_binary.enum_ptrs)(vptr, size, index - 1, pep);
+ }
+ case 0:
+ return dc_masked_enum_ptrs(vptr, size, index, pep);
+} }
+private RELOC_PTRS_BEGIN(dc_binary_masked_reloc_ptrs) {
+ dc_masked_reloc_ptrs(vptr, size, gcst);
+ (*gx_dc_procs_ht_binary.reloc_ptrs)(vptr, size, gcst);
+} RELOC_PTRS_END
+#undef cptr
+
+/* Macros for pattern loading */
+private int near pattern_load(P2(gx_device_color *, const gs_state *));
+#define FINISH_PATTERN_LOAD\
+ while ( !gx_pattern_cache_lookup(pdevc, pgs) )\
+ { code = pattern_load(pdevc, pgs);\
+ if ( code < 0 ) break;\
+ }\
+ return code;
+
+/* Ensure that a colored Pattern is loaded in the cache. */
+private int
+gx_dc_pattern_load(gx_device_color *pdevc, const gs_state *pgs)
+{ int code = 0;
+ FINISH_PATTERN_LOAD
+}
+/* Ensure that an uncolored Pattern is loaded in the cache. */
+private int
+gx_dc_pure_masked_load(gx_device_color *pdevc, const gs_state *pgs)
+{ int code = (*gx_dc_procs_pure.load)(pdevc, pgs);
+ if ( code < 0 )
+ return code;
+ FINISH_PATTERN_LOAD
+}
+private int
+gx_dc_binary_masked_load(gx_device_color *pdevc, const gs_state *pgs)
+{ int code = (*gx_dc_procs_ht_binary.load)(pdevc, pgs);
+ if ( code < 0 )
+ return code;
+ FINISH_PATTERN_LOAD
+}
+private int
+gx_dc_colored_masked_load(gx_device_color *pdevc, const gs_state *pgs)
+{ int code = (*gx_dc_procs_ht_colored.load)(pdevc, pgs);
+ if ( code < 0 )
+ return code;
+ FINISH_PATTERN_LOAD
+}
+
+/* Look up a pattern color in the cache. */
+bool
+gx_pattern_cache_lookup(gx_device_color *pdevc, const gs_state *pgs)
+{ gx_pattern_cache *pcache = pgs->pattern_cache;
+ gx_bitmap_id id = pdevc->id;
+ if ( id == gx_no_bitmap_id )
+ { color_set_null_pattern(pdevc);
+ return true;
+ }
+ if ( pcache != 0 )
+ { gx_color_tile *ctile = &pcache->tiles[id % pcache->num_tiles];
+ if ( ctile->id == id &&
+ (pdevc->type != &gx_dc_pattern ||
+ ctile->depth == gs_currentdevice_inline(pgs)->color_info.depth)
+ )
+ { if ( pdevc->type == &gx_dc_pattern ) /* colored */
+ { pdevc->colors.pattern.p_tile = ctile;
+ color_set_phase_mod(pdevc, pgs->ht_phase.x,
+ pgs->ht_phase.y,
+ ctile->tbits.rep_width,
+ ctile->tbits.rep_height);
+ }
+ pdevc->mask =
+ (ctile->tmask.data == 0 ? (gx_color_tile *)0 :
+ ctile);
+ return true;
+ }
+ }
+ return false;
+}
+/* Remap the current (Pattern) color before trying the cache again. */
+private int near
+pattern_load(gx_device_color *pdevc, const gs_state *pgs)
+{ const gs_color_space *pcs = pgs->color_space;
+ /****** pgs->ccolor IS WRONG ******/
+ return (*pcs->type->remap_color)(pgs->ccolor, pcs, pdevc, pgs);
+}
+
+#undef FINISH_PATTERN_LOAD
+
+/* Macros for filling with a possibly masked pattern. */
+/****** PHASE IS WRONG HERE ******/
+#define BEGIN_PATTERN_FILL\
+ { gx_device_tile_clip cdev;\
+ gx_device *pcdev;\
+ int code;\
+ if ( pdevc->mask == 0 ) /* no clipping */\
+ { code = 0;\
+ pcdev = dev;\
+ }\
+ else\
+ { code = tile_clip_initialize(&cdev,\
+ &pdevc->mask->tmask, dev,\
+ 0, 0);\
+ if ( code < 0 )\
+ return code;\
+ pcdev = (gx_device *)&cdev;\
+ }
+#define CLIPPING_FILL (pcdev == (gx_device *)&cdev)
+#define END_PATTERN_FILL\
+ return code;\
+ }
+
+/* Macros for filling with non-standard X and Y stepping. */
+/* Free variables: x, y, w, h, ptile. */
+/* tbits_or_tmask is whichever of tbits and tmask is supplying */
+/* the tile size. */
+/* This implementation could be sped up considerably! */
+#define BEGIN_STEPS(tbits_or_tmask)\
+ { gs_rect bbox, ibbox;\
+ gs_point offset;\
+ int x0 = x, x1 = x + w, y0 = y, y1 = y + h;\
+ int w0 = w, h0 = h;\
+ int i0, i1, j0, j1, i, j;\
+\
+ bbox.p.x = x0, bbox.p.y = y0;\
+ bbox.q.x = x1, bbox.q.y = y1;\
+ gs_bbox_transform_inverse(&bbox, &ptile->matrix, &ibbox);\
+ if_debug10('T',\
+ "[T]x,y=(%d,%d) w,h=(%d,%d) => (%g,%g),(%g,%g), offset=(%g,%g)\n",\
+ x, y, w, h,\
+ ibbox.p.x, ibbox.p.y, ibbox.q.x, ibbox.q.y,\
+ ptile->offset.x, ptile->offset.y);\
+ offset.x = ptile->matrix.tx + ptile->offset.x;\
+ offset.y = ptile->matrix.ty + ptile->offset.y;\
+ i0 = (int)(ibbox.p.x - ptile->bbox.q.x);\
+ i1 = (int)ceil(ibbox.q.x - ptile->bbox.p.x);\
+ j0 = (int)(ibbox.p.y - ptile->bbox.q.y);\
+ j1 = (int)ceil(ibbox.q.y - ptile->bbox.p.y);\
+ if_debug4('T', "[T]i=(%d,%d) j=(%d,%d)\n", i0, i1, j0, j1);\
+ for ( i = i0; i < i1; i++ )\
+ for ( j = j0; j < j1; j++ )\
+ { int xoff, yoff;\
+ x = (int)(ptile->matrix.xx * i +\
+ ptile->matrix.yx * j + offset.x);\
+ y = (int)(ptile->matrix.xy * i +\
+ ptile->matrix.yy * j + offset.y);\
+ if_debug4('T', "[T]i=%d j=%d x,y=(%d,%d)", i, j, x, y);\
+ w = ptile->tbits_or_tmask.size.x;\
+ h = ptile->tbits_or_tmask.size.y;\
+ if ( x < x0 ) xoff = x0 - x, x = x0, w -= xoff;\
+ else xoff = 0;\
+ if ( y < y0 ) yoff = y0 - y, y = y0, h -= yoff;\
+ else yoff = 0;\
+ if ( x + w > x1 ) w = x1 - x;\
+ if ( y + h > y1 ) h = y1 - y;\
+ if_debug4('T', "=>(%d,%d) w,h=(%d,%d)\n",\
+ x, y, w, h);\
+ if ( w > 0 && h > 0 )\
+ { if ( CLIPPING_FILL )\
+ tile_clip_set_phase(&cdev, xoff-x, yoff-y)
+#define SOURCE_STEP(src)\
+ (src).sdata = source->sdata + (y - y0) * source->sraster;\
+ (src).sourcex = source->sourcex + (x - x0);\
+ (src).sraster = source->sraster;\
+ (src).id = (w == w0 && h == h0 ?\
+ source->id : gx_no_bitmap_id);\
+ (src).scolors[0] = source->scolors[0];\
+ (src).scolors[1] = source->scolors[1];\
+ (src).use_scolors = source->use_scolors
+#define END_STEPS\
+ }\
+ }\
+ }
+
+/* Fill a rectangle with a colored Pattern. */
+/* Note that we treat this as "texture" for RasterOp. */
+private int
+gx_dc_pattern_fill_rectangle(const gx_device_color *pdevc, int x, int y,
+ int w, int h, gx_device *dev, gs_logical_operation_t lop,
+ const gx_rop_source_t *source)
+{ gx_color_tile *ptile = pdevc->colors.pattern.p_tile;
+ const gx_rop_source_t *rop_source =
+ (source == NULL ? &gx_rop_no_source : source);
+ gx_strip_bitmap *bits;
+
+ if ( ptile == 0 ) /* null pattern */
+ return 0;
+ bits = &ptile->tbits;
+ /****** PHASE IS WRONG HERE ******/
+ BEGIN_PATTERN_FILL
+ if ( ptile->is_simple )
+ { if ( source == NULL && lop_no_S_is_T(lop) )
+ code = (*dev_proc(pcdev, strip_tile_rectangle))(pcdev, bits,
+ x, y, w, h,
+ gx_no_color_index, gx_no_color_index,
+ pdevc->phase.x, pdevc->phase.y);
+ else
+ code = (*dev_proc(dev, strip_copy_rop))(dev,
+ rop_source->sdata, rop_source->sourcex,
+ rop_source->sraster, rop_source->id,
+ (rop_source->use_scolors ? rop_source->scolors : NULL),
+ bits, NULL,
+ x, y, w, h, pdevc->phase.x, pdevc->phase.y, lop);
+ }
+ else
+ { BEGIN_STEPS(tbits);
+ { const byte *data = bits->data + bits->raster * yoff;
+ bool full_transfer = (w == w0 && h == h0);
+ gx_bitmap_id id =
+ (full_transfer ? bits->id : gx_no_bitmap_id);
+
+ if ( source == NULL && lop_no_S_is_T(lop) )
+ code = (*dev_proc(pcdev, copy_color))(pcdev,
+ data, xoff, bits->raster, id,
+ x, y, w, h);
+ else
+ { /* We pass the partial pattern as the 'texture' */
+ /* even though it won't get repeated. */
+ gx_strip_bitmap data_tile;
+
+ data_tile.data = (byte *)data; /* actually const */
+ data_tile.raster = bits->raster;
+ data_tile.size.x = data_tile.rep_width = xoff + w;
+ data_tile.size.y = data_tile.rep_height = h;
+ data_tile.id = id;
+ /****** FOLLOWING IS WRONG ******/
+ data_tile.shift = data_tile.rep_shift = 0;
+ code = (*dev_proc(dev, strip_copy_rop))(dev,
+ rop_source->sdata + (y - y0) * rop_source->sraster,
+ rop_source->sourcex + (x - x0),
+ rop_source->sraster,
+ (full_transfer ? rop_source->id :
+ gx_no_bitmap_id),
+ (rop_source->use_scolors ?
+ rop_source->scolors : NULL),
+ &data_tile, NULL,
+ x, y, w, h, xoff, 0, lop);
+ }
+ if ( code < 0 )
+ return code;
+ }
+ END_STEPS
+ }
+ END_PATTERN_FILL
+}
+/* Fill a rectangle with an uncolored Pattern. */
+/* Note that we treat this as "texture" for RasterOp. */
+private int
+gx_dc_pure_masked_fill_rect(const gx_device_color *pdevc, int x, int y,
+ int w, int h, gx_device *dev, gs_logical_operation_t lop,
+ const gx_rop_source_t *source)
+{ gx_color_tile *ptile = pdevc->mask;
+
+ BEGIN_PATTERN_FILL
+ if ( ptile == 0 || ptile->is_simple )
+ code = (*gx_dc_procs_pure.fill_rectangle)(pdevc, x, y, w, h,
+ pcdev, lop, source);
+ else
+ { BEGIN_STEPS(tmask);
+ if ( source == NULL )
+ code = (*gx_dc_procs_pure.fill_rectangle)(pdevc, x, y, w, h,
+ pcdev, lop, source);
+ else
+ { gx_rop_source_t step_source;
+ SOURCE_STEP(step_source);
+ code = (*gx_dc_procs_pure.fill_rectangle)(pdevc,
+ x, y, w, h,
+ pcdev, lop, &step_source);
+ }
+ if ( code < 0 )
+ return code;
+ END_STEPS
+ }
+ END_PATTERN_FILL
+}
+private int
+gx_dc_binary_masked_fill_rect(const gx_device_color *pdevc, int x, int y,
+ int w, int h, gx_device *dev, gs_logical_operation_t lop,
+ const gx_rop_source_t *source)
+{ gx_color_tile *ptile = pdevc->mask;
+
+ BEGIN_PATTERN_FILL
+ if ( ptile == 0 || ptile->is_simple )
+ code = (*gx_dc_procs_ht_binary.fill_rectangle)(pdevc,
+ x, y, w, h,
+ pcdev, lop, source);
+ else
+ { BEGIN_STEPS(tmask);
+ if ( source == NULL )
+ code = (*gx_dc_procs_ht_binary.fill_rectangle)(pdevc,
+ x, y, w, h,
+ pcdev, lop, source);
+ else
+ { gx_rop_source_t step_source;
+ SOURCE_STEP(step_source);
+ code = (*gx_dc_procs_ht_binary.fill_rectangle)(pdevc,
+ x, y, w, h,
+ pcdev, lop, &step_source);
+ }
+ if ( code < 0 )
+ return code;
+ END_STEPS
+ }
+ END_PATTERN_FILL
+}
+private int
+gx_dc_colored_masked_fill_rect(const gx_device_color *pdevc, int x, int y,
+ int w, int h, gx_device *dev, gs_logical_operation_t lop,
+ const gx_rop_source_t *source)
+{ gx_color_tile *ptile = pdevc->mask;
+
+ BEGIN_PATTERN_FILL
+ if ( ptile == 0 || ptile->is_simple )
+ code = (*gx_dc_procs_ht_colored.fill_rectangle)(pdevc,
+ x, y, w, h,
+ pcdev, lop, source);
+ else
+ { BEGIN_STEPS(tmask);
+ if ( source == NULL )
+ code = (*gx_dc_procs_ht_colored.fill_rectangle)(pdevc,
+ x, y, w, h,
+ pcdev, lop, source);
+ else
+ { gx_rop_source_t step_source;
+ SOURCE_STEP(step_source);
+ code = (*gx_dc_procs_ht_colored.fill_rectangle)(pdevc,
+ x, y, w, h,
+ pcdev, lop, &step_source);
+ }
+ if ( code < 0 )
+ return code;
+ END_STEPS
+ }
+ END_PATTERN_FILL
+}
+
+#undef BEGIN_PATTERN_FILL
+#undef END_PATTERN_FILL
+
+/* Initialize a Pattern color. */
+private void
+gx_init_Pattern(gs_client_color *pcc, const gs_color_space *pcs)
+{ if ( pcs->params.pattern.has_base_space )
+ { const gs_color_space *pbcs =
+ (const gs_color_space *)&pcs->params.pattern.base_space;
+ cs_init_color(pcc, pbcs);
+ }
+ /*pcc->pattern = 0;*/ /* cs_full_init_color handles this */
+}
+
+/* Install a Pattern color space. */
+private int
+gx_install_Pattern(gs_color_space *pcs, gs_state *pgs)
+{ if ( !pcs->params.pattern.has_base_space )
+ return 0;
+ return (*pcs->params.pattern.base_space.type->install_cspace)
+ ((gs_color_space *)&pcs->params.pattern.base_space, pgs);
+}
+
+/* Adjust the reference counts for Pattern color spaces or colors. */
+private void
+gx_adjust_cspace_Pattern(const gs_color_space *pcs, gs_state *pgs, int delta)
+{ if ( pcs->params.pattern.has_base_space )
+ (*pcs->params.pattern.base_space.type->adjust_cspace_count)
+ ((const gs_color_space *)&pcs->params.pattern.base_space, pgs, delta);
+}
+
+private void
+gx_adjust_color_Pattern(const gs_client_color *pcc, const gs_color_space *pcs,
+ gs_state *pgs, int delta)
+{ gs_pattern_instance *pinst = pcc->pattern;
+ if ( pinst != 0 && (pinst->rc.ref_count += delta) == 0 )
+ { /* Release all the storage associated with the instance. */
+ gs_state *saved = pinst->saved;
+ gs_state_free(saved);
+ gs_free_object(saved->memory, pinst,
+ "gx_adjust_color_Pattern");
+ }
+ if ( pcs->params.pattern.has_base_space )
+ (*pcs->params.pattern.base_space.type->adjust_color_count)
+ (pcc, (const gs_color_space *)&pcs->params.pattern.base_space,
+ pgs, delta);
+}
+
+/* GC procedures */
+
+#define pcs ((gs_color_space *)vptr)
+
+private ENUM_PTRS_BEGIN_PROC(gx_enum_ptrs_Pattern) {
+ if ( !pcs->params.pattern.has_base_space )
+ return 0;
+ return (*pcs->params.pattern.base_space.type->enum_ptrs)
+ (&pcs->params.pattern.base_space,
+ sizeof(pcs->params.pattern.base_space), index, pep);
+} ENUM_PTRS_END_PROC
+private RELOC_PTRS_BEGIN(gx_reloc_ptrs_Pattern) {
+ if ( !pcs->params.pattern.has_base_space )
+ return;
+ (*pcs->params.pattern.base_space.type->reloc_ptrs)
+ (&pcs->params.pattern.base_space, sizeof(gs_paint_color_space), gcst);
+} RELOC_PTRS_END
+
+#undef pcs
diff --git a/pstoraster/gspenum.h b/pstoraster/gspenum.h
new file mode 100644
index 000000000..9c3745513
--- /dev/null
+++ b/pstoraster/gspenum.h
@@ -0,0 +1,39 @@
+/* Copyright (C) 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gspenum.h */
+/* Common definitions for client interface to path enumeration */
+
+#ifndef gspenum_INCLUDED
+# define gspenum_INCLUDED
+
+/* Define the path element types. */
+#define gs_pe_moveto 1
+#define gs_pe_lineto 2
+#define gs_pe_curveto 3
+#define gs_pe_closepath 4
+
+/* Define an abstract type for the path enumerator. */
+typedef struct gs_path_enum_s gs_path_enum;
+
+#endif /* gspenum_INCLUDED */
diff --git a/pstoraster/gsrefct.h b/pstoraster/gsrefct.h
new file mode 100644
index 000000000..4b80e8cfe
--- /dev/null
+++ b/pstoraster/gsrefct.h
@@ -0,0 +1,137 @@
+/* Copyright (C) 1993, 1994, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gsrefct.h */
+/* Reference counting definitions */
+
+#ifndef gsrefct_INCLUDED
+# define gsrefct_INCLUDED
+
+/*
+ * In many places below, a do {...} while (0) avoids problems with a possible
+ * enclosing 'if'.
+ */
+
+/*
+ * A reference-counted object must include the following header:
+ * rc_header rc;
+ * The header need not be the first element of the object.
+ */
+typedef struct rc_header_s rc_header;
+struct rc_header_s {
+ long ref_count;
+#define rc_free_proc(proc)\
+ void proc(P3(gs_memory_t *, void *, client_name_t))
+ rc_free_proc((*free));
+};
+
+/* ------ Allocate/free ------ */
+
+rc_free_proc(rc_free_struct_only);
+#define rc_alloc_struct_n(vp, typ, pstyp, mem, errstat, cname, rcinit)\
+ do\
+ { if ( (vp = gs_alloc_struct(mem, typ, pstyp, cname)) == 0 )\
+ errstat;\
+ vp->rc.ref_count = rcinit;\
+ vp->rc.free = rc_free_struct_only;\
+ }\
+ while (0)
+#define rc_alloc_struct_0(vp, typ, pstype, mem, errstat, cname)\
+ rc_alloc_struct_n(vp, typ, pstype, mem, errstat, cname, 0)
+#define rc_alloc_struct_1(vp, typ, pstype, mem, errstat, cname)\
+ rc_alloc_struct_n(vp, typ, pstype, mem, errstat, cname, 1)
+
+#define rc_free_struct(vp, mem, cname)\
+ (*vp->rc.free)(mem, (void *)vp, cname)
+
+/* ------ Reference counting ------ */
+
+/* Increment a reference count. */
+#define rc_increment(vp)\
+ do { if ( vp != 0 ) vp->rc.ref_count++; } while (0)
+
+/* Increment a reference count, allocating the structure if necessary. */
+#define rc_allocate_struct(vp, typ, pstype, mem, errstat, cname)\
+ do\
+ { if ( vp != 0 )\
+ vp->rc.ref_count++;\
+ else\
+ rc_alloc_struct_1(vp, typ, pstype, mem, errstat, cname);\
+ }\
+ while (0)
+
+/* Guarantee that a structure is allocated and is not shared. */
+#define rc_unshare_struct(vp, typ, pstype, mem, errstat, cname)\
+ do\
+ { if ( vp == 0 || vp->rc.ref_count > 1 )\
+ { typ *new;\
+ rc_alloc_struct_1(new, typ, pstype, mem, errstat, cname);\
+ if ( vp ) vp->rc.ref_count--;\
+ vp = new;\
+ }\
+ }\
+ while (0)
+
+/* Adjust a reference count either up or down. */
+#define rc_adjust_(vp, delta, mem, cname, body)\
+ do\
+ { if ( vp != 0 && !(vp->rc.ref_count += delta) )\
+ { rc_free_struct(vp, mem, cname);\
+ body;\
+ }\
+ }\
+ while (0)
+#define rc_adjust(vp, delta, mem, cname)\
+ rc_adjust_(vp, delta, mem, cname, vp = 0)
+#define rc_adjust_only(vp, delta, mem, cname)\
+ rc_adjust_(vp, delta, mem, cname, DO_NOTHING)
+#define rc_adjust_const(vp, delta, mem, cname)\
+ rc_adjust_only(vp, delta, mem, cname)
+#define rc_decrement(vp, mem, cname)\
+ rc_adjust(vp, -1, mem, cname)
+#define rc_decrement_only(vp, mem, cname)\
+ rc_adjust_only(vp, -1, mem, cname)
+
+/* Assign a pointer, adjusting reference counts. */
+#define rc_assign(vpto, vpfrom, mem, cname)\
+ do\
+ { if ( vpto != vpfrom )\
+ { rc_decrement_only(vpto, mem, cname);\
+ vpto = vpfrom;\
+ rc_increment(vpto);\
+ }\
+ }\
+ while (0)
+/* Adjust reference counts for assigning a pointer, */
+/* but don't do the assignment. We use this before assigning */
+/* an entire structure containing reference-counted pointers. */
+#define rc_pre_assign(vpto, vpfrom, mem, cname)\
+ do\
+ { if ( vpto != vpfrom )\
+ { rc_decrement_only(vpto, mem, cname);\
+ rc_increment(vpfrom);\
+ }\
+ }\
+ while (0)
+
+#endif /* gsrefct_INCLUDED */
diff --git a/pstoraster/gsrop.c b/pstoraster/gsrop.c
new file mode 100644
index 000000000..1cc0bb0a7
--- /dev/null
+++ b/pstoraster/gsrop.c
@@ -0,0 +1,91 @@
+/* Copyright (C) 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gsrop.c */
+/* RasterOp / transparency / render algorithm accessing for library */
+#include "gx.h"
+#include "gserrors.h"
+#include "gzstate.h"
+#include "gsrop.h"
+
+#define set_log_op(pgs, lopv)\
+ (pgs)->log_op = (lopv)
+
+/* setrasterop */
+void
+gs_setrasterop(gs_state *pgs, gs_rop3_t rop)
+{ set_log_op(pgs, (rop & rop3_1) | (pgs->log_op & ~rop3_1));
+}
+
+/* currentrasterop */
+gs_rop3_t
+gs_currentrasterop(const gs_state *pgs)
+{ return (gs_rop3_t)(pgs->log_op & lop_rop_mask);
+}
+
+/* setsourcetransparent */
+void
+gs_setsourcetransparent(gs_state *pgs, bool transparent)
+{ set_log_op(pgs,
+ (transparent ? pgs->log_op & ~lop_S_transparent:
+ pgs->log_op | lop_S_transparent));
+}
+
+/* currentsourcetransparent */
+bool
+gs_currentsourcetransparent(const gs_state *pgs)
+{ return (pgs->log_op & lop_S_transparent) != 0;
+}
+
+/* settexturetransparent */
+void
+gs_settexturetransparent(gs_state *pgs, bool transparent)
+{ set_log_op(pgs,
+ (transparent ? pgs->log_op & ~lop_T_transparent:
+ pgs->log_op | lop_T_transparent));
+}
+
+/* currenttexturetransparent */
+bool
+gs_currenttexturetransparent(const gs_state *pgs)
+{ return (pgs->log_op & lop_T_transparent) != 0;
+}
+
+/* setrenderalgorithm */
+int
+gs_setrenderalgorithm(gs_state *pgs, int render_algorithm)
+{ if ( render_algorithm < render_algorithm_min ||
+ render_algorithm > render_algorithm_max
+ )
+ return_error(gs_error_rangecheck);
+ set_log_op(pgs,
+ (render_algorithm << lop_ral_shift) |
+ (pgs->log_op & ~(lop_ral_mask << lop_ral_shift)));
+ return 0;
+}
+
+/* currentrenderalgorithm */
+int
+gs_currentrenderalgorithm(const gs_state *pgs)
+{ return (pgs->log_op >> lop_ral_shift) & lop_ral_mask;
+}
diff --git a/pstoraster/gsrop.h b/pstoraster/gsrop.h
new file mode 100644
index 000000000..243fa327a
--- /dev/null
+++ b/pstoraster/gsrop.h
@@ -0,0 +1,44 @@
+/* Copyright (C) 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gsrop.h */
+/* RasterOp / transparency / render algorithm procedure interface */
+
+#ifndef gsrop_INCLUDED
+# define gsrop_INCLUDED
+
+#include "gsropt.h"
+
+/* Procedural interface */
+
+void gs_setrasterop(P2(gs_state *, gs_rop3_t));
+gs_rop3_t
+ gs_currentrasterop(P1(const gs_state *));
+void gs_setsourcetransparent(P2(gs_state *, bool));
+bool gs_currentsourcetransparent(P1(const gs_state *));
+void gs_settexturetransparent(P2(gs_state *, bool));
+bool gs_currenttexturetransparent(P1(const gs_state *));
+int gs_setrenderalgorithm(P2(gs_state *, int));
+int gs_currentrenderalgorithm(P1(const gs_state *));
+
+#endif /* gsrop_INCLUDED */
diff --git a/pstoraster/gsropt.h b/pstoraster/gsropt.h
new file mode 100644
index 000000000..5091d9b8a
--- /dev/null
+++ b/pstoraster/gsropt.h
@@ -0,0 +1,180 @@
+/* Copyright (C) 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gsropt.h */
+/* RasterOp / transparency / render algorithm type definitions */
+
+#ifndef gsropt_INCLUDED
+# define gsropt_INCLUDED
+
+/*
+ * This file defines the types for some library extensions that are
+ * motivated by PCL5 and also made available for PostScript:
+ * RasterOp, source and pattern white-pixel transparency, and
+ * per-pixel "render algorithm" information.
+ */
+
+/*
+ * By the magic of Boolean algebra, we can operate on the rop codes using
+ * Boolean operators and get the right result. E.g., the value of
+ * (rop3_S & rop3_D) is the rop3 code for S & D. We just have to remember
+ * to mask results with rop2_1 or rop3_1 if necessary.
+ */
+
+/* 2-input RasterOp */
+typedef enum {
+ rop2_0 = 0,
+ rop2_S = 0xc, /* source */
+#define rop2_S_shift 2
+ rop2_D = 0xa, /* destination */
+#define rop2_D_shift 1
+ rop2_1 = 0xf,
+#define rop2_operand(shift, d, s)\
+ ((shift) == 2 ? (s) : (d))
+ rop2_default = rop2_S
+} gs_rop2_t;
+
+/*
+ * For the 3-input case, we follow H-P's inconsistent terminology:
+ * the transparency mode is called pattern transparency, but the third
+ * RasterOp operand is called texture, not pattern.
+ */
+
+/* 3-input RasterOp */
+typedef enum {
+ rop3_0 = 0,
+ rop3_T = 0xf0, /* texture */
+#define rop3_T_shift 4
+ rop3_S = 0xcc, /* source */
+#define rop3_S_shift 2
+ rop3_D = 0xaa, /* destination */
+#define rop3_D_shift 1
+ rop3_1 = 0xff,
+ rop3_default = rop3_T | rop3_S
+} gs_rop3_t;
+
+/* All the transformations on rop3s are designed so that */
+/* they can also be used on lops. The only place this costs anything */
+/* is in rop3_invert. */
+
+/*
+ * Invert an operand.
+ */
+#define rop3_invert_(op, mask, shift)\
+ ( (((op) & mask) >> shift) | (((op) & (rop3_1 - mask)) << shift) |\
+ ((op) & ~rop3_1) )
+#define rop3_invert_D(op) rop3_invert_(op, rop3_D, rop3_D_shift)
+#define rop3_invert_S(op) rop3_invert_(op, rop3_S, rop3_S_shift)
+#define rop3_invert_T(op) rop3_invert_(op, rop3_T, rop3_T_shift)
+/*
+ * Pin an operand to 0.
+ */
+#define rop3_know_0_(op, mask, shift)\
+ ( (((op) & (rop3_1 - mask)) << shift) | ((op) & ~mask) )
+#define rop3_know_S_0(op) rop3_know_0_(op, rop3_S, rop3_S_shift)
+#define rop3_know_T_0(op) rop3_know_0_(op, rop3_T, rop3_T_shift)
+/*
+ * Pin an operand to 1.
+ */
+#define rop3_know_1_(op, mask, shift)\
+ ( (((op) & mask) >> shift) | ((op) & ~(rop3_1 - mask)) )
+#define rop3_know_S_1(op) rop3_know_1_(op, rop3_S, rop3_S_shift)
+#define rop3_know_T_1(op) rop3_know_1_(op, rop3_T, rop3_T_shift)
+/*
+ * Swap S and T.
+ */
+#define rop3_swap_S_T(op)\
+ ( (((op) & rop3_S & ~rop3_T) << (rop3_T_shift - rop3_S_shift)) |\
+ (((op) & ~rop3_S & rop3_T) >> (rop3_T_shift - rop3_S_shift)) |\
+ ((op) & (~rop3_1 | (rop3_S ^ rop3_T))) )
+/*
+ * Account for transparency.
+ */
+#define rop3_use_D_when_0_(op, mask)\
+ (((op) & ~(rop3_1 - mask)) | (rop3_D & ~mask))
+#define rop3_use_D_when_1_(op, mask)\
+ (((op) & ~mask) | (rop3_D & mask))
+#define rop3_use_D_when_S_0(op) rop3_use_D_when_0_(op, rop3_S)
+#define rop3_use_D_when_S_1(op) rop3_use_D_when_1_(op, rop3_S)
+#define rop3_use_D_when_T_0(op) rop3_use_D_when_0_(op, rop3_T)
+#define rop3_use_D_when_T_1(op) rop3_use_D_when_1_(op, rop3_T)
+/*
+ * Invert the result.
+ */
+#define rop3_not(op) ((op) ^ rop3_1)
+/*
+ * Test whether an operand is used.
+ */
+#define rop3_uses_(op, mask, shift)\
+ ( ((((op) << shift) ^ (op)) & mask) != 0 )
+#define rop3_uses_D(op) rop3_uses_(op, rop3_D, rop3_D_shift)
+#define rop3_uses_S(op) rop3_uses_(op, rop3_S, rop3_S_shift)
+#define rop3_uses_T(op) rop3_uses_(op, rop3_T, rop3_T_shift)
+/*
+ * Test whether an operation is idempotent, i.e., whether
+ * f(D, S, T) = f(f(D, S, T), S, T). This is equivalent to the condition that
+ * for all values s and t, !( f(0,s,t) == 1 && f(1,s,t) == 0 ).
+ */
+#define rop3_is_idempotent(op)\
+ !( (op) & ~((op) << rop3_D_shift) & rop3_D )
+
+/* Transparency */
+#define source_transparent_default false
+#define pattern_transparent_default false
+
+/* Render algorithm */
+#define render_algorithm_default 0
+#define render_algorithm_min 0
+#define render_algorithm_max 14
+
+/*
+ * We define a logical operation as a RasterOp, transparency flags,
+ * and render algorithm all packed into a single integer.
+ * In principle, we should use a structure, but most C implementations
+ * implement structure values very inefficiently.
+ */
+#define lop_rop_mask rop3_1 /* must be low-order bits */
+#define lop_S_transparent 0x100
+#define lop_T_transparent 0x200
+#define lop_ral_shift 10
+#define lop_ral_mask 0xf
+typedef uint gs_logical_operation_t;
+#define lop_default\
+ (rop3_default |\
+ (source_transparent_default ? lop_S_transparent : 0) |\
+ (pattern_transparent_default ? lop_T_transparent : 0) |\
+ (render_algorithm_default << lop_ral_shift))
+
+/* Test whether a logical operation just sets D = x if y = 0. */
+#define lop_no_T_is_S(lop)\
+ (((lop) & (lop_S_transparent | (rop3_1 - rop3_T))) == (rop3_S & ~rop3_T))
+#define lop_no_S_is_T(lop)\
+ (((lop) & (lop_T_transparent | (rop3_1 - rop3_S))) == (rop3_T & ~rop3_S))
+/* Test whether a logical operation is idempotent. */
+#define lop_is_idempotent(lop) rop3_is_idempotent(lop)
+
+/* Define the interface to the table of 256 RasterOp procedures. */
+typedef unsigned rop_operand;
+typedef rop_operand (*rop_proc)(P3(rop_operand D, rop_operand S, rop_operand T));
+
+#endif /* gsropt_INCLUDED */
diff --git a/pstoraster/gsroptab.c b/pstoraster/gsroptab.c
new file mode 100644
index 000000000..efaccd08d
--- /dev/null
+++ b/pstoraster/gsroptab.c
@@ -0,0 +1,342 @@
+/* Copyright (C) 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gsroptab.c */
+/* Table of RasterOp procedures */
+#include "stdpre.h"
+#include "gsropt.h"
+
+/*
+ * The H-P documentation (probably copied from Microsoft documentation)
+ * specifies RasterOp algorithms using reverse Polish notation, with
+ * a = AND, o = OR, n = NOT, x = XOR
+ * We can use preprocessor macros to transcribe these algorithms almost
+ * literally into C code.
+ */
+
+#ifdef __PROTOTYPES__
+# define rop_proc(pname)\
+private rop_operand pname(rop_operand D, rop_operand S, rop_operand T)
+#else
+# define rop_proc(pname)\
+private rop_operand pname(D,S,T) rop_operand D; rop_operand S; rop_operand T;
+#endif
+#define r return
+
+#define a(u,v) (u&v)
+#define o(u,v) (u|v)
+#define x(u,v) (u^v)
+
+rop_proc(rop0) { r 0; } /* 0 */
+rop_proc(rop1) { r ~(D | (S | T)); } /* DTSoon */
+rop_proc(rop2) { r D & (~(S | T)); } /* DTSona */
+rop_proc(rop3) { r ~(S | T); } /* TSon */
+rop_proc(rop4) { r S & (~(D | T)); } /* SDTona */
+rop_proc(rop5) { r ~(D | T); } /* DTon */
+rop_proc(rop6) { r ~(T | (~(D ^ S))); } /* TDSxnon */
+rop_proc(rop7) { r ~(T | (D & S)); } /* TDSaon */
+rop_proc(rop8) { r S & (D & ~T); } /* SDTnaa */
+rop_proc(rop9) { r ~(T | (D ^ S)); } /* TDSxon */
+rop_proc(rop10) { r D & ~T; } /* DTna */
+rop_proc(rop11) { r ~(T | (S & ~D)); } /* TSDnaon */
+rop_proc(rop12) { r S & ~T; } /* STna */
+rop_proc(rop13) { r ~(T | (D & ~S)); } /* TDSnaon */
+rop_proc(rop14) { r ~(T | (~(D | S))); } /* TDSonon */
+rop_proc(rop15) { r ~T; } /* Tn */
+rop_proc(rop16) { r T & (~(D | S)); } /* TDSona */
+rop_proc(rop17) { r ~(D | S); } /* DSon */
+rop_proc(rop18) { r ~(S | (~(D ^ T))); } /* SDTxnon */
+rop_proc(rop19) { r ~(S | (D & T)); } /* SDTaon */
+rop_proc(rop20) { r ~(D | ~(T ^ S)); } /* DTSxnon */
+rop_proc(rop21) { r ~(D | (T & S)); } /* DTSaon */
+rop_proc(rop22) { r (T ^ (S ^ (D & ~(T & S)))); } /* TSDTSanaxx */
+rop_proc(rop23) { r ~(S ^ ((S ^ T) & (D ^ S))); } /* SSTxDSxaxn */
+rop_proc(rop24) { r (S ^ T) & (T ^ D); } /* STxTDxa */
+rop_proc(rop25) { r ~(S ^ (D & ~(T & S))); } /* SDTSanaxn */
+rop_proc(rop26) { r T ^ (D | (S & T)); } /* TDSTaox */
+rop_proc(rop27) { r ~(S ^ (D & (T ^ S))); } /* SDTSxaxn */
+rop_proc(rop28) { r T ^ (S | (D & T)); } /* TSDTaox */
+rop_proc(rop29) { r ~(D ^ (S & (T ^ D))); } /* DSTDxaxn */
+rop_proc(rop30) { r T ^ (D | S); } /* TDSox */
+rop_proc(rop31) { r ~(T & (D | S)); } /* TDSoan */
+rop_proc(rop32) { r D & (T & ~S); } /* DTSnaa */
+rop_proc(rop33) { r ~(S | (D ^ T)); } /* SDTxon */
+rop_proc(rop34) { r D & ~S; } /* DSna */
+rop_proc(rop35) { r ~(S | (T & ~D)); } /* STDnaon */
+rop_proc(rop36) { r (S ^ T) & (D ^ S); } /* STxDSxa */
+rop_proc(rop37) { r ~(T ^ (D & ~(S & T))); } /* TDSTanaxn */
+rop_proc(rop38) { r S ^ (D | (T & S)); } /* SDTSaox */
+rop_proc(rop39) { r S ^ (D | ~(T ^ S)); } /* SDTSxnox */
+rop_proc(rop40) { r D & (T ^ S); } /* DTSxa */
+rop_proc(rop41) { r ~(T ^ (S ^ (D | (T & S)))); } /* TSDTSaoxxn */
+rop_proc(rop42) { r D & ~(T & S); } /* DTSana */
+rop_proc(rop43) { r ~x(a(x(D,T),x(T,S)),S) ; } /* SSTxTDxaxn */
+rop_proc(rop44) { r (S ^ (T & (D | S))); } /* STDSoax */
+rop_proc(rop45) { r T ^ (S | ~D); } /* TSDnox */
+rop_proc(rop46) { r (T ^ (S | (D ^ T))); } /* TSDTxox */
+rop_proc(rop47) { r ~(T & (S | ~D)); } /* TSDnoan */
+rop_proc(rop48) { r T & ~S; } /* TSna */
+rop_proc(rop49) { r ~(S | (D & ~T)); } /* SDTnaon */
+rop_proc(rop50) { r S ^ (D | (T | S)); } /* SDTSoox */
+rop_proc(rop51) { r ~S; } /* Sn */
+rop_proc(rop52) { r S ^ (T |(D & S)); } /* STDSaox */
+rop_proc(rop53) { r S ^ (T | ~(D ^ S)); } /* STDSxnox */
+rop_proc(rop54) { r S ^ (D | T); } /* SDTox */
+rop_proc(rop55) { r ~(S & (D | T)); } /* SDToan */
+rop_proc(rop56) { r T ^ (S & (D | T)); } /* TSDToax */
+rop_proc(rop57) { r S ^ (T | ~D); } /* STDnox */
+rop_proc(rop58) { r S ^ (T | (D ^ S)); } /* STDSxox */
+rop_proc(rop59) { r ~(S & (T | ~D)); } /* STDnoan */
+rop_proc(rop60) { r T ^ S; } /* TSx */
+rop_proc(rop61) { r S ^ (T | ~(D | S)); } /* STDSonox */
+rop_proc(rop62) { r S ^ (T | (D & ~S)); } /* STDSnaox */
+rop_proc(rop63) { r ~(T & S); } /* TSan */
+rop_proc(rop64) { r T & (S & ~D); } /* TSDnaa */
+rop_proc(rop65) { r ~(D | (T ^ S)); } /* DTSxon */
+rop_proc(rop66) { r (S ^ D) & (T ^ D); } /* SDxTDxa */
+rop_proc(rop67) { r ~(S ^ (T & ~(D & S))); } /* STDSanaxn */
+rop_proc(rop68) { r S & ~D; } /* SDna */
+rop_proc(rop69) { r ~(D | (T & ~S)); } /* DTSnaon */
+rop_proc(rop70) { r D ^ (S | (T & D)); } /* DSTDaox */
+rop_proc(rop71) { r ~(T ^ (S & (D ^ T))); } /* TSDTxaxn */
+rop_proc(rop72) { r S & (D ^ T); } /* SDTxa */
+rop_proc(rop73) { r ~(T ^ (D ^ (S | (T & D)))); } /* TDSTDaoxxn */
+rop_proc(rop74) { r D ^ (T & (S | D)); } /* DTSDoax */
+rop_proc(rop75) { r T ^ (D | ~S); } /* TDSnox */
+rop_proc(rop76) { r S & ~(D & T); } /* SDTana */
+rop_proc(rop77) { r ~(S ^ ((S ^ T) | (D ^ S))); } /* SSTxDSxoxn */
+rop_proc(rop78) { r T ^ (D | (S ^ T)); } /* TDSTxox */
+rop_proc(rop79) { r ~(T & (D | ~S)); } /* TDSnoan */
+rop_proc(rop80) { r T & ~D; } /* TDna */
+rop_proc(rop81) { r ~(D | (S & ~T)); } /* DSTnaon */
+rop_proc(rop82) { r D ^ (T | (S & D)); } /* DTSDaox */
+rop_proc(rop83) { r ~(S ^ (T & (D ^ S))); } /* STDSxaxn */
+rop_proc(rop84) { r ~(D | ~(T | S)); } /* DTSonon */
+rop_proc(rop85) { r ~D; } /* Dn */
+rop_proc(rop86) { r D ^ (T | S); } /* DTSox */
+rop_proc(rop87) { r ~(D & (T | S)); } /* DTSoan */
+rop_proc(rop88) { r T ^ (D & (S | T)); } /* TDSToax */
+rop_proc(rop89) { r D ^ (T | ~S); } /* DTSnox */
+rop_proc(rop90) { r D ^ T; } /* DTx */
+rop_proc(rop91) { r D ^ (T | ~(S | D)); } /* DTSDonox */
+rop_proc(rop92) { r D ^ (T | (S ^ D)); } /* DTSDxox */
+rop_proc(rop93) { r ~(D & (T | ~S)); } /* DTSnoan */
+rop_proc(rop94) { r D ^ (T | (S & ~D)); } /* DTSDnaox */
+rop_proc(rop95) { r ~(D & T); } /* DTan */
+rop_proc(rop96) { r T & (D ^ S); } /* TDSxa */
+rop_proc(rop97) { r ~(D ^ (S ^ (T | (D & S)))); } /* DSTDSaoxxn */
+rop_proc(rop98) { r D ^ (S & (T | D)); } /* DSTDoax */
+rop_proc(rop99) { r S ^ (D | ~T); } /* SDTnox */
+rop_proc(rop100) { r S ^ (D & (T | S)); } /* SDTSoax */
+rop_proc(rop101) { r D ^ (S | ~T); } /* DSTnox */
+rop_proc(rop102) { r D ^ S; } /* DSx */
+rop_proc(rop103) { r S ^ (D | ~(T | S)); } /* SDTSonox */
+rop_proc(rop104) { r ~(D ^ (S ^ (T | ~(D | S)))); } /* DSTDSonoxxn */
+rop_proc(rop105) { r ~(T ^ (D ^ S)); } /* TDSxxn */
+rop_proc(rop106) { r D ^ (T & S); } /* DTSax */
+rop_proc(rop107) { r ~(T ^ (S ^ (D & (T | S)))); } /* TSDTSoaxxn */
+rop_proc(rop108) { r (D & T) ^ S; } /* SDTax */
+rop_proc(rop109) { r ~((((T | D) & S) ^ D) ^ T); } /* TDSTDoaxxn */
+rop_proc(rop110) { r ((~S | T) & D) ^ S; } /* SDTSnoax */
+rop_proc(rop111) { r ~(~(D ^ S) & T); } /* TDSxnan */
+rop_proc(rop112) { r ~(D & S) & T; } /* TDSana */
+rop_proc(rop113) { r ~(((S ^ D) & (T ^ D)) ^ S); } /* SSDxTDxaxn */
+rop_proc(rop114) { r ((T ^ S) | D) ^ S; } /* SDTSxox */
+rop_proc(rop115) { r ~((~T | D) & S); } /* SDTnoan */
+rop_proc(rop116) { r ((T ^ D) | S) ^ D; } /* DSTDxox */
+rop_proc(rop117) { r ~((~T | S) & D); } /* DSTnoan */
+rop_proc(rop118) { r ((~S & T) | D) ^ S; } /* SDTSnaox */
+rop_proc(rop119) { r ~(D & S); } /* DSan */
+rop_proc(rop120) { r (D & S) ^ T; } /* TDSax */
+rop_proc(rop121) { r ~((((D | S) & T) ^ S) ^ D); } /* DSTDSoaxxn */
+rop_proc(rop122) { r ((~D | S) & T) ^ D; } /* DTSDnoax */
+rop_proc(rop123) { r ~(~(D ^ T) & S); } /* SDTxnan */
+rop_proc(rop124) { r ((~S | D) & T) ^ S; } /* STDSnoax */
+rop_proc(rop125) { r ~(~(T ^ S) & D); } /* DTSxnan */
+rop_proc(rop126) { r (S ^ T) | (D ^ S); } /* STxDSxo */
+rop_proc(rop127) { r ~((T & S) & D); } /* DTSaan */
+rop_proc(rop128) { r (T & S) & D; } /* DTSaa */
+rop_proc(rop129) { r ~((S ^ T) | (D ^ S)); } /* STxDSxon */
+rop_proc(rop130) { r ~(T ^ S) & D; } /* DTSxna */
+rop_proc(rop131) { r ~(((~S | D) & T) ^ S); } /* STDSnoaxn */
+rop_proc(rop132) { r ~(D ^ T) & S; } /* SDTxna */
+rop_proc(rop133) { r ~(((~T | S) & D) ^ T); } /* TDSTnoaxn */
+rop_proc(rop134) { r (((D | S) & T) ^ S) ^ D; } /* DSTDSoaxx */
+rop_proc(rop135) { r ~((D & S) ^ T); } /* TDSaxn */
+rop_proc(rop136) { r D & S; } /* DSa */
+rop_proc(rop137) { r ~(((~S & T) | D) ^ S); } /* SDTSnaoxn */
+rop_proc(rop138) { r (~T | S) & D; } /* DSTnoa */
+rop_proc(rop139) { r ~(((T ^ D) | S) ^ D); } /* DSTDxoxn */
+rop_proc(rop140) { r (~T | D) & S; } /* SDTnoa */
+rop_proc(rop141) { r ~(((T ^ S) | D) ^ S); } /* SDTSxoxn */
+rop_proc(rop142) { r ((S ^ D) & (T ^ D)) ^ S; } /* SSDxTDxax */
+rop_proc(rop143) { r ~(~(D & S) & T); } /* TDSanan */
+rop_proc(rop144) { r ~(D ^ S) & T; } /* TDSxna */
+rop_proc(rop145) { r ~(((~S | T) & D) ^ S); } /* SDTSnoaxn */
+rop_proc(rop146) { r (((D | T) & S) ^ T) ^ D; } /* DTSDToaxx */
+rop_proc(rop147) { r ~((T & D) ^ S); } /* STDaxn */
+rop_proc(rop148) { r (((T | S) & D) ^ S) ^ T; } /* TSDTSoaxx */
+rop_proc(rop149) { r ~((T & S) ^ D); } /* DTSaxn */
+rop_proc(rop150) { r (T ^ S) ^ D; } /* DTSxx */
+rop_proc(rop151) { r ((~(T | S) | D) ^ S) ^ T; } /* TSDTSonoxx */
+rop_proc(rop152) { r ~((~(T | S) | D) ^ S); } /* SDTSonoxn */
+rop_proc(rop153) { r ~(D ^ S); } /* DSxn */
+rop_proc(rop154) { r (~S & T) ^ D; } /* DTSnax */
+rop_proc(rop155) { r ~(((T | S) & D) ^ S); } /* SDTSoaxn */
+rop_proc(rop156) { r (~D & T) ^ S; } /* STDnax */
+rop_proc(rop157) { r ~(((T | D) & S) ^ D); } /* DSTDoaxn */
+rop_proc(rop158) { r (((D & S) | T) ^ S) ^ D; } /* DSTDSaoxx */
+rop_proc(rop159) { r ~((D ^ S) & T); } /* TDSxan */
+rop_proc(rop160) { r D & T; } /* DTa */
+rop_proc(rop161) { r ~(((~T & S) | D) ^ T); } /* TDSTnaoxn */
+rop_proc(rop162) { r (~S | T) & D; } /* DTSnoa */
+rop_proc(rop163) { r ~(((D ^ S) | T) ^ D); } /* DTSDxoxn */
+rop_proc(rop164) { r ~((~(T | S) | D) ^ T) ; } /* TDSTonoxn */
+rop_proc(rop165) { r ~(D ^ T); } /* TDxn */
+rop_proc(rop166) { r (~T & S) ^ D; } /* DSTnax */
+rop_proc(rop167) { r ~(((T | S) & D) ^ T); } /* TDSToaxn */
+rop_proc(rop168) { r ((S | T) & D); } /* DTSoa */
+rop_proc(rop169) { r ~((S | T) ^ D); } /* DTSoxn */
+rop_proc(rop170) { r D; } /* D */
+rop_proc(rop171) { r ~(S | T) | D; } /* DTSono */
+rop_proc(rop172) { r (((S ^ D) & T) ^ S); } /* STDSxax */
+rop_proc(rop173) { r ~(((D & S) | T) ^ D); } /* DTSDaoxn */
+rop_proc(rop174) { r (~T & S) | D; } /* DSTnao */
+rop_proc(rop175) { r ~T | D; } /* DTno */
+rop_proc(rop176) { r (~S | D) & T; } /* TDSnoa */
+rop_proc(rop177) { r ~(((T ^ S) | D) ^ T) ; } /* TDSTxoxn */
+rop_proc(rop178) { r ((S ^ D) | (S ^ T)) ^ S; } /* SSTxDSxox */
+rop_proc(rop179) { r ~(~(T & D) & S); } /* SDTanan */
+rop_proc(rop180) { r (~D & S) ^ T; } /* TSDnax */
+rop_proc(rop181) { r ~(((D | S) & T) ^ D); } /* DTSDoaxn */
+rop_proc(rop182) { r (((T & D) | S) ^ T) ^ D; } /* DTSDTaoxx */
+rop_proc(rop183) { r ~((T ^ D) & S); } /* SDTxan */
+rop_proc(rop184) { r ((T ^ D) & S) ^ T; } /* TSDTxax */
+rop_proc(rop185) { r (~((D & T) | S) ^ D); } /* DSTDaoxn */
+rop_proc(rop186) { r (~S & T) | D; } /* DTSnao */
+rop_proc(rop187) { r ~S | D; } /* DSno */
+rop_proc(rop188) { r (~(S & D) & T) ^ S; } /* STDSanax */
+rop_proc(rop189) { r ~((D ^ T) & (D ^ S)); } /* SDxTDxan */
+rop_proc(rop190) { r (S ^ T) | D; } /* DTSxo */
+rop_proc(rop191) { r ~(S & T) | D; } /* DTSano */
+rop_proc(rop192) { r T & S; } /* TSa */
+rop_proc(rop193) { r ~(((~S & D) | T)^ S); } /* STDSnaoxn */
+rop_proc(rop194) { r ~x(o(~o(S,D),T),S) ; } /* STDSonoxn */
+rop_proc(rop195) { r ~(S ^ T); } /* TSxn */
+rop_proc(rop196) { r ((~D | T) & S); } /* STDnoa */
+rop_proc(rop197) { r ~(((S ^ D) | T) ^ S); } /* STDSxoxn */
+rop_proc(rop198) { r ((~T & D) ^ S); } /* SDTnax */
+rop_proc(rop199) { r ~(((T | D) & S) ^ T); } /* TSDToaxn */
+rop_proc(rop200) { r ((T | D) & S); } /* SDToa */
+rop_proc(rop201) { r ~((D | T) ^ S); } /* STDoxn */
+rop_proc(rop202) { r ((D ^ S) & T) ^ D; } /* DTSDxax */
+rop_proc(rop203) { r ~(((S & D) | T) ^ S); } /* STDSaoxn */
+rop_proc(rop204) { r S; } /* S */
+rop_proc(rop205) { r ~(T | D) | S; } /* SDTono */
+rop_proc(rop206) { r (~T & D) | S; } /* SDTnao */
+rop_proc(rop207) { r ~T | S; } /* STno */
+rop_proc(rop208) { r (~D | S) & T; } /* TSDnoa */
+rop_proc(rop209) { r ~(((T ^ D) | S) ^ T); } /* TSDTxoxn */
+rop_proc(rop210) { r (~S & D) ^ T; } /* TDSnax */
+rop_proc(rop211) { r ~(((S | D) & T) ^ S); } /* STDSoaxn */
+rop_proc(rop212) { r x(a(x(D,T),x(T,S)),S) ; } /* SSTxTDxax */
+rop_proc(rop213) { r ~(~(S & T) & D); } /* DTSanan */
+rop_proc(rop214) { r ((((S & T) | D) ^ S) ^ T); } /* TSDTS aoxx */
+rop_proc(rop215) { r ~((S ^ T) & D); } /* DTS xan */
+rop_proc(rop216) { r ((T ^ S) & D) ^ T; } /* TDST xax */
+rop_proc(rop217) { r ~(((S & T) | D) ^ S); } /* SDTS aoxn */
+rop_proc(rop218) { r x(a(~a(D,S),T),D) ; } /* DTSD anax */
+rop_proc(rop219) { r ~a(x(S,D),x(T,S)) ; } /* STxDSxan */
+rop_proc(rop220) { r (~D & T) | S; } /* STD nao */
+rop_proc(rop221) { r ~D | S; } /* SDno */
+rop_proc(rop222) { r (T ^ D) | S; } /* SDT xo */
+rop_proc(rop223) { r (~(T & D)) | S; } /* SDT ano */
+rop_proc(rop224) { r ((S | D) & T); } /* TDS oa */
+rop_proc(rop225) { r ~((S | D) ^ T); } /* TDS oxn */
+rop_proc(rop226) { r (((D ^ T) & S) ^ D); } /* DSTD xax */
+rop_proc(rop227) { r ~(((T & D) | S) ^ T); } /* TSDT aoxn */
+rop_proc(rop228) { r ((S ^ T) & D) ^ S; } /* SDTSxax */
+rop_proc(rop229) { r ~(((T & S) | D) ^ T); } /* TDST aoxn */
+rop_proc(rop230) { r (~(S & T) & D) ^ S; } /* SDTSanax */
+rop_proc(rop231) { r ~a(x(D,T),x(T,S)) ; } /* STxTDxan */
+rop_proc(rop232) { r x(a(x(S,D),x(T,S)),S) ; } /* SS TxD Sxax */
+rop_proc(rop233) { r ~x(x(a(~a(S,D),T),S),D) ; } /* DST DSan axxn */
+rop_proc(rop234) { r (S & T) | D; } /* DTSao */
+rop_proc(rop235) { r ~(S ^ T) | D; } /* DTSxno */
+rop_proc(rop236) { r (T & D) | S; } /* SDTao */
+rop_proc(rop237) { r ~(T ^ D) | S; } /* SDTxno */
+rop_proc(rop238) { r S | D; } /* DSo */
+rop_proc(rop239) { r (~T | D) | S; } /* SDTnoo */
+rop_proc(rop240) { r T; } /* T */
+rop_proc(rop241) { r ~(S | D) | T; } /* TDSono */
+rop_proc(rop242) { r (~S & D) | T; } /* TDSnao */
+rop_proc(rop243) { r ~S | T; } /* TSno */
+rop_proc(rop244) { r (~D & S) | T; } /* TSDnao */
+rop_proc(rop245) { r ~D | T; } /* TDno */
+rop_proc(rop246) { r (S ^ D) | T; } /* TDSxo */
+rop_proc(rop247) { r ~ (S & D) | T; } /* TDSano */
+rop_proc(rop248) { r (S & D) | T; } /* TDSao */
+rop_proc(rop249) { r ~ (S ^ D) | T; } /* TDSxno */
+rop_proc(rop250) { r D | T; } /* DTo */
+rop_proc(rop251) { r (~S | T) | D; } /* DTSnoo */
+rop_proc(rop252) { r S | T; } /* TSo */
+rop_proc(rop253) { r (~D | S) | T; } /* TSDnoo */
+rop_proc(rop254) { r S | T | D; } /* DTSoo */
+rop_proc(rop255) { r ~(rop_operand)0; } /* 1 */
+
+#undef rop_proc
+const far_data rop_proc rop_proc_tab[256] =
+{
+ rop0, rop1, rop2, rop3, rop4, rop5, rop6, rop7,
+ rop8, rop9, rop10, rop11, rop12, rop13, rop14, rop15,
+ rop16, rop17, rop18, rop19, rop20, rop21, rop22, rop23,
+ rop24, rop25, rop26, rop27, rop28, rop29, rop30, rop31,
+ rop32, rop33, rop34, rop35, rop36, rop37, rop38, rop39,
+ rop40, rop41, rop42, rop43, rop44, rop45, rop46, rop47,
+ rop48, rop49, rop50, rop51, rop52, rop53, rop54, rop55,
+ rop56, rop57, rop58, rop59, rop60, rop61, rop62, rop63,
+ rop64, rop65, rop66, rop67, rop68, rop69, rop70, rop71,
+ rop72, rop73, rop74, rop75, rop76, rop77, rop78, rop79,
+ rop80, rop81, rop82, rop83, rop84, rop85, rop86, rop87,
+ rop88, rop89, rop90, rop91, rop92, rop93, rop94, rop95,
+ rop96, rop97, rop98, rop99, rop100, rop101, rop102, rop103,
+ rop104, rop105, rop106, rop107, rop108, rop109, rop110, rop111,
+ rop112, rop113, rop114, rop115, rop116, rop117, rop118, rop119,
+ rop120, rop121, rop122, rop123, rop124, rop125, rop126, rop127,
+ rop128, rop129, rop130, rop131, rop132, rop133, rop134, rop135,
+ rop136, rop137, rop138, rop139, rop140, rop141, rop142, rop143,
+ rop144, rop145, rop146, rop147, rop148, rop149, rop150, rop151,
+ rop152, rop153, rop154, rop155, rop156, rop157, rop158, rop159,
+ rop160, rop161, rop162, rop163, rop164, rop165, rop166, rop167,
+ rop168, rop169, rop170, rop171, rop172, rop173, rop174, rop175,
+ rop176, rop177, rop178, rop179, rop180, rop181, rop182, rop183,
+ rop184, rop185, rop186, rop187, rop188, rop189, rop190, rop191,
+ rop192, rop193, rop194, rop195, rop196, rop197, rop198, rop199,
+ rop200, rop201, rop202, rop203, rop204, rop205, rop206, rop207,
+ rop208, rop209, rop210, rop211, rop212, rop213, rop214, rop215,
+ rop216, rop217, rop218, rop219, rop220, rop221, rop222, rop223,
+ rop224, rop225, rop226, rop227, rop228, rop229, rop230, rop231,
+ rop232, rop233, rop234, rop235, rop236, rop237, rop238, rop239,
+ rop240, rop241, rop242, rop243, rop244, rop245, rop246, rop247,
+ rop248, rop249, rop250, rop251, rop252, rop253, rop254, rop255
+};
diff --git a/pstoraster/gsstate.c b/pstoraster/gsstate.c
new file mode 100644
index 000000000..8d1e6b48d
--- /dev/null
+++ b/pstoraster/gsstate.c
@@ -0,0 +1,786 @@
+/* Copyright (C) 1989, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gsstate.c */
+/* Miscellaneous graphics state operators for Ghostscript library */
+#include "gx.h"
+#include "memory_.h"
+#include "gserrors.h"
+#include "gsstruct.h"
+#include "gsutil.h" /* for gs_next_ids */
+#include "gzstate.h"
+#include "gxcspace.h" /* here for gscolor2.h */
+#include "gscolor2.h"
+#include "gscoord.h" /* for gs_initmatrix */
+#include "gscie.h"
+#include "gxcmap.h"
+#include "gxdevice.h"
+#include "gzht.h"
+#include "gzline.h"
+#include "gspath.h"
+#include "gzpath.h"
+#include "gzcpath.h"
+
+/* Imported values */
+/* The following should include a 'const', but for some reason */
+/* the Watcom compiler won't accept it, even though it happily accepts */
+/* the same construct everywhere else. */
+extern /*const*/ gx_color_map_procs *cmap_procs_default;
+
+/* Forward references */
+private gs_state *gstate_alloc(P2(gs_memory_t *, client_name_t));
+private void gstate_set_contents(P2(gs_state *, gs_state_contents *));
+private gs_state *gstate_clone(P3(gs_state *, client_name_t, bool));
+private void gstate_free_contents(P1(gs_state *));
+private void gstate_share_paths(P1(const gs_state *));
+private void gstate_copy(P3(gs_state *, const gs_state *, client_name_t));
+
+/*
+ * Graphics state storage management is complicated. There are many
+ * different classes of storage associated with a graphics state:
+ *
+ * (1) The gstate object itself. This includes some objects physically
+ * embedded within the gstate object, but because of garbage collection
+ * requirements, there are no embedded objects that and can be
+ * referenced by non-transient pointers. We assume that the gstate
+ * stack "owns" its gstates and that we can free the top gstate when
+ * doing a restore.
+ *
+ * (2) Objects that are referenced directly by the gstate and whose lifetime
+ * is independent of the gstate. These are garbage collected, not
+ * reference counted, so we don't need to do anything special with them
+ * when manipulating gstates. Currently this includes:
+ * font, device
+ *
+ * (3) Objects that are referenced directly by the gstate, may be shared
+ * among gstates, and should disappear when no gstates reference them.
+ * We use reference counting to manage these. Currently these are:
+ * cie_render, black_generation, undercolor_removal,
+ * set_transfer.*, cie_joint_caches
+ * effective_transfer.* may point to some of the same objects as
+ * set_transfer.*, but don't contribute to the reference count.
+ *
+ * (4) Objects that are referenced directly by exactly one gstate and that
+ * are not referenced (except transiently) from any other object.
+ * We lump most of these together into a single gs_state_contents
+ * object in order to reduce the load on the allocator for gsave and
+ * grestore. Each gs_gstate_contents is referenced by exactly one
+ * gstate, and nowhere else. We reference the individual members
+ * through pointers, just as though they were allocated individually,
+ * so that we wouldn't have to keep remembering to put & in front of
+ * references to its components, which would otherwise have to be
+ * embedded in the gstate object. Again, we do not allow non-transient
+ * references to the objects embedded in this object, aside from
+ * a single pointer from the gstate object to each embedded component.
+ * Currently these are:
+ * path, clip_path, line_params (Not In Contents Object),
+ * halftone, color_space (NICO), ccolor, dev_color,
+ * dev_ht (NICO)
+ * color_space and dev_ht are special cases, since they are referenced
+ * both directly and also indirectly (from image enumerators and
+ * dev_color respectively). We simply allocate them as separate
+ * objects.
+ *
+ * (5) Objects referenced indirectly from gstate objects of category (4),
+ * including objects that may also be referenced directly by the gstate.
+ * The individual routines that manipulate these are responsible
+ * for doing the right kind of reference counting or whatever.
+ * Currently:
+ * path and clip_path require gx_path_share/release,
+ * which use a 1-bit reference count;
+ * color_space and ccolor require cs_adjust_color/cspace_count
+ * or cs_adjust_counts, which use a full reference count;
+ * We count on garbage collection or restore to deallocate
+ * sub-objects of line_params, halftone, and dev_ht;
+ * dev_color has no references to storage that it owns.
+ *
+ * (6) The "client data" for a gstate. For the interpreter, this is
+ * the refs associated with the gstate, such as the screen procedures.
+ * Again, we maintain the invariant that there is exactly one gstate
+ * that refers to a given client data object. Client-supplied
+ * procedures manage client data.
+ *
+ * This situation is unnecessarily complicated. We should get rid of
+ * the contents object and use reference counting to manage individually
+ * the objects it currently contains. We should use full reference
+ * counting for paths, and use the freeing procedure to release the
+ * individual path elements. However, making these changes runs a large
+ * risk of introducing hard-to-find bugs, so we don't plan to make them
+ * in the foreseeable future.
+ *
+ * Note that for client data and gstate-related composite objects, it is not
+ * necessarily the case that when we allocate a gstate, it will reference
+ * the related object that we allocate at the same time; for example,
+ * when we do a gsave, the old contents go with the new gstate object
+ * and vice versa. The same is true of freeing. */
+
+/* The structure for allocating (most of) the contents of a gstate */
+/* all at once. The typedef is in gzstate.c. */
+struct gs_state_contents_s {
+ gx_path path;
+ gx_clip_path clip_path;
+ gs_halftone halftone;
+ gs_client_color ccolor;
+ gx_device_color dev_color;
+};
+
+/* Enumerate the pointers in a graphics state, other than the ones */
+/* that point to the gs_state_contents. */
+#define gs_state_do_ptrs(m)\
+ m(0,saved) m(1,ht_cache) m(2,contents)\
+ m(3,cie_render) m(4,black_generation) m(5,undercolor_removal)\
+ m(6,set_transfer.colored.red) m(7,set_transfer.colored.green)\
+ m(8,set_transfer.colored.blue) m(9,set_transfer.colored.gray)\
+ m(10,effective_transfer.colored.red) m(11,effective_transfer.colored.green)\
+ m(12,effective_transfer.colored.blue) m(13,effective_transfer.colored.gray)\
+ m(14,cie_joint_caches) m(15,pattern_cache) m(16,font) m(17,root_font)\
+ m(18,show_gstate) /*m(---,device)*/ m(19,client_data)\
+ /****** OUT OF ORDER: ******/ m(20,dev_ht) m(21,color_space)
+#define gs_state_num_ptrs 22
+/* Enumerate the pointers to the gs_state_contents. */
+#define gs_state_do_contents_ptrs(m)\
+ m(0,path) m(1,clip_path) m(2,halftone) m(3,ccolor) m(4,dev_color)
+
+/* GC descriptors */
+private_st_line_params();
+private_st_imager_state();
+private_st_gs_state();
+/* Components of the graphics state */
+gs_private_st_composite(st_gs_state_contents, gs_state_contents,
+ "gs_state_contents", state_contents_enum_ptrs, state_contents_reloc_ptrs);
+public_st_transfer_map();
+
+/* GC procedures for gs_state */
+#define gsvptr ((gs_state *)vptr)
+private ENUM_PTRS_BEGIN(gs_state_enum_ptrs) ENUM_PREFIX(st_imager_state, gs_state_num_ptrs + 1);
+#define e1(i,elt) ENUM_PTR(i,gs_state,elt);
+ gs_state_do_ptrs(e1)
+ case gs_state_num_ptrs: /* handle device specially */
+ *pep = gx_device_enum_ptr(gsvptr->device);
+ break;
+#undef e1
+ENUM_PTRS_END
+private RELOC_PTRS_BEGIN(gs_state_reloc_ptrs) {
+ RELOC_PREFIX(st_imager_state);
+ { /* Save the contents pointer before relocation. */
+ byte *cont = (byte *)gsvptr->contents;
+ long reloc;
+#define r1(i,elt) RELOC_PTR(gs_state,elt);
+ gs_state_do_ptrs(r1)
+#undef r1
+ gsvptr->device = gx_device_reloc_ptr(gsvptr->device, gcst);
+ /* Now relocate the pointers into the contents. */
+ reloc = cont - (byte *)gsvptr->contents;
+#define r1(i,elt)\
+ gsvptr->elt = (void *)((byte *)gsvptr->elt - reloc);
+ gs_state_do_contents_ptrs(r1)
+#undef r1
+ }
+} RELOC_PTRS_END
+#undef gsvptr
+
+/* GC procedures for gs_state_contents */
+#define cptr ((gs_state_contents *)vptr)
+private ENUM_PTRS_BEGIN_PROC(state_contents_enum_ptrs) {
+ gs_ptr_type_t ret;
+#define next_comp(np, st, e)\
+ if ( index < np ) { ret = (*st.enum_ptrs)(&cptr->e, sizeof(cptr->e), index, pep); goto rx; }\
+ index -= np
+#define last_comp(np, st, e)\
+ return (*st.enum_ptrs)(&cptr->e, sizeof(cptr->e), index, pep)
+ next_comp(st_path_max_ptrs, st_path, path);
+ next_comp(st_clip_path_max_ptrs, st_clip_path, clip_path);
+ next_comp(st_halftone_max_ptrs, st_halftone, halftone);
+ next_comp(st_client_color_max_ptrs, st_client_color, ccolor);
+ last_comp(st_device_color_max_ptrs, st_device_color, dev_color);
+#undef next_comp
+#undef last_comp
+rx: if ( ret == 0 )
+ { /* A component ran out of pointers early. */
+ /* Just return a null so we can keep going. */
+ *pep = 0;
+ return ptr_struct_type;
+ }
+ return ret;
+ENUM_PTRS_END_PROC }
+private RELOC_PTRS_BEGIN(state_contents_reloc_ptrs) {
+ (*st_path.reloc_ptrs)(&cptr->path, sizeof(gx_path), gcst);
+ (*st_clip_path.reloc_ptrs)(&cptr->clip_path, sizeof(gx_clip_path), gcst);
+ (*st_halftone.reloc_ptrs)(&cptr->halftone, sizeof(gs_halftone), gcst);
+ (*st_client_color.reloc_ptrs)(&cptr->ccolor, sizeof(gs_client_color), gcst);
+ (*st_device_color.reloc_ptrs)(&cptr->dev_color, sizeof(gx_device_color), gcst);
+} RELOC_PTRS_END
+#undef cptr
+
+/* ------ Operations on the entire graphics state ------ */
+
+/* Allocate and initialize a graphics state. */
+private float
+null_transfer(floatp gray, const gx_transfer_map *pmap)
+{ return gray;
+}
+gs_state *
+gs_state_alloc(gs_memory_t *mem)
+{ register gs_state *pgs = gstate_alloc(mem, "gs_state_alloc");
+ if ( pgs == 0 )
+ return 0;
+ pgs->saved = 0;
+ gstate_set_contents(pgs, pgs->contents);
+
+ /* Initialize all reference-counted pointers in the gs_state. */
+
+ pgs->cie_render = 0;
+ pgs->black_generation = 0;
+ pgs->undercolor_removal = 0;
+ /* Allocate an initial transfer map. */
+ rc_alloc_struct_n(pgs->set_transfer.colored.gray,
+ gx_transfer_map, &st_transfer_map,
+ mem, return 0, "gs_state_alloc", 4);
+ pgs->set_transfer.colored.gray->proc = null_transfer;
+ pgs->set_transfer.colored.gray->id = gs_next_ids(1);
+ pgs->set_transfer.colored.gray->values[0] = frac_0;
+ pgs->set_transfer.colored.red =
+ pgs->set_transfer.colored.green =
+ pgs->set_transfer.colored.blue =
+ pgs->set_transfer.colored.gray;
+ pgs->effective_transfer = pgs->set_transfer;
+ pgs->cie_joint_caches = 0;
+ pgs->client_data = 0;
+
+ /* Initialize other things not covered by initgraphics */
+
+ gx_path_init(pgs->path, mem);
+ gx_cpath_init(pgs->clip_path, mem);
+ pgs->ht_cache = gx_ht_alloc_cache(mem,
+ gx_ht_cache_default_tiles(),
+ gx_ht_cache_default_bits());
+ { /*
+ * It would be nice if we could use statically initialized
+ * structures for the levels and bits arrays, but that would
+ * confuse the GC.
+ */
+ uint *levels =
+ (uint *)gs_alloc_bytes(mem, sizeof(uint),
+ "gs_state_alloc(ht levels)");
+ gx_ht_bit *bits =
+ (gx_ht_bit *)gs_alloc_bytes(mem, sizeof(gx_ht_bit),
+ "gs_state_alloc(ht bits)");
+ gx_device_halftone *pdht = pgs->dev_ht;
+
+ pdht->order.width = pdht->order.height = 1;
+ pdht->order.raster = bitmap_raster(1);
+ pdht->order.shift = 0;
+ pdht->order.num_levels = pdht->order.num_bits = 1;
+ levels[0] = 1;
+ pdht->order.levels = levels;
+ bits[0].offset = 0;
+ bits[0].mask = 0;
+ pdht->order.bits = bits;
+ pdht->order.cache = pgs->ht_cache;
+ pdht->order.transfer = 0;
+ pdht->components = 0;
+ }
+ pgs->pattern_cache = 0;
+ gs_sethalftonephase(pgs, 0, 0);
+ /* Initialize things so that gx_remap_color won't crash. */
+ pgs->color_space->type = &gs_color_space_type_DeviceGray;
+ gx_set_device_color_1(pgs);
+ pgs->overprint = false;
+ pgs->cmap_procs = cmap_procs_default;
+ gs_nulldevice(pgs);
+ gs_setalpha(pgs, 1.0);
+ gs_settransfer(pgs, null_transfer);
+ gs_setflat(pgs, 1.0);
+ gs_setfilladjust(pgs, 0.25, 0.25);
+ gs_setstrokeadjust(pgs, true);
+ pgs->font = 0; /* Not right, but acceptable until the */
+ /* PostScript code does the first setfont. */
+ pgs->root_font = 0; /* ditto */
+ pgs->in_cachedevice = 0;
+ pgs->in_charpath = 0;
+ pgs->show_gstate = 0;
+ pgs->level = 0;
+ pgs->client_data = 0;
+ if ( gs_initgraphics(pgs) < 0 )
+ { /* Something went very wrong */
+ return 0;
+ }
+ return pgs;
+}
+
+/* Set the client data in a graphics state. */
+/* This should only be done to a newly created state. */
+void
+gs_state_set_client(gs_state *pgs, void *pdata,
+ const gs_state_client_procs *pprocs)
+{ pgs->client_data = pdata;
+ pgs->client_procs = *pprocs;
+}
+
+/* Get the client data from a graphics state. */
+#undef gs_state_client_data /* gzstate.h makes this a macro */
+void *
+gs_state_client_data(const gs_state *pgs)
+{ return pgs->client_data;
+}
+
+/* Free a graphics state */
+int
+gs_state_free(gs_state *pgs)
+{ gstate_free_contents(pgs);
+ gs_free_object(pgs->memory, pgs, "gs_state_free");
+ return 0;
+}
+
+/* Save the graphics state */
+int
+gs_gsave(gs_state *pgs)
+{ gs_state *pnew = gstate_clone(pgs, "gs_gsave", true);
+ if ( pnew == 0 )
+ return_error(gs_error_VMerror);
+ gx_path_share(pgs->path);
+ gx_cpath_share(pgs->clip_path);
+ pgs->saved = pnew;
+ if ( pgs->show_gstate == pgs )
+ pgs->show_gstate = pnew->show_gstate = pnew;
+ pgs->level++;
+ if_debug2('g', "[g]gsave -> 0x%lx, level = %d\n",
+ (ulong)pnew, pgs->level);
+ return 0;
+}
+
+/* Restore the graphics state. */
+int
+gs_grestore(gs_state *pgs)
+{ gs_state *saved = pgs->saved;
+ void *pdata = pgs->client_data;
+ void *sdata;
+ if_debug2('g', "[g]grestore 0x%lx, level was %d\n",
+ (ulong)saved, pgs->level);
+ if ( !saved ) /* shouldn't happen */
+ return gs_gsave(pgs);
+ sdata = saved->client_data;
+ if ( saved->pattern_cache == 0 )
+ saved->pattern_cache = pgs->pattern_cache;
+ /* Swap back the client data pointers. */
+ pgs->client_data = sdata;
+ saved->client_data = pdata;
+ if ( pdata != 0 && sdata != 0 )
+ (*pgs->client_procs.copy)(pdata, sdata);
+ gstate_free_contents(pgs);
+ *pgs = *saved;
+ if ( pgs->show_gstate == saved )
+ pgs->show_gstate = pgs;
+ gs_free_object(pgs->memory, saved, "gs_grestore");
+ if ( pgs->saved )
+ return 0;
+ return gs_gsave(pgs);
+}
+
+/* Restore to the bottommost graphics state. Also clear */
+/* the halftone caches, so stale pointers don't survive a restore. */
+int
+gs_grestoreall(gs_state *pgs)
+{ int code;
+ if ( !pgs->saved ) /* shouldn't happen */
+ return gs_gsave(pgs);
+ while ( pgs->saved->saved )
+ { int code = gs_grestore(pgs);
+ if ( code < 0 )
+ return code;
+ }
+ code = gs_grestore(pgs);
+ if ( code < 0 )
+ return code;
+ gx_ht_clear_cache(pgs->ht_cache);
+ return code;
+}
+
+/* Allocate and return a new graphics state. */
+gs_state *
+gs_gstate(gs_state *pgs)
+{ gs_state *pnew;
+ gstate_share_paths(pgs);
+ pnew = gstate_clone(pgs, "gs_gstate", false);
+ if ( pnew == 0 )
+ return 0;
+ pnew->saved = 0;
+ /*
+ * Prevent dangling references from the show_gstate pointer. If
+ * this context is its own show_gstate, set the pointer in the clone
+ * to point to the clone; otherwise, set the pointer in the clone to
+ * 0, and let gs_setgstate fix it up.
+ */
+ pnew->show_gstate =
+ (pgs->show_gstate == pgs ? pnew : 0);
+ return pnew;
+}
+
+/* Copy one previously allocated graphics state to another. */
+int
+gs_copygstate(gs_state *pto, const gs_state *pfrom)
+{ gstate_copy(pto, pfrom, "gs_copygstate");
+ return 0;
+}
+
+/* Copy the current graphics state to a previously allocated one. */
+int
+gs_currentgstate(gs_state *pto, const gs_state *pgs)
+{ gstate_share_paths(pgs);
+ gstate_copy(pto, pgs, "gs_currentgstate");
+ return 0;
+}
+
+/* Restore the current graphics state from a previously allocated one. */
+int
+gs_setgstate(gs_state *pgs, const gs_state *pfrom)
+{ /*
+ * The implementation is the same as currentgstate,
+ * except we must preserve the saved pointer, the level,
+ * and possibly the show_gstate.
+ */
+ gs_state *saved = pgs->saved;
+ gs_state *saved_show = pgs->show_gstate;
+ int level = pgs->level;
+
+ gstate_copy(pgs, pfrom, "gs_setgstate");
+ pgs->saved = saved;
+ pgs->level = level;
+ pgs->show_gstate =
+ (pgs->show_gstate == pfrom ? pgs : saved_show);
+ return 0;
+}
+
+/* Get the allocator pointer of a graphics state. */
+/* This is provided only for the interpreter */
+/* and for color space implementation. */
+gs_memory_t *
+gs_state_memory(const gs_state *pgs)
+{ return pgs->memory;
+}
+
+/* Get the saved pointer of the graphics state. */
+/* This is provided only for Level 2 grestore. */
+gs_state *
+gs_state_saved(const gs_state *pgs)
+{ return pgs->saved;
+}
+
+/* Swap the saved pointer of the graphics state. */
+/* This is provided only for save/restore. */
+gs_state *
+gs_state_swap_saved(gs_state *pgs, gs_state *new_saved)
+{ gs_state *saved = pgs->saved;
+ pgs->saved = new_saved;
+ return saved;
+}
+
+/* Swap the memory pointer of the graphics state. */
+/* This is provided only for the interpreter. */
+gs_memory_t *
+gs_state_swap_memory(gs_state *pgs, gs_memory_t *mem)
+{ gs_memory_t *memory = pgs->memory;
+ pgs->memory = mem;
+ return memory;
+}
+
+/* ------ Operations on components ------ */
+
+/* Reset most of the graphics state */
+int
+gs_initgraphics(register gs_state *pgs)
+{ int code;
+ gs_initmatrix(pgs);
+ if ( (code = gs_newpath(pgs)) < 0 ||
+ (code = gs_initclip(pgs)) < 0 ||
+ (code = gs_setlinewidth(pgs, 1.0)) < 0 ||
+ (code = gs_setlinecap(pgs, gs_cap_butt)) < 0 ||
+ (code = gs_setlinejoin(pgs, gs_join_miter)) < 0 ||
+ (code = gs_setdash(pgs, (float *)0, 0, 0.0)) < 0 ||
+ (code = gs_setgray(pgs, 0.0)) < 0 ||
+ (code = gs_setmiterlimit(pgs, 10.0)) < 0
+ )
+ return code;
+ gs_init_rop(pgs);
+ return 0;
+}
+
+/* setfilladjust */
+int
+gs_setfilladjust(gs_state *pgs, floatp adjust_x, floatp adjust_y)
+{
+#define adjust_fill_adjust(v)\
+ if ( v < 0 ) v = 0; else if ( v > 0.5 ) v = 0.5
+ adjust_fill_adjust(adjust_x);
+ pgs->fill_adjust.x = float2fixed(adjust_x);
+ adjust_fill_adjust(adjust_y);
+ pgs->fill_adjust.y = float2fixed(adjust_y);
+ return 0;
+}
+
+/* currentfilladjust */
+int
+gs_currentfilladjust(const gs_state *pgs, gs_point *adjust)
+{ adjust->x = fixed2float(pgs->fill_adjust.x);
+ adjust->y = fixed2float(pgs->fill_adjust.y);
+ return 0;
+}
+
+/* ------ Internal routines ------ */
+
+/* Allocate a gstate and its contents. */
+private gs_state *
+gstate_alloc(gs_memory_t *mem, client_name_t cname)
+{ gs_state *pgs =
+ gs_alloc_struct(mem, gs_state, &st_gs_state, cname);
+ gs_state_contents *cont =
+ gs_alloc_struct(mem, gs_state_contents, &st_gs_state_contents, cname);
+ gx_device_halftone *pdht =
+ gs_alloc_struct(mem, gx_device_halftone, &st_device_halftone, cname);
+ gs_color_space *pcs =
+ gs_alloc_struct(mem, gs_color_space, &st_color_space, cname);
+
+ if ( pgs == 0 || cont == 0 || pdht == 0 || pcs == 0 )
+ { gs_free_object(mem, pcs, cname);
+ gs_free_object(mem, pdht, cname);
+ gs_free_object(mem, cont, cname);
+ gs_free_object(mem, pgs, cname);
+ return 0;
+ }
+ pgs->memory = mem;
+ pgs->contents = cont;
+ pgs->dev_ht = pdht;
+ pgs->color_space = pcs;
+ return pgs;
+}
+
+/* Set the contents pointers of a gstate. */
+private void
+gstate_set_contents(gs_state *pgs, gs_state_contents *cont)
+{ pgs->contents = cont;
+#define gset(element)\
+ pgs->element = &cont->element;
+ gset(path);
+ gset(clip_path);
+ gset(halftone);
+ gset(ccolor);
+ gset(dev_color);
+#undef gset
+}
+
+/* Clone an existing graphics state. */
+/* Return 0 if the allocation fails. */
+/* The client is responsible for calling gx_[c]path_share on */
+/* whichever of the old and new paths is appropriate. */
+/* If for_gsave is true, the clone refers to the old contents, */
+/* and we switch the old state to refer to the new contents. */
+private gs_state *
+gstate_clone(register gs_state *pfrom, client_name_t cname, bool for_gsave)
+{ gs_memory_t *mem = pfrom->memory;
+ gs_state_contents *cfrom = pfrom->contents;
+ gx_device_halftone *dhtfrom = pfrom->dev_ht;
+ gs_color_space *csfrom = pfrom->color_space;
+ gs_state *pgs = gstate_alloc(mem, cname);
+ gs_state_contents *cont;
+ gx_device_halftone *pdht;
+ gs_color_space *pcs;
+
+ if ( pgs == 0 )
+ return 0;
+ cont = pgs->contents;
+ pdht = pgs->dev_ht;
+ pcs = pgs->color_space;
+ /* Increment references from gstate object. */
+ *pgs = *pfrom;
+ if ( pgs->client_data != 0 )
+ { void *pdata = pgs->client_data =
+ (*pgs->client_procs.alloc)(mem);
+ if ( pdata == 0 ||
+ (*pgs->client_procs.copy)(pdata, pfrom->client_data) < 0
+ )
+ { gs_free_object(mem, pcs, cname);
+ gs_free_object(mem, pdht, cname);
+ gs_free_object(mem, cont, cname);
+ gs_free_object(mem, pgs, cname);
+ return 0;
+ }
+ }
+ rc_increment(pgs->set_transfer.colored.gray);
+ rc_increment(pgs->set_transfer.colored.red);
+ rc_increment(pgs->set_transfer.colored.green);
+ rc_increment(pgs->set_transfer.colored.blue);
+ rc_increment(pgs->cie_render);
+ rc_increment(pgs->black_generation);
+ rc_increment(pgs->undercolor_removal);
+ rc_increment(pgs->cie_joint_caches);
+ if ( for_gsave )
+ { gstate_set_contents(pgs, cfrom);
+ pgs->dev_ht = dhtfrom;
+ pgs->color_space = csfrom;
+ gstate_set_contents(pfrom, cont);
+ pfrom->dev_ht = pdht;
+ pfrom->color_space = pcs;
+ }
+ else
+ { gstate_set_contents(pgs, cont);
+ pgs->dev_ht = pdht;
+ pgs->color_space = pcs;
+ }
+ *cont = *cfrom;
+ *pdht = *dhtfrom;
+ *pcs = *csfrom;
+ cs_adjust_counts(pgs, 1);
+ return pgs;
+}
+
+/* Release the composite parts of a graphics state, */
+/* but not the state itself. */
+private void
+gstate_free_contents(gs_state *pgs)
+{ gs_memory_t *mem = pgs->memory;
+ static const char cname[] = "gstate_free_contents";
+#define rcdecr(element)\
+ rc_decrement(pgs->element, mem, cname)
+ gx_path_release(pgs->path);
+ gx_cpath_release(pgs->clip_path);
+ rcdecr(cie_joint_caches);
+ rcdecr(set_transfer.colored.gray);
+ rcdecr(set_transfer.colored.blue);
+ rcdecr(set_transfer.colored.green);
+ rcdecr(set_transfer.colored.red);
+ rcdecr(undercolor_removal);
+ rcdecr(black_generation);
+ rcdecr(cie_render);
+ cs_adjust_counts(pgs, -1);
+ if ( pgs->client_data != 0 )
+ (*pgs->client_procs.free)(pgs->client_data, mem);
+ gs_free_object(mem, pgs->color_space, cname);
+ gs_free_object(mem, pgs->dev_ht, cname);
+ gs_free_object(mem, pgs->contents, cname);
+#undef rcdecr
+}
+
+/*
+ * Mark both the old and new paths as shared when copying a gstate off-stack.
+ * If the old path was previously shared, we must search up
+ * the graphics state stack so we can mark its original ancestor
+ * as shared, because the off-stack copy defeats the one-bit
+ * reference count.
+ */
+private void
+gstate_share_paths(const gs_state *pgs)
+{ if ( pgs->path->shares_segments )
+ { const gs_state *pcur;
+ const gs_state *prev;
+ const subpath *first;
+ for ( pcur = pgs, first = pgs->path->first_subpath;
+ (prev = pcur->saved) != 0 &&
+ prev->path->first_subpath == first;
+ pcur = prev
+ )
+ if ( !prev->path->shares_segments )
+ { gx_path_share(prev->path);
+ break;
+ }
+ }
+ else
+ gx_path_share(pgs->path);
+ if ( pgs->clip_path->path.shares_segments )
+ { const gs_state *pcur;
+ const gs_state *prev;
+ const subpath *first;
+ for ( pcur = pgs, first = pgs->clip_path->path.first_subpath;
+ (prev = pcur->saved) != 0 &&
+ prev->clip_path->path.first_subpath == first;
+ pcur = prev
+ )
+ if ( !prev->clip_path->path.shares_segments )
+ { gx_cpath_share(prev->clip_path);
+ break;
+ }
+ }
+ if ( pgs->clip_path->shares_list )
+ { const gs_state *pcur;
+ const gs_state *prev;
+ const gx_clip_rect *head;
+ for ( pcur = pgs, head = pgs->clip_path->list.head;
+ (prev = pcur->saved) != 0 &&
+ prev->clip_path->list.head == head;
+ pcur = prev
+ )
+ if ( !prev->clip_path->shares_list )
+ { gx_cpath_share(prev->clip_path);
+ break;
+ }
+ }
+ gx_cpath_share(pgs->clip_path);
+}
+
+/* Copy one gstate to another. */
+private void
+gstate_copy(gs_state *pto, const gs_state *pfrom, client_name_t cname)
+{ gs_memory_t *mem = pto->memory;
+ gs_state_contents *cto = pto->contents;
+ gx_device_halftone *dhtto = pto->dev_ht;
+ gs_color_space *csto = pto->color_space;
+
+ /* It's OK to decrement the counts before incrementing them, */
+ /* because anything that is going to survive has a count of */
+ /* at least 2 (pto and somewhere else) initially. */
+ /* Handle references from contents. */
+ cs_adjust_counts(pto, -1);
+ gx_path_release(pto->path);
+ gx_cpath_release(pto->clip_path);
+ *cto = *pfrom->contents;
+ *dhtto = *pfrom->dev_ht;
+ *csto = *pfrom->color_space;
+ cs_adjust_counts(pto, 1);
+ gx_path_share(pto->path);
+ gx_cpath_share(pto->clip_path);
+ /* Handle references from gstate object. */
+#define rccopy(element)\
+ rc_pre_assign(pto->element, pfrom->element, mem, cname)
+ rccopy(cie_joint_caches);
+ rccopy(set_transfer.colored.gray);
+ rccopy(set_transfer.colored.blue);
+ rccopy(set_transfer.colored.green);
+ rccopy(set_transfer.colored.red);
+ rccopy(undercolor_removal);
+ rccopy(black_generation);
+ rccopy(cie_render);
+#undef rccopy
+ { struct gx_pattern_cache_s *pcache = pto->pattern_cache;
+ void *pdata = pto->client_data;
+ *pto = *pfrom;
+ pto->client_data = pdata;
+ if ( pto->pattern_cache == 0 )
+ pto->pattern_cache = pcache;
+ if ( pfrom->client_data != 0 )
+ (*pfrom->client_procs.copy)(pdata, pfrom->client_data);
+ }
+ gstate_set_contents(pto, cto);
+ pto->dev_ht = dhtto;
+ pto->color_space = csto;
+ pto->show_gstate =
+ (pfrom->show_gstate == pfrom ? pto : 0);
+}
diff --git a/pstoraster/gsstate.h b/pstoraster/gsstate.h
new file mode 100644
index 000000000..d288a6750
--- /dev/null
+++ b/pstoraster/gsstate.h
@@ -0,0 +1,68 @@
+/* Copyright (C) 1989, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gsstate.h */
+/* Public graphics state API */
+
+#ifndef gsstate_INCLUDED
+# define gsstate_INCLUDED
+
+/* Opaque type for a graphics state */
+#ifndef gs_state_DEFINED
+# define gs_state_DEFINED
+typedef struct gs_state_s gs_state;
+#endif
+
+/* Initial allocation and freeing */
+gs_state *gs_state_alloc(P1(gs_memory_t *)); /* 0 if fails */
+int gs_state_free(P1(gs_state *));
+
+/* Initialization, saving, restoring, and copying */
+int gs_gsave(P1(gs_state *)),
+ gs_grestore(P1(gs_state *)),
+ gs_grestoreall(P1(gs_state *));
+gs_state *gs_gstate(P1(gs_state *));
+int gs_copygstate(P2(gs_state * /*to*/, const gs_state * /*from*/)),
+ gs_currentgstate(P2(gs_state * /*to*/, const gs_state * /*from*/)),
+ gs_setgstate(P2(gs_state * /*to*/, const gs_state * /*from*/));
+int gs_initgraphics(P1(gs_state *));
+
+/* Device control */
+#include "gsdevice.h"
+
+/* Line parameters and quality */
+#include "gsline.h"
+
+/* Color and gray */
+#include "gscolor.h"
+
+/* Halftone screen */
+#include "gsht.h"
+int gs_sethalftonephase(P3(gs_state *, int, int));
+int gs_currenthalftonephase(P2(const gs_state *, gs_int_point *));
+
+/* Miscellaneous */
+int gs_setfilladjust(P3(gs_state *, floatp, floatp));
+int gs_currentfilladjust(P2(const gs_state *, gs_point *));
+
+#endif /* gsstate_INCLUDED */
diff --git a/pstoraster/gsstruct.h b/pstoraster/gsstruct.h
new file mode 100644
index 000000000..e325885d2
--- /dev/null
+++ b/pstoraster/gsstruct.h
@@ -0,0 +1,602 @@
+/* Copyright (C) 1993, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gsstruct.h */
+/* Definitions for Ghostscript modules that define allocatable structures */
+/* Requires gstypes.h */
+
+#ifndef gsstruct_INCLUDED
+# define gsstruct_INCLUDED
+
+/*
+ * Ghostscript structures are defined with names of the form (gs_)xxx_s,
+ * with a corresponding typedef of the form (gs_)xxx or (gs_)xxx_t.
+ * By extension, the structure descriptor is named st_[gs_]xxx.
+ * (Note that the descriptor name may omit the gs_ even if the type has it.)
+ * Structure descriptors are always allocated statically and are
+ * always const; they may be either public or private.
+ *
+ * In order to ensure that there is a descriptor for each structure type,
+ * we require, by convention, that the following always appear together
+ * if the structure is defined in a .h file:
+ * - The definition of the structure xxx_s;
+ * - If the descriptor is public, an extern_st(st_xxx);
+ * - The definition of a macro public_st_xxx() or private_st_xxx()
+ * that creates the actual descriptor.
+ * This convention makes the descriptor visible (if public) to any module
+ * that can see the structure definition. This is more liberal than
+ * we would like, but it is a reasonable compromise between restricting
+ * visibility and keeping all the definitional elements of a structure
+ * together. We require that there be no other externs for (public)
+ * structure descriptors; if the definer of a structure wants to make
+ * available the ability to create an instance but does not want to
+ * expose the structure definition, it must export a creator procedure.
+ *
+ * Because of bugs in some compilers' bookkeeping for undefined structure
+ * types, any file that uses extern_st must include gsstruct.h.
+ * (If it weren't for these bugs, the definition of extern_st could
+ * go in gsmemory.h.)
+ */
+#define extern_st(st) extern const gs_memory_struct_type_t st
+/*
+ * If the structure is defined in a .c file, we require that the following
+ * appear together:
+ * - The definition of the structure xxx_s;
+ * - The gs_private_st_xxx macro that creates the descriptor.
+ * Note that we only allow this if the structure is completely private
+ * to a single file. Again, the file must export a creator procedure
+ * if it wants external clients to be able to create instances.
+ *
+ * Some structures are embedded inside others. In order to be able to
+ * construct the composite pointer enumeration procedures, for such
+ * structures we must define not only the st_xxx descriptor, but also
+ * a st_xxx_max_ptrs constant that gives the maximum number of pointers
+ * the enumeration procedure will return. This is an unfortunate consequence
+ * of the method we have chosen for implementing pointer enumeration.
+ *
+ * Some structures may exist as elements of homogenous arrays.
+ * In order to be able to enumerate and relocate such arrays, we adopt
+ * the convention that the structure representing an element must be
+ * distinguished from the structure per se, and the name of the element
+ * structure always ends with "_element". Element structures cannot be
+ * embedded in other structures.
+ *
+ * Note that the definition of the xxx_s structure may be separate from
+ * the typedef for the type xxx(_t). This still allows us to have full
+ * structure type abstraction.
+ *
+ * Descriptor definitions are not required for structures to which
+ * no traceable pointers from garbage-collectable space will ever exist.
+ * For example, the struct that defines structure types themselves does not
+ * require a descriptor.
+ */
+
+/* An opaque type for an object header. */
+#ifndef obj_header_DEFINED
+# define obj_header_DEFINED
+typedef struct obj_header_s obj_header_t;
+#endif
+
+/*
+ * A descriptor for an object (structure) type.
+ */
+typedef struct struct_shared_procs_s struct_shared_procs_t;
+struct gs_memory_struct_type_s {
+ uint ssize;
+ struct_name_t sname;
+
+ /* ------ Procedures shared among many structure types. ------ */
+ /* Note that this pointer is usually 0. */
+
+ const struct_shared_procs_t _ds *shared;
+
+ /* ------ Procedures specific to this structure type. ------ */
+ /* Note that these procedures may be 0. */
+
+ /* Clear the marks of a structure. */
+
+#define struct_proc_clear_marks(proc)\
+ void proc(P2(void /*obj_header_t*/ *pre, uint size))
+ struct_proc_clear_marks((*clear_marks));
+
+ /* Enumerate the pointers in a structure. */
+
+#define struct_proc_enum_ptrs(proc)\
+ gs_ptr_type_t proc(P4(void /*obj_header_t*/ *ptr, uint size, uint index, void **pep))
+ struct_proc_enum_ptrs((*enum_ptrs));
+
+ /* Relocate all the pointers in this structure. */
+
+#define struct_proc_reloc_ptrs(proc)\
+ void proc(P3(void /*obj_header_t*/ *ptr, uint size, gc_state_t *gcst))
+ struct_proc_reloc_ptrs((*reloc_ptrs));
+
+ /*
+ * Finalize this structure just before freeing it.
+ * Finalization procedures must not allocate or resize
+ * any objects in any space managed by the allocator,
+ * and must not assume that any objects in such spaces
+ * referenced by this structure still exist. However,
+ * finalization procedures may free such objects, and
+ * may allocate, free, and reference objects allocated
+ * in other ways, such as objects allocated with malloc
+ * by libraries.
+ */
+
+#define struct_proc_finalize(proc)\
+ void proc(P1(void /*obj_header_t*/ *ptr))
+ struct_proc_finalize((*finalize));
+
+};
+#define struct_type_name_string(pstype) ((const char *)((pstype)->sname))
+/* Default pointer processing */
+struct_proc_enum_ptrs(gs_no_struct_enum_ptrs);
+struct_proc_reloc_ptrs(gs_no_struct_reloc_ptrs);
+/* Standard relocation procedures */
+ptr_proc_reloc(gs_reloc_struct_ptr, void /*obj_header_t*/);
+void gs_reloc_string(P2(gs_string *, gc_state_t *));
+void gs_reloc_const_string(P2(gs_const_string *, gc_state_t *));
+
+/* Define a 'type' descriptor for free blocks. */
+extern_st(st_free);
+
+/* Define a type descriptor for byte objects. */
+extern_st(st_bytes);
+
+/* ================ Macros for defining structure types ================ */
+
+#define public_st public const gs_memory_struct_type_t
+#define private_st private const gs_memory_struct_type_t
+
+/* -------------- Simple structures (no internal pointers). -------------- */
+
+#define gs__st_simple(scope_st, stname, stype, sname)\
+ scope_st stname = { sizeof(stype), sname, 0, 0, 0, 0, 0 }
+#define gs_public_st_simple(stname, stype, sname)\
+ gs__st_simple(public_st, stname, stype, sname)
+#define gs_private_st_simple(stname, stype, sname)\
+ gs__st_simple(private_st, stname, stype, sname)
+
+/* ---------------- Structures with explicit procedures. ---------------- */
+
+ /* Complex structures with their own clear_marks, */
+ /* enum, reloc, and finalize procedures. */
+
+#define gs__st_complex_only(scope_st, stname, stype, sname, pclear, penum, preloc, pfinal)\
+ scope_st stname = { sizeof(stype), sname, 0, pclear, penum, preloc, pfinal }
+#define gs_public_st_complex_only(stname, stype, sname, pclear, penum, preloc, pfinal)\
+ gs__st_complex_only(public_st, stname, stype, sname, pclear, penum, preloc, pfinal)
+#define gs_private_st_complex_only(stname, stype, sname, pclear, penum, preloc, pfinal)\
+ gs__st_complex_only(private_st, stname, stype, sname, pclear, penum, preloc, pfinal)
+
+#define gs__st_complex(scope_st, stname, stype, sname, pclear, penum, preloc, pfinal)\
+ private struct_proc_clear_marks(pclear);\
+ private struct_proc_enum_ptrs(penum);\
+ private struct_proc_reloc_ptrs(preloc);\
+ private struct_proc_finalize(pfinal);\
+ gs__st_complex_only(scope_st, stname, stype, sname, pclear, penum, preloc, pfinal)
+#define gs_public_st_complex(stname, stype, sname, pclear, penum, preloc, pfinal)\
+ gs__st_complex(public_st, stname, stype, sname, pclear, penum, preloc, pfinal)
+#define gs_private_st_complex(stname, stype, sname, pclear, penum, preloc, pfinal)\
+ gs__st_complex(private_st, stname, stype, sname, pclear, penum, preloc, pfinal)
+
+ /* Composite structures with their own enum and reloc procedures. */
+
+#define gs__st_composite(scope_st, stname, stype, sname, penum, preloc)\
+ private struct_proc_enum_ptrs(penum);\
+ private struct_proc_reloc_ptrs(preloc);\
+ gs__st_complex_only(scope_st, stname, stype, sname, 0, penum, preloc, 0)
+#define gs_public_st_composite(stname, stype, sname, penum, preloc)\
+ gs__st_composite(public_st, stname, stype, sname, penum, preloc)
+#define gs_private_st_composite(stname, stype, sname, penum, preloc)\
+ gs__st_composite(private_st, stname, stype, sname, penum, preloc)
+
+ /* Composite structures with finalization. */
+
+#define gs__st_composite_final(scope_st, stname, stype, sname, penum, preloc, pfinal)\
+ private struct_proc_enum_ptrs(penum);\
+ private struct_proc_reloc_ptrs(preloc);\
+ private struct_proc_finalize(pfinal);\
+ gs__st_complex_only(scope_st, stname, stype, sname, 0, penum, preloc, pfinal)
+#define gs_public_st_composite_final(stname, stype, sname, penum, preloc, pfinal)\
+ gs__st_composite_final(public_st, stname, stype, sname, penum, preloc, pfinal)
+#define gs_private_st_composite_final(stname, stype, sname, penum, preloc, pfinal)\
+ gs__st_composite_final(private_st, stname, stype, sname, penum, preloc, pfinal)
+
+ /* Composite structures with enum and reloc procedures */
+ /* already declared. */
+
+#define gs__st_composite_only(scope_st, stname, stype, sname, penum, preloc)\
+ gs__st_complex_only(scope_st, stname, stype, sname, 0, penum, preloc, 0)
+#define gs_public_st_composite_only(stname, stype, sname, penum, preloc)\
+ gs__st_composite_only(public_st, stname, stype, sname, penum, preloc)
+#define gs_private_st_composite_only(stname, stype, sname, penum, preloc)\
+ gs__st_composite_only(private_st, stname, stype, sname, penum, preloc)
+
+/* ---------------- Special kinds of structures ---------------- */
+
+ /* Element structures, for use in arrays of structures. */
+ /* Note that these require that the underlying structure's */
+ /* enum_ptrs procedure always return the same number of pointers. */
+
+#define gs__st_element(scope_st, stname, stype, sname, penum, preloc, basest)\
+ private ENUM_PTRS_BEGIN_PROC(penum) {\
+ uint count = size / (uint)sizeof(stype);\
+ if ( count == 0 ) return 0;\
+ return (*basest.enum_ptrs)((char *)vptr + (index % count) * sizeof(stype),\
+ sizeof(stype), index / count, pep);\
+ } ENUM_PTRS_END_PROC\
+ private RELOC_PTRS_BEGIN(preloc) {\
+ uint count = size / (uint)sizeof(stype);\
+ for ( ; count; count--, vptr = (char *)vptr + sizeof(stype) )\
+ (*basest.reloc_ptrs)(vptr, sizeof(stype), gcst);\
+ } RELOC_PTRS_END\
+ gs__st_composite_only(scope_st, stname, stype, sname, penum, preloc)
+#define gs_public_st_element(stname, stype, sname, penum, preloc, basest)\
+ gs__st_element(public_st, stname, stype, sname, penum, preloc, basest)
+#define gs_private_st_element(stname, stype, sname, penum, preloc, basest)\
+ gs__st_element(private_st, stname, stype, sname, penum, preloc, basest)
+
+ /* A "structure" just consisting of a pointer. */
+ /* Note that in this case only, stype is a pointer type. */
+
+#define gs__st_ptr(scope_st, stname, stype, sname, penum, preloc)\
+ private ENUM_PTRS_BEGIN(penum) return 0;\
+ case 0: *pep = (void *)*(stype *)vptr; break;\
+ ENUM_PTRS_END\
+ private RELOC_PTRS_BEGIN(preloc) ;\
+ *(stype *)vptr = gs_reloc_struct_ptr((const void *)*(stype *)vptr, gcst);\
+ RELOC_PTRS_END\
+ gs__st_composite_only(scope_st, stname, stype, sname, penum, preloc)
+#define gs_public_st_ptr(stname, stype, sname, penum, preloc)\
+ gs__st_ptr(public_st, stname, stype, sname, penum, preloc)
+#define gs_private_st_ptr(stname, stype, sname, penum, preloc)\
+ gs__st_ptr(private_st, stname, stype, sname, penum, preloc)
+
+/* ---------------- Ordinary structures ---------------- */
+
+/*
+ * The simplest kind of composite structure is one with a fixed set of
+ * pointers, each of which points to a struct. We provide macros for
+ * defining this kind of structure conveniently, either all at once in
+ * the structure definition macro, or using the following template:
+
+ENUM_PTRS_BEGIN(xxx_enum_ptrs) return 0;
+ ... ENUM_PTR(i, xxx, elt); ...
+ENUM_PTRS_END
+RELOC_PTRS_BEGIN(xxx_reloc_ptrs) ;
+ ... RELOC_PTR(xxx, elt) ...
+RELOC_PTRS_END
+
+ */
+/*
+ * We have to pull the 'private' outside the ENUM_PTRS_BEGIN and
+ * RELOC_PTRS_BEGIN macros because of a bug in the Borland C++ preprocessor.
+ * We also have to make sure there is more on the line after these
+ * macros, so as not to confuse ansi2knr.
+ */
+#ifdef __PROTOTYPES__
+# define ENUM_PTRS_BEGIN_PROC(proc)\
+ gs_ptr_type_t proc(void *vptr, uint size, uint index, void **pep)
+#else
+# define ENUM_PTRS_BEGIN_PROC(proc)\
+ gs_ptr_type_t proc(vptr, size, index, pep) void *vptr; uint size; uint index; void **pep;
+#endif
+#define ENUM_PTRS_BEGIN(proc)\
+ ENUM_PTRS_BEGIN_PROC(proc) { switch ( index ) { default:
+#define ENUM_PTR(i, typ, elt)\
+ case i: ENUM_RETURN_PTR(typ, elt)
+#define ENUM_RETURN_PTR(typ, elt)\
+ ENUM_RETURN(((typ *)vptr)->elt)
+#define ENUM_RETURN(ptr)\
+ *pep = (void *)(ptr); break /* discard const */
+#define ENUM_STRING_PTR(i, typ, elt)\
+ case i: ENUM_RETURN_STRING_PTR(typ, elt)
+#define ENUM_RETURN_STRING_PTR(typ, elt)\
+ *pep = (void *)&((typ *)vptr)->elt; return ptr_string_type
+#define ENUM_CONST_STRING_PTR(i, typ, elt)\
+ case i: ENUM_RETURN_CONST_STRING_PTR(typ, elt)
+#define ENUM_RETURN_CONST_STRING_PTR(typ, elt)\
+ *pep = (void *)&((typ *)vptr)->elt; return ptr_const_string_type
+#define ENUM_PTRS_END\
+ } return ptr_struct_type; ENUM_PTRS_END_PROC }
+#define ENUM_PTRS_END_PROC /* */
+#ifdef __PROTOTYPES__
+# define RELOC_PTRS_BEGIN(proc)\
+ void proc(void *vptr, uint size, gc_state_t *gcst) {
+#else
+# define RELOC_PTRS_BEGIN(proc)\
+ void proc(vptr, size, gcst) void *vptr; uint size; gc_state_t *gcst; {
+#endif
+#define RELOC_PTR(typ, elt)\
+ ((typ *)vptr)->elt =\
+ gs_reloc_struct_ptr((const void *)((const typ *)vptr)->elt, gcst)
+/* Relocate a pointer that points to a known offset within an object. */
+/* OFFSET is for byte offsets, TYPED_OFFSET is for element offsets. */
+#define RELOC_OFFSET_PTR(typ, elt, offset)\
+ ((typ *)vptr)->elt = (void *)\
+ ((char *)gs_reloc_struct_ptr((char *)((typ *)vptr)->elt - (offset), gcst) +\
+ (offset))
+#define RELOC_TYPED_OFFSET_PTR(typ, elt, offset)\
+ (((typ *)vptr)->elt = (void *)\
+ gs_reloc_struct_ptr(((typ *)vptr)->elt - (offset), gcst),\
+ ((typ *)vptr)->elt += (offset))
+#define RELOC_STRING_PTR(typ, elt)\
+ gs_reloc_string(&((typ *)vptr)->elt, gcst)
+#define RELOC_CONST_STRING_PTR(typ, elt)\
+ gs_reloc_const_string(&((typ *)vptr)->elt, gcst)
+#define RELOC_PTRS_END\
+ }
+
+/*
+ * Boilerplate for clear_marks procedures.
+ */
+#ifdef __PROTOTYPES__
+# define CLEAR_MARKS_PROC(proc)\
+ void proc(void *vptr, uint size)
+#else
+# define CLEAR_MARKS_PROC(proc)\
+ void proc(vptr, size) void *vptr; uint size;
+#endif
+
+/* ---------------- Structures with a fixed set of pointers ---------------- */
+
+ /* Structures with 1 pointer. */
+
+#define gs__st_ptrs1(scope_st, stname, stype, sname, penum, preloc, e1)\
+ private ENUM_PTRS_BEGIN(penum) return 0;\
+ ENUM_PTR(0,stype,e1);\
+ ENUM_PTRS_END\
+ private RELOC_PTRS_BEGIN(preloc) ;\
+ RELOC_PTR(stype,e1);\
+ RELOC_PTRS_END\
+ gs__st_composite_only(scope_st, stname, stype, sname, penum, preloc)
+#define gs_public_st_ptrs1(stname, stype, sname, penum, preloc, e1)\
+ gs__st_ptrs1(public_st, stname, stype, sname, penum, preloc, e1)
+#define gs_private_st_ptrs1(stname, stype, sname, penum, preloc, e1)\
+ gs__st_ptrs1(private_st, stname, stype, sname, penum, preloc, e1)
+
+ /* Structures with 2 pointers. */
+
+#define gs__st_ptrs2(scope_st, stname, stype, sname, penum, preloc, e1, e2)\
+ private ENUM_PTRS_BEGIN(penum) return 0;\
+ ENUM_PTR(0,stype,e1); ENUM_PTR(1,stype,e2);\
+ ENUM_PTRS_END\
+ private RELOC_PTRS_BEGIN(preloc) ;\
+ RELOC_PTR(stype,e1); RELOC_PTR(stype,e2);\
+ RELOC_PTRS_END\
+ gs__st_composite_only(scope_st, stname, stype, sname, penum, preloc)
+#define gs_public_st_ptrs2(stname, stype, sname, penum, preloc, e1, e2)\
+ gs__st_ptrs2(public_st, stname, stype, sname, penum, preloc, e1, e2)
+#define gs_private_st_ptrs2(stname, stype, sname, penum, preloc, e1, e2)\
+ gs__st_ptrs2(private_st, stname, stype, sname, penum, preloc, e1, e2)
+
+ /* Structures with 3 pointers. */
+
+#define gs__st_ptrs3(scope_st, stname, stype, sname, penum, preloc, e1, e2, e3)\
+ private ENUM_PTRS_BEGIN(penum) return 0;\
+ ENUM_PTR(0,stype,e1); ENUM_PTR(1,stype,e2); ENUM_PTR(2,stype,e3);\
+ ENUM_PTRS_END\
+ private RELOC_PTRS_BEGIN(preloc) ;\
+ RELOC_PTR(stype,e1); RELOC_PTR(stype,e2); RELOC_PTR(stype,e3);\
+ RELOC_PTRS_END\
+ gs__st_composite_only(scope_st, stname, stype, sname, penum, preloc)
+#define gs_public_st_ptrs3(stname, stype, sname, penum, preloc, e1, e2, e3)\
+ gs__st_ptrs3(public_st, stname, stype, sname, penum, preloc, e1, e2, e3)
+#define gs_private_st_ptrs3(stname, stype, sname, penum, preloc, e1, e2, e3)\
+ gs__st_ptrs3(private_st, stname, stype, sname, penum, preloc, e1, e2, e3)
+
+ /* Structures with 4 pointers. */
+
+#define gs__st_ptrs4(scope_st, stname, stype, sname, penum, preloc, e1, e2, e3, e4)\
+ private ENUM_PTRS_BEGIN(penum) return 0;\
+ ENUM_PTR(0,stype,e1); ENUM_PTR(1,stype,e2); ENUM_PTR(2,stype,e3);\
+ ENUM_PTR(3,stype,e4);\
+ ENUM_PTRS_END\
+ private RELOC_PTRS_BEGIN(preloc) ;\
+ RELOC_PTR(stype,e1); RELOC_PTR(stype,e2); RELOC_PTR(stype,e3);\
+ RELOC_PTR(stype,e4);\
+ RELOC_PTRS_END\
+ gs__st_composite_only(scope_st, stname, stype, sname, penum, preloc)
+#define gs_public_st_ptrs4(stname, stype, sname, penum, preloc, e1, e2, e3, e4)\
+ gs__st_ptrs4(public_st, stname, stype, sname, penum, preloc, e1, e2, e3, e4)
+#define gs_private_st_ptrs4(stname, stype, sname, penum, preloc, e1, e2, e3, e4)\
+ gs__st_ptrs4(private_st, stname, stype, sname, penum, preloc, e1, e2, e3, e4)
+
+ /* Structures with 5 pointers. */
+
+#define gs__st_ptrs5(scope_st, stname, stype, sname, penum, preloc, e1, e2, e3, e4, e5)\
+ private ENUM_PTRS_BEGIN(penum) return 0;\
+ ENUM_PTR(0,stype,e1); ENUM_PTR(1,stype,e2); ENUM_PTR(2,stype,e3);\
+ ENUM_PTR(3,stype,e4); ENUM_PTR(4,stype,e5);\
+ ENUM_PTRS_END\
+ private RELOC_PTRS_BEGIN(preloc) ;\
+ RELOC_PTR(stype,e1); RELOC_PTR(stype,e2); RELOC_PTR(stype,e3);\
+ RELOC_PTR(stype,e4); RELOC_PTR(stype,e5);\
+ RELOC_PTRS_END\
+ gs__st_composite_only(scope_st, stname, stype, sname, penum, preloc)
+#define gs_public_st_ptrs5(stname, stype, sname, penum, preloc, e1, e2, e3, e4, e5)\
+ gs__st_ptrs5(public_st, stname, stype, sname, penum, preloc, e1, e2, e3, e4, e5)
+#define gs_private_st_ptrs5(stname, stype, sname, penum, preloc, e1, e2, e3, e4, e5)\
+ gs__st_ptrs5(private_st, stname, stype, sname, penum, preloc, e1, e2, e3, e4, e5)
+
+/* ---------------- Suffix subclasses ---------------- */
+
+/*
+ * Boilerplate for suffix subclasses. Special subclasses constructed
+ * 'by hand' may use this also.
+ */
+#define ENUM_PREFIX(supst, n)\
+ return (supst.enum_ptrs ? (*supst.enum_ptrs)(vptr,size,index-(n),pep) : 0)
+#define RELOC_PREFIX(supst)\
+ if ( supst.reloc_ptrs ) (*supst.reloc_ptrs)(vptr,size,gcst)
+
+ /* Suffix subclasses with no additional pointers. */
+
+#define gs__st_suffix_add0(scope_st, stname, stype, sname, penum, preloc, supstname)\
+ private ENUM_PTRS_BEGIN_PROC(penum) {\
+ return (*supstname.enum_ptrs)(vptr, size, index, pep);\
+ } ENUM_PTRS_END_PROC\
+ private RELOC_PTRS_BEGIN(preloc) {\
+ (*supstname.reloc_ptrs)(vptr, size, gcst);\
+ } RELOC_PTRS_END\
+ gs__st_composite_only(scope_st, stname, stype, sname, penum, preloc)
+#define gs_public_st_suffix_add0(stname, stype, sname, penum, preloc, supstname)\
+ gs__st_suffix_add0(public_st, stname, stype, sname, penum, preloc, supstname)
+#define gs_private_st_suffix_add0(stname, stype, sname, penum, preloc, supstname)\
+ gs__st_suffix_add0(private_st, stname, stype, sname, penum, preloc, supstname)
+
+ /* Suffix subclasses with no additional pointers and finalization. */
+ /* This is a hack -- subclasses should inherit finalization, */
+ /* but that would require a subclass pointer in the descriptor, */
+ /* which would perturb things too much right now. */
+
+#define gs__st_suffix_add0_final(scope_st, stname, stype, sname, penum, preloc, pfinal, supstname)\
+ private ENUM_PTRS_BEGIN_PROC(penum) {\
+ return (*supstname.enum_ptrs)(vptr, size, index, pep);\
+ } ENUM_PTRS_END_PROC\
+ private RELOC_PTRS_BEGIN(preloc) {\
+ (*supstname.reloc_ptrs)(vptr, size, gcst);\
+ } RELOC_PTRS_END\
+ gs__st_complex_only(scope_st, stname, stype, sname, 0, penum, preloc, pfinal)
+#define gs_public_st_suffix_add0_final(stname, stype, sname, penum, preloc, pfinal, supstname)\
+ gs__st_suffix_add0_final(public_st, stname, stype, sname, penum, preloc, pfinal, supstname)
+#define gs_private_st_suffix_add0_final(stname, stype, sname, penum, preloc, pfinal, supstname)\
+ gs__st_suffix_add0_final(private_st, stname, stype, sname, penum, preloc, pfinal, supstname)
+
+ /* Suffix subclasses with 1 additional pointer. */
+
+#define gs__st_suffix_add1(scope_st, stname, stype, sname, penum, preloc, supstname, e1)\
+ private ENUM_PTRS_BEGIN(penum) ENUM_PREFIX(supstname,1);\
+ ENUM_PTR(0,stype,e1);\
+ ENUM_PTRS_END\
+ private RELOC_PTRS_BEGIN(preloc) RELOC_PREFIX(supstname);\
+ RELOC_PTR(stype,e1);\
+ RELOC_PTRS_END\
+ gs__st_composite_only(scope_st, stname, stype, sname, penum, preloc)
+#define gs_public_st_suffix_add1(stname, stype, sname, penum, preloc, supstname, e1)\
+ gs__st_suffix_add1(public_st, stname, stype, sname, penum, preloc, supstname, e1)
+#define gs_private_st_suffix_add1(stname, stype, sname, penum, preloc, supstname, e1)\
+ gs__st_suffix_add1(private_st, stname, stype, sname, penum, preloc, supstname, e1)
+
+ /* Suffix subclasses with 1 additional pointer and finalization. */
+ /* This is a hack -- see above. */
+
+#define gs__st_suffix_add1_final(scope_st, stname, stype, sname, penum, preloc, pfinal, supstname, e1)\
+ private ENUM_PTRS_BEGIN(penum) ENUM_PREFIX(supstname,1);\
+ ENUM_PTR(0,stype,e1);\
+ ENUM_PTRS_END\
+ private RELOC_PTRS_BEGIN(preloc) RELOC_PREFIX(supstname);\
+ RELOC_PTR(stype,e1);\
+ RELOC_PTRS_END\
+ gs__st_complex_only(scope_st, stname, stype, sname, 0, penum, preloc, pfinal)
+#define gs_public_st_suffix_add1_final(stname, stype, sname, penum, preloc, pfinal, supstname, e1)\
+ gs__st_suffix_add1_final(public_st, stname, stype, sname, penum, preloc, pfinal, supstname, e1)
+#define gs_private_st_suffix_add1_final(stname, stype, sname, penum, preloc, pfinal, supstname, e1)\
+ gs__st_suffix_add1_final(private_st, stname, stype, sname, penum, preloc, pfinal, supstname, e1)
+
+ /* Suffix subclasses with 2 additional pointers. */
+
+#define gs__st_suffix_add2(scope_st, stname, stype, sname, penum, preloc, supstname, e1, e2)\
+ private ENUM_PTRS_BEGIN(penum) ENUM_PREFIX(supstname,2);\
+ ENUM_PTR(0,stype,e1); ENUM_PTR(1,stype,e2);\
+ ENUM_PTRS_END\
+ private RELOC_PTRS_BEGIN(preloc) RELOC_PREFIX(supstname);\
+ RELOC_PTR(stype,e1); RELOC_PTR(stype,e2);\
+ RELOC_PTRS_END\
+ gs__st_composite_only(scope_st, stname, stype, sname, penum, preloc)
+#define gs_public_st_suffix_add2(stname, stype, sname, penum, preloc, supstname, e1, e2)\
+ gs__st_suffix_add2(public_st, stname, stype, sname, penum, preloc, supstname, e1, e2)
+#define gs_private_st_suffix_add2(stname, stype, sname, penum, preloc, supstname, e1, e2)\
+ gs__st_suffix_add2(private_st, stname, stype, sname, penum, preloc, supstname, e1, e2)
+
+ /* Suffix subclasses with 3 additional pointers. */
+
+#define gs__st_suffix_add3(scope_st, stname, stype, sname, penum, preloc, supstname, e1, e2, e3)\
+ private ENUM_PTRS_BEGIN(penum) ENUM_PREFIX(supstname,3);\
+ ENUM_PTR(0,stype,e1); ENUM_PTR(1,stype,e2); ENUM_PTR(2,stype,e3);\
+ ENUM_PTRS_END\
+ private RELOC_PTRS_BEGIN(preloc) RELOC_PREFIX(supstname);\
+ RELOC_PTR(stype,e1); RELOC_PTR(stype,e2); RELOC_PTR(stype,e3);\
+ RELOC_PTRS_END\
+ gs__st_composite_only(scope_st, stname, stype, sname, penum, preloc)
+#define gs_public_st_suffix_add3(stname, stype, sname, penum, preloc, supstname, e1, e2, e3)\
+ gs__st_suffix_add3(public_st, stname, stype, sname, penum, preloc, supstname, e1, e2, e3)
+#define gs_private_st_suffix_add3(stname, stype, sname, penum, preloc, supstname, e1, e2, e3)\
+ gs__st_suffix_add3(private_st, stname, stype, sname, penum, preloc, supstname, e1, e2, e3)
+
+/* ---------------- General subclasses ---------------- */
+
+/*
+ * Boilerplate for general subclasses.
+ */
+#define ENUM_SUPER(stype, supst, member, n)\
+ return (*supst.enum_ptrs)(&((stype *)vptr)->member, sizeof(((stype *)vptr)->member),\
+ index-(n), pep)
+#define RELOC_SUPER(stype, supst, member)\
+ (*supst.reloc_ptrs)(&((stype *)vptr)->member, sizeof(((stype *)vptr)->member), gcst)
+
+ /* General subclasses with no additional pointers. */
+
+#define gs__st_ptrs_add0(scope_st, stname, stype, sname, penum, preloc, supstname, member)\
+ private ENUM_PTRS_BEGIN(penum) ENUM_SUPER(stype,supstname,member,0);\
+ ENUM_PTRS_END\
+ private RELOC_PTRS_BEGIN(preloc) RELOC_SUPER(stype,supstname,member);\
+ RELOC_PTRS_END\
+ gs__st_composite_only(scope_st, stname, stype, sname, penum, preloc)
+#define gs_public_st_ptrs_add0(stname, stype, sname, penum, preloc, supstname, member)\
+ gs__st_ptrs_add0(public_st, stname, stype, sname, penum, preloc, supstname, member)
+#define gs_private_st_ptrs_add0(stname, stype, sname, penum, preloc, supstname, member)\
+ gs__st_ptrs_add0(private_st, stname, stype, sname, penum, preloc, supstname, member)
+
+ /* General subclasses with 1 additional pointer. */
+
+#define gs__st_ptrs_add1(scope_st, stname, stype, sname, penum, preloc, supstname, member, e1)\
+ private ENUM_PTRS_BEGIN(penum) ENUM_SUPER(stype,supstname,member,1);\
+ ENUM_PTR(0,stype,e1);\
+ ENUM_PTRS_END\
+ private RELOC_PTRS_BEGIN(preloc) RELOC_SUPER(stype,supstname,member);\
+ RELOC_PTR(stype,e1);\
+ RELOC_PTRS_END\
+ gs__st_composite_only(scope_st, stname, stype, sname, penum, preloc)
+#define gs_public_st_ptrs_add1(stname, stype, sname, penum, preloc, supstname, member, e1)\
+ gs__st_ptrs_add1(public_st, stname, stype, sname, penum, preloc, supstname, member, e1)
+#define gs_private_st_ptrs_add1(stname, stype, sname, penum, preloc, supstname, member, e1)\
+ gs__st_ptrs_add1(private_st, stname, stype, sname, penum, preloc, supstname, member, e1)
+
+ /* General subclasses with 2 additional pointers. */
+
+#define gs__st_ptrs_add2(scope_st, stname, stype, sname, penum, preloc, supstname, member, e1, e2)\
+ private ENUM_PTRS_BEGIN(penum) ENUM_SUPER(stype,supstname,member,2);\
+ ENUM_PTR(0,stype,e1); ENUM_PTR(1,stype,e2);\
+ ENUM_PTRS_END\
+ private RELOC_PTRS_BEGIN(preloc) RELOC_SUPER(stype,supstname,member);\
+ RELOC_PTR(stype,e1); RELOC_PTR(stype,e2);\
+ RELOC_PTRS_END\
+ gs__st_composite_only(scope_st, stname, stype, sname, penum, preloc)
+#define gs_public_st_ptrs_add2(stname, stype, sname, penum, preloc, supstname, member, e1, e2)\
+ gs__st_ptrs_add2(public_st, stname, stype, sname, penum, preloc, supstname, member, e1, e2)
+#define gs_private_st_ptrs_add2(stname, stype, sname, penum, preloc, supstname, member, e1, e2)\
+ gs__st_ptrs_add2(private_st, stname, stype, sname, penum, preloc, supstname, member, e1, e2)
+
+#endif /* gsstruct_INCLUDED */
diff --git a/pstoraster/gstype1.c b/pstoraster/gstype1.c
new file mode 100644
index 000000000..cd59adfc4
--- /dev/null
+++ b/pstoraster/gstype1.c
@@ -0,0 +1,966 @@
+/* Copyright (C) 1990, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gstype1.c */
+/* Adobe Type 1 font routines for Ghostscript library */
+#include "math_.h"
+#include "memory_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gsstruct.h"
+#include "gxarith.h"
+#include "gxfixed.h"
+#include "gxmatrix.h"
+#include "gxcoord.h"
+#include "gxistate.h"
+#include "gsline.h" /* for gs_setflat */
+#include "gspath.h"
+#include "gzpath.h"
+#include "gxchar.h"
+#include "gxfont.h"
+#include "gxfont1.h"
+#include "gxtype1.h"
+#include "gxop1.h"
+
+/* Define whether to always do Flex segments as curves. */
+/* This is only an issue because some old Adobe DPS fonts */
+/* seem to violate the Flex specification in a way that requires this. */
+#define ALWAYS_DO_FLEX_AS_CURVE 1
+
+/* Define whether or not to force hints to "big pixel" boundaries */
+/* when rasterizing at higher resolution. With the current algorithms, */
+/* a value of 1 is better for devices without alpha capability, */
+/* but 0 is better if alpha is available. */
+#define FORCE_HINTS_TO_BIG_PIXELS 1
+
+/* Structure descriptor */
+public_st_gs_font_type1();
+
+/* Encrypt a string. */
+int
+gs_type1_encrypt(byte *dest, const byte *src, uint len, crypt_state *pstate)
+{ register crypt_state state = *pstate;
+ register const byte *from = src;
+ register byte *to = dest;
+ register uint count = len;
+ while ( count )
+ { encrypt_next(*from, state, *to);
+ from++, to++, count--;
+ }
+ *pstate = state;
+ return 0;
+}
+/* Decrypt a string. */
+int
+gs_type1_decrypt(byte *dest, const byte *src, uint len, crypt_state *pstate)
+{ register crypt_state state = *pstate;
+ register const byte *from = src;
+ register byte *to = dest;
+ register uint count = len;
+ while ( count )
+ { /* If from == to, we can't use the obvious */
+ /* decrypt_next(*from, state, *to); */
+ register byte ch = *from++;
+ decrypt_next(ch, state, *to);
+ to++, count--;
+ }
+ *pstate = state;
+ return 0;
+}
+
+/* Define the structure type for a Type 1 interpreter state. */
+public_st_gs_type1_state();
+/* GC procedures */
+#define pcis ((gs_type1_state *)vptr)
+private ENUM_PTRS_BEGIN(gs_type1_state_enum_ptrs) {
+ if ( index < pcis->ips_count + 3 )
+ { ENUM_RETURN_CONST_STRING_PTR(gs_type1_state,
+ ipstack[index - 3].char_string);
+ }
+ return 0;
+ }
+ ENUM_PTR(0, gs_type1_state, pfont);
+ ENUM_PTR(1, gs_type1_state, pis);
+ ENUM_PTR(2, gs_type1_state, path);
+ENUM_PTRS_END
+private RELOC_PTRS_BEGIN(gs_type1_state_reloc_ptrs) {
+ int i;
+ RELOC_PTR(gs_type1_state, pfont);
+ RELOC_PTR(gs_type1_state, pis);
+ RELOC_PTR(gs_type1_state, path);
+ for ( i = 0; i < pcis->ips_count; i++ )
+ { ip_state *ipsp = &pcis->ipstack[i];
+ int diff = ipsp->ip - ipsp->char_string.data;
+ gs_reloc_const_string(&ipsp->char_string, gcst);
+ ipsp->ip = ipsp->char_string.data + diff;
+ }
+} RELOC_PTRS_END
+#undef pcis
+
+/* Define the state used by operator procedures. */
+/* These macros refer to a current instance (s) of gs_op1_state. */
+#define sppath s.ppath
+#define sfc s.fc
+#define spt s.p
+#define ptx s.p.x
+#define pty s.p.y
+
+/* Accumulate relative coordinates */
+/****** THESE ARE NOT ACCURATE FOR NON-INTEGER DELTAS. ******/
+/* This probably doesn't make any difference in practice. */
+#define c_fixed(d, c) m_fixed(d, c, sfc, max_coeff_bits)
+#define accum_x(dx)\
+ ptx += c_fixed(dx, xx);\
+ if ( sfc.skewed ) pty += c_fixed(dx, xy)
+#define accum_y(dy)\
+ pty += c_fixed(dy, yy);\
+ if ( sfc.skewed ) ptx += c_fixed(dy, yx)
+#define accum_xy(dx,dy)\
+ accum_xy_proc(&s, dx, dy)
+
+#define s (*ps)
+
+private void near
+accum_xy_proc(register is_ptr ps, fixed dx, fixed dy)
+{ ptx += c_fixed(dx, xx),
+ pty += c_fixed(dy, yy);
+ if ( sfc.skewed )
+ ptx += c_fixed(dy, yx),
+ pty += c_fixed(dx, xy);
+}
+
+/* Initialize a Type 1 interpreter. */
+/* The caller must supply a string to the first call of gs_type1_interpret. */
+int
+gs_type1_interp_init(register gs_type1_state *pcis, gs_imager_state *pis,
+ gx_path *ppath, const gs_log2_scale_point *pscale, bool charpath_flag,
+ int paint_type, gs_font_type1 *pfont)
+{ static const gs_log2_scale_point no_scale = { 0, 0 };
+ const gs_log2_scale_point *plog2_scale =
+ (FORCE_HINTS_TO_BIG_PIXELS ? pscale : &no_scale);
+
+ pcis->pfont = pfont;
+ pcis->pis = pis;
+ pcis->path = ppath;
+ /*
+ * charpath_flag controls coordinate rounding, hinting, and
+ * flatness enhancement. If we allow it to be set to true,
+ * charpath may produce results quite different from show.
+ */
+ pcis->charpath_flag = false /*charpath_flag*/;
+ pcis->paint_type = paint_type;
+ pcis->os_count = 0;
+ pcis->ips_count = 1;
+ pcis->ipstack[0].ip = 0;
+ pcis->ipstack[0].char_string.data = 0;
+ pcis->ipstack[0].char_string.size = 0;
+ pcis->ignore_pops = 0;
+ pcis->init_done = -1;
+ pcis->sb_set = false;
+ pcis->width_set = false;
+
+ /* Set the sampling scale. */
+ set_pixel_scale(&pcis->scale.x, plog2_scale->x);
+ set_pixel_scale(&pcis->scale.y, plog2_scale->y);
+
+ return 0;
+}
+/* Preset the left side bearing and/or width. */
+void
+gs_type1_set_lsb(gs_type1_state *pcis, const gs_point *psbpt)
+{ pcis->lsb.x = float2fixed(psbpt->x);
+ pcis->lsb.y = float2fixed(psbpt->y);
+ pcis->sb_set = true;
+}
+void
+gs_type1_set_width(gs_type1_state *pcis, const gs_point *pwpt)
+{ pcis->width.x = float2fixed(pwpt->x);
+ pcis->width.y = float2fixed(pwpt->y);
+ pcis->width_set = true;
+}
+/* Finish initializing the interpreter if we are actually rasterizing */
+/* the character, as opposed to just computing the side bearing and width. */
+private void
+gs_type1_finish_init(register gs_type1_state *pcis, gs_op1_state _ss *ps)
+{ gs_imager_state *pis = pcis->pis;
+
+ /* Set up the fixed version of the transformation. */
+ gx_matrix_to_fixed_coeff(&ctm_only(pis), &pcis->fc, max_coeff_bits);
+ sfc = pcis->fc;
+
+ /* Set the current point of the path to the origin, */
+ /* in anticipation of the initial [h]sbw. */
+ { gx_path *ppath = pcis->path;
+ ptx = pcis->origin.x = ppath->position.x;
+ pty = pcis->origin.y = ppath->position.y;
+ }
+
+ /* Initialize hint-related scalars. */
+ pcis->seac_base = -1;
+ pcis->asb_diff = pcis->adxy.x = pcis->adxy.y = 0;
+ pcis->flex_count = flex_max; /* not in Flex */
+ pcis->dotsection_flag = dotsection_out;
+ pcis->vstem3_set = false;
+ pcis->vs_offset.x = pcis->vs_offset.y = 0;
+ pcis->hints_initial = 0; /* probably not needed */
+ pcis->hint_next = 0;
+ pcis->hints_pending = 0;
+
+ /* Assimilate the hints proper. */
+ { gs_log2_scale_point log2_scale;
+ log2_scale.x = pcis->scale.x.log2_unit;
+ log2_scale.y = pcis->scale.y.log2_unit;
+ if ( pcis->charpath_flag )
+ reset_font_hints(&pcis->fh, &log2_scale);
+ else
+ compute_font_hints(&pcis->fh, &pis->ctm, &log2_scale,
+ &pcis->pfont->data);
+ }
+ reset_stem_hints(pcis);
+
+ /*
+ * Set the flatness to a value that is likely to produce reasonably
+ * good-looking curves, regardless of its current value in the
+ * graphics state. If the character is very small, set the flatness
+ * to zero, which will produce very accurate curves.
+ */
+ { float cxx = fabs(pis->ctm.xx), cyy = fabs(pis->ctm.yy);
+ if ( cyy < cxx )
+ cxx = cyy;
+ if ( is_skewed(&pis->ctm) )
+ { float cxy = fabs(pis->ctm.xy), cyx = fabs(pis->ctm.yx);
+ if ( cxy < cxx )
+ cxx = cxy;
+ if ( cyx < cxx )
+ cxx = cyx;
+ }
+ /* Don't let the flatness be worse than the default. */
+ if ( cxx > pis->flatness )
+ cxx = pis->flatness;
+ /* If the character is tiny, force accurate curves. */
+ if ( cxx < 0.2 )
+ cxx = 0;
+ pcis->flatness = cxx;
+ }
+
+ /* Move to the side bearing point. */
+ accum_xy(pcis->lsb.x, pcis->lsb.y);
+ pcis->position.x = ptx;
+ pcis->position.y = pty;
+
+ pcis->init_done = 1;
+}
+
+/* Tracing for type 1 interpreter */
+#ifdef DEBUG
+# define dc(str) if ( gs_debug['1'] ) type1_trace(cip, c, str);
+private void near
+type1_trace(const byte *cip, byte c, const char _ds *str)
+{ dprintf3("[1]0x%lx: %02x %s\n",
+ (ulong)(cip - 1), c, (const char *)str);
+}
+#else
+# define dc(str)
+#endif
+
+/* If not in a charpath, we round all endpoints of lines or curves */
+/* to the nearest quarter-pixel, and suppress null lines. */
+/* (Rounding to the half-pixel causes too many dropouts.) */
+/* This saves a lot of rendering work for small characters. */
+#define pixel_quantum float2fixed(0.25)
+#define pixel_rounded(fx)\
+ (((fx) + (pixel_quantum >> 1)) & -pixel_quantum)
+#define must_draw_to(lpx, lpy, px, py)\
+ (pcis->charpath_flag ? (lpx = (px), lpy = (py), 1) :\
+ ((lpx = pixel_rounded(px)), (lpy = pixel_rounded(py)),\
+ (psub = sppath->current_subpath) == 0 ||\
+ (pseg = psub->last)->type == s_line_close ||\
+ lpx != pseg->pt.x || lpy != pseg->pt.y))
+
+/* ------ Operator procedures ------ */
+
+/* We put these before the interpreter to save having to write */
+/* prototypes for all of them. */
+
+int
+gs_op1_closepath(register is_ptr ps)
+{ /* Note that this does NOT reset the current point! */
+ gx_path *ppath = sppath;
+ subpath *psub;
+ segment *pseg;
+ fixed ctemp;
+ int code;
+ /* Check for and suppress a microscopic closing line. */
+ if ( (psub = ppath->current_subpath) != 0 &&
+ (pseg = psub->last) != 0 && pseg->type == s_line &&
+ (ctemp = pseg->pt.x - psub->pt.x,
+ any_abs(ctemp) < float2fixed(0.1)) &&
+ (ctemp = pseg->pt.y - psub->pt.y,
+ any_abs(ctemp) < float2fixed(0.1))
+ )
+ code = gx_path_pop_close_subpath(sppath);
+ else
+ code = gx_path_close_subpath(sppath);
+ if ( code < 0 )
+ return code;
+ return gx_path_add_point(ppath, ptx, pty); /* put the point where it was */
+}
+
+int
+gs_op1_rrcurveto(register is_ptr ps, fixed dx1, fixed dy1,
+ fixed dx2, fixed dy2, fixed dx3, fixed dy3)
+{ gs_fixed_point pt1, pt2;
+ fixed ax0 = sppath->position.x - ptx;
+ fixed ay0 = sppath->position.y - pty;
+ accum_xy(dx1, dy1);
+ pt1.x = ptx + ax0, pt1.y = pty + ay0;
+ accum_xy(dx2, dy2);
+ pt2.x = ptx, pt2.y = pty;
+ accum_xy(dx3, dy3);
+ return gx_path_add_curve(sppath, pt1.x, pt1.y, pt2.x, pt2.y, ptx, pty);
+}
+
+#undef s
+
+private void
+type1_sbw(register gs_type1_state *pcis, const fixed *cstack)
+{ if ( !pcis->sb_set )
+ pcis->lsb.x = cstack[0], pcis->lsb.y = cstack[1],
+ pcis->sb_set = true; /* needed for accented chars */
+ if ( !pcis->width_set )
+ pcis->width.x = cstack[2], pcis->width.y = cstack[3],
+ pcis->width_set = true;
+ if_debug4('1',"[1]sb=(%g,%g) w=(%g,%g)\n",
+ fixed2float(pcis->lsb.x), fixed2float(pcis->lsb.y),
+ fixed2float(pcis->width.x), fixed2float(pcis->width.y));
+}
+
+/* ------ Main interpreter ------ */
+
+/* Define a pointer to the CharString interpreter stack. */
+typedef fixed _ss *cs_ptr;
+
+/* Forward references */
+private int near type1_endchar(P3(gs_type1_state *, gs_imager_state *, gx_path *));
+
+/* Continue interpreting a Type 1 CharString. */
+/* If str != 0, it is taken as the byte string to interpret. */
+/* Return 0 on successful completion, <0 on error, */
+/* or >0 when client intervention is required. */
+/* The int * argument is where the character is stored for seac, */
+/* or the othersubr # for callothersubr. */
+int
+gs_type1_interpret(register gs_type1_state *pcis, const gs_const_string *str,
+ int *pindex)
+{ gs_font_type1 *pfont = pcis->pfont;
+ gs_imager_state *pis = pcis->pis;
+ gs_type1_data *pdata = &pfont->data;
+ gs_op1_state s;
+ fixed cstack[ostack_size];
+#define cs0 cstack[0]
+#define ics0 fixed2int_var(cs0)
+#define cs1 cstack[1]
+#define ics1 fixed2int_var(cs1)
+#define cs2 cstack[2]
+#define ics2 fixed2int_var(cs2)
+#define cs3 cstack[3]
+#define ics3 fixed2int_var(cs3)
+#define cs4 cstack[4]
+#define ics4 fixed2int_var(cs4)
+#define cs5 cstack[5]
+#define ics5 fixed2int_var(cs5)
+ cs_ptr csp;
+#define clear csp = cstack - 1
+ ip_state *ipsp = &pcis->ipstack[pcis->ips_count - 1];
+ register const byte *cip;
+ register crypt_state state;
+ register int c;
+ int code = 0;
+ fixed ftx = pcis->origin.x, fty = pcis->origin.y;
+
+ switch ( pcis->init_done )
+ {
+ case -1:
+ break;
+ case 0:
+ gs_type1_finish_init(pcis, &s); /* sets sfc, ptx, pty, origin */
+ ftx = pcis->origin.x, fty = pcis->origin.y;
+ break;
+ default /*case 1*/:
+ ptx = pcis->position.x;
+ pty = pcis->position.y;
+ sfc = pcis->fc;
+ }
+ sppath = pcis->path;
+ s.pcis = pcis;
+
+ /* Copy the operand stack out of the saved state. */
+ if ( pcis->os_count == 0 )
+ { clear;
+ }
+ else
+ { memcpy(cstack, pcis->ostack, pcis->os_count * sizeof(fixed));
+ csp = &cstack[pcis->os_count - 1];
+ }
+
+ if ( str == 0 )
+ goto cont;
+ ipsp->char_string = *str;
+ cip = str->data;
+call: state = crypt_charstring_seed;
+ { int skip = pdata->lenIV;
+ /* Skip initial random bytes */
+ for ( ; skip > 0; --skip )
+ { decrypt_skip_next(*cip, state); ++cip;
+ }
+ }
+ goto top;
+cont: cip = ipsp->ip;
+ state = ipsp->dstate;
+top: while ( 1 )
+ { uint c0;
+ c = decrypt_this((c0 = *cip++), state);
+ decrypt_skip_next(c0, state);
+ switch ( (char_command)c )
+ {
+#define cnext clear; goto top
+#define inext goto top
+ case c_hstem: dc("hstem")
+ apply_path_hints(pcis, false);
+ type1_hstem(pcis, cs0, cs1);
+ cnext;
+ case c_vstem: dc("vstem")
+ apply_path_hints(pcis, false);
+ type1_vstem(pcis, cs0, cs1);
+ cnext;
+ case c_vmoveto: dc("vmoveto")
+ cs1 = cs0;
+ cs0 = 0;
+ accum_y(cs1);
+move: /* cs0 = dx, cs1 = dy for hint checking. */
+ if ( (pcis->hint_next != 0 || sppath->subpath_open > 0) &&
+ pcis->flex_count == flex_max
+ )
+ apply_path_hints(pcis, true);
+ code = gx_path_add_point(sppath, ptx, pty);
+ goto cc;
+ case c_rlineto: dc("rlineto")
+ accum_xy(cs0, cs1);
+line: /* cs0 = dx, cs1 = dy for hint checking. */
+ code = gx_path_add_line(sppath, ptx, pty);
+cc: if ( code < 0 )
+ return code;
+pp: if_debug2('1', "[1]pt=(%g,%g)\n",
+ fixed2float(ptx), fixed2float(pty));
+ cnext;
+ case c_hlineto: dc("hlineto")
+ accum_x(cs0);
+ cs1 = 0;
+ goto line;
+ case c_vlineto: dc("vlineto")
+ cs1 = cs0;
+ cs0 = 0;
+ accum_y(cs1);
+ goto line;
+ case c_rrcurveto: dc("rrcurveto")
+ code = gs_op1_rrcurveto(&s, cs0, cs1, cs2, cs3, cs4, cs5);
+ goto cc;
+ case c_closepath: dc("closepath")
+ code = gs_op1_closepath(&s);
+ apply_path_hints(pcis, true);
+ goto cc;
+ case c_callsubr: dc("callsubr")
+ { int index = fixed2int_var(*csp);
+ code = (*pdata->subr_proc)(pfont, index, &ipsp[1].char_string);
+ if ( code < 0 )
+ return_error(code);
+ --csp;
+ ipsp->ip = cip, ipsp->dstate = state;
+ ++ipsp;
+ cip = ipsp->char_string.data;
+ }
+ goto call;
+ case c_return: dc("return")
+ --ipsp;
+ goto cont;
+ case c_escape: dc("escape:")
+ decrypt_next(*cip, state, c); ++cip;
+ switch ( (char_extended_command)c )
+ {
+ case ce_dotsection: dc(" dotsection")
+ pcis->dotsection_flag ^=
+ (dotsection_in ^ dotsection_out);
+ cnext;
+ case ce_vstem3: dc(" vstem3")
+ apply_path_hints(pcis, false);
+ if ( !pcis->vstem3_set && pcis->fh.use_x_hints )
+ { center_vstem(pcis, pcis->lsb.x + cs2, cs3);
+ /* Adjust the current point */
+ /* (center_vstem handles everything else). */
+ ptx += pcis->vs_offset.x;
+ pty += pcis->vs_offset.y;
+ pcis->vstem3_set = true;
+ }
+ type1_vstem(pcis, cs0, cs1);
+ type1_vstem(pcis, cs2, cs3);
+ type1_vstem(pcis, cs4, cs5);
+ cnext;
+ case ce_hstem3: dc(" hstem3")
+ apply_path_hints(pcis, false);
+ type1_hstem(pcis, cs0, cs1);
+ type1_hstem(pcis, cs2, cs3);
+ type1_hstem(pcis, cs4, cs5);
+ cnext;
+ case ce_seac: dc(" seac")
+ { gs_const_string acstr;
+ /* Do the accent now. When it finishes */
+ /* (detected in endchar), do the base character. */
+ pcis->seac_base = ics3;
+ /* Adjust the origin of the coordinate system */
+ /* for the accent (endchar puts it back). */
+ ptx = ftx, pty = fty;
+ pcis->asb_diff = cs0 - pcis->lsb.x;
+ pcis->adxy.x = cs1;
+ pcis->adxy.y = cs2;
+ accum_xy(cs1, cs2);
+ sppath->position.x = pcis->position.x = ptx;
+ sppath->position.y = pcis->position.y = pty;
+ pcis->os_count = 0; /* clear */
+ /* Ask the caller to provide a new string. */
+ code = (*pdata->seac_proc)(pfont, ics4, &acstr);
+ if ( code != 0 )
+ { *pindex = ics4;
+ return code;
+ }
+ /* Continue with the supplied string. */
+ clear;
+ ipsp->char_string = acstr;
+ cip = acstr.data;
+ goto call;
+ }
+ case ce_sbw: dc(" sbw")
+ goto rsbw;
+ case ce_div: dc(" div")
+ csp[-1] = float2fixed((float)csp[-1] / (float)*csp);
+ --csp; goto pushed;
+ case ce_undoc15: dc(" undoc15")
+ /* See gstype1.h for information on this opcode. */
+ cnext;
+ case ce_callothersubr: dc(" callothersubr")
+ { int num_results;
+#define fpts pcis->flex_points
+ /* We must remember to pop both the othersubr # */
+ /* and the argument count off the stack. */
+ switch ( *pindex = fixed2int_var(*csp) )
+ {
+ case 0:
+ { /* We have to do something really sleazy */
+ /* here, namely, make it look as though */
+ /* the rmovetos never really happened, */
+ /* because we don't want to interrupt */
+ /* the current subpath. */
+ gs_fixed_point ept;
+#if defined(DEBUG) || !ALWAYS_DO_FLEX_AS_CURVE
+ fixed fheight = csp[-4];
+#endif
+ gs_fixed_point hpt;
+
+ if ( pcis->flex_count != 8 )
+ return_error(gs_error_invalidfont);
+ /* Assume the next two opcodes */
+ /* are `pop' `pop'. Unfortunately, some */
+ /* Monotype fonts put these in a Subr, */
+ /* so we can't just look ahead in the */
+ /* opcode stream. */
+ pcis->ignore_pops = 2;
+ csp[-4] = csp[-3] - pcis->asb_diff;
+ csp[-3] = csp[-2];
+ csp -= 3;
+ gx_path_current_point(sppath, &ept);
+ gx_path_add_point(sppath, fpts[0].x, fpts[0].y);
+ sppath->subpath_open = /* <--- sleaze */
+ pcis->flex_path_was_open;
+ /* Decide whether to do the flex as a curve. */
+ hpt.x = fpts[1].x - fpts[4].x;
+ hpt.y = fpts[1].y - fpts[4].y;
+ if_debug3('1',
+ "[1]flex: d=(%g,%g), height=%g\n",
+ fixed2float(hpt.x), fixed2float(hpt.y),
+ fixed2float(fheight) / 100);
+#if !ALWAYS_DO_FLEX_AS_CURVE /* See beginning of file. */
+ if ( any_abs(hpt.x) + any_abs(hpt.y) <
+ fheight / 100
+ )
+ { /* Do the flex as a line. */
+ code = gx_path_add_line(sppath,
+ ept.x, ept.y);
+ }
+ else
+#endif
+ { /* Do the flex as a curve. */
+ code = gx_path_add_curve(sppath,
+ fpts[2].x, fpts[2].y,
+ fpts[3].x, fpts[3].y,
+ fpts[4].x, fpts[4].y);
+ if ( code < 0 )
+ return code;
+ code = gx_path_add_curve(sppath,
+ fpts[5].x, fpts[5].y,
+ fpts[6].x, fpts[6].y,
+ fpts[7].x, fpts[7].y);
+ }
+ }
+ if ( code < 0 )
+ return code;
+ pcis->flex_count = flex_max; /* not inside flex */
+ inext;
+ case 1:
+ gx_path_current_point(sppath, &fpts[0]);
+ pcis->flex_path_was_open = /* <--- more sleaze */
+ sppath->subpath_open;
+ pcis->flex_count = 1;
+ csp -= 2;
+ inext;
+ case 2:
+ if ( pcis->flex_count >= flex_max )
+ return_error(gs_error_invalidfont);
+ gx_path_current_point(sppath,
+ &fpts[pcis->flex_count++]);
+ csp -= 2;
+ inext;
+ case 3:
+ /* Assume the next opcode is a `pop'. */
+ /* See above as to why we don't just */
+ /* look ahead in the opcode stream. */
+ pcis->ignore_pops = 1;
+ apply_path_hints(pcis, false);
+ reset_stem_hints(pcis);
+ csp -= 2;
+ inext;
+ case 14:
+ num_results = 1;
+blend: { int num_values = fixed2int_var(csp[-1]);
+ int k1 = num_values / num_results - 1;
+ int i, j;
+ cs_ptr base, deltas;
+
+ if ( num_values < num_results ||
+ num_values % num_results != 0
+ )
+ return_error(gs_error_invalidfont);
+ base = csp - 1 - num_values;
+ deltas = base + num_results - 1;
+ for ( j = 0; j < num_results;
+ j++, base++, deltas += k1
+ )
+ for ( i = 1; i <= k1; i++ )
+ *base += deltas[i] *
+ pdata->WeightVector[i];
+ csp = base - 1;
+ }
+ pcis->ignore_pops = num_results;
+ inext;
+ 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;
+ goto blend;
+ }
+ }
+#undef fpts
+ /* Not a recognized othersubr, */
+ /* let the client handle it. */
+ { int scount = csp - cstack;
+ int n;
+ /* Copy the arguments to the caller's stack. */
+ if ( scount < 1 || csp[-1] < 0 ||
+ csp[-1] > int2fixed(scount - 1)
+ )
+ return_error(gs_error_invalidfont);
+ n = fixed2int_var(csp[-1]);
+ code = (*pdata->push_proc)(pfont, csp - (n + 1), n);
+ if ( code < 0 )
+ return_error(code);
+ scount -= n + 1;
+ pcis->position.x = ptx;
+ pcis->position.y = pty;
+ apply_path_hints(pcis, false);
+ /* Exit to caller */
+ ipsp->ip = cip, ipsp->dstate = state;
+ pcis->os_count = scount;
+ pcis->ips_count = ipsp - &pcis->ipstack[0] + 1;
+ if ( scount )
+ memcpy(pcis->ostack, cstack, scount * sizeof(fixed));
+ return type1_result_callothersubr;
+ }
+ case ce_pop: dc(" pop")
+ /* Check whether we're ignoring the pops after */
+ /* a known othersubr. */
+ if ( pcis->ignore_pops != 0 )
+ { pcis->ignore_pops--;
+ inext;
+ }
+ ++csp;
+ code = (*pdata->pop_proc)(pfont, csp);
+ if ( code < 0 )
+ return_error(code);
+ goto pushed;
+ case ce_setcurrentpoint: dc(" setcurrentpoint")
+ ptx = ftx, pty = fty;
+ cs0 += pcis->adxy.x;
+ cs1 += pcis->adxy.y;
+ accum_xy(cs0, cs1);
+ goto pp;
+ default:
+ return_error(gs_error_invalidfont);
+ }
+ break;
+ case c_hsbw: dc("hsbw")
+ cs3 = fixed_0, cs2 = cs1, cs1 = fixed_0;
+rsbw: /* Save the side bearing and width. */
+ type1_sbw(pcis, cstack);
+ /* Give the caller the opportunity to intervene. */
+ pcis->os_count = 0; /* clear */
+ ipsp->ip = cip, ipsp->dstate = state;
+ pcis->ips_count = ipsp - &pcis->ipstack[0] + 1;
+ /* If we aren't in a seac, do nothing else now; */
+ /* finish_init will take care of the rest. */
+ if ( pcis->init_done < 0 )
+ { /* Finish init when we return. */
+ pcis->init_done = 0;
+ }
+ else
+ { /* Do accumulate the side bearing now. */
+ accum_xy(pcis->lsb.x, pcis->lsb.y);
+ pcis->position.x = ptx;
+ pcis->position.y = pty;
+ }
+ return type1_result_sbw;
+ case c_endchar: dc("endchar")
+ if ( pcis->seac_base >= 0 )
+ { /* We just finished the accent of a seac. */
+ /* Do the base character. */
+ gs_const_string bstr;
+ int bchar = pcis->seac_base;
+ pcis->seac_base = -1;
+ /* Restore the coordinate system origin */
+ pcis->asb_diff = pcis->adxy.x = pcis->adxy.y = 0;
+ sppath->position.x = pcis->position.x = ftx;
+ sppath->position.y = pcis->position.y = fty;
+ pcis->os_count = 0; /* clear */
+ /* Clear the ipstack, in case the accent ended */
+ /* inside a subroutine. */
+ pcis->ips_count = 1;
+ /* Remove any accent hints. */
+ reset_stem_hints(pcis);
+ /* Ask the caller to provide a new string. */
+ code = (*pdata->seac_proc)(pfont, bchar, &bstr);
+ if ( code != 0 )
+ { *pindex = bchar;
+ return code;
+ }
+ /* Continue with the supplied string. */
+ clear;
+ ptx = ftx, pty = fty;
+ ipsp = &pcis->ipstack[0];
+ ipsp->char_string = bstr;
+ cip = bstr.data;
+ goto call;
+ }
+ /* This is a real endchar. Handle it below. */
+ return type1_endchar(pcis, pis, sppath);
+ case c_undoc15: dc(" undoc15")
+ /* See gstype1.h for information on this opcode. */
+ cnext;
+ case c_rmoveto: dc("rmoveto")
+ accum_xy(cs0, cs1);
+ goto move;
+ case c_hmoveto: dc("hmoveto")
+ accum_x(cs0);
+ cs1 = 0;
+ goto move;
+ case c_vhcurveto: dc("vhcurveto")
+ { gs_fixed_point pt1, pt2;
+ fixed ax0 = sppath->position.x - ptx;
+ fixed ay0 = sppath->position.y - pty;
+ accum_y(cs0);
+ pt1.x = ptx + ax0, pt1.y = pty + ay0;
+ accum_xy(cs1, cs2);
+ pt2.x = ptx, pt2.y = pty;
+ accum_x(cs3);
+ code = gx_path_add_curve(sppath, pt1.x, pt1.y, pt2.x, pt2.y, ptx, pty);
+ } goto cc;
+ case c_hvcurveto: dc("hvcurveto")
+ { gs_fixed_point pt1, pt2;
+ fixed ax0 = sppath->position.x - ptx;
+ fixed ay0 = sppath->position.y - pty;
+ accum_x(cs0);
+ pt1.x = ptx + ax0, pt1.y = pty + ay0;
+ accum_xy(cs1, cs2);
+ pt2.x = ptx, pt2.y = pty;
+ accum_y(cs3);
+ code = gx_path_add_curve(sppath, pt1.x, pt1.y, pt2.x, pt2.y, ptx, pty);
+ } goto cc;
+
+ /* Fill up the dispatch up to 32. */
+
+ case c_undef0: case c_undef2:
+ case c_undef16: case c_undef17: case c_undef18: case c_undef19:
+ case c_undef20: case c_undef23:
+ case c_undef24: case c_undef25: case c_undef26: case c_undef27:
+ case c_undef28: case c_undef29:
+ return_error(gs_error_invalidfont);
+
+ /* Fill up the dispatch for 1-byte numbers. */
+
+#define icase(n) case n:
+#define ncase(n) case n: *++csp = int2fixed(c_value_num1(n)); goto pushed;
+#define icase10(n)\
+ icase(n) icase(n+1) icase(n+2) icase(n+3) icase(n+4)\
+ icase(n+5) icase(n+6) icase(n+7) icase(n+8) icase(n+9)
+#define ncase10(n)\
+ ncase(n) ncase(n+1) ncase(n+2) ncase(n+3) ncase(n+4)\
+ ncase(n+5) ncase(n+6) ncase(n+7) ncase(n+8) ncase(n+9)
+ icase(32) icase(33) icase(34)
+ icase(35) icase(36) icase(37) icase(38) icase(39)
+ icase10(40)
+ icase10(50) icase10(60) icase10(70) icase10(80) icase10(90)
+ icase10(100) icase10(110) goto pi; ncase10(120) ncase10(130) ncase10(140)
+ ncase10(150) icase10(160) icase10(170) icase10(180) icase10(190)
+ icase10(200) icase10(210) icase10(220) icase10(230)
+ icase(240) icase(241) icase(242) icase(243) icase(244)
+ icase(245) icase(246)
+pi: *++csp = int2fixed(c_value_num1(c));
+pushed: if_debug3('1', "[1]%d: (%d) %f\n",
+ (int)(csp - cstack), c, fixed2float(*csp));
+ break;
+
+ /* Handle 2-byte positive numbers. */
+
+ case c_pos2_0: c = c_value_pos2(c_pos2_0, 0);
+pos2: { c0 = *cip++;
+ if_debug2('1', "[1] (%d)+%d\n",
+ c, decrypt_this(c0, state));
+ *++csp = int2fixed((int)decrypt_this(c0, state) + c);
+ decrypt_skip_next(c0, state);
+ } goto pushed;
+ case c_pos2_1: c = c_value_pos2(c_pos2_1, 0); goto pos2;
+ case c_pos2_2: c = c_value_pos2(c_pos2_2, 0); goto pos2;
+ case c_pos2_3: c = c_value_pos2(c_pos2_3, 0); goto pos2;
+
+ /* Handle 2-byte negative numbers. */
+
+ case c_neg2_0: c = c_value_neg2(c_neg2_0, 0);
+neg2: { c0 = *cip++;
+ if_debug2('1', "[1] (%d)-%d\n",
+ c, decrypt_this(c0, state));
+ *++csp = int2fixed(c - (int)decrypt_this(c0, state));
+ decrypt_skip_next(c0, state);
+ } goto pushed;
+ case c_neg2_1: c = c_value_neg2(c_neg2_1, 0); goto neg2;
+ case c_neg2_2: c = c_value_neg2(c_neg2_2, 0); goto neg2;
+ case c_neg2_3: c = c_value_neg2(c_neg2_3, 0); goto neg2;
+
+ /* Handle 5-byte numbers. */
+
+ case c_num4:
+ { long lw = 0;
+ int i;
+ for ( i = 4; --i >= 0; )
+ { decrypt_next(*cip, state, c0);
+ lw = (lw << 8) + c0;
+ cip++;
+ }
+ *++csp = int2fixed(lw);
+ if ( lw != fixed2long(*csp) )
+ return_error(gs_error_rangecheck);
+ } goto pushed;
+ }
+ }
+}
+
+/* ------ Termination ------ */
+
+/* Handle the end of a character. */
+private int near
+type1_endchar(gs_type1_state *pcis, gs_imager_state *pis, gx_path *ppath)
+{ if ( pcis->hint_next != 0 || ppath->subpath_open > 0 )
+ apply_path_hints(pcis, true);
+ /* Set the current point to the character origin */
+ /* plus the width. */
+ { gs_fixed_point pt;
+ gs_point_transform2fixed(&pis->ctm,
+ fixed2float(pcis->width.x),
+ fixed2float(pcis->width.y),
+ &pt);
+ gx_path_add_point(ppath, pt.x, pt.y);
+ }
+ if ( pcis->scale.x.log2_unit + pcis->scale.y.log2_unit == 0 )
+ { /*
+ * Tweak up the fill adjustment. This is a hack for when
+ * we can't oversample. The values here are based entirely
+ * on experience, not theory, and are designed primarily
+ * for displays and low-resolution fax.
+ */
+ gs_fixed_rect bbox;
+ int dx, dy, dmax;
+ gx_path_bbox(pcis->path, &bbox);
+ dx = fixed2int_ceiling(bbox.q.x - bbox.p.x);
+ dy = fixed2int_ceiling(bbox.q.y - bbox.p.y);
+ dmax = max(dx, dy);
+ if ( pcis->fh.snap_h.count || pcis->fh.snap_v.count ||
+ pcis->fh.a_zone_count
+ )
+ { /* We have hints. Only tweak up a little at */
+ /* very small sizes, to help nearly-vertical */
+ /* or nearly-horizontal diagonals. */
+ pis->fill_adjust.x = pis->fill_adjust.y =
+ (dmax < 15 ? float2fixed(0.15) :
+ dmax < 25 ? float2fixed(0.1) :
+ fixed_0);
+ }
+ else
+ { /* No hints. Tweak a little more to compensate */
+ /* for lack of snapping to pixel grid. */
+ pis->fill_adjust.x = pis->fill_adjust.y =
+ (dmax < 10 ? float2fixed(0.2) :
+ dmax < 25 ? float2fixed(0.1) :
+ float2fixed(0.05));
+ }
+ }
+ else
+ { /* Don't do any adjusting. */
+ pis->fill_adjust.x = pis->fill_adjust.y = fixed_0;
+ }
+ /* Set the flatness for curve rendering. */
+ if ( !pcis->charpath_flag )
+ gs_setflat((gs_state *)pis, pcis->flatness);
+ return 0;
+}
diff --git a/pstoraster/gstype1.h b/pstoraster/gstype1.h
new file mode 100644
index 000000000..247013fb2
--- /dev/null
+++ b/pstoraster/gstype1.h
@@ -0,0 +1,174 @@
+/* Copyright (C) 1990, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gstype1.h */
+/* Client interface to Adobe Type 1 font routines for Ghostscript library */
+
+/* ------ Normal client interface ------ */
+
+#define crypt_charstring_seed 4330
+typedef struct gs_type1_state_s gs_type1_state;
+#ifndef gx_path_DEFINED
+# define gx_path_DEFINED
+typedef struct gx_path_s gx_path;
+#endif
+#ifndef gs_show_enum_s_DEFINED
+struct gs_show_enum_s;
+#endif
+#ifndef gs_type1_data_s_DEFINED
+struct gs_type1_data_s;
+#endif
+int gs_type1_interp_init(P7(gs_type1_state *pcis, gs_imager_state *pis,
+ gx_path *ppath, const gs_log2_scale_point *pscale,
+ bool charpath_flag, int paint_type,
+ gs_font_type1 *pfont));
+void gs_type1_set_lsb(P2(gs_type1_state *pcis, const gs_point *psbpt));
+void gs_type1_set_width(P2(gs_type1_state *pcis, const gs_point *pwpt));
+/* Backward compatibility */
+#define gs_type1_init(pcis, penum, psbpt, charpath_flag, paint_type, pfont)\
+ (gs_type1_interp_init(pcis, (gs_imager_state *)((penum)->pgs),\
+ (penum)->pgs->path, &(penum)->log2_current_scale,\
+ charpath_flag, paint_type, pfont) |\
+ ((psbpt) == 0 ? 0 : (gs_type1_set_lsb(pcis, psbpt), 0)))
+/*
+ * Continue interpreting a Type 1 CharString.
+ * If str != 0, it is taken as the byte string to interpret.
+ * Return 0 on successful completion, <0 on error,
+ * or >0 when client intervention is required (or allowed).
+ * The int * argument is where the character is stored for seac,
+ * or the othersubr # for callothersubr.
+ */
+#define type1_result_sbw 1 /* allow intervention after [h]sbw */
+#define type1_result_callothersubr 2
+/*#define type1_result_seac 3*/ /* no longer used */
+
+int gs_type1_interpret(P3(gs_type1_state *, const gs_const_string *, int *));
+
+/* ------ CharString representation ------ */
+
+/* Define the charstring command set */
+typedef enum {
+ c_undef0 = 0,
+ c_hstem = 1,
+ c_undef2 = 2,
+ c_vstem = 3,
+ c_vmoveto = 4,
+ c_rlineto = 5,
+ c_hlineto = 6,
+ c_vlineto = 7,
+ c_rrcurveto = 8,
+ c_closepath = 9,
+ c_callsubr = 10,
+ c_return = 11,
+ c_escape = 12, /* extends the command set */
+ c_hsbw = 13,
+ c_endchar = 14,
+ c_undoc15 = 15, /* An obsolete and undocumented */
+ /* 'moveto' command, */
+ /* used in some Adobe fonts. */
+ c_undef16 = 16,
+ c_undef17 = 17,
+ c_undef18 = 18,
+ c_undef19 = 19,
+ c_undef20 = 20,
+ c_rmoveto = 21,
+ c_hmoveto = 22,
+ c_undef23 = 23,
+ c_undef24 = 24,
+ c_undef25 = 25,
+ c_undef26 = 26,
+ c_undef27 = 27,
+ c_undef28 = 28,
+ c_undef29 = 29,
+ c_vhcurveto = 30,
+ c_hvcurveto = 31,
+
+ /* Values from 32 to 246 represent small integers. */
+ c_num1 = 32,
+#define c_value_num1(ch) ((int)(byte)(ch) - 139)
+ /* We have to declare all these values in the enumeration */
+ /* so that some compilers won't complain when we use them */
+ /* in the big switch statement. */
+#define c_v8(v,a,b,c,d,e,f,g,h)\
+ a=v, b=v+1, c=v+2, d=v+3, e=v+4, f=v+5, g=v+6, h=v+7
+#define c_v9(v,a,b,c,d,e,f,g,h,i)\
+ c_v8(v,a,b,c,d,e,f,g,h), i=v+8
+#define c_v10(v,a,b,c,d,e,f,g,h,i,j)\
+ c_v9(v,a,b,c,d,e,f,g,h,i), j=v+9
+ c_v8( 32, c_n107,c_n106,c_n105,c_n104,c_n103,c_n102,c_n101,c_n100),
+ c_v10(40, c_n99,c_n98,c_n97,c_n96,c_n95,c_n94,c_n93,c_n92,c_n91,c_n90),
+ c_v10(50, c_n89,c_n88,c_n87,c_n86,c_n85,c_n84,c_n83,c_n82,c_n81,c_n80),
+ c_v10(60, c_n79,c_n78,c_n77,c_n76,c_n75,c_n74,c_n73,c_n72,c_n71,c_n70),
+ c_v10(70, c_n69,c_n68,c_n67,c_n66,c_n65,c_n64,c_n63,c_n62,c_n61,c_n60),
+ c_v10(80, c_n59,c_n58,c_n57,c_n56,c_n55,c_n54,c_n53,c_n52,c_n51,c_n50),
+ c_v10(90, c_n49,c_n48,c_n47,c_n46,c_n45,c_n44,c_n43,c_n42,c_n41,c_n40),
+ c_v10(100,c_n39,c_n38,c_n37,c_n36,c_n35,c_n34,c_n33,c_n32,c_n31,c_n30),
+ c_v10(110,c_n29,c_n28,c_n27,c_n26,c_n25,c_n24,c_n23,c_n22,c_n21,c_n20),
+ c_v10(120,c_n19,c_n18,c_n17,c_n16,c_n15,c_n14,c_n13,c_n12,c_n11,c_n10),
+ c_v9( 130, c_n9, c_n8, c_n7, c_n6, c_n5, c_n4, c_n3, c_n2, c_n1),
+ c_v10(139, c_0, c_1, c_2, c_3, c_4, c_5, c_6, c_7, c_8, c_9),
+ c_v10(149, c_10, c_11, c_12, c_13, c_14, c_15, c_16, c_17, c_18, c_19),
+ c_v10(159, c_20, c_21, c_22, c_23, c_24, c_25, c_26, c_27, c_28, c_29),
+ c_v10(169, c_30, c_31, c_32, c_33, c_34, c_35, c_36, c_37, c_38, c_39),
+ c_v10(179, c_40, c_41, c_42, c_43, c_44, c_45, c_46, c_47, c_48, c_49),
+ c_v10(189, c_50, c_51, c_52, c_53, c_54, c_55, c_56, c_57, c_58, c_59),
+ c_v10(199, c_60, c_61, c_62, c_63, c_64, c_65, c_66, c_67, c_68, c_69),
+ c_v10(209, c_70, c_71, c_72, c_73, c_74, c_75, c_76, c_77, c_78, c_79),
+ c_v10(219, c_80, c_81, c_82, c_83, c_84, c_85, c_86, c_87, c_88, c_89),
+ c_v10(229, c_90, c_91, c_92, c_93, c_94, c_95, c_96, c_97, c_98, c_99),
+ c_v8( 239,c_100,c_101,c_102,c_103,c_104,c_105,c_106,c_107),
+
+ /* The next 4 values represent 2-byte positive integers. */
+ c_pos2_0 = 247,
+ c_pos2_1 = 248,
+ c_pos2_2 = 249,
+ c_pos2_3 = 250,
+#define c_value_pos2(c1,c2)\
+ (((int)(byte)((c1) - (int)c_pos2_0) << 8) + (int)(byte)(c2) + 108)
+
+ /* The next 4 values represent 2-byte negative integers. */
+ c_neg2_0 = 251,
+ c_neg2_1 = 252,
+ c_neg2_2 = 253,
+ c_neg2_3 = 254,
+#define c_value_neg2(c1,c2)\
+ -(((int)(byte)((c1) - (int)c_neg2_0) << 8) + (int)(byte)(c2) + 108)
+
+ /* Finally, there is an escape for 4-byte 2's complement */
+ /* numbers in big-endian order. */
+ c_num4 = 255
+} char_command;
+typedef enum { /* extended commands */
+ ce_dotsection = 0,
+ ce_vstem3 = 1,
+ ce_hstem3 = 2,
+ ce_seac = 6,
+ ce_sbw = 7,
+ ce_div = 12,
+ ce_undoc15 = 15, /* An obsolete and undocumented */
+ /* 'addifgt' command, */
+ /* used in some Adobe fonts. */
+ ce_callothersubr = 16,
+ ce_pop = 17,
+ ce_setcurrentpoint = 33
+} char_extended_command;
diff --git a/pstoraster/gstype42.c b/pstoraster/gstype42.c
new file mode 100644
index 000000000..dd1a5e38c
--- /dev/null
+++ b/pstoraster/gstype42.c
@@ -0,0 +1,431 @@
+/* Copyright (C) 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gstype42.c */
+/* Type 42 (TrueType) font library routines */
+#include "memory_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gsstruct.h"
+#include "gsccode.h"
+#include "gsmatrix.h"
+#include "gxfixed.h" /* for gxpath.h */
+#include "gxpath.h"
+#include "gxfont.h"
+#include "gxfont42.h"
+#include "gxistate.h"
+
+/*
+ * This Type 42 / TrueType rasterizer is about as primitive as it can be
+ * and still produce useful output. Here are some things it doesn't handle:
+ * - left side bearings;
+ * and, of course, instructions (hints).
+ */
+
+/* Structure descriptor */
+public_st_gs_font_type42();
+
+/* Set up a pointer to a substring of the font data. */
+/* Free variables: pfont, string_proc. */
+#define access(base, length, vptr)\
+ code = (*string_proc)(pfont, (ulong)(base), length, &vptr);\
+ if ( code < 0 ) return code
+
+/* Get 2- or 4-byte quantities from a table. */
+#define u16(p) (((uint)((p)[0]) << 8) + (p)[1])
+#define s16(p) (int)((u16(p) ^ 0x8000) - 0x8000)
+#define u32(p) (((ulong)u16(p) << 16) + u16((p) + 2))
+#define s32(p) (long)((u32(p) ^ 0x80000000) - 0x80000000)
+
+/* Initialize the cached values in a Type 42 font. */
+int
+gs_type42_font_init(gs_font_type42 *pfont)
+{ int (*string_proc)(P4(gs_font_type42 *, ulong, uint, const byte **)) =
+ pfont->data.string_proc;
+ const byte *OffsetTable;
+ uint numTables;
+ const byte *TableDirectory;
+ uint i;
+ int code;
+ byte head_box[8];
+
+ access(0, 12, OffsetTable);
+ { static const byte version1_0[4] = {0,1,0,0};
+ if ( memcmp(OffsetTable, version1_0, 4) )
+ return_error(gs_error_invalidfont);
+ }
+ numTables = u16(OffsetTable + 4);
+ access(12, numTables * 16, TableDirectory);
+ for ( i = 0; i < numTables; ++i )
+ { const byte *tab = TableDirectory + i * 16;
+ ulong offset = u32(tab + 8);
+ if ( !memcmp(tab, "glyf", 4) )
+ pfont->data.glyf = offset;
+ else if ( !memcmp(tab, "head", 4) )
+ { const byte *head;
+ access(offset, 54, head);
+ pfont->data.unitsPerEm = u16(head + 18);
+ memcpy(head_box, head + 36, 8);
+ pfont->data.indexToLocFormat = u16(head + 50);
+ }
+ else if ( !memcmp(tab, "hhea", 4) )
+ { const byte *hhea;
+ access(offset, 36, hhea);
+ pfont->data.numLongMetrics = u16(hhea + 34);
+ }
+ else if ( !memcmp(tab, "hmtx", 4) )
+ pfont->data.hmtx = offset,
+ pfont->data.hmtx_length = (uint)u32(tab + 12);
+ else if ( !memcmp(tab, "loca", 4) )
+ pfont->data.loca = offset;
+ }
+ /* If the font doesn't have a valid FontBBox, */
+ /* compute one from the 'head' information. */
+ if ( pfont->FontBBox.p.x >= pfont->FontBBox.q.x ||
+ pfont->FontBBox.p.y >= pfont->FontBBox.q.y
+ )
+ { float upem = pfont->data.unitsPerEm;
+ pfont->FontBBox.p.x = s16(head_box) / upem;
+ pfont->FontBBox.p.y = s16(head_box + 2) / upem;
+ pfont->FontBBox.q.x = s16(head_box + 4) / upem;
+ pfont->FontBBox.q.y = s16(head_box + 6) / upem;
+ }
+ return 0;
+}
+
+/* Get the metrics of a glyph. */
+int
+gs_type42_get_metrics(gs_font_type42 *pfont, uint glyph_index,
+ float psbw[4])
+{ int (*string_proc)(P4(gs_font_type42 *, ulong, uint, const byte **)) =
+ pfont->data.string_proc;
+ float scale = pfont->data.unitsPerEm;
+ uint widthx;
+ int lsbx;
+ int code;
+
+ { uint num_metrics = pfont->data.numLongMetrics;
+ const byte *hmetrics;
+ if ( glyph_index < num_metrics )
+ { access(pfont->data.hmtx + glyph_index * 4, 4, hmetrics);
+ widthx = u16(hmetrics);
+ lsbx = s16(hmetrics + 2);
+ }
+ else
+ { uint offset = pfont->data.hmtx + (num_metrics - 1) * 4;
+ const byte *lsb;
+ access(offset, 4, hmetrics);
+ widthx = u16(hmetrics);
+ offset += 4 + (glyph_index - num_metrics) * 2;
+ if ( offset >= pfont->data.hmtx_length )
+ offset = pfont->data.hmtx_length - 2;
+ access(offset, 2, lsb);
+ lsbx = s16(lsb);
+ }
+ }
+ psbw[0] = lsbx / scale;
+ psbw[1] = 0;
+ psbw[2] = widthx / scale;
+ psbw[3] = 0;
+ return 0;
+}
+
+/* Define the bits in the glyph flags. */
+#define gf_OnCurve 1
+#define gf_xShort 2
+#define gf_yShort 4
+#define gf_Repeat 8
+#define gf_xPos 16 /* xShort */
+#define gf_xSame 16 /* !xShort */
+#define gf_yPos 32 /* yShort */
+#define gf_ySame 32 /* !yShort */
+
+/* Define the bits in the component glyph flags. */
+#define cg_argsAreWords 1
+#define cg_argsAreXYValues 2
+#define cg_haveScale 8
+#define cg_moreComponents 32
+#define cg_haveXYScale 64
+#define cg_have2x2 128
+
+/* Forward references */
+private int append_outline(P4(uint glyph_index, const gs_matrix_fixed *pmat,
+ gx_path *ppath, gs_font_type42 *pfont));
+
+/* Append a TrueType outline to a path. */
+/* Note that this does not append the final moveto for the width. */
+int
+gs_type42_append(uint glyph_index, gs_imager_state *pis,
+ gx_path *ppath, const gs_log2_scale_point *pscale, bool charpath_flag,
+ int paint_type, gs_font_type42 *pfont)
+{ /*
+ * This is where we should do something about the l.s.b., but I
+ * can't figure out from the TrueType documentation what it should
+ * be.
+ */
+ return append_outline(glyph_index, &pis->ctm, ppath, pfont);
+}
+
+/* Get the data for a glyph. */
+/* Set *pglyph = 0 if the glyph is empty. */
+private int
+get_glyph_data(uint glyph_index, gs_font_type42 *pfont,
+ const byte **pglyph)
+{ int (*string_proc)(P4(gs_font_type42 *, ulong, uint, const byte **)) =
+ pfont->data.string_proc;
+ const byte *ploca;
+ ulong glyph_start;
+ uint glyph_length;
+ int code;
+
+ if ( pfont->data.indexToLocFormat )
+ { access(pfont->data.loca + glyph_index * 4, 8, ploca);
+ glyph_start = u32(ploca);
+ glyph_length = u32(ploca + 4) - glyph_start;
+ }
+ else
+ { access(pfont->data.loca + glyph_index * 2, 4, ploca);
+ glyph_start = (ulong)u16(ploca) << 1;
+ glyph_length = ((ulong)u16(ploca + 2) << 1) - glyph_start;
+ }
+ if ( glyph_length == 0 )
+ *pglyph = 0;
+ else
+ { access(pfont->data.glyf + glyph_start, glyph_length, *pglyph);
+ }
+ return 0;
+}
+
+/* Append a simple glyph outline. */
+private int
+append_simple(const byte *glyph, const gs_matrix_fixed *pmat, gx_path *ppath,
+ gs_font_type42 *pfont)
+{ int numContours = s16(glyph);
+ const byte *pends = glyph + 10;
+ const byte *pinstr = pends + numContours * 2;
+ uint npoints = u16(pinstr - 2) + 1;
+ const byte *pflags = pinstr + 2 + u16(pinstr);
+ const byte *pxc, *pyc;
+ int code;
+
+ /*
+ * It appears that the only way to find the X and Y coordinate
+ * tables is to parse the flags. If this is true, it is an
+ * incredible piece of bad design.
+ */
+ { const byte *pf = pflags;
+ uint xbytes = npoints;
+ uint np = npoints;
+ while ( np > 0 )
+ { byte flags = *pf++;
+ uint reps = (flags & gf_Repeat ? *pf++ + 1 : 1);
+ if ( !(flags & gf_xShort) )
+ { if ( flags & gf_xSame )
+ xbytes -= reps;
+ else
+ xbytes += reps;
+ }
+ np -= reps;
+ }
+ pxc = pf;
+ pyc = pxc + xbytes;
+ }
+
+ /* Interpret the contours. */
+
+ { uint i, np;
+ gs_fixed_point pt;
+ float scale = pfont->data.unitsPerEm;
+ uint reps = 0;
+ byte flags;
+
+ gs_point_transform2fixed(pmat, 0.0, 0.0, &pt);
+ for ( i = 0, np = 0; i < numContours; ++i )
+ { bool move = true;
+ uint last_point = u16(pends + i * 2);
+ float dx, dy;
+ int off_curve = 0;
+ gs_fixed_point start;
+ gs_fixed_point cpoints[3];
+
+ for ( ; np <= last_point; --reps, ++np )
+ { gs_fixed_point dpt;
+ if ( reps == 0 )
+ { flags = *pflags++;
+ reps = (flags & gf_Repeat ? *pflags++ + 1 : 1);
+ }
+ if ( flags & gf_xShort )
+ dx = (flags & gf_xPos ? *pxc++ : -(int)*pxc++) / scale;
+ else if ( !(flags & gf_xSame) )
+ dx = s16(pxc) / scale, pxc += 2;
+ else
+ dx = 0;
+ if ( flags & gf_yShort )
+ dy = (flags & gf_yPos ? *pyc++ : -(int)*pyc++) / scale;
+ else if ( !(flags & gf_ySame) )
+ dy = s16(pyc) / scale, pyc += 2;
+ else
+ dy = 0;
+ code = gs_distance_transform2fixed(pmat, dx, dy, &dpt);
+ if ( code < 0 )
+ return code;
+ pt.x += dpt.x, pt.y += dpt.y;
+#define control1(xy) cpoints[1].xy
+#define control2(xy) cpoints[2].xy
+#define control3off(xy) ((cpoints[2].xy + pt.xy) / 2)
+ if ( move )
+ { if_debug2('1', "[1t]start (%g,%g)\n",
+ fixed2float(pt.x), fixed2float(pt.y));
+ start = pt;
+ code = gx_path_add_point(ppath, pt.x, pt.y);
+ cpoints[0] = pt;
+ move = false;
+ }
+ else if ( flags & gf_OnCurve )
+ { if_debug2('1', "[1t]ON (%g,%g)\n",
+ fixed2float(pt.x), fixed2float(pt.y));
+ if ( off_curve )
+ code = gx_path_add_curve(ppath, control1(x),
+ control1(y), control2(x),
+ control2(y), pt.x, pt.y);
+ else
+ code = gx_path_add_line(ppath, pt.x, pt.y);
+ cpoints[0] = pt;
+ off_curve = 0;
+ }
+ else
+ { if_debug2('1', "[1t]...off (%g,%g)\n",
+ fixed2float(pt.x), fixed2float(pt.y));
+ switch ( off_curve++ )
+ {
+ default: /* >= 2 */
+ code = gx_path_add_curve(ppath,
+ control1(x), control1(y),
+ control2(x), control2(y),
+ control3off(x), control3off(y));
+ cpoints[0] = cpoints[2];
+ off_curve = 1;
+ /* falls through */
+ case 0:
+ cpoints[1] = pt;
+ /* falls through */
+ case 1:
+ cpoints[2] = pt;
+ }
+ }
+ if ( code < 0 )
+ return code;
+ }
+ if ( off_curve )
+ code = gx_path_add_curve(ppath, control1(x), control1(y),
+ control2(x), control2(y),
+ start.x, start.y);
+ code = gx_path_close_subpath(ppath);
+ if ( code < 0 )
+ return code;
+ }
+ }
+ return 0;
+}
+
+/* Append a glyph outline. */
+private int
+append_outline(uint glyph_index, const gs_matrix_fixed *pmat, gx_path *ppath,
+ gs_font_type42 *pfont)
+{ const byte *glyph;
+ int numContours;
+ int code;
+
+ code = get_glyph_data(glyph_index, pfont, &glyph);
+ if ( code < 0 )
+ return code;
+ if ( glyph == 0 ) /* empty glyph */
+ return 0;
+ numContours = s16(glyph);
+ if ( numContours >= 0 )
+ return append_simple(glyph, pmat, ppath, pfont);
+ if ( numContours != -1 )
+ return_error(gs_error_rangecheck);
+ /* This is a component glyph. Things get messy. */
+ { uint flags;
+ float scale = pfont->data.unitsPerEm;
+
+ glyph += 10;
+ do
+ { uint comp_index = u16(glyph + 2);
+ gs_matrix_fixed mat;
+ gs_matrix scale_mat;
+
+ flags = u16(glyph);
+ glyph += 4;
+ mat = *pmat;
+ if ( flags & cg_argsAreXYValues )
+ { int arg1, arg2;
+ gs_fixed_point pt;
+ if ( flags & cg_argsAreWords )
+ arg1 = s16(glyph), arg2 = s16(glyph + 2), glyph += 4;
+ else
+ arg1 = glyph[0], arg2 = glyph[1], glyph += 2;
+ gs_point_transform2fixed(pmat, arg1 / scale,
+ arg2 / scale, &pt);
+ /****** HACK: WE KNOW ABOUT FIXED MATRICES ******/
+ mat.tx = fixed2float(mat.tx_fixed = pt.x);
+ mat.ty = fixed2float(mat.ty_fixed = pt.y);
+ }
+ else
+ { /****** WE DON'T HANDLE POINT MATCHING YET ******/
+ glyph += (flags & cg_argsAreWords ? 4 : 2);
+ }
+ scale_mat.tx = 0;
+ scale_mat.ty = 0;
+#define s2_14(p) (s16(p) / 16384.0)
+ if ( flags & cg_haveScale )
+ { scale_mat.xx = scale_mat.yy = s2_14(glyph);
+ scale_mat.xy = scale_mat.yx = 0;
+ glyph += 2;
+ }
+ else if ( flags & cg_haveXYScale )
+ { scale_mat.xx = s2_14(glyph);
+ scale_mat.yy = s2_14(glyph + 2);
+ scale_mat.xy = scale_mat.yx = 0;
+ glyph += 4;
+ }
+ else if ( flags & cg_have2x2 )
+ { scale_mat.xx = s2_14(glyph);
+ scale_mat.xy = s2_14(glyph + 2);
+ scale_mat.yx = s2_14(glyph + 4);
+ scale_mat.yy = s2_14(glyph + 6);
+ glyph += 8;
+ }
+ /* The scale doesn't affect mat.t{x,y}, so we don't */
+ /* need to update the fixed components. */
+ gs_matrix_multiply(&scale_mat, (const gs_matrix *)&mat,
+ (gs_matrix *)&mat);
+ code = append_outline(comp_index, &mat, ppath, pfont);
+ if ( code < 0 )
+ return code;
+ }
+ while ( flags & cg_moreComponents );
+ }
+ return 0;
+}
diff --git a/pstoraster/gstypes.h b/pstoraster/gstypes.h
new file mode 100644
index 000000000..d2ffce6ed
--- /dev/null
+++ b/pstoraster/gstypes.h
@@ -0,0 +1,84 @@
+/* Copyright (C) 1989, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gstypes.h */
+/* Miscellaneous common types for Ghostscript library */
+
+#ifndef gstypes_INCLUDED
+# define gstypes_INCLUDED
+
+/*
+ * Define a type used internally for unique IDs of various kinds
+ * (primarily, but not exclusively, character and halftone bitmaps).
+ * These IDs bear no relation to any other ID space; we generate them all
+ * ourselves.
+ */
+typedef ulong gs_id;
+#define gs_no_id 0L
+
+/*
+ * Define a sensible representation of a string, as opposed to
+ * the C char * type (which can't store arbitrary data, represent
+ * substrings, or perform concatenation without destroying aliases).
+ */
+typedef struct gs_string_s {
+ byte *data;
+ uint size;
+} gs_string;
+typedef struct gs_const_string_s {
+ const byte *data;
+ uint size;
+} gs_const_string;
+
+/*
+ * Define types for Cartesian points.
+ */
+typedef struct gs_point_s {
+ double x, y;
+} gs_point;
+typedef struct gs_int_point_s {
+ int x, y;
+} gs_int_point;
+
+/*
+ * Define a scale for oversampling. Clients don't actually use this,
+ * but this seemed like the handiest place for it.
+ */
+typedef struct gs_log2_scale_point_s {
+ int x, y;
+} gs_log2_scale_point;
+
+/*
+ * Define types for rectangles in the Cartesian plane.
+ * Note that rectangles are half-open, i.e.: their width is
+ * q.x-p.x and their height is q.y-p.y; they include the points
+ * (x,y) such that p.x<=x<q.x and p.y<=y<q.y.
+ */
+typedef struct gs_rect_s {
+ gs_point p, q; /* origin point, corner point */
+} gs_rect;
+typedef struct gs_int_rect_s {
+ gs_int_point p, q;
+} gs_int_rect;
+
+#endif /* gstypes_INCLUDED */
diff --git a/pstoraster/gsuid.h b/pstoraster/gsuid.h
new file mode 100644
index 000000000..250d22306
--- /dev/null
+++ b/pstoraster/gsuid.h
@@ -0,0 +1,72 @@
+/* Copyright (C) 1992, 1993 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gsuid.h */
+/* Unique id definitions for Ghostscript */
+
+#ifndef gsuid_INCLUDED
+# define gsuid_INCLUDED
+
+/* A unique id (uid) may be either a UniqueID or an XUID. */
+/* (XUIDs are a Level 2 feature.) */
+#ifndef gs_uid_DEFINED
+# define gs_uid_DEFINED
+typedef struct gs_uid_s gs_uid;
+#endif
+struct gs_uid_s {
+ /* id >= 0 is a UniqueID, xvalues is 0. */
+ /* id < 0 is an XUID, size of xvalues is -id. */
+ long id;
+ long *xvalues;
+};
+
+/* A UniqueID of no_UniqueID is an indication that there is no uid. */
+#define no_UniqueID 0x1000000
+#define uid_is_valid(puid)\
+ ((puid)->id != no_UniqueID)
+#define uid_set_invalid(puid)\
+ ((puid)->id = no_UniqueID, (puid)->xvalues = 0)
+#define uid_is_UniqueID(puid)\
+ (((puid)->id & ~0xffffff) == 0)
+#define uid_is_XUID(puid)\
+ ((puid)->id < 0)
+
+/* Initialize a uid. */
+#define uid_set_UniqueID(puid, idv)\
+ ((puid)->id = idv, (puid)->xvalues = 0)
+#define uid_set_XUID(puid, pvalues, siz)\
+ ((puid)->id = -siz, (puid)->xvalues = pvalues)
+
+/* Get the size and the data of an XUID. */
+#define uid_XUID_size(puid) ((uint)(-(puid)->id))
+#define uid_XUID_values(puid) ((puid)->xvalues)
+
+/* Compare two uids for equality. */
+/* This could be a macro, but the Zortech compiler compiles it wrong. */
+bool uid_equal(P2(const gs_uid *, const gs_uid *)); /* in gsutil.c */
+
+/* Free the XUID array of a uid if necessary. */
+#define uid_free(puid, mem, cname)\
+ gs_free_object(mem, (puid)->xvalues, cname)
+
+#endif /* gsuid_INCLUDED */
diff --git a/pstoraster/gsutil.c b/pstoraster/gsutil.c
new file mode 100644
index 000000000..04d911b58
--- /dev/null
+++ b/pstoraster/gsutil.c
@@ -0,0 +1,222 @@
+/* Copyright (C) 1992, 1993, 1994 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gsutil.c */
+/* Utilities for Ghostscript library */
+#include "std.h"
+#include "string_.h"
+#include "memory_.h"
+#include "gstypes.h"
+#include "gconfigv.h" /* for USE_ASM */
+#include "gsuid.h"
+#include "gsutil.h" /* for prototype checking */
+
+/* ------ Unique IDs ------ */
+
+/* Generate a block of unique IDs. */
+static ulong gs_next_id = 0;
+ulong
+gs_next_ids(uint count)
+{ ulong id;
+ if ( gs_next_id == 0 ) gs_next_id++;
+ id = gs_next_id;
+ gs_next_id += count;
+ return id;
+}
+
+/* ------ Memory utilities ------ */
+
+/* Transpose an 8 x 8 block of bits. line_size is the raster of */
+/* the input data. dist is the distance between output bytes. */
+/* This routine may be supplanted by assembly code. */
+#if !USE_ASM
+
+void
+memflip8x8(const byte *inp, int line_size, byte *outp, int dist)
+{ register uint ae, bf, cg, dh;
+ { const byte *ptr4 = inp + (line_size << 2);
+ ae = ((uint)*inp << 8) + *ptr4;
+ inp += line_size, ptr4 += line_size;
+ bf = ((uint)*inp << 8) + *ptr4;
+ inp += line_size, ptr4 += line_size;
+ cg = ((uint)*inp << 8) + *ptr4;
+ inp += line_size, ptr4 += line_size;
+ dh = ((uint)*inp << 8) + *ptr4;
+ }
+
+ /* Check for all 8 bytes being the same. */
+ /* This is especially worth doing for the case where all are zero. */
+ if ( ae == bf && ae == cg && ae == dh && (ae >> 8) == (ae & 0xff) )
+ { if ( ae == 0 ) goto store;
+ *outp = -((ae >> 7) & 1);
+ outp += dist;
+ *outp = -((ae >> 6) & 1);
+ outp += dist;
+ *outp = -((ae >> 5) & 1);
+ outp += dist;
+ *outp = -((ae >> 4) & 1);
+ outp += dist;
+ *outp = -((ae >> 3) & 1);
+ outp += dist;
+ *outp = -((ae >> 2) & 1);
+ outp += dist;
+ *outp = -((ae >> 1) & 1);
+ outp += dist;
+ *outp = -(ae & 1);
+ return;
+ }
+
+ { register uint temp;
+
+/* Transpose a block of bits between registers. */
+#define transpose(r,s,mask,shift)\
+ r ^= (temp = ((s >> shift) ^ r) & mask);\
+ s ^= temp << shift
+
+/* Transpose blocks of 4 x 4 */
+#define transpose4(r) transpose(r,r,0x00f0,4)
+ transpose4(ae);
+ transpose4(bf);
+ transpose4(cg);
+ transpose4(dh);
+
+/* Transpose blocks of 2 x 2 */
+ transpose(ae, cg, 0x3333, 2);
+ transpose(bf, dh, 0x3333, 2);
+
+/* Transpose blocks of 1 x 1 */
+ transpose(ae, bf, 0x5555, 1);
+ transpose(cg, dh, 0x5555, 1);
+
+ }
+
+store: *outp = ae >> 8;
+ outp += dist;
+ *outp = bf >> 8;
+ outp += dist;
+ *outp = cg >> 8;
+ outp += dist;
+ *outp = dh >> 8;
+ outp += dist;
+ *outp = (byte)ae;
+ outp += dist;
+ *outp = (byte)bf;
+ outp += dist;
+ *outp = (byte)cg;
+ outp += dist;
+ *outp = (byte)dh;
+}
+
+#endif /* !USE_ASM */
+
+/* ------ String utilities ------ */
+
+/* Compare two strings, returning -1 if the first is less, */
+/* 0 if they are equal, and 1 if first is greater. */
+/* We can't use memcmp, because we always use unsigned characters. */
+int
+bytes_compare(const byte *s1, uint len1, const byte *s2, uint len2)
+{ register uint len = len1;
+ if ( len2 < len ) len = len2;
+ { register const byte *p1 = s1;
+ register const byte *p2 = s2;
+ while ( len-- )
+ if ( *p1++ != *p2++ )
+ return (p1[-1] < p2[-1] ? -1 : 1);
+ }
+ /* Now check for differing lengths */
+ return (len1 == len2 ? 0 : len1 < len2 ? -1 : 1);
+}
+
+/* Test whether a string matches a pattern with wildcards. */
+/* '*' = any substring, '?' = any character, '\' quotes next character. */
+private const string_match_params smp_default = { '*', '?', '\\', false };
+bool
+string_match(const byte *str, uint len, const byte *pstr, uint plen,
+ register const string_match_params *psmp)
+{ const byte *pback = 0;
+ const byte *spback = 0; /* initialized only to pacify gcc */
+ const byte *p = pstr, *pend = pstr + plen;
+ const byte *sp = str, *spend = str + len;
+ if ( psmp == 0 )
+ psmp = &smp_default;
+again: while ( p < pend )
+ { register byte ch = *p;
+ if ( ch == psmp->any_substring )
+ { pback = ++p, spback = sp;
+ continue;
+ }
+ else if ( ch == psmp->any_char )
+ { if ( sp == spend )
+ return false; /* str too short */
+ p++, sp++;
+ continue;
+ }
+ else if ( ch == psmp->quote_next )
+ { if ( ++p == pend )
+ return true; /* bad pattern */
+ ch = *p;
+ }
+ if ( sp == spend )
+ return false; /* str too short */
+ if ( *sp == ch ||
+ (psmp->ignore_case && (*sp ^ ch) == 0x20 &&
+ (ch &= ~0x20) >= 0x41 && ch <= 0x5a)
+ )
+ p++, sp++;
+ else if ( pback == 0 )
+ return false; /* no * to back up to */
+ else
+ { sp = ++spback;
+ p = pback;
+ }
+ }
+ if ( sp < spend )
+ { /* We got a match, but there are chars left over. */
+ /* If we can back up, back up to the only place that */
+ /* could produce a complete match, otherwise fail. */
+ if ( pback == 0 )
+ return false;
+ p = pback;
+ pback = 0;
+ sp = spend - (pend - p);
+ goto again;
+ }
+ return true;
+}
+
+/* ------ UID utilities ------ */
+
+/* Compare two UIDs for equality. */
+/* We know that at least one of them is valid. */
+bool
+uid_equal(register const gs_uid *puid1, register const gs_uid *puid2)
+{ if ( puid1->id != puid2->id )
+ return false;
+ if ( puid1->id >= 0 )
+ return true; /* UniqueID */
+ return
+ !memcmp((const char *)puid1->xvalues,
+ (const char *)puid2->xvalues,
+ (uint)-(puid1->id) * sizeof(long));
+}
diff --git a/pstoraster/gsutil.h b/pstoraster/gsutil.h
new file mode 100644
index 000000000..d3af37293
--- /dev/null
+++ b/pstoraster/gsutil.h
@@ -0,0 +1,66 @@
+/* Copyright (C) 1992, 1993 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gsutil.h */
+/* Prototypes for procedures in gsutil.c */
+
+#ifndef gsutil_INCLUDED
+# define gsutil_INCLUDED
+
+/* ------ Unique IDs ------ */
+
+/* Generate a block of unique IDs. */
+gs_id gs_next_ids(P1(uint count));
+
+/* ------ Memory utilities ------ */
+
+/* Transpose an 8 x 8 block of bits. */
+/* line_size is the raster of the input data; */
+/* dist is the distance between output bytes. */
+/* Dot matrix printers need this. */
+/* Note that with a negative dist value, */
+/* this will rotate an 8 x 8 block 90 degrees counter-clockwise. */
+void memflip8x8(P4(const byte *inp, int line_size, byte *outp, int dist));
+
+/* ------ String utilities ------ */
+
+/* Compare two strings, returning -1 if the first is less, */
+/* 0 if they are equal, and 1 if first is greater. */
+/* We can't use memcmp, because we always use unsigned characters. */
+int bytes_compare(P4(const byte *str1, uint len1,
+ const byte *str2, uint len2));
+
+/* Test whether a string matches a pattern with wildcards. */
+/* If psmp == NULL, use standard parameters: '*' = any substring, */
+/* '?' = any character, '\\' quotes next character, don't ignore case. */
+typedef struct string_match_params_s {
+ int any_substring; /* '*' */
+ int any_char; /* '?' */
+ int quote_next; /* '\\' */
+ bool ignore_case;
+} string_match_params;
+bool string_match(P5(const byte *str, uint len,
+ const byte *pstr, uint plen,
+ const string_match_params *psmp));
+
+#endif /* gsutil_INCLUDED */
diff --git a/pstoraster/gsxfont.h b/pstoraster/gsxfont.h
new file mode 100644
index 000000000..25db902c7
--- /dev/null
+++ b/pstoraster/gsxfont.h
@@ -0,0 +1,43 @@
+/* Copyright (C) 1993 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gsxfont.h */
+/* External font client definitions for Ghostscript library */
+
+#ifndef gsxfont_INCLUDED
+# define gsxfont_INCLUDED
+
+/* Define a character glyph identifier. This is opaque, probably an index */
+/* into the font. Glyph identifiers are font-specific. */
+typedef ulong gx_xglyph;
+#define gx_no_xglyph ((gx_xglyph)~0L)
+
+/* Structure for xfont procedures. */
+struct gx_xfont_procs_s;
+typedef struct gx_xfont_procs_s gx_xfont_procs;
+
+/* A generic xfont. */
+struct gx_xfont_s;
+typedef struct gx_xfont_s gx_xfont;
+
+#endif /* gsxfont_INCLUDED */
diff --git a/pstoraster/gx.h b/pstoraster/gx.h
new file mode 100644
index 000000000..aa2f062ba
--- /dev/null
+++ b/pstoraster/gx.h
@@ -0,0 +1,43 @@
+/* Copyright (C) 1989, 1991, 1994 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gx.h */
+/* Common internal definitions for Ghostscript library */
+#include "stdio_.h" /* includes std.h */
+#include "gserror.h"
+#include "gsio.h"
+#include "gstypes.h"
+#include "gsmemory.h"
+#include "gdebug.h"
+
+/* Define opaque types for the graphics state. */
+/* This is used so pervasively that we define it here, */
+/* rather than at a higher level as perhaps would be more appropriate. */
+#ifndef gs_imager_state_DEFINED
+# define gs_imager_state_DEFINED
+typedef struct gs_imager_state_s gs_imager_state;
+#endif
+#ifndef gs_state_DEFINED
+# define gs_state_DEFINED
+typedef struct gs_state_s gs_state;
+#endif
diff --git a/pstoraster/gxacpath.c b/pstoraster/gxacpath.c
new file mode 100644
index 000000000..71e4e97a7
--- /dev/null
+++ b/pstoraster/gxacpath.c
@@ -0,0 +1,424 @@
+/* Copyright (C) 1993, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxacpath.c */
+/* Accumulator for clipping paths */
+#include "gx.h"
+#include "gserrors.h"
+#include "gsstruct.h"
+#include "gsutil.h"
+#include "gsdcolor.h"
+#include "gxdevice.h"
+#include "gxfixed.h"
+#include "gzpath.h"
+#include "gxpaint.h"
+#include "gzcpath.h"
+#include "gzacpath.h"
+
+/* Imported procedures */
+extern gx_device *gs_currentdevice(P1(const gs_state *));
+extern float gs_currentflat(P1(const gs_state *));
+extern bool clip_list_validate(P1(const gx_clip_list *));
+
+/* Device procedures */
+private dev_proc_open_device(accum_open);
+private dev_proc_close_device(accum_close);
+private dev_proc_fill_rectangle(accum_fill_rectangle);
+
+/* The device descriptor */
+/* Many of these procedures won't be called; they are set to NULL. */
+private const gx_device_cpath_accum gs_cpath_accum_device =
+{ std_device_std_body(gx_device_cpath_accum, 0, "clip list accumulator",
+ 0, 0, 1, 1),
+ { accum_open,
+ NULL, /* get_initial_matrix */
+ NULL, /* sync_output */
+ NULL, /* output_page */
+ accum_close,
+ NULL, /* map_rgb_color */
+ NULL, /* map_color_rgb */
+ accum_fill_rectangle,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ gx_default_fill_path,
+ gx_default_stroke_path,
+ NULL,
+ gx_default_fill_trapezoid,
+ gx_default_fill_parallelogram,
+ gx_default_fill_triangle,
+ gx_default_draw_thin_line,
+ gx_default_begin_image,
+ gx_default_image_data,
+ gx_default_end_image
+ }
+};
+
+/* Start accumulating a clipping path. */
+void
+gx_cpath_accum_begin(gx_device_cpath_accum *padev, gs_memory_t *mem)
+{ *padev = gs_cpath_accum_device;
+ padev->list_memory = mem;
+ (*dev_proc(padev, open_device))((gx_device *)padev);
+}
+
+/* Finish accumulating a clipping path. */
+int
+gx_cpath_accum_end(const gx_device_cpath_accum *padev, gx_clip_path *pcpath)
+{ int code = (*dev_proc(padev, close_device))((gx_device *)padev);
+ if ( code < 0 )
+ return code;
+ pcpath->list = padev->list;
+ gx_path_init(&pcpath->path, pcpath->path.memory);
+ pcpath->path.bbox.p.x = int2fixed(padev->bbox.p.x);
+ pcpath->path.bbox.p.y = int2fixed(padev->bbox.p.y);
+ pcpath->path.bbox.q.x = int2fixed(padev->bbox.q.x);
+ pcpath->path.bbox.q.y = int2fixed(padev->bbox.q.y);
+ /* Using the setbbox flag here is slightly bogus, */
+ /* but it's as good a way as any to indicate that */
+ /* the bbox is accurate. */
+ pcpath->path.bbox_set = 1;
+ /* Note that the result of the intersection might be */
+ /* a single rectangle. This will cause clip_path_is_rect.. */
+ /* to return true. This, in turn, requires that */
+ /* we set pcpath->inner_box correctly. */
+ if ( clip_list_is_rectangle(&padev->list) )
+ pcpath->inner_box = pcpath->path.bbox;
+ else
+ { /* The quick check must fail. */
+ pcpath->inner_box.p.x = pcpath->inner_box.p.y = 0;
+ pcpath->inner_box.q.x = pcpath->inner_box.q.y = 0;
+ }
+ gx_cpath_set_outer_box(pcpath);
+ pcpath->segments_valid = 0;
+ pcpath->shares_list = 0;
+ pcpath->id = gs_next_ids(1); /* path changed => change id */
+ return 0;
+}
+
+/* Discard an accumulator in case of error. */
+void
+gx_cpath_accum_discard(gx_device_cpath_accum *padev)
+{ gx_clip_list_free(&padev->list, padev->list_memory);
+}
+
+/* Intersect two clipping paths using an accumulator. */
+int
+gx_cpath_intersect_slow(gs_state *pgs, gx_clip_path *pcpath, gx_path *ppath,
+ int rule)
+{ bool outside = pcpath->list.outside;
+ gx_device_cpath_accum adev;
+ gx_device_color devc;
+ gx_fill_params params;
+ int code;
+
+ gx_cpath_accum_begin(&adev, pcpath->path.memory);
+ color_set_pure(&devc, 0); /* arbitrary, but not transparent */
+ params.rule = rule;
+ params.adjust.x = params.adjust.y = fixed_half;
+ params.flatness = gs_currentflat(pgs);
+ params.fill_zero_width = true;
+ code = gx_fill_path_only(ppath, (gx_device *)&adev,
+ (const gs_imager_state *)pgs,
+ &params, &devc, pcpath);
+ if ( code < 0 || (code = gx_cpath_accum_end(&adev, pcpath)) < 0 )
+ gx_cpath_accum_discard(&adev);
+ pcpath->list.outside = outside;
+ return code;
+}
+
+/* ------ Device implementation ------ */
+
+#define adev ((gx_device_cpath_accum *)dev)
+
+/* Initialize the accumulation device. */
+private int
+accum_open(register gx_device *dev)
+{ gx_clip_list_init(&adev->list);
+ adev->bbox.p.x = adev->bbox.p.y = max_int;
+ adev->bbox.q.x = adev->bbox.q.y = min_int;
+ return 0;
+}
+
+/* Close the accumulation device. */
+private int
+accum_close(gx_device *dev)
+{
+#ifdef DEBUG
+if ( gs_debug_c('q') )
+ { gx_clip_rect *rp =
+ (adev->list.count <= 1 ? &adev->list.single : adev->list.head);
+ dprintf4("[q]list at 0x%lx, count=%d, head=0x%lx, tail=0x%lx:\n",
+ (ulong)&adev->list, adev->list.count,
+ (ulong)adev->list.head, (ulong)adev->list.tail);
+ while ( rp != 0 )
+ { clip_rect_print('q', " ", rp);
+ rp = rp->next;
+ }
+ }
+ if ( !clip_list_validate(&adev->list) )
+ { lprintf1("[q]Bad clip list 0x%lx!\n", (ulong)&adev->list);
+ return_error(gs_error_Fatal);
+ }
+#endif
+ return 0;
+}
+
+/* Accumulate one rectangle. */
+#undef adev
+/* Allocate a rectangle to be added to the list. */
+static const gx_clip_rect clip_head_rect =
+ { 0, 0, min_int, min_int, min_int, min_int };
+static const gx_clip_rect clip_tail_rect =
+ { 0, 0, max_int, max_int, max_int, max_int };
+private gx_clip_rect *
+accum_alloc_rect(gx_device_cpath_accum *adev)
+{ gs_memory_t *mem = adev->list_memory;
+ gx_clip_rect *ar = gs_alloc_struct(mem, gx_clip_rect, &st_clip_rect,
+ "accum_alloc_rect");
+ if ( ar == 0 )
+ return 0;
+ if ( adev->list.count == 2 )
+ { /* We're switching from a single rectangle to a list. */
+ /* Allocate the head and tail entries. */
+ gx_clip_rect *head = ar;
+ gx_clip_rect *tail =
+ gs_alloc_struct(mem, gx_clip_rect, &st_clip_rect,
+ "accum_alloc_rect(tail)");
+ gx_clip_rect *single =
+ gs_alloc_struct(mem, gx_clip_rect, &st_clip_rect,
+ "accum_alloc_rect(single)");
+ ar = gs_alloc_struct(mem, gx_clip_rect, &st_clip_rect,
+ "accum_alloc_rect(head)");
+ if ( tail == 0 || single == 0 || ar == 0 )
+ { gs_free_object(mem, ar, "accum_alloc_rect");
+ gs_free_object(mem, single, "accum_alloc_rect(single)");
+ gs_free_object(mem, tail, "accum_alloc_rect(tail)");
+ gs_free_object(mem, head, "accum_alloc_rect(head)");
+ return 0;
+ }
+ *head = clip_head_rect;
+ head->next = single;
+ *single = adev->list.single;
+ single->prev = head;
+ single->next = tail;
+ *tail = clip_tail_rect;
+ tail->prev = single;
+ adev->list.head = head;
+ adev->list.tail = tail;
+ }
+ return ar;
+}
+#define accum_alloc(s, ar, px, py, qx, qy)\
+ if ( ++(adev->list.count) == 1 )\
+ ar = &adev->list.single;\
+ else if ( (ar = accum_alloc_rect(adev)) == 0 )\
+ return_error(gs_error_VMerror);\
+ accum_set(s, ar, px, py, qx, qy)
+#define accum_set(s, ar, px, py, qx, qy)\
+ (ar)->xmin = px, (ar)->ymin = py, (ar)->xmax = qx, (ar)->ymax = qy;\
+ clip_rect_print('Q', s, ar)
+/* Link or unlink a rectangle in the list. */
+#define accum_add_last(ar)\
+ accum_add_before(ar, adev->list.tail)
+#define accum_add_after(ar, rprev)\
+ ar->prev = (rprev), (ar->next = (rprev)->next)->prev = ar,\
+ (rprev)->next = ar
+#define accum_add_before(ar, rnext)\
+ (ar->prev = (rnext)->prev)->next = ar, ar->next = (rnext),\
+ (rnext)->prev = ar
+#define accum_remove(ar)\
+ ar->next->prev = ar->prev, ar->prev->next = ar->next
+/* Free a rectangle that was removed from the list. */
+#define accum_free(s, ar)\
+ if ( --(adev->list.count) )\
+ { clip_rect_print('Q', s, ar);\
+ gs_free_object(adev->list_memory, ar, "accum_rect");\
+ }
+/*
+ * Add a rectangle to the list. It would be wonderful if rectangles
+ * were always disjoint and always presented in the correct order,
+ * but they aren't: the fill loop works by trapezoids, not by scan lines,
+ * and may produce slightly overlapping rectangles because of "fattening".
+ * All we can count on is that they are approximately disjoint and
+ * approximately in order.
+ *
+ * Because of the way the fill loop handles a path that is just a single
+ * rectangle, we take special care to merge Y-adjacent rectangles when
+ * this is possible.
+ */
+private int
+accum_add_rect(gx_device_cpath_accum *adev, int x, int y, int xe, int ye)
+{ gx_clip_rect *nr;
+ gx_clip_rect *ar;
+ register gx_clip_rect *rptr;
+ int ymin, ymax;
+
+top: if ( adev->list.count == 0 ) /* very first rectangle */
+ { adev->list.count = 1;
+ accum_set("single", &adev->list.single, x, y, xe, ye);
+ return 0;
+ }
+ if ( adev->list.count == 1 ) /* check for Y merging */
+#define rptr (&adev->list.single)
+ { if ( x == rptr->xmin && xe == rptr->xmax &&
+ y <= rptr->ymax && y >= rptr->ymin
+ )
+ { if ( ye > rptr->ymax )
+ rptr->ymax = ye;
+ return 0;
+ }
+ }
+#undef rptr
+ accum_alloc("accum", nr, x, y, xe, ye);
+ rptr = adev->list.tail->prev;
+ if ( y >= rptr->ymax ||
+ (y == rptr->ymin && ye == rptr->ymax && x >= rptr->xmax)
+ )
+ { accum_add_last(nr);
+ return 0;
+ }
+ /* Work backwards till we find the insertion point. */
+ while ( ye <= rptr->ymin ) rptr = rptr->prev;
+ ymin = rptr->ymin;
+ ymax = rptr->ymax;
+ if ( ye > ymax )
+ { if ( y >= ymax )
+ { /* Insert between two bands. */
+ accum_add_after(nr, rptr);
+ return 0;
+ }
+ /* Split off the top part of the new rectangle. */
+ accum_alloc("a.top", ar, x, ymax, xe, ye);
+ accum_add_after(ar, rptr);
+ ye = nr->ymax = ymax;
+ clip_rect_print('Q', " ymax", nr);
+ }
+ /* Here we know ymin < ye <= ymax; */
+ /* rptr points to the last node with this value of ymin/ymax. */
+ /* If necessary, split off the part of the existing band */
+ /* that is above the new band. */
+ if ( ye < ymax )
+ { gx_clip_rect *rsplit = rptr;
+ while ( rsplit->ymax == ymax )
+ { accum_alloc("s.top", ar, rsplit->xmin, ye, rsplit->xmax, ymax);
+ accum_add_after(ar, rptr);
+ rsplit->ymax = ye;
+ rsplit = rsplit->prev;
+ }
+ ymax = ye;
+ }
+ /* Now ye = ymax. If necessary, split off the part of the */
+ /* existing band that is below the new band. */
+ if ( y > ymin )
+ { gx_clip_rect *rbot = rptr, *rsplit;
+ while ( rbot->prev->ymin == ymin )
+ rbot = rbot->prev;
+ for ( rsplit = rbot; ; )
+ { accum_alloc("s.bot", ar, rsplit->xmin, ymin, rsplit->xmax, y);
+ accum_add_before(ar, rbot);
+ rsplit->ymin = y;
+ if ( rsplit == rptr ) break;
+ rsplit = rsplit->next;
+ }
+ ymin = y;
+ }
+ /* Now y <= ymin as well. (y < ymin is possible.) */
+ nr->ymin = ymin;
+ /* Search for the X insertion point. */
+ for ( ; rptr->ymin == ymin; rptr = rptr->prev )
+ { if ( xe < rptr->xmin ) continue; /* still too far to right */
+ if ( x > rptr->xmax ) break; /* disjoint */
+ /* The new rectangle overlaps an existing one. Merge them. */
+ if ( xe > rptr->xmax )
+ { rptr->xmax = nr->xmax; /* might be > xe if */
+ /* we already did a merge */
+ clip_rect_print('Q', "widen", rptr);
+ }
+ accum_free("free", nr);
+ if ( x >= rptr->xmin )
+ goto out;
+ /* Might overlap other rectangles to the left. */
+ rptr->xmin = x;
+ nr = rptr;
+ accum_remove(rptr);
+ clip_rect_print('Q', "merge", nr);
+ }
+ accum_add_after(nr, rptr);
+out: /* Check whether there are only 0 or 1 rectangles left. */
+ if ( adev->list.count <= 1 )
+ { /* We're switching from a list to at most 1 rectangle. */
+ /* Free the head and tail entries. */
+ gs_memory_t *mem = adev->list_memory;
+ gx_clip_rect *single = adev->list.head->next;
+ if ( single != adev->list.tail )
+ { adev->list.single = *single;
+ gs_free_object(mem, single, "accum_free_rect(single)");
+ adev->list.single.next = adev->list.single.prev = 0;
+ }
+ gs_free_object(mem, adev->list.tail, "accum_free_rect(tail)");
+ gs_free_object(mem, adev->list.head, "accum_free_rect(head)");
+ adev->list.head = 0;
+ adev->list.tail = 0;
+ }
+ /* Check whether there is still more of the new band to process. */
+ if ( y < ymin )
+ { /* Continue with the bottom part of the new rectangle. */
+ clip_rect_print('Q', " ymin", nr);
+ ye = ymin;
+ goto top;
+ }
+ return 0;
+}
+#define adev ((gx_device_cpath_accum *)dev)
+private int
+accum_fill_rectangle(gx_device *dev, int x, int y, int w, int h,
+ gx_color_index color)
+{ int xe, ye;
+
+ if ( w <= 0 || h <= 0 ) return 0;
+ xe = x + w, ye = y + h;
+ /* Update the bounding box. */
+ if ( x < adev->bbox.p.x )
+ adev->bbox.p.x = x;
+ if ( y < adev->bbox.p.y )
+ adev->bbox.p.y = y;
+ if ( xe > adev->bbox.q.x )
+ adev->bbox.q.x = xe;
+ if ( ye > adev->bbox.q.y )
+ adev->bbox.q.y = ye;
+ return accum_add_rect(adev, x, y, xe, ye);
+}
diff --git a/pstoraster/gxalloc.h b/pstoraster/gxalloc.h
new file mode 100644
index 000000000..7cbfff125
--- /dev/null
+++ b/pstoraster/gxalloc.h
@@ -0,0 +1,351 @@
+/* Copyright (C) 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxalloc.h */
+/* Memory manager internal definitions for Ghostscript */
+/* Requires gsmemory.h, gsstruct.h */
+
+#ifndef gs_ref_memory_DEFINED
+# define gs_ref_memory_DEFINED
+typedef struct gs_ref_memory_s gs_ref_memory_t;
+#endif
+
+#include "gsalloc.h"
+#include "gxobj.h"
+
+/* ================ Chunks ================ */
+
+/*
+ * We obtain memory from the operating system in `chunks'. A chunk
+ * may hold only a single large object (or string), or it may hold
+ * many objects (allocated from the bottom up, always aligned)
+ * and strings (allocated from the top down, not aligned).
+ */
+
+/*
+ * Refs are allocated in the bottom-up section, along with struct objects.
+ * In order to keep the overhead for refs small, we make consecutive
+ * blocks of refs into a single allocator object of type st_refs.
+ * To do this, we remember the start of the current ref object (if any),
+ * and the end of the last block of allocated refs. As long as
+ * the latter is equal to the top of the allocated area, we can add
+ * more refs to the current object; otherwise, we have to start a new one.
+ * We check and then assume that sizeof(ref) % obj_align_mod == 0;
+ * this means that if we ever have to pad a block of refs, we never add
+ * as much as one entire ref.
+ */
+#ifdef r_type /* i.e., we know about refs */
+# if (arch_sizeof_ref % obj_align_mod) != 0
+Error : arch_sizeof_ref % obj_align_mod != 0 :
+# endif
+#endif
+
+/*
+ * When we do a save, we create a new 'inner' chunk out of the remaining
+ * space in the currently active chunk. Inner chunks must not be freed
+ * by a restore.
+ *
+ * The garbage collector implements relocation for refs by scanning
+ * forward to a free object. Because of this, every ref object must end
+ * with a dummy ref that can hold the relocation for the last block.
+ */
+
+/*
+ * Strings carry some additional overhead for use by the GC.
+ * At the top of the chunk is a table of relocation values for
+ * 16N-character blocks of strings, where N is sizeof(uint).
+ * This table is aligned, by adding padding above it if necessary.
+ * Just below it is a mark table for the strings. This table is also aligned,
+ * to improve GC performance. The actual string data start below
+ * the mark table. These tables are not needed for a chunk that holds
+ * a single large (non-string) object, but they are needed for all other
+ * chunks, including chunks created to hold a single large string.
+ */
+
+/*
+ * Define the unit of data manipulation for marking strings.
+ */
+typedef uint string_mark_unit;
+#define log2_sizeof_string_mark_unit arch_log2_sizeof_int
+/*
+ * Define the quantum of relocation for strings, which determines
+ * the quantum for reserving space. This value must be a power of 2,
+ * must be at least sizeof(string_mark_unit) * 8, and (because of the
+ * unrolled loops in igcstr.c) currently must be equal to either 32 or 64.
+ */
+typedef uint string_reloc_offset;
+#define log2_string_data_quantum (arch_log2_sizeof_int + 4)
+#define string_data_quantum (1 << log2_string_data_quantum)
+/*
+ * Define the quantum for reserving string space, including data,
+ * marks, and relocation.
+ */
+#define string_space_quantum\
+ (string_data_quantum + (string_data_quantum / 8) +\
+ sizeof(string_reloc_offset))
+/*
+ * Compute the amount of space needed for a chunk that holds only
+ * a string of a given size.
+ */
+#define string_chunk_space(nbytes)\
+ (((nbytes) + (string_data_quantum - 1)) / string_data_quantum *\
+ string_space_quantum)
+/*
+ * Compute the number of string space quanta in a given amount of storage.
+ */
+#define string_space_quanta(spacebytes)\
+ ((spacebytes) / string_space_quantum)
+/*
+ * Compute the size of string marks for a given number of quanta.
+ */
+#define string_quanta_mark_size(nquanta)\
+ ((nquanta) * (string_data_quantum / 8))
+
+/*
+ * To allow the garbage collector to combine chunks, we store in the
+ * head of each chunk the address to which its contents will be moved.
+ */
+/*typedef struct chunk_head_s chunk_head_t;*/ /* in gxobj.h */
+
+/* Structure for a chunk. */
+typedef struct chunk_s chunk_t;
+struct chunk_s {
+ chunk_head_t *chead; /* chunk head, bottom of chunk */
+ /* Note that allocation takes place both from the bottom up */
+ /* (aligned objects) and from the top down (strings). */
+ byte *cbase; /* bottom of chunk data area */
+ byte *cbot; /* bottom of free area */
+ /* (top of aligned objects) */
+ obj_header_t *rcur; /* current refs object, 0 if none */
+ byte *rtop; /* top of rcur */
+ byte *ctop; /* top of free area */
+ /* (bottom of strings) */
+ byte *climit; /* top of strings */
+ byte *cend; /* top of chunk */
+ chunk_t *cprev; /* chain chunks together, */
+ chunk_t *cnext; /* sorted by address */
+ chunk_t *outer; /* the chunk of which this is */
+ /* an inner chunk, if any */
+ uint inner_count; /* number of chunks of which this is */
+ /* the outer chunk, if any */
+ bool has_refs; /* true if any refs in chunk */
+ /* The remaining members are for the GC. */
+ byte *odest; /* destination for objects */
+ byte *smark; /* mark bits for strings */
+ uint smark_size;
+ byte *sbase; /* base for computing smark offsets */
+ string_reloc_offset *sreloc; /* relocation for string blocks */
+ byte *sdest; /* destination for (top of) strings */
+ byte *rescan_bot; /* bottom of rescanning range if */
+ /* the GC mark stack overflows */
+ byte *rescan_top; /* top of range ditto */
+};
+/* The chunk descriptor is exported only for isave.c. */
+extern_st(st_chunk);
+#define public_st_chunk() /* in ialloc.c */\
+ gs_public_st_ptrs2(st_chunk, chunk_t, "chunk_t",\
+ chunk_enum_ptrs, chunk_reloc_ptrs, cprev, cnext)
+
+/*
+ * Macros for scanning a chunk linearly, with the following schema:
+ * SCAN_CHUNK_OBJECTS(cp) << declares pre, size >>
+ * << code for all objects -- size not set yet >>
+ * DO_LARGE
+ * << code for large objects >>
+ * DO_SMALL
+ * << code for small objects >>
+ * END_OBJECTS_SCAN
+ * If large and small objects are treated alike, one can use DO_ALL instead
+ * of DO_LARGE and DO_SMALL.
+ */
+#define SCAN_CHUNK_OBJECTS(cp)\
+ { obj_header_t *pre = (obj_header_t *)((cp)->cbase);\
+ obj_header_t *end = (obj_header_t *)((cp)->cbot);\
+ ulong size; /* long because of large objects */\
+ for ( ; pre < end;\
+ pre = (obj_header_t *)((char *)pre + obj_size_round(size))\
+ )\
+ {
+#define DO_LARGE\
+ if ( pre->o_large )\
+ { size = pre_obj_large_size(pre);\
+ {
+#define DO_SMALL\
+ }\
+ } else\
+ { size = pre_obj_small_size(pre);\
+ {
+#define DO_ALL\
+ { size = pre_obj_contents_size(pre);\
+ {
+#ifdef DEBUG
+# define END_OBJECTS_SCAN\
+ }\
+ }\
+ }\
+ if ( pre != end )\
+ { lprintf2("Chunk parsing error, 0x%lx != 0x%lx\n",\
+ (ulong)pre, (ulong)end);\
+ gs_exit(1);\
+ }\
+ }
+#else
+# define END_OBJECTS_SCAN\
+ }\
+ }\
+ }\
+ }
+#endif
+
+/* Initialize a chunk. */
+/* This is exported for save/restore. */
+void alloc_init_chunk(P5(chunk_t *, byte *, byte *, bool, chunk_t *));
+
+/* Find the chunk for a pointer. */
+/* Note that ptr_is_within_chunk returns true even if the pointer */
+/* is in an inner chunk of the chunk being tested. */
+#define ptr_is_within_chunk(ptr, cp)\
+ ptr_between((const byte *)(ptr), (cp)->cbase, (cp)->cend)
+#define ptr_is_in_inner_chunk(ptr, cp)\
+ ((cp)->inner_count != 0 &&\
+ ptr_between((const byte *)(ptr), (cp)->cbot, (cp)->ctop))
+#define ptr_is_in_chunk(ptr, cp)\
+ (ptr_is_within_chunk(ptr, cp) && !ptr_is_in_inner_chunk(ptr, cp))
+typedef struct chunk_locator_s {
+ const gs_ref_memory_t *memory; /* for head & tail of chain */
+ chunk_t *cp; /* one-element cache */
+} chunk_locator_t;
+bool chunk_locate_ptr(P2(const void *, chunk_locator_t *));
+#define chunk_locate(ptr, clp)\
+ (((clp)->cp != 0 && ptr_is_in_chunk(ptr, (clp)->cp)) ||\
+ chunk_locate_ptr(ptr, clp))
+
+/* Close up the current chunk. */
+/* This is exported for save/restore and for the GC. */
+void alloc_close_chunk(P1(gs_ref_memory_t *mem));
+
+/* Reopen the current chunk after a GC. */
+void alloc_open_chunk(P1(gs_ref_memory_t *mem));
+
+/* Insert or remove a chunk in the address-ordered chain. */
+/* These are exported for the GC. */
+void alloc_link_chunk(P2(chunk_t *, gs_ref_memory_t *));
+void alloc_unlink_chunk(P2(chunk_t *, gs_ref_memory_t *));
+
+/* Free a chunk. This is exported for save/restore and for the GC. */
+void alloc_free_chunk(P2(chunk_t *, gs_ref_memory_t *));
+
+/* Print a chunk debugging message. */
+/* Unfortunately, the ANSI C preprocessor doesn't allow us to */
+/* define the list of variables being printed as a macro. */
+#define dprintf_chunk_format\
+ "%s 0x%lx (0x%lx..0x%lx, 0x%lx..0x%lx..0x%lx)\n"
+#define dprintf_chunk(msg, cp)\
+ dprintf7(dprintf_chunk_format,\
+ msg, (ulong)(cp), (ulong)(cp)->cbase, (ulong)(cp)->cbot,\
+ (ulong)(cp)->ctop, (ulong)(cp)->climit, (ulong)(cp)->cend)
+#define if_debug_chunk(c, msg, cp)\
+ if_debug7(c, dprintf_chunk_format,\
+ msg, (ulong)(cp), (ulong)(cp)->cbase, (ulong)(cp)->cbot,\
+ (ulong)(cp)->ctop, (ulong)(cp)->climit, (ulong)(cp)->cend)
+
+/* ================ Allocator state ================ */
+
+/* Structures for save/restore (not defined here). */
+struct alloc_save_s;
+struct alloc_change_s;
+
+/* Define the number of freelists. The index in the freelist array */
+/* is the ceiling of the size of the object contents (i.e., not including */
+/* the header) divided by obj_align_mod. */
+#define max_freelist_size 800 /* big enough for gstate & contents */
+#define num_freelists\
+ ((max_freelist_size + obj_align_mod - 1) / obj_align_mod + 1)
+
+/* Define the memory manager subclass for this allocator. */
+struct gs_ref_memory_s {
+ /* The following are set at initialization time. */
+ gs_memory_common;
+ gs_memory_t *parent; /* for allocating chunks */
+ uint chunk_size;
+ uint large_size; /* min size to give large object */
+ /* its own chunk: must be */
+ /* 1 mod obj_align_mod */
+ gs_ref_memory_t *global; /* global VM for this allocator */
+ /* (may point to itself) */
+ uint space; /* a_local, a_global, a_system */
+ /* Callers can change the following dynamically */
+ /* (through a procedural interface). */
+ gs_memory_gc_status_t gc_status;
+ /* The following are updated dynamically. */
+ ulong limit; /* signal a VMerror when total */
+ /* allocated exceeds this */
+ chunk_t *cfirst; /* head of chunk list */
+ chunk_t *clast; /* tail of chunk list */
+ chunk_t cc; /* current chunk */
+ chunk_t *pcc; /* where to store cc */
+ chunk_locator_t cfreed; /* chunk where last object freed */
+ ulong allocated; /* total size of all chunks */
+ /* allocated at this save level */
+ long inherited; /* chunks allocated at outer save */
+ /* levels that should be counted */
+ /* towards the GC threshold */
+ /* (may be negative, but allocated + */
+ /* inherited >= 0 always) */
+ ulong gc_allocated; /* value of (allocated + */
+ /* previous_status.allocated) after last GC */
+ ulong freed_lost; /* space freed and 'lost' */
+ /* Garbage collector information */
+ gs_gc_root_t *roots; /* roots for GC */
+ /* Sharing / saved state information */
+ int num_contexts; /* # of contexts sharing this VM */
+ struct alloc_change_s *changes;
+ struct alloc_save_s *saved;
+ struct alloc_save_s *reloc_saved; /* for GC */
+ gs_memory_status_t previous_status; /* total allocated & used */
+ /* in outer save levels */
+ /* We put the freelists last to keep the scalar */
+ /* offsets small. */
+ obj_header_t *freelists[num_freelists];
+};
+/* The descriptor for gs_ref_memory_t is exported only for */
+/* the alloc_save_t subclass; otherwise, it should be private. */
+extern_st(st_ref_memory);
+#define public_st_ref_memory() /* in ialloc.c */\
+ gs_public_st_composite(st_ref_memory, gs_ref_memory_t,\
+ "gs_ref_memory", ref_memory_enum_ptrs, ref_memory_reloc_ptrs)
+#define st_ref_memory_max_ptrs 2 /* changes, saved */
+
+/*
+ * Scan the chunks of an allocator:
+ * SCAN_MEM_CHUNKS(mem, cp)
+ * << code to process chunk cp >>
+ * END_CHUNKS_SCAN
+ */
+#define SCAN_MEM_CHUNKS(mem, cp)\
+ { chunk_t *cp = (mem)->cfirst;\
+ for ( ; cp != 0; cp = cp->cnext )\
+ {
+#define END_CHUNKS_SCAN\
+ }\
+ }
diff --git a/pstoraster/gxarith.h b/pstoraster/gxarith.h
new file mode 100644
index 000000000..bd3e80e2c
--- /dev/null
+++ b/pstoraster/gxarith.h
@@ -0,0 +1,83 @@
+/* Copyright (C) 1990, 1993, 1994, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+#ifndef gxarith_INCLUDED
+# define gxarith_INCLUDED
+
+/* gxarith.h */
+/* Arithmetic macros for Ghostscript library */
+
+/* Define an in-line abs function, good for any signed numeric type. */
+#define any_abs(x) ((x) < 0 ? -(x) : (x))
+
+/* Compute M modulo N. Requires N > 0; guarantees 0 <= imod(M,N) < N, */
+/* regardless of the whims of the % operator for negative operands. */
+int imod(P2(int m, int n));
+
+/* Compute the GCD of two integers. */
+int igcd(P2(int x, int y));
+
+/* Test whether an integral value fits in a given number of bits. */
+/* This works for all integral types. */
+#define fits_in_bits(i, n)\
+ (sizeof(i) <= sizeof(int) ? fits_in_ubits((i) + (1 << ((n) - 1)), (n) + 1) :\
+ fits_in_ubits((i) + (1L << ((n) - 1)), (n) + 1))
+#define fits_in_ubits(i, n) (((i) >> (n)) == 0)
+
+/*
+ * There are some floating point operations that can be implemented
+ * very efficiently on machines that have no floating point hardware,
+ * assuming IEEE representation and no range overflows.
+ * We define straightforward versions of them here, and alternate versions
+ * for no-floating-point machines in gxfarith.h.
+ */
+/* Test floating point values against constants. */
+#define is_fzero(f) ((f) == 0.0)
+#define is_fzero2(f1,f2) ((f1) == 0.0 && (f2) == 0.0)
+#define is_fneg(f) ((f) < 0.0)
+#define is_fge1(f) ((f) >= 1.0)
+/* Test whether a floating point value fits in a given number of bits. */
+#define f_fits_in_bits(f, n)\
+ ((f) >= -2.0 * (1L << ((n) - 2)) && (f) < 2.0 * (1L << ((n) - 2)))
+#define f_fits_in_ubits(f, n)\
+ ((f) >= 0 && (f) < 4.0 * (1L << ((n) - 2)))
+
+/*
+ * Define a macro for computing log2(n), where n=1,2,4,...,128.
+ * Because some compilers limit the total size of a statement,
+ * this macro must only mention n once. The macro should really
+ * only be used with compile-time constant arguments, but it will work
+ * even if n is an expression computed at run-time.
+ */
+#define small_exact_log2(n)\
+ ((uint)(05637042010L >> ((((n) % 11) - 1) * 3)) & 7)
+
+/*
+ * The following doesn't give rise to a macro, but is used in several
+ * places in Ghostscript. We observe that if M = 2^n-1 and V < M^2,
+ * then the quotient Q and remainder R can be computed as:
+ * Q = V / M = (V + (V >> n) + 1) >> n;
+ * R = V % M = (V + (V / M)) & M = V - (Q << n) + Q.
+ */
+
+#endif /* gxarith_INCLUDED */
diff --git a/pstoraster/gxbcache.c b/pstoraster/gxbcache.c
new file mode 100644
index 000000000..58b11843a
--- /dev/null
+++ b/pstoraster/gxbcache.c
@@ -0,0 +1,147 @@
+/* Copyright (C) 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxbcache.c */
+/* Bitmap cache implementation */
+#include "memory_.h"
+#include "gx.h"
+#include "gsmdebug.h"
+#include "gxbcache.h"
+
+/* ------ Entire cache ------ */
+
+/* Initialize a cache. The caller must allocate and initialize */
+/* the first chunk. */
+void
+gx_bits_cache_init(gx_bits_cache *bc, gx_bits_cache_chunk *bck)
+{ bck->next = bck;
+ bc->chunks = bck;
+ bc->cnext = 0;
+ bc->bsize = 0;
+ bc->csize = 0;
+}
+
+/* ------ Chunks ------ */
+
+/* Initialize a chunk. The caller must allocate it and its data. */
+void
+gx_bits_cache_chunk_init(gx_bits_cache_chunk *bck, byte *data, uint size)
+{ bck->next = 0;
+ bck->data = data;
+ bck->size = size;
+ bck->allocated = 0;
+ if ( data != 0 )
+ { gx_cached_bits_head *cbh = (gx_cached_bits_head *)data;
+ cbh->size = size;
+ cb_head_set_free(cbh);
+ }
+}
+
+/* ------ Individual entries ------ */
+
+/* Attempt to allocate an entry. If successful, set *pcbh and return 0. */
+/* If there isn't enough room, set *pcbh to an entry requiring freeing, */
+/* or to 0 if we are at the end of the chunk, and return -1. */
+int
+gx_bits_cache_alloc(gx_bits_cache *bc, ulong lsize, gx_cached_bits_head **pcbh)
+{
+#define ssize ((uint)lsize)
+ ulong lsize1 = lsize + sizeof(gx_cached_bits_head);
+#define ssize1 ((uint)lsize1)
+ uint cnext = bc->cnext;
+ gx_bits_cache_chunk *bck = bc->chunks;
+ uint left = bck->size - cnext;
+ gx_cached_bits_head *cbh;
+ gx_cached_bits_head *cbh_next;
+ uint fsize = 0;
+
+ if ( lsize1 > bck->size - cnext && lsize != left )
+ { /* Not enough room to allocate in this chunk. */
+ *pcbh = 0;
+ return -1;
+ }
+ /* Look for and/or free enough space. */
+ cbh = cbh_next = (gx_cached_bits_head *)(bck->data + cnext);
+ while ( fsize < ssize1 && fsize != ssize )
+ { if ( !cb_head_is_free(cbh_next) )
+ { /* Ask the caller to free the entry. */
+ if ( fsize )
+ cbh->size = fsize;
+ *pcbh = cbh_next;
+ return -1;
+ }
+ fsize += cbh_next->size;
+ if_debug2('K', "[K]merging free bits 0x%lx(%u)\n",
+ (ulong)cbh_next, cbh_next->size);
+ cbh_next = (gx_cached_bits_head *)((byte *)cbh + fsize);
+ }
+ if ( fsize > ssize ) /* fsize >= ssize1 */
+ { cbh_next = (gx_cached_bits_head *)((byte *)cbh + ssize);
+ cbh_next->size = fsize - ssize;
+ cb_head_set_free(cbh_next);
+ if_debug2('K', "[K]shortening bits 0x%lx by %u (initial)\n",
+ (ulong)cbh, fsize - ssize);
+ }
+ gs_alloc_fill(cbh, gs_alloc_fill_block, ssize);
+ cbh->size = ssize;
+ bc->bsize += ssize;
+ bc->csize++;
+ bc->cnext += ssize;
+ bck->allocated += ssize;
+ *pcbh = cbh;
+ return 0;
+#undef ssize
+#undef ssize1
+}
+
+/* Shorten an entry by a given amount. */
+void
+gx_bits_cache_shorten(gx_bits_cache *bc, gx_cached_bits_head *cbh,
+ uint diff, gx_bits_cache_chunk *bck)
+{ gx_cached_bits_head *next;
+
+ if ( (byte *)cbh + cbh->size == bck->data + bc->cnext &&
+ bck == bc->chunks
+ )
+ bc->cnext -= diff;
+ bc->bsize -= diff;
+ bck->allocated -= diff;
+ cbh->size -= diff;
+ next = (gx_cached_bits_head *)((byte *)cbh + cbh->size);
+ cb_head_set_free(next);
+ next->size = diff;
+}
+
+/* Free an entry. The caller is responsible for removing the entry */
+/* from any other structures (like a hash table). */
+void
+gx_bits_cache_free(gx_bits_cache *bc, gx_cached_bits_head *cbh,
+ gx_bits_cache_chunk *bck)
+{ uint size = cbh->size;
+ bc->csize--;
+ bc->bsize -= size;
+ bck->allocated -= size;
+ gs_alloc_fill(cbh, gs_alloc_fill_deleted, size);
+ cbh->size = size; /* gs_alloc_fill may have overwritten */
+ cb_head_set_free(cbh);
+}
diff --git a/pstoraster/gxbcache.h b/pstoraster/gxbcache.h
new file mode 100644
index 000000000..b980ae5b7
--- /dev/null
+++ b/pstoraster/gxbcache.h
@@ -0,0 +1,121 @@
+/* Copyright (C) 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxbcache.h */
+/* Bitmap cache structures */
+#include "gxbitmap.h"
+
+/*
+ * These structures are superclasses for a cache in which the 'value' is
+ * a bitmap. The structures defined here don't take any position about
+ * the nature of the 'key'.
+ */
+
+/* ---------------- Bitmap cache entry ---------------- */
+
+/*
+ * The cache may contain both used and free blocks.
+ * All blocks have a common header; free blocks have ONLY the header.
+ */
+typedef struct gx_cached_bits_head_s {
+ uint size; /* total block size in bytes */
+ uint depth; /* bits per pixel, free block if 0 */
+} gx_cached_bits_head;
+#define cb_head_is_free(cbh) ((cbh)->depth == 0)
+#define cb_head_set_free(cbh) ((cbh)->depth = 0)
+#define gx_cached_bits_common\
+ gx_cached_bits_head head; /* must be first */\
+ /* The rest of the entry is an abbreviation of */\
+ /* gx_strip_bitmap, sans data. */\
+ ushort width, height, shift;\
+ ushort raster;\
+ gx_bitmap_id id
+/* Define aliases for head members. */
+#define cb_depth head.depth
+/* Define aliases for common members formerly in the head. */
+#define cb_raster raster
+typedef struct gx_cached_bits_s {
+ gx_cached_bits_common;
+} gx_cached_bits;
+#define cb_is_free(cb) cb_head_is_free(&(cb)->head)
+/*
+ * Define the alignment of the gx_cached_bits structure. We must ensure
+ * that an immediately following bitmap will be properly aligned.
+ */
+#define align_cached_bits_mod\
+ (max(align_bitmap_mod, max(arch_align_ptr_mod, arch_align_long_mod)))
+
+/*
+ * We may allocate a bitmap cache in chunks, so as not to tie up memory
+ * prematurely if it isn't needed (or something else needs it more).
+ * Thus there is a structure for managing an entire cache, and another
+ * structure for managing each chunk.
+ */
+typedef struct gx_bits_cache_chunk_s gx_bits_cache_chunk;
+struct gx_bits_cache_chunk_s {
+ gx_bits_cache_chunk *next;
+ byte *data; /* gx_cached_bits_head * */
+ uint size;
+ uint allocated; /* amount of allocated data */
+};
+
+/* ---------------- Bitmap cache ---------------- */
+
+#define gx_bits_cache_common\
+ gx_bits_cache_chunk *chunks; /* current chunk in circular list */\
+ uint cnext; /* rover for allocating entries */\
+ /* in current chunk */\
+ uint bsize; /* total # of bytes for all entries */\
+ uint csize /* # of entries */
+typedef struct gx_bits_cache_s {
+ gx_bits_cache_common;
+} gx_bits_cache;
+
+/* ---------------- Procedural interface ---------------- */
+
+/* ------ Entire cache ------ */
+
+/* Initialize a cache. The caller must allocate and initialize */
+/* the first chunk. */
+void gx_bits_cache_init(P2(gx_bits_cache *, gx_bits_cache_chunk *));
+
+/* ------ Chunks ------ */
+
+/* Initialize a chunk. The caller must allocate it and its data. */
+void gx_bits_cache_chunk_init(P3(gx_bits_cache_chunk *, byte *, uint));
+
+/* ------ Individual entries ------ */
+
+/* Attempt to allocate an entry. If successful, set *pcbh and return 0. */
+/* If there isn't enough room, set *pcbh to an entry requiring freeing, */
+/* or to 0 if we are at the end of the chunk, and return -1. */
+int gx_bits_cache_alloc(P3(gx_bits_cache *, ulong, gx_cached_bits_head **));
+
+/* Shorten an entry by a given amount. */
+void gx_bits_cache_shorten(P4(gx_bits_cache *, gx_cached_bits_head *,
+ uint, gx_bits_cache_chunk *));
+
+/* Free an entry. The caller is responsible for removing the entry */
+/* from any other structures (like a hash table). */
+void gx_bits_cache_free(P3(gx_bits_cache *, gx_cached_bits_head *,
+ gx_bits_cache_chunk *));
diff --git a/pstoraster/gxbitmap.h b/pstoraster/gxbitmap.h
new file mode 100644
index 000000000..c02a469c7
--- /dev/null
+++ b/pstoraster/gxbitmap.h
@@ -0,0 +1,133 @@
+/* Copyright (C) 1989, 1993, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxbitmap.h */
+/* Definitions for stored bitmaps for Ghostscript */
+
+#ifndef gxbitmap_INCLUDED
+# define gxbitmap_INCLUDED
+
+/*
+ * Drivers such as the X driver and the command list (band list) driver
+ * benefit greatly by being able to cache bitmaps (tiles and characters)
+ * and refer to them later. To help them recognize when a bitmap is the
+ * same as one that they have seen before, the core code passes an optional
+ * ID with the property that if two bitmaps have the same ID, they are
+ * guaranteed to have the same contents. (The converse is *not* true,
+ * however: two bitmaps may have different IDs and still be the same.)
+ */
+typedef gs_id gx_bitmap_id;
+#define gx_no_bitmap_id gs_no_id
+
+/*
+ * Bitmaps are stored bit-big-endian (i.e., the 0x80 bit of the first
+ * byte corresponds to x=0), as a sequence of bytes (i.e., you can't
+ * do word-oriented operations on them if you're on a little-endian
+ * platform like the Intel 80x86 or VAX). Each scan line must start on
+ * a `word' (long) boundary, and hence is padded to a word boundary,
+ * although this should rarely be of concern, since the raster and width
+ * are specified individually. The first scan line corresponds to y=0
+ * in whatever coordinate system is relevant.
+ */
+/* We assume arch_align_long_mod is 1-4 or 8. */
+#if arch_align_long_mod <= 4
+# define log2_align_bitmap_mod 2
+#else
+#if arch_align_long_mod == 8
+# define log2_align_bitmap_mod 3
+#endif
+#endif
+#define align_bitmap_mod (1 << log2_align_bitmap_mod)
+#define bitmap_raster(width_bits)\
+ ((uint)(((width_bits + (align_bitmap_mod * 8 - 1))\
+ >> (log2_align_bitmap_mod + 3)) << log2_align_bitmap_mod))
+/*
+ * The basic structure for a bitmap does not specify the depth;
+ * this is implicit in the context of use. Requirements:
+ * size.x > 0, size.y > 0
+ * raster >= (size.x * depth + 7) / 8
+ */
+#define gx_bitmap_common\
+ byte *data;\
+ int raster; /* bytes per scan line */\
+ gs_int_point size; /* width, height */\
+ gx_bitmap_id id
+typedef struct gx_bitmap_s {
+ gx_bitmap_common;
+} gx_bitmap;
+/*
+ * For bitmaps used as halftone tiles, we may replicate the tile in
+ * X and/or Y, but it is still valuable to know the true tile dimensions
+ * (i.e., the dimensions prior to replication). Requirements:
+ * width % rep_width = 0
+ * height % rep_height = 0
+ * Note that rep_height means something slightly different if shift != 0;
+ * see below.
+ */
+#define gx_tile_bitmap_common\
+ gx_bitmap_common;\
+ ushort rep_width, rep_height /* true size of tile */
+typedef struct gx_tile_bitmap_s {
+ gx_tile_bitmap_common;
+} gx_tile_bitmap;
+/*
+ * For halftones at arbitrary angles, we provide for storing the halftone
+ * data as a strip that must be shifted in X for different values of Y. For
+ * an ordinary (non-shifted) halftone that has a repetition width of W and a
+ * repetition height of H, the pixel at coordinate (X,Y) corresponds to
+ * halftone pixel (X mod W, Y mod H), ignoring phase; for a strip halftone
+ * with strip shift S and strip height H, the pixel at (X,Y) corresponds to
+ * halftone pixel ((X + S * floor(Y/H)) mod W, Y mod H). In other words,
+ * each Y increment of H shifts the strip left by S pixels.
+ *
+ * As for non-shifted tiles, a strip bitmap may include multiple copies
+ * in X or Y to reduce loop overhead. In this case, we must distinguish:
+ * - The height of an individual strip, which is the same as
+ * the height of the bitmap being replicated (rep_height, H);
+ * - The height of the entire bitmap (size.y).
+ * Similarly, we must distinguish:
+ * - The shift per strip (rep_shift, S);
+ * - The shift for the entire bitmap (shift).
+ * Note that shift = (rep_shift * size.y / rep_height) mod rep_width,
+ * so the shift member of the structure is only an accelerator. It is,
+ * however, an important one, since it indicates whether the overall
+ * bitmap requires shifting or not.
+ *
+ * If the bitmap consists of a multiple of W / gcd(S, W) copies in Y, the
+ * effective shift is zero, reducing it to a tile. For simplicity, we
+ * require that if shift is non-zero, the bitmap height be less than H * W /
+ * gcd(S, W). I.e., we don't allow strip bitmaps that are large enough to
+ * include a complete tile but that don't include an integral number of
+ * tiles. Requirements:
+ * rep_shift < rep_width
+ * shift = (rep_shift * (size.y / rep_height)) % rep_width
+ */
+#define gx_strip_bitmap_common\
+ gx_tile_bitmap_common;\
+ ushort rep_shift;\
+ ushort shift
+typedef struct gx_strip_bitmap_s {
+ gx_strip_bitmap_common;
+} gx_strip_bitmap;
+
+#endif /* gxbitmap_INCLUDED */
diff --git a/pstoraster/gxccache.c b/pstoraster/gxccache.c
new file mode 100644
index 000000000..3a82eec16
--- /dev/null
+++ b/pstoraster/gxccache.c
@@ -0,0 +1,437 @@
+/* Copyright (C) 1989, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxccache.c */
+/* Fast case character cache routines for Ghostscript library */
+#include "gx.h"
+#include "gpcheck.h"
+#include "gserrors.h"
+#include "gsstruct.h"
+#include "gxfixed.h"
+#include "gxmatrix.h"
+#include "gzstate.h"
+#include "gzpath.h"
+#include "gxdevice.h"
+#include "gxdevmem.h"
+#include "gzcpath.h"
+#include "gxchar.h"
+#include "gxfont.h"
+#include "gxfcache.h"
+#include "gxxfont.h"
+#include "gscspace.h" /* for gsimage.h */
+#include "gsimage.h"
+#include "gxhttile.h"
+
+/* Forward references */
+private byte *compress_alpha_bits(P2(const cached_char *, gs_memory_t *));
+
+/* Define a scale factor of 1. */
+static const gs_log2_scale_point scale_log2_1 = { 0, 0 };
+
+/* Look up, and if necessary add, a font/matrix pair in the cache */
+cached_fm_pair *
+gx_lookup_fm_pair(gs_font *pfont, register const gs_state *pgs)
+{ float mxx = pgs->char_tm.xx, mxy = pgs->char_tm.xy,
+ myx = pgs->char_tm.yx, myy = pgs->char_tm.yy;
+ gs_font *font = pfont;
+ register gs_font_dir *dir = font->dir;
+ register cached_fm_pair *pair =
+ dir->fmcache.mdata + dir->fmcache.mnext;
+ int count = dir->fmcache.mmax;
+ gs_uid uid;
+ if ( font->FontType == ft_composite )
+ { uid_set_invalid(&uid);
+ }
+ else
+ { uid = ((gs_font_base *)font)->UID;
+ if ( uid_is_valid(&uid) )
+ font = 0;
+ }
+ while ( count-- )
+ { if ( pair == dir->fmcache.mdata )
+ pair += dir->fmcache.mmax;
+ pair--;
+ /* We have either a non-zero font and an invalid UID, */
+ /* or a zero font and a valid UID. */
+ /* We have to break up the test */
+ /* because of a bug in the Zortech compiler. */
+ if ( font != 0 )
+ { if ( pair->font != font ) continue;
+ }
+ else
+ { if ( !uid_equal(&pair->UID, &uid) ) continue;
+ }
+ if ( pair->mxx == mxx && pair->mxy == mxy &&
+ pair->myx == myx && pair->myy == myy
+ )
+ { if ( pair->font == 0 )
+ { pair->font = pfont;
+ if_debug2('k', "[k]updating pair 0x%lx with font 0x%lx\n",
+ (ulong)pair, (ulong)pfont);
+ }
+ else
+ { if_debug2('k', "[k]found pair 0x%lx: font=0x%lx\n",
+ (ulong)pair, (ulong)pair->font);
+ }
+ return pair;
+ }
+ }
+ return gx_add_fm_pair(dir, pfont, &uid, pgs);
+}
+
+/* Look up a glyph in the cache. */
+/* The character depth must be either 1 or alt_depth. */
+/* Return the cached_char or 0. */
+cached_char *
+gx_lookup_cached_char(const gs_font *pfont, const cached_fm_pair *pair,
+ gs_glyph glyph, int wmode, int alt_depth)
+{ gs_font_dir *dir = pfont->dir;
+ uint chi = chars_head_index(glyph, pair);
+ register cached_char *cc;
+ while ( (cc = dir->ccache.table[chi & dir->ccache.table_mask]) != 0 )
+ { if ( cc->code == glyph && cc_pair(cc) == pair &&
+ cc->wmode == wmode && (cc_depth(cc) == 1 || cc_depth(cc) == alt_depth)
+ )
+ { if_debug4('K', "[K]found 0x%lx (depth=%d) for glyph=0x%lx, wmode=%d\n",
+ (ulong)cc, cc_depth(cc), (ulong)glyph, wmode);
+ return cc;
+ }
+ chi++;
+ }
+ if_debug3('K', "[K]not found: glyph=0x%lx, wmode=%d, alt_depth=%d\n",
+ (ulong)glyph, wmode, alt_depth);
+ return 0;
+}
+
+/* Look up a character in an external font. */
+/* Return the cached_char or 0. */
+cached_char *
+gx_lookup_xfont_char(const gs_state *pgs, cached_fm_pair *pair,
+ gs_char chr, gs_glyph glyph, const gx_xfont_callbacks *callbacks, int wmode)
+{ gs_font *font = pair->font;
+ int enc_index;
+ gx_xfont *xf;
+ gx_xglyph xg;
+ gs_log2_scale_point log2_scale;
+ gs_point wxy;
+ gs_int_rect bbox;
+ cached_char *cc;
+
+ if ( font == 0 )
+ return NULL;
+ enc_index =
+ (font->FontType == ft_composite ? -1 :
+ ((gs_font_base *)font)->nearest_encoding_index);
+ if ( !pair->xfont_tried )
+ { /* Look for an xfont now. */
+ gx_lookup_xfont(pgs, pair, enc_index);
+ pair->xfont_tried = true;
+ }
+ xf = pair->xfont;
+ if ( xf == 0 )
+ return NULL;
+ { const gx_xfont_procs *procs = xf->common.procs;
+ if ( procs->char_xglyph2 == 0 )
+ { /* The xfont can't recognize reencoded fonts. */
+ /* Use the registered encoding only if this glyph */
+ /* is the same as the one in the registered encoding. */
+ if ( enc_index >= 0 &&
+ (*callbacks->known_encode)(chr, enc_index) != glyph
+ )
+ enc_index = -1;
+ xg = (*procs->char_xglyph)(xf, chr, enc_index, glyph,
+ callbacks->glyph_name);
+ }
+ else
+ { /* The xfont can recognize reencoded fonts. */
+ xg = (*procs->char_xglyph2)(xf, chr, enc_index, glyph,
+ callbacks);
+ }
+ if ( xg == gx_no_xglyph )
+ return NULL;
+ if ( (*procs->char_metrics)(xf, xg, wmode, &wxy, &bbox) < 0 )
+ return NULL;
+ }
+ log2_scale.x = log2_scale.y = 1;
+ cc = gx_alloc_char_bits(font->dir, NULL, NULL, bbox.q.x - bbox.p.x,
+ bbox.q.y - bbox.p.y, &log2_scale, 1);
+ if ( cc == 0 )
+ return NULL;
+ /* Success. Make the cache entry. */
+ cc->code = glyph;
+ cc->wmode = wmode;
+ cc->xglyph = xg;
+ cc->wxy.x = float2fixed(wxy.x);
+ cc->wxy.y = float2fixed(wxy.y);
+ cc->offset.x = int2fixed(-bbox.p.x);
+ cc->offset.y = int2fixed(-bbox.p.y);
+ if_debug5('k', "[k]xfont %s char %d/0x%x#0x%lx=>0x%lx\n",
+ font->font_name.chars, enc_index, (int)chr,
+ (ulong)glyph, (ulong)xg);
+ if_debug6('k', " wxy=(%g,%g) bbox=(%d,%d),(%d,%d)\n",
+ wxy.x, wxy.y, bbox.p.x, bbox.p.y, bbox.q.x, bbox.q.y);
+ gx_add_cached_char(font->dir, NULL, cc, pair, &scale_log2_1);
+ return cc;
+}
+
+/* Copy a cached character to the screen. */
+/* Assume the caller has already done gx_color_load. */
+/* Return 0 if OK, 1 if we couldn't do the operation but no error */
+/* should be signalled, or a negative error code. */
+int
+gx_image_cached_char(register gs_show_enum *penum, register cached_char *cc)
+{ register gs_state *pgs = penum->pgs;
+ gx_device_color *pdevc = pgs->dev_color;
+ int x, y, w, h, depth;
+ int code;
+ gs_fixed_point pt;
+ gx_device *dev = gs_currentdevice_inline(pgs);
+ gx_device *orig_dev = dev;
+ gx_device_clip cdev;
+ gx_xglyph xg = cc->xglyph;
+ gx_xfont *xf;
+ byte *bits;
+
+top: code = gx_path_current_point_inline(pgs->path, &pt);
+ if ( code < 0 ) return code;
+ /*
+ * If the character doesn't lie entirely within the inner
+ * clipping rectangle, we set up an intermediate clipping device.
+ * Note that if the original device implements fill_mask, we may
+ * never actually use the clipping device.
+ */
+ pt.x -= cc->offset.x;
+ x = fixed2int_var_rounded(pt.x) + penum->ftx;
+ pt.y -= cc->offset.y;
+ y = fixed2int_var_rounded(pt.y) + penum->fty;
+ w = cc->width;
+ h = cc->height;
+#ifdef DEBUG
+ if ( gs_debug_c('K') )
+ { if ( cc_has_bits(cc) )
+ debug_dump_bitmap(cc_bits(cc), cc_raster(cc), h,
+ "[K]bits");
+ else
+ dputs("[K]no bits\n");
+ dprintf3("[K]copying 0x%lx, offset=(%g,%g)\n", (ulong)cc,
+ fixed2float(-cc->offset.x),
+ fixed2float(-cc->offset.y));
+ dprintf6(" at (%g,%g)+(%d,%d)->(%d,%d)\n",
+ fixed2float(pt.x), fixed2float(pt.y),
+ penum->ftx, penum->fty, x, y);
+ }
+#endif
+ if ( (x < penum->ibox.p.x || x + w > penum->ibox.q.x ||
+ y < penum->ibox.p.y || y + h > penum->ibox.q.y) &&
+ dev != (gx_device *)&cdev /* might be 2nd time around */
+ )
+ { /* Check for the character falling entirely outside */
+ /* the clipping region. */
+ if ( x >= penum->obox.q.x || x + w <= penum->obox.p.x ||
+ y >= penum->obox.q.y || y + h <= penum->obox.p.y
+ )
+ return 0; /* nothing to do */
+ gx_make_clip_device(&cdev, &cdev, &pgs->clip_path->list);
+ cdev.target = dev;
+ dev = (gx_device *)&cdev;
+ (*dev_proc(dev, open_device))(dev);
+ if_debug0('K', "[K](clipping)\n");
+ }
+ /* If an xfont can render this character, use it. */
+ if ( xg != gx_no_xglyph && (xf = cc_pair(cc)->xfont) != 0 )
+ { int cx = x + fixed2int(cc->offset.x);
+ int cy = y + fixed2int(cc->offset.y);
+ /*
+ * Note that we prefer a 1-bit xfont implementation over
+ * a multi-bit cached bitmap. Eventually we should change
+ * the xfont interface so it can deliver multi-bit bitmaps,
+ * or else implement oversampling for xfonts.
+ */
+ if ( gs_color_writes_pure(pgs) )
+ { code = (*xf->common.procs->render_char)(xf, xg,
+ dev, cx, cy, pdevc->colors.pure, 0);
+ if_debug8('K', "[K]render_char display: xfont=0x%lx, glyph=0x%lx\n\tdev=0x%lx(%s) x,y=%d,%d, color=0x%lx => %d\n",
+ (ulong)xf, (ulong)xg, (ulong)dev,
+ dev->dname, cx, cy,
+ (ulong)pdevc->colors.pure, code);
+ if ( code == 0 )
+ return_check_interrupt(0);
+ }
+ /* Can't render directly. If we don't have a bitmap yet, */
+ /* get it from the xfont now. */
+ if ( !cc_has_bits(cc) )
+ { gx_device_memory mdev;
+ gs_make_mem_mono_device(&mdev, 0, dev);
+ gx_open_cache_device(&mdev, cc);
+ code = (*xf->common.procs->render_char)(xf, xg,
+ (gx_device *)&mdev, cx - x, cy - y,
+ (gx_color_index)1, 1);
+ if_debug7('K', "[K]render_char to bits: xfont=0x%lx, glyph=0x%lx\n\tdev=0x%lx(%s) x,y=%d,%d => %d\n",
+ (ulong)xf, (ulong)xg, (ulong)&mdev,
+ mdev.dname, cx - x, cy - y, code);
+ if ( code != 0 )
+ return_check_interrupt(1);
+ gx_add_char_bits(cc_pair(cc)->font->dir,
+ cc, &scale_log2_1);
+ /* gx_add_char_bits may change width, height, */
+ /* raster, and/or offset. It's easiest to */
+ /* start over from the top. Clear xg so that */
+ /* we don't waste time trying render_char again. */
+ xg = gx_no_xglyph;
+ goto top;
+ }
+ }
+ /*
+ * No xfont. Render from the cached bits. If the cached bits
+ * have more than 1 bit of alpha, and the color isn't pure or
+ * the copy_alpha operation fails, construct a single-bit mask
+ * by taking the high-order alpha bit.
+ */
+ bits = cc_bits(cc);
+ depth = cc_depth(cc);
+ if ( orig_dev->std_procs.fill_mask != gx_default_fill_mask )
+ { code = (*dev_proc(orig_dev, fill_mask))
+ (orig_dev, bits, 0, cc_raster(cc), cc->id,
+ x, y, w, h, pdevc, depth, pgs->log_op, pgs->clip_path);
+ if ( code >= 0 )
+ goto done;
+ }
+ else if ( gs_color_writes_pure(pgs) )
+ { gx_color_index color = pdevc->colors.pure;
+ if ( depth > 1 )
+ { code = (*dev_proc(dev, copy_alpha))
+ (dev, bits, 0, cc_raster(cc), cc->id,
+ x, y, w, h, color, depth);
+ if ( code >= 0 )
+ return_check_interrupt(0);
+ /* copy_alpha failed, construct a monobit mask. */
+ bits = compress_alpha_bits(cc, &gs_memory_default);
+ if ( bits == 0 )
+ return 1; /* VMerror, but recoverable */
+ }
+ code = (*dev_proc(dev, copy_mono))
+ (dev, bits, 0, cc_raster(cc), cc->id,
+ x, y, w, h, gx_no_color_index, pdevc->colors.pure);
+ goto done;
+ }
+ if ( depth > 1 )
+ { /* Complex color or fill_mask / copy_alpha failed, */
+ /* construct a monobit mask. */
+ bits = compress_alpha_bits(cc, &gs_memory_default);
+ if ( bits == 0 )
+ return 1; /* VMerror, but recoverable */
+
+ }
+ { /* Use imagemask to render the character. */
+ gs_image_enum *pie = gs_image_enum_alloc(&gs_memory_default,
+ "image_char(image_enum)");
+ gs_image_t image;
+ int iy;
+ uint used;
+
+ if ( pie == 0 )
+ { if ( bits != cc_bits(cc) )
+ gs_free_object(&gs_memory_default, bits,
+ "compress_alpha_bits");
+ return 1; /* VMerror, but recoverable */
+ }
+ /* Make a matrix that will place the image */
+ /* at (x,y) with no transformation. */
+ gs_image_t_init_mask(&image, true);
+#define mat image.ImageMatrix
+ gs_make_translation((floatp)-x, (floatp)-y, &mat);
+ gs_matrix_multiply(&ctm_only(pgs), &mat, &mat);
+#undef mat
+ image.Width = w;
+ image.Height = h;
+ image.adjust = false;
+ code = gs_image_init(pie, &image, false, pgs);
+ switch ( code )
+ {
+ case 1: /* empty image */
+ code = 0;
+ default:
+ break;
+ case 0:
+ for ( iy = 0; iy < h && code >= 0; iy++ )
+ code = gs_image_next(pie, bits + iy * cc_raster(cc),
+ (w + 7) >> 3, &used);
+ gs_image_cleanup(pie);
+ }
+ gs_free_object(&gs_memory_default, pie,
+ "image_char(image_enum)");
+ }
+done: if ( bits != cc_bits(cc) )
+ gs_free_object(&gs_memory_default, bits, "compress_alpha_bits");
+ if ( code > 0 )
+ code = 0;
+ return_check_interrupt(code);
+}
+
+/* ------ Image manipulation ------ */
+
+/*
+ * Compress a mask with 2 or 4 bits of alpha to a monobit mask.
+ * Allocate and return the address of the monobit mask.
+ */
+private byte *
+compress_alpha_bits(const cached_char *cc, gs_memory_t *mem)
+{ const byte *data = cc_bits(cc);
+ uint width = cc->width;
+ uint height = cc->height;
+ int log2_scale = cc_depth(cc);
+ int scale = 1 << log2_scale;
+ uint sraster = cc_raster(cc);
+ uint sskip = sraster - ((width * scale + 7) >> 3);
+ uint draster = bitmap_raster(width);
+ uint dskip = draster - ((width + 7) >> 3);
+ byte *mask = gs_alloc_bytes(mem, draster * height,
+ "compress_alpha_bits");
+ const byte *sptr = data;
+ byte *dptr = mask;
+ uint h;
+
+ if ( mask == 0 )
+ return 0;
+ for ( h = height; h; --h )
+ { byte sbit = 0x80;
+ byte d = 0;
+ byte dbit = 0x80;
+ uint w;
+
+ for ( w = width; w; --w )
+ { if ( *sptr & sbit )
+ d += dbit;
+ if ( !(sbit >>= log2_scale) )
+ sbit = 0x80, sptr++;
+ if ( !(dbit >>= 1) )
+ dbit = 0x80, dptr++, d = 0;
+ }
+ if ( dbit != 0x80 )
+ *dptr++ = d;
+ for ( w = dskip; w != 0; --w )
+ *dptr++ = 0;
+ sptr += sskip;
+ }
+ return mask;
+}
diff --git a/pstoraster/gxccman.c b/pstoraster/gxccman.c
new file mode 100644
index 000000000..7be2c69ae
--- /dev/null
+++ b/pstoraster/gxccman.c
@@ -0,0 +1,768 @@
+/* Copyright (C) 1989, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxccman.c */
+/* Character cache management routines for Ghostscript library */
+#include "gx.h"
+#include "memory_.h"
+#include "gpcheck.h"
+#include "gserrors.h"
+#include "gsstruct.h"
+#include "gsbitops.h"
+#include "gsutil.h" /* for gs_next_ids */
+#include "gxfixed.h"
+#include "gxmatrix.h"
+#include "gzstate.h"
+#include "gzpath.h"
+#include "gxdevice.h"
+#include "gxdevmem.h"
+#include "gxchar.h"
+#include "gxfont.h"
+#include "gxfcache.h"
+#include "gxxfont.h"
+
+/* Define the descriptors for the cache structures. */
+private_st_cached_fm_pair();
+private_st_cached_fm_pair_elt();
+/*private_st_cached_char();*/ /* unused */
+private_st_cached_char_ptr(); /* unused */
+private_st_cached_char_ptr_elt();
+/* GC procedures */
+/* We do all the work in font_dir_enum/reloc_ptrs in gsfont.c. */
+/* See gxfcache.h for details. */
+private ENUM_PTRS_BEGIN(cc_ptr_enum_ptrs) return 0;
+} }
+private RELOC_PTRS_BEGIN(cc_ptr_reloc_ptrs) {
+} RELOC_PTRS_END
+
+/* Forward references */
+private gx_xfont *lookup_xfont_by_name(P6(gx_device *, gx_xfont_procs *, gs_font_name *, int, const cached_fm_pair *, const gs_matrix *));
+private cached_char *alloc_char(P2(gs_font_dir *, ulong));
+private cached_char *alloc_char_in_chunk(P2(gs_font_dir *, ulong));
+private void hash_remove_cached_char(P2(gs_font_dir *, uint));
+private void shorten_cached_char(P3(gs_font_dir *, cached_char *, uint));
+
+/* ====== Initialization ====== */
+
+/* Allocate and initialize the character cache elements of a font directory. */
+int
+gx_char_cache_alloc(gs_memory_t *mem, register gs_font_dir *pdir,
+ uint bmax, uint mmax, uint cmax, uint upper)
+{ /* Since we use open hashing, we must increase cmax somewhat. */
+ uint chsize = (cmax + (cmax >> 1)) | 31;
+ cached_fm_pair *mdata;
+ cached_char **chars;
+
+ /* Round up chsize to a power of 2. */
+ while ( chsize & (chsize + 1) )
+ chsize |= chsize >> 1;
+ chsize++;
+ mdata = gs_alloc_struct_array(mem, mmax, cached_fm_pair,
+ &st_cached_fm_pair_element,
+ "font_dir_alloc(mdata)");
+ chars = gs_alloc_struct_array(mem, chsize, cached_char *,
+ &st_cached_char_ptr_element,
+ "font_dir_alloc(chars)");
+ if ( mdata == 0 || chars == 0 )
+ { gs_free_object(mem, chars, "font_dir_alloc(chars)");
+ gs_free_object(mem, mdata, "font_dir_alloc(mdata)");
+ return_error(gs_error_VMerror);
+ }
+ pdir->fmcache.mmax = mmax;
+ pdir->fmcache.mdata = mdata;
+ pdir->ccache.memory = mem;
+ pdir->ccache.bmax = bmax;
+ pdir->ccache.cmax = cmax;
+ pdir->ccache.lower = upper / 10;
+ pdir->ccache.upper = upper;
+ pdir->ccache.table = chars;
+ pdir->ccache.table_mask = chsize - 1;
+ gx_char_cache_init(pdir);
+ return 0;
+}
+
+/* Initialize the character cache. */
+void
+gx_char_cache_init(register gs_font_dir *dir)
+{ int i;
+ cached_fm_pair *pair;
+ char_cache_chunk *cck =
+ (char_cache_chunk *)gs_malloc(1, sizeof(char_cache_chunk),
+ "initial_chunk");
+
+ dir->fmcache.msize = 0;
+ dir->fmcache.mnext = 0;
+ gx_bits_cache_chunk_init(cck, NULL, 0);
+ gx_bits_cache_init((gx_bits_cache *)&dir->ccache, cck);
+ dir->ccache.bspace = 0;
+ memset((char *)dir->ccache.table, 0,
+ (dir->ccache.table_mask + 1) * sizeof(cached_char *));
+ for ( i = 0, pair = dir->fmcache.mdata;
+ i < dir->fmcache.mmax; i++, pair++
+ )
+ { pair->index = i;
+ fm_pair_init(pair);
+ }
+}
+
+/* ====== Purging ====== */
+
+/* Purge from the character cache all entries selected by */
+/* a client-supplied procedure. */
+void
+gx_purge_selected_cached_chars(gs_font_dir *dir,
+ bool (*proc)(P2(cached_char *, void *)), void *proc_data)
+{ int chi;
+ int cmax = dir->ccache.table_mask;
+
+ for ( chi = 0; chi <= cmax; )
+ { cached_char *cc = dir->ccache.table[chi];
+ if ( cc != 0 && (*proc)(cc, proc_data) )
+ { hash_remove_cached_char(dir, chi);
+ gx_free_cached_char(dir, cc);
+ }
+ else
+ chi++;
+ }
+}
+
+/* ====== Font-level routines ====== */
+
+/* Add a font/matrix pair to the cache. */
+/* (This is only exported for gxccache.c.) */
+cached_fm_pair *
+gx_add_fm_pair(register gs_font_dir *dir, gs_font *font, const gs_uid *puid,
+ const gs_state *pgs)
+{ register cached_fm_pair *pair =
+ dir->fmcache.mdata + dir->fmcache.mnext;
+ cached_fm_pair *mend =
+ dir->fmcache.mdata + dir->fmcache.mmax;
+
+ if ( dir->fmcache.msize == dir->fmcache.mmax ) /* cache is full */
+ { /* Prefer an entry with num_chars == 0, if any. */
+ int count;
+ for ( count = dir->fmcache.mmax;
+ --count >= 0 && pair->num_chars != 0;
+ )
+ if ( ++pair == mend )
+ pair = dir->fmcache.mdata;
+ gs_purge_fm_pair(dir, pair, 0);
+ }
+ else
+ { /* Look for an empty entry. (We know there is one.) */
+ while ( !fm_pair_is_free(pair) )
+ if ( ++pair == mend )
+ pair = dir->fmcache.mdata;
+ }
+ dir->fmcache.msize++;
+ dir->fmcache.mnext = pair + 1 - dir->fmcache.mdata;
+ if ( dir->fmcache.mnext == dir->fmcache.mmax )
+ dir->fmcache.mnext = 0;
+ pair->font = font;
+ pair->UID = *puid;
+ /* The OSF/1 compiler doesn't like casting a pointer to */
+ /* a shorter int.... */
+ pair->hash = (uint)(ulong)pair % 549; /* scramble bits */
+ pair->mxx = pgs->char_tm.xx, pair->mxy = pgs->char_tm.xy;
+ pair->myx = pgs->char_tm.yx, pair->myy = pgs->char_tm.yy;
+ pair->num_chars = 0;
+ pair->xfont_tried = false;
+ pair->xfont = 0;
+ if_debug8('k', "[k]adding pair 0x%lx: font=0x%lx [%g %g %g %g] UID %ld, 0x%lx\n",
+ (ulong)pair, (ulong)font,
+ pair->mxx, pair->mxy, pair->myx, pair->myy,
+ (long)pair->UID.id, (ulong)pair->UID.xvalues);
+ return pair;
+}
+
+/* Look up the xfont for a font/matrix pair. */
+/* (This is only exported for gxccache.c.) */
+void
+gx_lookup_xfont(const gs_state *pgs, cached_fm_pair *pair, int encoding_index)
+{ gx_device *dev = gs_currentdevice(pgs);
+ gx_device *fdev = (*dev_proc(dev, get_xfont_device))(dev);
+ gs_font *font = pair->font;
+ gx_xfont_procs *procs = (*dev_proc(fdev, get_xfont_procs))(fdev);
+ gx_xfont *xf = 0;
+
+ /* We mustn't attempt to use xfonts for stroked characters, */
+ /* because such characters go outside their bounding box. */
+ if ( procs != 0 && font->PaintType == 0 )
+ { gs_matrix mat;
+
+ mat.xx = pair->mxx, mat.xy = pair->mxy;
+ mat.yx = pair->myx, mat.yy = pair->myy;
+ mat.tx = 0, mat.ty = 0;
+ /* xfonts can outlive their invocations, */
+ /* but restore purges them properly. */
+ pair->memory = pgs->memory;
+ if ( font->key_name.size != 0 )
+ xf = lookup_xfont_by_name(fdev, procs,
+ &font->key_name, encoding_index,
+ pair, &mat);
+#define font_name_eq(pfn1,pfn2)\
+ ((pfn1)->size == (pfn2)->size &&\
+ !memcmp((char *)(pfn1)->chars, (char *)(pfn2)->chars, (pfn1)->size))
+ if ( xf == 0 && font->font_name.size != 0 &&
+ /* Avoid redundant lookup */
+ !font_name_eq(&font->font_name, &font->key_name)
+ )
+ xf = lookup_xfont_by_name(fdev, procs,
+ &font->font_name, encoding_index,
+ pair, &mat);
+ if ( xf == 0 && font->FontType != ft_composite &&
+ uid_is_valid(&((gs_font_base *)font)->UID)
+ )
+ { /* Look for an original font with the same UID. */
+ gs_font_dir *pdir = font->dir;
+ gs_font *pfont;
+
+ for ( pfont = pdir->orig_fonts; pfont != 0;
+ pfont = pfont->next
+ )
+ { if ( pfont->FontType != ft_composite &&
+ uid_equal(&((gs_font_base *)pfont)->UID,
+ &((gs_font_base *)font)->UID) &&
+ pfont->key_name.size != 0 &&
+ !font_name_eq(&font->key_name,
+ &pfont->key_name)
+ )
+ { xf = lookup_xfont_by_name(fdev, procs,
+ &pfont->key_name,
+ encoding_index, pair, &mat);
+ if ( xf != 0 )
+ break;
+ }
+ }
+ }
+ }
+ pair->xfont = xf;
+}
+
+/* ------ Internal routines ------ */
+
+/* Purge from the caches all references to a given font/matrix pair, */
+/* or just characters that depend on its xfont. */
+#define cpair ((cached_fm_pair *)vpair)
+private bool
+purge_fm_pair_char(cached_char *cc, void *vpair)
+{ return cc_pair(cc) == cpair;
+}
+private bool
+purge_fm_pair_char_xfont(cached_char *cc, void *vpair)
+{ return cc_pair(cc) == cpair && cpair->xfont == 0 && !cc_has_bits(cc);
+}
+#undef cpair
+void
+gs_purge_fm_pair(gs_font_dir *dir, cached_fm_pair *pair, int xfont_only)
+{ if_debug2('k', "[k]purging pair 0x%lx%s\n",
+ (ulong)pair, (xfont_only ? " (xfont only)" : ""));
+ if ( pair->xfont != 0 )
+ { (*pair->xfont->common.procs->release)(pair->xfont,
+ pair->memory);
+ pair->xfont_tried = false;
+ pair->xfont = 0;
+ }
+ gx_purge_selected_cached_chars(dir,
+ (xfont_only ? purge_fm_pair_char_xfont :
+ purge_fm_pair_char),
+ pair);
+ if ( !xfont_only )
+ {
+#ifdef DEBUG
+ if ( pair->num_chars != 0 )
+ { lprintf1("Error in gs_purge_fm_pair: num_chars =%d\n",
+ pair->num_chars);
+ }
+#endif
+ fm_pair_set_free(pair);
+ dir->fmcache.msize--;
+ }
+}
+
+/* Look up an xfont by name. */
+/* The caller must already have done get_xfont_device to get the proper */
+/* device to pass as the first argument to lookup_font. */
+private gx_xfont *
+lookup_xfont_by_name(gx_device *fdev, gx_xfont_procs *procs,
+ gs_font_name *pfstr, int encoding_index, const cached_fm_pair *pair,
+ const gs_matrix *pmat)
+{ gx_xfont *xf;
+ if_debug5('k', "[k]lookup xfont %s [%g %g %g %g]\n",
+ pfstr->chars, pmat->xx, pmat->xy, pmat->yx, pmat->yy);
+ xf = (*procs->lookup_font)(fdev,
+ &pfstr->chars[0], pfstr->size,
+ encoding_index, &pair->UID,
+ pmat, pair->memory);
+ if_debug1('k', "[k]... xfont=0x%lx\n", (ulong)xf);
+ return xf;
+}
+
+/* ====== Character-level routines ====== */
+
+/*
+ * Allocate storage for caching a rendered character with possible
+ * oversampling and/or alpha. Return the cached_char if OK, 0 if too big.
+ * If the character is being oversampled, make the size decision
+ * on the basis of the final (scaled-down) size.
+ *
+ * The iwidth and iheight parameters include scaling up for oversampling
+ * (multiplication by 1 << pscale->{x,y}.)
+ * The depth parameter is the final number of alpha bits;
+ * depth <= x scale * y scale.
+ * If dev == NULL, this is an xfont-only entry.
+ * If dev != NULL, set up the memory device(s); in this case, if dev2 is
+ * not NULL, dev should be an alpha-buffer device with dev2 (an alpha
+ * device) as target.
+ */
+cached_char *
+gx_alloc_char_bits(gs_font_dir *dir, gx_device_memory *dev,
+ gx_device_memory *dev2, ushort iwidth, ushort iheight,
+ const gs_log2_scale_point *pscale, int depth)
+{ int log2_xscale = pscale->x;
+ int log2_yscale = pscale->y;
+ int log2_depth = depth >> 1; /* works for 1,2,4 */
+ uint nwidth_bits = (iwidth >> log2_xscale) << log2_depth;
+ ulong isize, icdsize;
+ uint iraster;
+ cached_char *cc;
+ gx_device_memory mdev;
+ gx_device_memory *pdev = dev;
+ gx_device_memory *pdev2;
+
+ if ( dev == NULL )
+ { mdev.memory = 0;
+ mdev.target = 0;
+ pdev = &mdev;
+ }
+ pdev2 = (dev2 == 0 ? pdev : dev2);
+
+ /* Compute the scaled-down bitmap size, and test against */
+ /* the maximum cachable character size. */
+
+ iraster = bitmap_raster(nwidth_bits);
+ if ( iraster != 0 && iheight >> log2_yscale > dir->ccache.upper / iraster )
+ { if_debug5('k', "[k]no cache bits: scale=%dx%d, raster/scale=%u, height/scale=%u, upper=%u\n",
+ 1 << log2_xscale, 1 << log2_yscale,
+ iraster, iheight, dir->ccache.upper);
+ return 0; /* too big */
+ }
+
+ /* Compute the actual bitmap size(s) and allocate the bits. */
+
+ if ( dev2 == 0 )
+ { /* Render to a full (possibly oversampled) bitmap; */
+ /* compress (if needed) when done. */
+ gs_make_mem_mono_device(pdev, pdev->memory, pdev->target);
+ pdev->width = iwidth;
+ pdev->height = iheight;
+ isize = gdev_mem_bitmap_size(pdev);
+ }
+ else
+ { /* Use an alpha-buffer device to compress as we go. */
+ gs_make_mem_alpha_device(dev2, dev2->memory, NULL, depth);
+ dev2->width = iwidth >> log2_xscale;
+ dev2->height = iheight >> log2_yscale;
+ gs_make_mem_abuf_device(dev, dev->memory, (gx_device *)dev2,
+ pscale, depth, 0);
+ dev->width = iwidth;
+ dev->height = 2 << log2_yscale;
+ isize = gdev_mem_bitmap_size(dev) +
+ gdev_mem_bitmap_size(dev2);
+ }
+ icdsize = isize + sizeof_cached_char;
+ cc = alloc_char(dir, icdsize);
+ if ( cc == 0 )
+ return 0;
+ if_debug4('k', "[k]adding char 0x%lx:%u(%u,%u)\n",
+ (ulong)cc, (uint)icdsize, iwidth, iheight);
+
+ /* Fill in the entry. */
+
+ cc_set_depth(cc, depth);
+ cc->xglyph = gx_no_xglyph;
+ /* Set the width and height to those of the device. */
+ /* Note that if we are oversampling without an alpha buffer. */
+ /* these are not the final unscaled dimensions. */
+ cc->width = pdev2->width;
+ cc->height = pdev2->height;
+ cc->shift = 0;
+ cc_set_raster(cc, gdev_mem_raster(pdev2));
+ cc_set_pair_only(cc, 0); /* not linked in yet */
+ cc->id = gx_no_bitmap_id;
+
+ /* Open the cache device(s). */
+
+ if ( dev2 )
+ { /* The second device is an alpha device that targets */
+ /* the real storage for the character. */
+ byte *bits = cc_bits(cc);
+ uint bsize = (uint)gdev_mem_bitmap_size(dev2);
+
+ memset(bits, 0, bsize);
+ dev2->base = bits;
+ (*dev_proc(dev2, open_device))((gx_device *)dev2);
+ dev->base = bits + bsize;
+ (*dev_proc(dev, open_device))((gx_device *)dev);
+ }
+ else if ( dev )
+ gx_open_cache_device(dev, cc);
+
+ return cc;
+}
+
+/* Open the cache device. */
+void
+gx_open_cache_device(gx_device_memory *dev, cached_char *cc)
+{ byte *bits = cc_bits(cc);
+
+ dev->width = cc->width;
+ dev->height = cc->height;
+ memset((char *)bits, 0, (uint)gdev_mem_bitmap_size(dev));
+ dev->base = bits;
+ (*dev_proc(dev, open_device))((gx_device *)dev); /* initialize */
+}
+
+/* Remove a character from the cache. */
+void
+gx_free_cached_char(gs_font_dir *dir, cached_char *cc)
+{ char_cache_chunk *cck = cc->chunk;
+
+ dir->ccache.chunks = cck;
+ dir->ccache.cnext = (byte *)cc - cck->data;
+ if ( cc_pair(cc) != 0 )
+ { /* might be allocated but not added to table yet */
+ cc_pair(cc)->num_chars--;
+ }
+ if_debug2('k', "[k]freeing char 0x%lx, pair=0x%lx\n",
+ (ulong)cc, (ulong)cc_pair(cc));
+ gx_bits_cache_free((gx_bits_cache *)&dir->ccache, &cc->head, cck);
+}
+
+/* Add a character to the cache */
+void
+gx_add_cached_char(gs_font_dir *dir, gx_device_memory *dev,
+ cached_char *cc, cached_fm_pair *pair, const gs_log2_scale_point *pscale)
+{ if_debug5('k', "[k]chaining char 0x%lx: pair=0x%lx, glyph=0x%lx, wmode=%d, depth=%d\n",
+ (ulong)cc, (ulong)pair, (ulong)cc->code,
+ cc->wmode, cc_depth(cc));
+ if ( dev != NULL )
+ { static const gs_log2_scale_point no_scale = { 0, 0 };
+ /* Close the device, to flush the alpha buffer if any. */
+ (*dev_proc(dev, close_device))((gx_device *)dev);
+ gx_add_char_bits(dir, cc,
+ (gs_device_is_abuf((gx_device *)dev) ?
+ &no_scale : pscale));
+ }
+ /* Add the new character to the hash table. */
+ { uint chi = chars_head_index(cc->code, pair);
+ while ( dir->ccache.table[chi &= dir->ccache.table_mask] != 0 )
+ chi++;
+ dir->ccache.table[chi] = cc;
+ cc_set_pair(cc, pair);
+ pair->num_chars++;
+ }
+}
+
+/* Adjust the bits of a newly-rendered character, by unscaling */
+/* and compressing or converting to alpha values if necessary. */
+void
+gx_add_char_bits(gs_font_dir *dir, cached_char *cc,
+ const gs_log2_scale_point *plog2_scale)
+{ int log2_x = plog2_scale->x, log2_y = plog2_scale->y;
+ uint raster = cc_raster(cc);
+ byte *bits = cc_bits(cc);
+ int depth = cc_depth(cc);
+ int log2_depth = depth >> 1; /* works for 1,2,4 */
+ uint nwidth_bits, nraster;
+ gs_int_rect bbox;
+
+#ifdef DEBUG
+ if ( cc->width % (1 << log2_x) != 0 ||
+ cc->height % (1 << log2_y) != 0
+ )
+ { lprintf4("size %d,%d not multiple of scale %d,%d!\n",
+ cc->width, cc->height,
+ 1 << log2_x, 1 << log2_y);
+ cc->width &= -1 << log2_x;
+ cc->height &= -1 << log2_y;
+ }
+#endif
+
+ /*
+ * Compute the bounding box before compressing.
+ * We may have to scan more bits, but this is a lot faster than
+ * compressing the white space. Note that all bbox values are
+ * in bits, not pixels.
+ */
+
+ bits_bounding_box(bits, cc->height, raster, &bbox);
+
+ /*
+ * If the character was oversampled, compress it now.
+ * In this case we know that log2_depth <= log2_x.
+ * If the character was not oversampled, or if we converted
+ * oversampling to alpha dynamically (using an alpha buffer
+ * intermediate device), log2_x and log2_y are both zero,
+ * but in the latter case we may still have depth > 1.
+ */
+
+ if ( (log2_x | log2_y) != 0 )
+ { if_debug5('k', "[k]compressing %dx%d by %dx%d to depth=%d\n",
+ cc->width, cc->height, 1 << log2_x, 1 << log2_y,
+ depth);
+ if ( gs_debug_c('K') )
+ debug_dump_bitmap(bits, raster, cc->height,
+ "[K]uncompressed bits");
+ /* Truncate/round the bbox to a multiple of the scale. */
+ { int scale_x = 1 << log2_x;
+ bbox.p.x &= -scale_x;
+ bbox.q.x = (bbox.q.x + scale_x - 1) & -scale_x;
+ }
+ { int scale_y = 1 << log2_y;
+ bbox.p.y &= -scale_y;
+ bbox.q.y = (bbox.q.y + scale_y - 1) & -scale_y;
+ }
+ cc->width = (bbox.q.x - bbox.p.x) >> log2_x;
+ cc->height = (bbox.q.y - bbox.p.y) >> log2_y;
+ nwidth_bits = cc->width << log2_depth;
+ nraster = bitmap_raster(nwidth_bits);
+ bits_compress_scaled(bits + raster * bbox.p.y, bbox.p.x,
+ cc->width << log2_x,
+ cc->height << log2_y,
+ raster,
+ bits, nraster, plog2_scale, log2_depth);
+ bbox.p.x >>= log2_x;
+ bbox.p.y >>= log2_y;
+ }
+ else
+ { /* No oversampling, just remove white space. */
+ const byte *from = bits + raster * bbox.p.y + (bbox.p.x >> 3);
+
+ cc->height = bbox.q.y - bbox.p.y;
+ /*
+ * We'd like to trim off left and right blank space,
+ * but currently we're only willing to move bytes, not bits.
+ * (If we ever want to do better, we must remember that
+ * we can only trim whole pixels, and a pixel may occupy
+ * more than one bit.)
+ */
+ bbox.p.x &= ~7; /* adjust to byte boundary */
+ bbox.p.x >>= log2_depth; /* bits => pixels */
+ bbox.q.x = (bbox.q.x + depth - 1) >> log2_depth; /* ditto */
+ cc->width = bbox.q.x - bbox.p.x;
+ nwidth_bits = cc->width << log2_depth;
+ nraster = bitmap_raster(nwidth_bits);
+ if ( bbox.p.x != 0 || nraster != raster )
+ { /* Move the bits down and over. */
+ byte *to = bits;
+ uint n = cc->height;
+ /* We'd like to move only
+ uint nbytes = (nwidth_bits + 7) >> 3;
+ * bytes per scan line, but unfortunately this drops
+ * the guaranteed zero padding at the end.
+ */
+
+ for ( ; n--; from += raster, to += nraster )
+ memmove(to, from, /*nbytes*/nraster);
+ }
+ else if ( bbox.p.y != 0 )
+ { /* Just move the bits down. */
+ memmove(bits, from, raster * cc->height);
+ }
+ }
+
+ /* Adjust the offsets to account for removed white space. */
+
+ cc->offset.x -= int2fixed(bbox.p.x);
+ cc->offset.y -= int2fixed(bbox.p.y);
+
+ /* Discard the memory device overhead that follows the bits, */
+ /* and any space reclaimed from unscaling or compression. */
+
+ cc_set_raster(cc, nraster);
+ { uint diff = round_down(cc->head.size - sizeof_cached_char -
+ nraster * cc->height,
+ align_cached_char_mod);
+
+ if ( diff >= sizeof(cached_char_head) )
+ { shorten_cached_char(dir, cc, diff);
+ if_debug2('K', "[K]shortening char 0x%lx by %u (adding)\n",
+ (ulong)cc, diff);
+ }
+ }
+
+ /* Assign a bitmap id. */
+
+ cc->id = gs_next_ids(1);
+}
+
+/* Purge from the caches all references to a given font. */
+void
+gs_purge_font_from_char_caches(gs_font_dir *dir, const gs_font *font)
+{ cached_fm_pair *pair = dir->fmcache.mdata;
+ int count = dir->fmcache.mmax;
+
+ if_debug1('k', "[k]purging font 0x%lx\n",
+ (ulong)font);
+ while ( count-- )
+ { if ( pair->font == font )
+ { if ( uid_is_valid(&pair->UID) )
+ { /* Keep the entry. */
+ pair->font = 0;
+ }
+ else
+ gs_purge_fm_pair(dir, pair, 0);
+ }
+ pair++;
+ }
+}
+
+/* ------ Internal routines ------ */
+
+/* Allocate data space for a cached character, adding a new chunk if needed. */
+private cached_char *
+alloc_char(gs_font_dir *dir, ulong icdsize)
+{ /* Try allocating at the current position first. */
+ cached_char *cc = alloc_char_in_chunk(dir, icdsize);
+
+ if ( cc == 0 )
+ { if ( dir->ccache.bspace < dir->ccache.bmax )
+ { /* Allocate another chunk. */
+ char_cache_chunk *cck_prev = dir->ccache.chunks;
+ char_cache_chunk *cck;
+ uint cksize = dir->ccache.bmax / 5 + 1;
+ uint tsize = dir->ccache.bmax - dir->ccache.bspace;
+ byte *cdata;
+
+ if ( cksize > tsize )
+ cksize = tsize;
+ if ( icdsize + sizeof(cached_char_head) > cksize )
+ { if_debug2('k', "[k]no cache bits: cdsize+head=%lu, cksize=%u\n",
+ icdsize + sizeof(cached_char_head),
+ cksize);
+ return 0; /* wouldn't fit */
+ }
+ cck = (char_cache_chunk *)gs_malloc(1, sizeof(*cck),
+ "char cache chunk");
+ if ( cck == 0 )
+ return 0;
+ cdata = (byte *)gs_malloc(cksize, 1,
+ "char cache chunk");
+ if ( cdata == 0 )
+ { gs_free((char *)cck, 1, sizeof(*cck),
+ "char cache chunk");
+ return 0;
+ }
+ gx_bits_cache_chunk_init(cck, cdata, cksize);
+ cck->next = cck_prev->next;
+ cck_prev->next = cck;
+ dir->ccache.bspace += cksize;
+ dir->ccache.chunks = cck;
+ }
+ else
+ { /* Cycle through existing chunks. */
+ char_cache_chunk *cck_init = dir->ccache.chunks;
+ char_cache_chunk *cck = cck_init;
+ while ( (dir->ccache.chunks = cck = cck->next) != cck_init )
+ { dir->ccache.cnext = 0;
+ cc = alloc_char_in_chunk(dir, icdsize);
+ if ( cc != 0 )
+ return cc;
+ }
+ }
+ dir->ccache.cnext = 0;
+ cc = alloc_char_in_chunk(dir, icdsize);
+ }
+ return cc;
+}
+
+/* Allocate a character in the current chunk. */
+private cached_char *
+alloc_char_in_chunk(gs_font_dir *dir, ulong icdsize)
+{ char_cache_chunk *cck = dir->ccache.chunks;
+ cached_char_head *cch;
+#define cc ((cached_char *)cch)
+ int code;
+
+ while ( (code = gx_bits_cache_alloc((gx_bits_cache *)&dir->ccache,
+ icdsize, &cch)) < 0
+ )
+ { if ( cch == 0 )
+ { /* Not enough room to allocate in this chunk. */
+ return 0;
+ }
+ { /* Free the character */
+ cached_fm_pair *pair = cc_pair(cc);
+
+ if ( pair != 0 )
+ { uint chi = chars_head_index(cc->code, pair);
+ while ( dir->ccache.table[chi & dir->ccache.table_mask] != cc )
+ chi++;
+ hash_remove_cached_char(dir, chi);
+ }
+ gx_free_cached_char(dir, cc);
+ }
+ }
+ cc->chunk = cck;
+ cc->loc = (byte *)cc - cck->data;
+ return cc;
+#undef cc
+}
+
+/* Remove the cached_char at a given index in the hash table. */
+/* In order not to slow down lookup, we relocate following entries. */
+private void
+hash_remove_cached_char(gs_font_dir *dir, uint chi)
+{ uint mask = dir->ccache.table_mask;
+ uint from = ((chi &= mask) + 1) & mask;
+ cached_char *cc;
+
+ dir->ccache.table[chi] = 0;
+ while ( (cc = dir->ccache.table[from]) != 0 )
+ { /* Loop invariants: chars[chi] == 0; */
+ /* chars[chi+1..from] != 0. */
+ uint fchi = chars_head_index(cc->code, cc_pair(cc));
+
+ /* If chi <= fchi < from, we relocate the character. */
+ /* Note that '<=' must take wraparound into account. */
+ if ( (chi < from ? chi <= fchi && fchi < from :
+ chi <= fchi || fchi < from)
+ )
+ { dir->ccache.table[chi] = cc;
+ dir->ccache.table[from] = 0;
+ chi = from;
+ }
+ from = (from + 1) & mask;
+ }
+}
+
+/* Shorten a cached character. */
+/* diff >= sizeof(cached_char_head). */
+private void
+shorten_cached_char(gs_font_dir *dir, cached_char *cc, uint diff)
+{ gx_bits_cache_shorten((gx_bits_cache *)&dir->ccache, &cc->head,
+ diff, cc->chunk);
+ if_debug2('K', "[K]shortening creates free block 0x%lx(%u)\n",
+ (ulong)((byte *)cc + cc->head.size), diff);
+}
diff --git a/pstoraster/gxchar.h b/pstoraster/gxchar.h
new file mode 100644
index 000000000..2f913641c
--- /dev/null
+++ b/pstoraster/gxchar.h
@@ -0,0 +1,158 @@
+/* Copyright (C) 1989, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxchar.h */
+/* Internal character definition for Ghostscript library */
+/* Requires gsmatrix.h, gxfixed.h */
+#include "gschar.h"
+
+/* The type of cached characters is opaque. */
+#ifndef cached_char_DEFINED
+# define cached_char_DEFINED
+typedef struct cached_char_s cached_char;
+#endif
+
+/* The type of cached font/matrix pairs is opaque. */
+#ifndef cached_fm_pair_DEFINED
+# define cached_fm_pair_DEFINED
+typedef struct cached_fm_pair_s cached_fm_pair;
+#endif
+
+/* The type of font objects is opaque. */
+#ifndef gs_font_DEFINED
+# define gs_font_DEFINED
+typedef struct gs_font_s gs_font;
+#endif
+
+/* The types of memory and null devices may be opaque. */
+#ifndef gx_device_memory_DEFINED
+# define gx_device_memory_DEFINED
+typedef struct gx_device_memory_s gx_device_memory;
+#endif
+#ifndef gx_device_null_DEFINED
+# define gx_device_null_DEFINED
+typedef struct gx_device_null_s gx_device_null;
+#endif
+
+/*
+ * Define the stack for composite fonts.
+ * If the current font is not composite, depth = -1.
+ * If the current font is composite, 0 <= depth <= max_font_depth.
+ * items[0] through items[depth] are occupied.
+ * items[0].font is the root font; items[0].index = 0.
+ * The root font must be composite, but may be of any map type.
+ * items[0..N-1] are modal composite fonts, for some N <= depth.
+ * items[N..depth-1] are non-modal composite fonts.
+ * items[depth] is a base (non-composite) font.
+ * Note that if depth >= 0, the font member of the graphics state
+ * for a base font BuildChar/Glyph is the same as items[depth].font.
+ */
+#define max_font_depth 5
+typedef struct gx_font_stack_item_s {
+ gs_font *font; /* font at this level */
+ uint index; /* index of this font in parent's */
+ /* Encoding */
+} gx_font_stack_item;
+typedef struct gx_font_stack_s {
+ int depth;
+ gx_font_stack_item items[1 + max_font_depth];
+} gx_font_stack;
+
+/* An enumeration object for string display. */
+typedef enum {
+ sws_none,
+ sws_cache, /* setcachedevice[2] */
+ sws_no_cache, /* setcharwidth */
+ sws_cache_width_only /* setcharwidth for xfont char */
+} show_width_status;
+struct gs_show_enum_s {
+ /* Following are set at creation time */
+ gs_state *pgs;
+ int level; /* save the level of pgs */
+ gs_const_string str;
+ float wcx, wcy; /* for widthshow */
+ gs_char wchr; /* ditto */
+ float ax, ay; /* for ashow */
+ bool add; /* true if a[width]show */
+ int do_kern; /* 1 if kshow, -1 if [x][y]show */
+ /* or cshow, 0 otherwise */
+ bool slow_show; /* [a][width]show or kshow or */
+ /* [x][y]show or cshow */
+ gs_char_path_mode charpath_flag;
+ int stringwidth_flag; /* 0 for show/charpath, */
+ /* 1 for stringwidth, */
+ /* -1 for cshow */
+ int can_cache; /* -1 if can't use cache at all, */
+ /* 0 if can read but not load, */
+ /* 1 if can read and load */
+ gs_int_rect ibox; /* int version of quick-check */
+ /* (inner) clipping box */
+ gs_int_rect obox; /* int version of (outer) clip box */
+ int ftx, fty; /* transformed font translation */
+ /* Following are updated dynamically */
+ gs_glyph (*encode_char)(P3(gs_show_enum *, gs_font *, gs_char *));
+ /* copied from font, */
+ /* except for glyphshow */
+ gs_log2_scale_point log2_suggested_scale; /* suggested scaling */
+ /* factors for oversampling, */
+ /* based on FontBBox and CTM */
+ gx_device_memory *dev_cache; /* cache device */
+ gx_device_memory *dev_cache2; /* underlying alpha memory device, */
+ /* if dev_cache is an alpha buffer */
+ gx_device_null *dev_null; /* null device for stringwidth */
+ uint index; /* index within string */
+ gs_char current_char; /* current char for render or move */
+ gs_glyph current_glyph; /* current glyph ditto */
+ gs_fixed_point wxy; /* width of current char */
+ /* in device coords */
+ gs_fixed_point origin; /* unrounded origin of current char */
+ /* in device coords, needed for */
+ /* charpath and WMode=1 */
+ cached_char *cc; /* being accumulated */
+ gs_point width; /* total width of string, set at end */
+ show_width_status width_status;
+ gs_log2_scale_point log2_current_scale;
+ gx_font_stack fstack;
+ int (*continue_proc)(P1(gs_show_enum *)); /* continuation procedure */
+};
+#define gs_show_enum_s_DEFINED
+#define private_st_gs_show_enum() /* in gschar.c */\
+ gs_private_st_composite(st_gs_show_enum, gs_show_enum, "gs_show_enum",\
+ show_enum_enum_ptrs, show_enum_reloc_ptrs)
+
+/* Cached character procedures (in gxccache.c and gxccman.c) */
+#ifndef gs_font_dir_DEFINED
+# define gs_font_dir_DEFINED
+typedef struct gs_font_dir_s gs_font_dir;
+#endif
+cached_char *
+ gx_alloc_char_bits(P7(gs_font_dir *, gx_device_memory *, gx_device_memory *, ushort, ushort, const gs_log2_scale_point *, int));
+void gx_open_cache_device(P2(gx_device_memory *, cached_char *));
+void gx_free_cached_char(P2(gs_font_dir *, cached_char *));
+void gx_add_cached_char(P5(gs_font_dir *, gx_device_memory *, cached_char *, cached_fm_pair *, const gs_log2_scale_point *));
+void gx_add_char_bits(P3(gs_font_dir *, cached_char *, const gs_log2_scale_point *));
+cached_char *
+ gx_lookup_cached_char(P5(const gs_font *, const cached_fm_pair *, gs_glyph, int, int));
+cached_char *
+ gx_lookup_xfont_char(P6(const gs_state *, cached_fm_pair *, gs_char, gs_glyph, const gx_xfont_callbacks *, int));
+int gx_image_cached_char(P2(gs_show_enum *, cached_char *));
diff --git a/pstoraster/gxcht.c b/pstoraster/gxcht.c
new file mode 100644
index 000000000..9cb059cee
--- /dev/null
+++ b/pstoraster/gxcht.c
@@ -0,0 +1,525 @@
+/* Copyright (C) 1993, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxcht.c */
+/* Color halftone rendering for Ghostscript imaging library */
+#include "gx.h"
+#include "gserrors.h"
+#include "gsutil.h" /* for id generation */
+#include "gsdcolor.h"
+#include "gxfixed.h"
+#include "gxmatrix.h"
+#include "gxdevice.h"
+#include "gxcmap.h"
+#include "gzstate.h"
+#include "gzht.h"
+
+/* Define the size of the tile buffer allocated on the stack. */
+#define tile_longs_LARGE 256
+#define tile_longs_SMALL 64
+#if arch_small_memory
+# define tile_longs_allocated tile_longs_SMALL
+# define tile_longs tile_longs_SMALL
+#else
+# define tile_longs_allocated tile_longs_LARGE
+# define tile_longs\
+ (gs_if_debug_c('.') ? tile_longs_SMALL : tile_longs_LARGE)
+#endif
+
+/* Define the colored halftone device color type. */
+private dev_color_proc_load(gx_dc_ht_colored_load);
+private dev_color_proc_fill_rectangle(gx_dc_ht_colored_fill_rectangle);
+private struct_proc_enum_ptrs(dc_ht_colored_enum_ptrs);
+private struct_proc_reloc_ptrs(dc_ht_colored_reloc_ptrs);
+const gx_device_color_procs
+ gx_dc_procs_ht_colored =
+ { gx_dc_ht_colored_load, gx_dc_ht_colored_fill_rectangle,
+ dc_ht_colored_enum_ptrs, dc_ht_colored_reloc_ptrs
+ };
+#undef gx_dc_type_ht_colored
+const gx_device_color_procs _ds *gx_dc_type_ht_colored = &gx_dc_procs_ht_colored;
+#define gx_dc_type_ht_colored (&gx_dc_procs_ht_colored)
+/* GC procedures */
+#define cptr ((gx_device_color *)vptr)
+private ENUM_PTRS_BEGIN(dc_ht_colored_enum_ptrs) return 0;
+ ENUM_PTR(0, gx_device_color, colors.colored.c_ht);
+ENUM_PTRS_END
+private RELOC_PTRS_BEGIN(dc_ht_colored_reloc_ptrs) {
+ RELOC_PTR(gx_device_color, colors.colored.c_ht);
+} RELOC_PTRS_END
+#undef cptr
+
+/* Forward references. */
+private void set_ht_colors(P6(gx_color_index [16], gx_strip_bitmap *[4],
+ const gx_device_color *, gx_device *, gx_ht_cache *[4], int));
+private void set_color_ht(P9(gx_strip_bitmap *, int, int, int, int, int, int,
+ const gx_color_index [16], const gx_strip_bitmap *[4]));
+
+/* Define a table for expanding 8x1 bits to 8x4. */
+private const bits32 far_data expand_8x1_to_8x4[256] = {
+#define x16(c)\
+ c+0, c+1, c+0x10, c+0x11, c+0x100, c+0x101, c+0x110, c+0x111,\
+ c+0x1000, c+0x1001, c+0x1010, c+0x1011, c+0x1100, c+0x1101, c+0x1110, c+0x1111
+ x16(0x00000000), x16(0x00010000), x16(0x00100000), x16(0x00110000),
+ x16(0x01000000), x16(0x01010000), x16(0x01100000), x16(0x01110000),
+ x16(0x10000000), x16(0x10010000), x16(0x10100000), x16(0x10110000),
+ x16(0x11000000), x16(0x11010000), x16(0x11100000), x16(0x11110000)
+#undef x16
+};
+
+/* Prepare to use a colored halftone, by loading the default cache. */
+private int
+gx_dc_ht_colored_load(gx_device_color *pdevc, const gs_state *pgs)
+{ gx_device_halftone *pdht = pgs->dev_ht;
+ gx_ht_order *porder = &pdht->components[0].corder;
+ gx_ht_cache *pcache = pgs->ht_cache;
+
+ if ( pcache->order.bits != porder->bits )
+ gx_ht_init_cache(pcache, porder);
+ /* Set the cache pointers in the default order. */
+ pdht->order.cache = porder->cache = pcache;
+ return 0;
+}
+
+/* Fill a rectangle with a colored halftone. */
+/* Note that we treat this as "texture" for RasterOp. */
+private int
+gx_dc_ht_colored_fill_rectangle(const gx_device_color *pdevc, int x, int y,
+ int w, int h, gx_device *dev, gs_logical_operation_t lop,
+ const gx_rop_source_t *source)
+{ ulong tbits[tile_longs_allocated];
+ const uint tile_bytes = tile_longs * size_of(long);
+ gx_strip_bitmap tiles;
+ const gx_device_halftone *pdht = pdevc->colors.colored.c_ht;
+ int depth = dev->color_info.depth;
+ int nplanes = dev->color_info.num_components;
+ gx_color_index colors[16];
+ gx_strip_bitmap *sbits[4];
+ gx_ht_cache *caches[4];
+ int code = 0;
+ int raster;
+ uint size_x;
+ int dw, dh;
+ int lw = pdht->lcm_width, lh = pdht->lcm_height;
+
+ if ( w <= 0 || h <= 0 )
+ return 0;
+ tiles.data = (byte *)tbits;
+ if ( pdht->components == 0 )
+ caches[0] = caches[1] = caches[2] = caches[3] = pdht->order.cache;
+ else
+ { gx_ht_order_component *pocs = pdht->components;
+ caches[0] = pocs[pdht->color_indices[0]].corder.cache;
+ caches[1] = pocs[pdht->color_indices[1]].corder.cache;
+ caches[2] = pocs[pdht->color_indices[2]].corder.cache;
+ caches[3] = pocs[pdht->color_indices[3]].corder.cache;
+ }
+ set_ht_colors(colors, sbits, pdevc, dev, caches, nplanes);
+ /* If the LCM of the plane cell sizes is smaller than */
+ /* the rectangle being filled, compute a single tile and */
+ /* let tile_rectangle do the replication. */
+ if ( (lw < w || lh < h) &&
+ (raster = bitmap_raster(lw * depth)) <= tile_bytes / lh
+ )
+ { tiles.raster = raster;
+ tiles.rep_width = tiles.size.x = lw;
+ tiles.rep_height = tiles.size.y = lh;
+ tiles.id = gs_next_ids(1);
+ tiles.rep_shift = tiles.shift = 0;
+ /* See below for why we need to cast bits. */
+ set_color_ht(&tiles, 0, 0, lw, lh,
+ depth, nplanes, colors,
+ (const gx_strip_bitmap **)sbits);
+ if ( source == NULL && lop_no_S_is_T(lop) )
+ return (*dev_proc(dev, strip_tile_rectangle))(dev, &tiles,
+ x, y, w, h,
+ gx_no_color_index, gx_no_color_index,
+ pdevc->phase.x, pdevc->phase.y);
+ if ( source == NULL )
+ source = &gx_rop_no_source;
+ return (*dev_proc(dev, strip_copy_rop))(dev, source->sdata,
+ source->sourcex, source->sraster, source->id,
+ (source->use_scolors ? source->scolors : NULL),
+ &tiles, NULL,
+ x, y, w, h,
+ pdevc->phase.x, pdevc->phase.y,
+ rop3_know_S_0(lop));
+ }
+ tiles.id = gx_no_bitmap_id;
+ size_x = w * depth;
+ raster = bitmap_raster(size_x);
+ if ( raster > tile_bytes )
+ { /* We can't even do an entire line at once. */
+ dw = tile_bytes * 8 / depth;
+ size_x = dw * depth;
+ raster = bitmap_raster(size_x);
+ dh = 1;
+ }
+ else
+ { /* Do as many lines as will fit. */
+ dw = w;
+ dh = tile_bytes / raster;
+ if ( dh > h ) dh = h;
+ }
+ /* Now the tile will definitely fit. */
+ tiles.raster = raster;
+ tiles.rep_width = tiles.size.x = size_x / depth;
+ tiles.rep_shift = tiles.shift = 0;
+ while ( w )
+ { int cy = y, ch = dh, left = h;
+ tiles.rep_height = tiles.size.y = ch;
+ for ( ; ; )
+ { /* The cast in the following statement is bogus, */
+ /* but some compilers won't accept an array type, */
+ /* and won't accept the ** type without a cast. */
+ set_color_ht(&tiles, x, cy, dw, ch,
+ depth, nplanes, colors,
+ (const gx_strip_bitmap **)sbits);
+ if ( lop_no_S_is_T(lop) )
+ { code = (*dev_proc(dev, copy_color))(dev,
+ tiles.data, 0, raster,
+ gx_no_bitmap_id, x, cy, dw, ch);
+ }
+ else
+ { gs_logical_operation_t lop_st = rop3_swap_S_T(lop);
+ code = (*dev_proc(dev, strip_copy_rop))(dev,
+ tiles.data, 0, raster,
+ gx_no_bitmap_id,
+ NULL,
+ NULL,
+ pdevc->colors.binary.color /*arb*/,
+ x, cy, dw, ch, 0, 0,
+ rop3_know_T_0(lop_st));
+ }
+ if ( code < 0 )
+ return code;
+ if ( !(left -= ch) )
+ break;
+ cy += ch;
+ if ( ch > left )
+ tiles.rep_height = tiles.size.y = ch = left;
+ }
+ if ( !(w -= dw) )
+ break;
+ x += dw;
+ if ( dw > w)
+ dw = w, tiles.rep_width = tiles.size.x = size_x / depth;
+ }
+ return code;
+}
+
+/*
+ * We construct color halftone tiles out of 3 or 4 "planes".
+ * Each plane specifies halftoning for one component (R/G/B or C/M/Y/K).
+ */
+
+/* Set up the colors and the individual plane halftone bitmaps. */
+private void
+set_ht_colors(gx_color_index colors[16], gx_strip_bitmap *sbits[4],
+ const gx_device_color *pdc, gx_device *dev, gx_ht_cache *caches[4],
+ int nplanes)
+{ gx_color_value v0[4], v1[4];
+ static const ulong no_bitmap_data[] =
+ { 0, 0, 0, 0, 0, 0, 0, 0 };
+ static gx_strip_bitmap no_bitmap =
+ { 0, sizeof(ulong), { sizeof(ulong) * 8, countof(no_bitmap_data) },
+ gx_no_bitmap_id, 1, 1, 0, 0
+ };
+ gx_color_value max_color = dev->color_info.dither_colors - 1;
+ no_bitmap.data = (byte *)no_bitmap_data; /* actually const */
+#define cb(i) pdc->colors.colored.c_base[i]
+#define cl(i) pdc->colors.colored.c_level[i]
+#define set_plane_color(i)\
+{ uint q = cb(i);\
+ uint r = cl(i);\
+ v0[i] = fractional_color(q, max_color);\
+ if ( r == 0 )\
+ v1[i] = v0[i], sbits[i] = &no_bitmap;\
+ else\
+ v1[i] = fractional_color(q+1, max_color),\
+ sbits[i] = &gx_render_ht(caches[i], r)->tiles;\
+}
+#define map8(m)\
+ m(0, v0[0], v0[1], v0[2]); m(1, v1[0], v0[1], v0[2]);\
+ m(2, v0[0], v1[1], v0[2]); m(3, v1[0], v1[1], v0[2]);\
+ m(4, v0[0], v0[1], v1[2]); m(5, v1[0], v0[1], v1[2]);\
+ m(6, v0[0], v1[1], v1[2]); m(7, v1[0], v1[1], v1[2])
+ set_plane_color(0);
+ set_plane_color(1);
+ set_plane_color(2);
+ if ( nplanes == 3 )
+ { gx_color_value alpha = pdc->colors.colored.alpha;
+ if ( alpha == gx_max_color_value )
+ {
+#ifdef DEBUG
+# define map1(r, g, b) gx_map_rgb_color(dev, r, g, b)
+#else
+ dev_proc_map_rgb_color((*map)) =
+ dev_proc(dev, map_rgb_color);
+# define map1(r, g, b) (*map)(dev, r, g, b)
+#endif
+#define mapc(i, r, g, b)\
+ colors[i] = map1(r, g, b)
+ map8(mapc);
+#undef map1
+#undef mapc
+ }
+ else
+ {
+#ifdef DEBUG
+# define map1(r, g, b) gx_map_rgb_alpha_color(dev, r, g, b, alpha)
+#else
+ dev_proc_map_rgb_alpha_color((*map)) =
+ dev_proc(dev, map_rgb_alpha_color);
+# define map1(r, g, b) (*map)(dev, r, g, b, alpha)
+#endif
+#define mapc(i, r, g, b)\
+ colors[i] = map1(r, g, b)
+ map8(mapc);
+#undef map1
+#undef mapc
+ }
+ }
+ else
+ {
+#ifdef DEBUG
+# define map1(r, g, b, w) gx_map_cmyk_color(dev, r, g, b, w)
+#else
+ dev_proc_map_cmyk_color((*map)) =
+ dev_proc(dev, map_cmyk_color);
+# define map1(r, g, b, w) (*map)(dev, r, g, b, w)
+#endif
+ set_plane_color(3);
+#define mapc(i, r, g, b)\
+ colors[i] = map1(r, g, b, v0[3]);\
+ colors[i+8] = map1(r, g, b, v1[3])
+ map8(mapc);
+#undef map1
+#undef mapc
+ }
+#undef map8
+#undef set_plane_color
+#undef cb
+#undef cl
+}
+
+/* Render the combined halftone. */
+private void
+set_color_ht(
+ gx_strip_bitmap *ctiles, /* the output tile; data, raster, size are set */
+ int px, /* the initial phase of the output tile */
+ int py,
+ int w, /* how much of the tile to set */
+ int h,
+ int depth, /* depth of tile (4, 8, 16, 24, 32) */
+ int nplanes, /* # of source planes, 3 or 4 */
+ const gx_color_index colors[16], /* the actual colors for the tile, */
+ /* actually [1 << nplanes] */
+ const gx_strip_bitmap *sbits[4] /* the bitmaps for the planes, */
+ /* actually [nplanes] */
+)
+{ /* Note that the planes are specified in the order RGB or CMYK, but */
+ /* the indices used for the internal colors array are BGR or KYMC. */
+
+ int x, y;
+ struct tile_cursor_s {
+ int tile_shift; /* X shift per copy of tile */
+ int xoffset;
+ int xshift;
+ uint xbytes;
+ int xbits;
+ const byte *row;
+ const byte *tdata;
+ uint raster;
+ const byte *data;
+ int bit_shift;
+ } cursor[4];
+ int dbytes = depth >> 3;
+ uint dest_raster = ctiles->raster;
+ byte *dest_row =
+ ctiles->data + dest_raster * (h - 1) + (w * depth) / 8;
+ int endx = w + px;
+
+ if_debug6('h',
+ "[h]color_ht: x=%d y=%d w=%d h=%d nplanes=%d depth=%d\n",
+ px, py, w, h, nplanes, depth);
+
+ /* Do one-time cursor initialization. */
+ { int lasty = h - 1 + py;
+#define set_start(i, c, btile)\
+{ int tw = btile->size.x;\
+ int bx = ((c.tile_shift = btile->shift) == 0 ? endx :\
+ endx + lasty / btile->size.y * c.tile_shift) % tw;\
+ int by = lasty % btile->size.y;\
+ c.xoffset = bx >> 3;\
+ c.xshift = 8 - (bx & 7);\
+ c.xbytes = (tw - 1) >> 3;\
+ c.xbits = ((tw - 1) & 7) + 1;\
+ c.tdata = btile->data;\
+ c.raster = btile->raster;\
+ c.row = c.tdata + by * c.raster;\
+ if_debug5('h', "[h]plane %d: size=%d,%d bx=%d by=%d\n",\
+ i, tw, btile->size.y, bx, by);\
+}
+ set_start(0, cursor[0], sbits[0]);
+ set_start(1, cursor[1], sbits[1]);
+ set_start(2, cursor[2], sbits[2]);
+ if ( nplanes == 4 )
+ set_start(3, cursor[3], sbits[3]);
+#undef set_start
+ }
+
+ /* Now compute the actual tile. */
+ for ( y = h; ; dest_row -= dest_raster )
+ { byte *dest = dest_row;
+#define set_row(c)\
+ { c.data = c.row + c.xoffset;\
+ c.bit_shift = c.xshift;\
+ }
+ set_row(cursor[0]);
+ set_row(cursor[1]);
+ set_row(cursor[2]);
+ if ( nplanes == 4 )
+ { set_row(cursor[3]);
+ }
+#undef set_row
+ --y;
+ for ( x = w; x > 0; )
+ { bits32 indices;
+ int nx, i;
+ register uint bits;
+/* Get the next byte's worth of bits. Note that there may be */
+/* excess bits set beyond the 8th. */
+#define next_bits(c)\
+{ if ( c.data > c.row )\
+ { bits = ((c.data[-1] << 8) | *c.data) >> c.bit_shift;\
+ c.data--;\
+ }\
+ else\
+ { bits = *c.data >> c.bit_shift;\
+ c.data += c.xbytes;\
+ if ( (c.bit_shift -= c.xbits) < 0 )\
+ { bits |= *c.data << -c.bit_shift;\
+ c.bit_shift += 8;\
+ }\
+ else\
+ { bits |= ((c.data[-1] << 8) | *c.data) >> c.bit_shift;\
+ c.data--;\
+ }\
+ }\
+}
+ if ( nplanes == 4 )
+ { next_bits(cursor[3]);
+ indices = expand_8x1_to_8x4[bits & 0xff] << 1;
+ }
+ else
+ indices = 0;
+ next_bits(cursor[2]);
+ indices = (indices | expand_8x1_to_8x4[bits & 0xff]) << 1;
+ next_bits(cursor[1]);
+ indices = (indices | expand_8x1_to_8x4[bits & 0xff]) << 1;
+ next_bits(cursor[0]);
+ indices |= expand_8x1_to_8x4[bits & 0xff];
+#undef next_bits
+ nx = min(x, 8);
+ x -= nx;
+ switch ( dbytes )
+ {
+ case 0: /* 4 */
+ for ( i = nx; --i >= 0; indices >>= 4 )
+ { byte tcolor =
+ (byte)colors[(uint)indices & 0xf];
+ if ( (x + i) & 1 )
+ *--dest = tcolor;
+ else
+ *dest = (*dest & 0xf) + (tcolor << 4);
+ }
+ break;
+ case 4: /* 32 */
+ for ( i = nx; --i >= 0; indices >>= 4 )
+ { gx_color_index tcolor =
+ colors[(uint)indices & 0xf];
+ dest -= 4;
+ dest[3] = (byte)tcolor;
+ dest[2] = (byte)(tcolor >> 8);
+ tcolor >>= 16;
+ dest[1] = (byte)tcolor;
+ dest[0] = (byte)((uint)tcolor >> 8);
+ }
+ break;
+ case 3: /* 24 */
+ for ( i = nx; --i >= 0; indices >>= 4 )
+ { gx_color_index tcolor =
+ colors[(uint)indices & 0xf];
+ dest -= 3;
+ dest[2] = (byte)tcolor;
+ dest[1] = (byte)((uint)tcolor >> 8);
+ tcolor >>= 16;
+ dest[0] = (byte)((uint)tcolor >> 8);
+ }
+ break;
+ case 2: /* 16 */
+ for ( i = nx; --i >= 0; indices >>= 4 )
+ { uint tcolor =
+ (uint)colors[(uint)indices & 0xf];
+ dest -= 2;
+ dest[1] = (byte)tcolor;
+ dest[0] = (byte)(tcolor >> 8);
+ }
+ break;
+ case 1: /* 8 */
+ for ( i = nx; --i >= 0; indices >>= 4 )
+ *--dest = (byte)colors[(uint)indices & 0xf];
+ break;
+ }
+ }
+ if ( y == 0 )
+ break;
+
+#define step_row(c, i)\
+ if ( c.row > c.tdata )\
+ c.row -= c.raster;\
+ else /* wrap around to end of tile, taking shift into account */\
+ { c.row += c.raster * (sbits[i]->size.y - 1);\
+ if ( c.tile_shift )\
+ { if ( (c.xshift += c.tile_shift) >= 8 )\
+ { if ( (c.xoffset -= c.xshift >> 3) < 0 )\
+ { /* wrap around in X */\
+ int bx = c.xoffset + sbits[i]->size.x;\
+ c.xoffset = bx >> 3;\
+ c.xshift = 8 - (bx & 7);\
+ }\
+ else\
+ c.xshift &= 7;\
+ }\
+ }\
+ }
+
+ step_row(cursor[0], 0);
+ step_row(cursor[1], 1);
+ step_row(cursor[2], 2);
+ if ( nplanes == 4)
+ step_row(cursor[3], 3);
+#undef step_row
+ }
+}
diff --git a/pstoraster/gxcindex.h b/pstoraster/gxcindex.h
new file mode 100644
index 000000000..bfbc00540
--- /dev/null
+++ b/pstoraster/gxcindex.h
@@ -0,0 +1,101 @@
+/* Copyright (C) 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxcindex.h */
+/* Define the device color index type and macros */
+/* Requires gxbitmap.h. */
+
+#ifndef gxcindex_INCLUDED
+# define gxcindex_INCLUDED
+
+/*
+ * Internally, a (pure) device color is represented by opaque values of
+ * type gx_color_index, which are tied to the specific device. The driver
+ * maps between these values and RGB[alpha] or CMYK values. In this way,
+ * the driver can convert RGB values to its most natural color representation,
+ * and have the graphics library cache the result.
+ */
+
+/* Define the type for device color indices. */
+typedef unsigned long gx_color_index;
+#define arch_log2_sizeof_color_index arch_log2_sizeof_long
+#define arch_sizeof_color_index arch_sizeof_long
+
+/* Define the 'transparent' color index. */
+#define gx_no_color_index_value (-1) /* no cast -> can be used in #if */
+
+/* The SGI C compiler provided with Irix 5.2 gives error messages */
+/* if we use the proper definition of gx_no_color_index: */
+/*#define gx_no_color_index ((gx_color_index)gx_no_color_index_value)*/
+/* Instead, we must spell out the typedef: */
+#define gx_no_color_index ((unsigned long)gx_no_color_index_value)
+
+/*
+ * Define macros for accumulating a scan line of a colored image.
+ * The usage is as follows:
+ * declare_line_accum(line, bpp, xo);
+ * for ( x = xo; x < xe; ++x )
+ * { << compute color at x >>
+ * line_accum(color, bpp);
+ * }
+ * line_accum_copy(dev, line, bpp, xo, xe, raster, y);
+ * This code must be enclosed in { }, since declare_line_accum declares
+ * variables.
+ *
+ * Note that declare_line_accum declares the variables l_dst, l_bits, l_shift,
+ * and l_xprev. Other code in the loop may use these variables.
+ */
+#define declare_line_accum(line, bpp, xo)\
+ byte *l_dst = (line);\
+ uint l_bits = 0;\
+ int l_shift = 8 - (bpp);\
+ int l_xprev = (xo)
+#define line_accum(color, bpp)\
+ switch ( (bpp) >> 3 )\
+ {\
+ case 0:\
+ l_bits += (uint)((color) << l_shift);\
+ if ( (l_shift -= (bpp)) < 0 )\
+ *l_dst++ = (byte)l_bits, l_bits = 0,\
+ l_shift += 8;\
+ break;\
+ case 4: *l_dst++ = (byte)((color) >> 24);\
+ case 3: *l_dst++ = (byte)((color) >> 16);\
+ case 2: *l_dst++ = (byte)((color) >> 8);\
+ case 1: *l_dst++ = (byte)(color);\
+ }
+#define line_accum_store(bpp)\
+ if ( l_shift != 8 - (bpp) )\
+ *l_dst = (byte)l_bits
+#define line_accum_copy(dev, line, bpp, xo, xe, raster, y)\
+ if ( (xe) > l_xprev )\
+ { int code;\
+ line_accum_store(bpp);\
+ code = (*dev_proc(dev, copy_color))\
+ (dev, line, l_xprev - (xo), raster,\
+ gx_no_bitmap_id, l_xprev, y, (xe) - l_xprev, 1);\
+ if ( code < 0 )\
+ return code;\
+ }
+
+#endif /* gxcindex_INCLUDED */
diff --git a/pstoraster/gxclbits.c b/pstoraster/gxclbits.c
new file mode 100644
index 000000000..b246afe2e
--- /dev/null
+++ b/pstoraster/gxclbits.c
@@ -0,0 +1,593 @@
+/* Copyright (C) 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxclbits.c */
+/* Halftone and bitmap writing for command lists */
+#include "memory_.h"
+#include "gx.h"
+#include "gpcheck.h"
+#include "gserrors.h"
+#include "gsbitops.h"
+#include "gxdevice.h"
+#include "gxdevmem.h" /* must precede gxcldev.h */
+#include "gxcldev.h"
+
+/*
+ * Define when, if ever, to write character bitmaps in all bands.
+ * Set this to:
+ * 0 to always write in all bands;
+ * N to write in all bands when the character has been seen in N+1
+ * bands on a page;
+ * max_ushort to never write in all bands.
+ */
+#define CHAR_ALL_BANDS_COUNT max_ushort
+
+/* ------ Writing ------ */
+
+/*
+ * Put a bitmap in the buffer, compressing if appropriate.
+ * pcls == 0 means put the bitmap in all bands.
+ * Return <0 if error, otherwise the compression method.
+ * A return value of gs_error_limitcheck means that the bitmap was too big
+ * to fit in the command reading buffer.
+ * Note that this leaves room for the command and initial arguments,
+ * but doesn't fill them in.
+ */
+int
+cmd_put_bits(gx_device_clist_writer *cldev, gx_clist_state *pcls,
+ const byte *data, uint width_bits, uint height, uint raster, int op_size,
+ int compression_mask, byte **pdp, uint *psize)
+{ uint full_raster = bitmap_raster(width_bits);
+ uint short_raster = (width_bits + 7) >> 3;
+ uint full_size =
+ (height <= 1 ? short_raster * height :
+ full_raster * (height - 1) + short_raster);
+ uint uncompressed_size =
+ (height <= 1 ? full_size : full_raster * height);
+ uint short_size =
+ (short_raster <= cmd_max_short_width_bytes ?
+ short_raster * height : full_size);
+ byte *dp;
+ int compress = 0;
+
+ /* See if compressing the bits is possible and worthwhile. */
+ /* Currently we can't compress if the compressed data */
+ /* won't fit in the command reading buffer, or if the */
+ /* decompressed data won't fit in the buffer and */
+ /* decompress_elsewhere isn't set. */
+ if ( short_size >= 50 &&
+ (compression_mask & ~decompress_elsewhere) != 0 &&
+ (uncompressed_size <= cbuf_size ||
+ (compression_mask & decompress_elsewhere) != 0)
+ )
+ { stream_cursor_read r;
+ byte *wbase;
+ stream_cursor_write w;
+ int status;
+ uint wcount;
+
+ *psize = op_size + uncompressed_size;
+ if ( pcls != 0 )
+ { set_cmd_put_op(dp, cldev, pcls, 0, *psize);
+ }
+ else
+ { set_cmd_put_all_op(dp, cldev, 0, *psize);
+ }
+ cmd_uncount_op(0, *psize);
+ wbase = dp + (op_size - 1);
+ r.ptr = data - 1;
+ r.limit = r.ptr + uncompressed_size;
+ w.ptr = wbase;
+ w.limit = w.ptr + uncompressed_size;
+ if ( compression_mask & (1 << cmd_compress_cfe) )
+ { /* Try CCITTFax compression. */
+ stream_CFE_state sstate;
+ clist_cfe_init(&sstate, width_bits);
+ status =
+ (*s_CFE_template.process)
+ ((stream_state *)&sstate, &r, &w, true);
+ (*s_CFE_template.release)((stream_state *)&sstate);
+ compress = cmd_compress_cfe;
+ }
+ else if ( compression_mask & (1 << cmd_compress_rle) )
+ { /* Try RLE compression. */
+ stream_RLE_state sstate;
+ clist_rle_init(&sstate);
+ status =
+ (*s_RLE_template.process)
+ ((stream_state *)&sstate, &r, &w, true);
+ compress = cmd_compress_rle;
+ }
+ if ( compress != 0 && status == 0 &&
+ (wcount = w.ptr - wbase) <= short_size >> 1 &&
+ wcount <= cbuf_size
+ )
+ { /* Use compressed representation. */
+ cmd_shorten_list_op(cldev,
+ (pcls ? &pcls->list : &cldev->all_band_list),
+ uncompressed_size - wcount);
+ *psize = op_size + wcount;
+ goto out;
+ }
+ if ( uncompressed_size > cbuf_size )
+ { cmd_shorten_list_op(cldev,
+ (pcls ? &pcls->list : &cldev->all_band_list),
+ *psize);
+ return gs_error_limitcheck;
+ }
+ if ( uncompressed_size != short_size )
+ { cmd_shorten_list_op(cldev,
+ (pcls ? &pcls->list : &cldev->all_band_list),
+ uncompressed_size - short_size);
+ *psize = op_size + short_size;
+ }
+ compress = 0;
+ }
+ else if ( full_size > cbuf_size )
+ return gs_error_limitcheck;
+ else
+ { *psize = op_size + short_size;
+ if ( pcls != 0 )
+ { set_cmd_put_op(dp, cldev, pcls, 0, *psize);
+ }
+ else
+ { set_cmd_put_all_op(dp, cldev, 0, *psize);
+ }
+ cmd_uncount_op(0, *psize);
+ }
+ if ( short_raster > cmd_max_short_width_bytes &&
+ !(compression_mask & decompress_spread)
+ )
+ short_raster = full_raster; /* = short_size / height */
+ bytes_copy_rectangle(dp + op_size, short_raster, data, raster,
+ short_raster, height);
+out: *pdp = dp;
+ return compress;
+}
+
+/* Add a command to set the tile size and depth. */
+private int
+cmd_put_tile_params(gx_device_clist_writer *cldev, const gx_strip_bitmap *tile,
+ int depth)
+{ int tcsize = 2 + cmd_size_w(tile->size.x) + cmd_size_w(tile->size.y) +
+ cmd_size_w(tile->rep_width) + cmd_size_w(tile->rep_height) +
+ cmd_size_w(tile->rep_shift);
+ byte *dp;
+
+ set_cmd_put_all_op(dp, cldev, cmd_opv_set_tile_size, tcsize);
+ dp[1] = depth;
+ dp += 2;
+ dp = cmd_put_w(tile->size.x, dp);
+ dp = cmd_put_w(tile->size.y, dp);
+ dp = cmd_put_w(tile->rep_width, dp);
+ dp = cmd_put_w(tile->rep_height, dp);
+ cmd_put_w(tile->rep_shift, dp);
+ return 0;
+}
+
+/* Add a command to set the tile index. */
+/* This is a relatively high-frequency operation, so we make it a macro. */
+/* Note that it may do a 'return' with an error code. */
+#define cmd_put_tile_index_inline(cldev, pcls, index)\
+ { int idelta = (index) - (pcls)->tile_index + 8; byte *dp;\
+ if ( !(idelta & ~15) )\
+ { set_cmd_put_op(dp, cldev, pcls, cmd_op_delta_tile_index + idelta, 1); }\
+ else\
+ { set_cmd_put_op(dp, cldev, pcls, cmd_op_set_tile_index + ((index) >> 8), 2);\
+ dp[1] = (index) & 0xff;\
+ if_debug2('L', "[L]writing index=%u, offset=%lu\n",\
+ index, cldev->tile_table[index].offset);\
+ }\
+ }
+
+/* Add commands to represent a halftone order. */
+/****** NOT USED YET ******/
+int
+cmd_put_ht_order(gx_device_clist_writer *cldev, const gx_ht_order *porder)
+{ long offset;
+ byte command[max(cmd_largest_size - 1, 100)];
+ byte *cp;
+ uint len;
+ byte *dp;
+ uint i, n;
+
+ /* Put out the order parameters. */
+ /****** SET offset ******/
+ offset = 0; /****** bogus, to pacify compilers ******/
+ cp = cmd_put_w(offset, command); /****** WRONG ******/
+ cp = cmd_put_w(porder->width, cp);
+ cp = cmd_put_w(porder->height, cp);
+ cp = cmd_put_w(porder->raster, cp);
+ cp = cmd_put_w(porder->shift, cp);
+ cp = cmd_put_w(porder->num_levels, cp);
+ cp = cmd_put_w(porder->num_bits, cp);
+ len = cp - command;
+ set_cmd_put_all_op(dp, cldev, cmd_opv_set_ht_size, len + 1);
+ memcpy(dp + 1, command, len);
+
+ /* Put out the levels array. */
+#define nlevels ((sizeof(command) - 2) / sizeof(*porder->levels))
+ for ( i = 0; i < porder->num_levels; i += n )
+ { n = porder->num_levels - i;
+ if ( n > nlevels )
+ n = nlevels;
+ set_cmd_put_all_op(dp, cldev, cmd_opv_set_ht_data,
+ 2 + n * sizeof(*porder->levels));
+ dp[1] = n;
+ memcpy(dp + 2, porder->levels + i, n * sizeof(*porder->levels));
+ }
+#undef nlevels
+
+ /* Put out the bits array. */
+#define nbits ((sizeof(command) - 2) / sizeof(*porder->bits))
+ for ( i = 0; i < porder->num_bits; i += n )
+ { n = porder->num_bits - i;
+ if ( n > nbits )
+ n = nbits;
+ set_cmd_put_all_op(dp, cldev, cmd_opv_set_ht_data,
+ 2 + n * sizeof(*porder->bits));
+ dp[1] = n;
+ memcpy(dp + 2, porder->bits + i, n * sizeof(*porder->bits));
+ }
+#undef nbits
+
+ return 0;
+}
+
+/* ------ Tile cache management ------ */
+
+/* We want consecutive ids to map to consecutive hash slots if possible, */
+/* so we can use a delta representation when setting the index. */
+#define tile_id_hash(id) (id)
+#define tile_hash_next(index) ((index) + 413) /* arbitrary large odd # */
+typedef struct tile_loc_s {
+ uint index;
+ tile_slot *tile;
+} tile_loc;
+
+/* Look up a tile or character in the cache. If found, set the index and */
+/* pointer; if not, set the index to the insertion point. */
+private bool
+clist_find_bits(gx_device_clist_writer *cldev, gx_bitmap_id id, tile_loc *ploc)
+{ uint index = tile_id_hash(id);
+ const tile_hash *table = cldev->tile_table;
+ uint mask = cldev->tile_hash_mask;
+ ulong offset;
+
+ for ( ; (offset = table[index &= mask].offset) != 0;
+ index = tile_hash_next(index)
+ )
+ { tile_slot *tile = (tile_slot *)(cldev->data + offset);
+ if ( tile->id == id )
+ { ploc->index = index;
+ ploc->tile = tile;
+ return true;
+ }
+ }
+ ploc->index = index;
+ return false;
+}
+
+/* Delete a tile from the cache. */
+private void
+clist_delete_tile(gx_device_clist_writer *cldev, tile_slot *slot)
+{ tile_hash *table = cldev->tile_table;
+ uint mask = cldev->tile_hash_mask;
+ uint index = slot->index;
+ ulong offset;
+
+ if_debug2('L', "[L]deleting index=%u, offset=%lu\n",
+ index, (ulong)((byte *)slot - cldev->data));
+ gx_bits_cache_free(&cldev->bits, (gx_cached_bits_head *)slot,
+ &cldev->chunk);
+ table[index].offset = 0;
+ /* Delete the entry from the hash table. */
+ /* We'd like to move up any later entries, so that we don't need */
+ /* a deleted mark, but it's too difficult to note this in the */
+ /* band list, so instead, we just delete any entries that */
+ /* would need to be moved. */
+ while ( (offset = table[index = tile_hash_next(index) & mask].offset) != 0 )
+ { tile_slot *tile = (tile_slot *)(cldev->data + offset);
+ tile_loc loc;
+ if ( !clist_find_bits(cldev, tile->id, &loc) )
+ { /* We didn't find it, so it should be moved into a slot */
+ /* that we just vacated; instead, delete it. */
+ if_debug2('L', "[L]move-deleting index=%u, offset=%lu\n",
+ index, offset);
+ gx_bits_cache_free(&cldev->bits,
+ (gx_cached_bits_head *)(cldev->data + offset),
+ &cldev->chunk);
+ table[index].offset = 0;
+ }
+ }
+}
+
+/* Add a tile to the cache. */
+/* tile->raster holds the raster for the replicated tile; */
+/* we pass the raster of the actual data separately. */
+private int
+clist_add_tile(gx_device_clist_writer *cldev, const gx_strip_bitmap *tiles,
+ uint sraster, int depth)
+{ uint raster = tiles->raster;
+ uint size_bytes = raster * tiles->size.y;
+ uint tsize =
+ sizeof(tile_slot) + cldev->tile_band_mask_size + size_bytes;
+ gx_cached_bits_head *slot_head;
+#define slot ((tile_slot *)slot_head)
+
+ if ( cldev->bits.csize == cldev->tile_max_count )
+ { /* Don't let the hash table get too full: delete an entry. */
+ /* Since gx_bits_cache_alloc returns an entry to delete when */
+ /* it fails, just force it to fail. */
+ gx_bits_cache_alloc(&cldev->bits, (ulong)cldev->chunk.size,
+ &slot_head);
+ if ( slot_head == 0 )
+ { /* Wrap around and retry. */
+ cldev->bits.cnext = 0;
+ gx_bits_cache_alloc(&cldev->bits, (ulong)cldev->chunk.size,
+ &slot_head);
+#ifdef DEBUG
+ if ( slot_head == 0 )
+ { lprintf("No entry to delete!\n");
+ return_error(gs_error_Fatal);
+ }
+#endif
+ }
+ clist_delete_tile(cldev, slot);
+ }
+ /* Allocate the space for the new entry, deleting entries as needed. */
+ while ( gx_bits_cache_alloc(&cldev->bits, (ulong)tsize, &slot_head) < 0 )
+ { if ( slot_head == 0 )
+ { /* Wrap around. */
+ if ( cldev->bits.cnext == 0 )
+ { /* Too big to fit. We should probably detect this */
+ /* sooner, since if we get here, we've cleared the */
+ /* cache. */
+ return_error(gs_error_limitcheck);
+ }
+ cldev->bits.cnext = 0;
+ }
+ else
+ clist_delete_tile(cldev, slot);
+ }
+ /* Fill in the entry. */
+ slot->cb_depth = depth;
+ slot->cb_raster = raster;
+ slot->width = tiles->rep_width;
+ slot->height = tiles->rep_height;
+ slot->shift = tiles->rep_shift;
+ slot->id = tiles->id;
+ memset(ts_mask(slot), 0, cldev->tile_band_mask_size);
+ bytes_copy_rectangle(ts_bits(cldev, slot), raster,
+ tiles->data, sraster,
+ (tiles->rep_width * depth + 7) >> 3,
+ tiles->rep_height);
+ /* Make the hash table entry. */
+ { tile_loc loc;
+#ifdef DEBUG
+ if ( clist_find_bits(cldev, tiles->id, &loc) )
+ lprintf1("clist_find_bits(0x%lx) should have failed!\n",
+ (ulong)tiles->id);
+#else
+ clist_find_bits(cldev, tiles->id, &loc); /* always fails */
+#endif
+ slot->index = loc.index;
+ cldev->tile_table[loc.index].offset =
+ (byte *)slot_head - cldev->data;
+ if_debug2('L', "[L]adding index=%u, offset=%lu\n",
+ loc.index, cldev->tile_table[loc.index].offset);
+ }
+ slot->num_bands = 0;
+ return 0;
+}
+
+/* ------ Driver procedure support ------ */
+
+/* Change tile for clist_tile_rectangle. */
+int
+clist_change_tile(gx_device_clist_writer *cldev, gx_clist_state *pcls,
+ const gx_strip_bitmap *tiles, int depth)
+{ tile_loc loc;
+ int code;
+
+top: if ( clist_find_bits(cldev, tiles->id, &loc) )
+ { /* The bitmap is in the cache. Check whether this band */
+ /* knows about it. */
+ uint band_index = pcls - cldev->states;
+ byte *bptr = ts_mask(loc.tile) + (band_index >> 3);
+ byte bmask = 1 << (band_index & 7);
+
+ if ( *bptr & bmask )
+ { /* Already known. Just set the index. */
+ if ( pcls->tile_index == loc.index )
+ return 0;
+ cmd_put_tile_index_inline(cldev, pcls, loc.index);
+ }
+ else
+ { /*
+ * Not known yet. Output the bits. Note that the offset we
+ * write is the one used by the reading phase, not the
+ * writing phase. Note also that the size of the cached and
+ * written tile may differ from that of the client's tile.
+ * Finally, note that this tile's size parameters are
+ * guaranteed to be compatible with those stored in the
+ * device (cldev->tile_params).
+ */
+ ulong offset = (byte *)loc.tile - cldev->chunk.data;
+ uint rsize = 1 + cmd_size_w(loc.index) + cmd_size_w(offset);
+ byte *dp;
+ uint csize;
+ int code =
+ cmd_put_bits(cldev, pcls, ts_bits(cldev, loc.tile),
+ tiles->rep_width * depth, tiles->rep_height,
+ loc.tile->cb_raster, rsize,
+ (cldev->tile_params.size.x > tiles->rep_width ?
+ decompress_elsewhere | decompress_spread :
+ decompress_elsewhere),
+ &dp, &csize);
+
+ if ( code < 0 )
+ return code;
+ *dp = cmd_count_op(cmd_opv_set_tile_bits, csize);
+ dp++;
+ dp = cmd_put_w(loc.index, dp);
+ cmd_put_w(offset, dp);
+ *bptr |= bmask;
+ loc.tile->num_bands++;
+ }
+ pcls->tile_index = loc.index;
+ pcls->tile_id = loc.tile->id;
+ return 0;
+ }
+ /* The tile is not in the cache. */
+ /* Ensure that the tile size is compatible. */
+ if ( tiles->rep_width != cldev->tile_params.rep_width ||
+ tiles->rep_height != cldev->tile_params.rep_height ||
+ tiles->rep_shift != cldev->tile_params.rep_shift ||
+ depth != cldev->tile_depth
+ )
+ { /* Change the tile dimensions. We do this for all bands at once. */
+ gx_strip_bitmap new_tile;
+ uint rep_width_bits = tiles->rep_width * depth;
+
+ /*
+ * Adjust the replication factors. If we can, we replicate
+ * the tile in X up to 32 bytes, and then in Y up to 4 copies,
+ * as long as we don't exceed a total tile size of 256 bytes.
+ * However, don't attempt Y replication if shifting is required.
+ */
+#define max_tile_bytes_x 32
+#define max_tile_reps_y 4
+#define max_tile_bytes 256
+ new_tile = *tiles;
+ { uint max_bits_x = max_tile_bytes * 8 / new_tile.rep_height;
+ uint reps_x =
+ min(max_bits_x, max_tile_bytes_x * 8) / rep_width_bits;
+ uint reps_y;
+
+ new_tile.size.x = max(reps_x, 1) * new_tile.rep_width;
+ new_tile.raster = bitmap_raster(new_tile.size.x * depth);
+ if ( tiles->shift != 0 )
+ reps_y = 1;
+ else
+ { reps_y =
+ max_tile_bytes / (new_tile.raster * new_tile.rep_height);
+ if ( reps_y > max_tile_reps_y )
+ reps_y = max_tile_reps_y;
+ else if ( reps_y < 1 )
+ reps_y = 1;
+ }
+ new_tile.size.y = reps_y * new_tile.rep_height;
+ }
+#undef max_tile_bytes_x
+#undef max_tile_reps_y
+#undef max_tile_bytes
+ code = cmd_put_tile_params(cldev, &new_tile, depth);
+ if ( code < 0 )
+ return code;
+ cldev->tile_params = new_tile;
+ cldev->tile_depth = depth;
+ }
+ else
+ { cldev->tile_params.id = tiles->id;
+ cldev->tile_params.data = tiles->data;
+ }
+
+ code = clist_add_tile(cldev, &cldev->tile_params,
+ tiles->raster, depth);
+ if ( code < 0 )
+ return code;
+ goto top;
+}
+
+/* Change "tile" for clist_copy_*. tiles->[rep_]shift must be zero. */
+int
+clist_change_bits(gx_device_clist_writer *cldev, gx_clist_state *pcls,
+ const gx_strip_bitmap *tiles, int depth)
+{ tile_loc loc;
+ int code;
+
+top: if ( clist_find_bits(cldev, tiles->id, &loc) )
+ { /* The bitmap is in the cache. Check whether this band */
+ /* knows about it. */
+ uint band_index = pcls - cldev->states;
+ byte *bptr = ts_mask(loc.tile) + (band_index >> 3);
+ byte bmask = 1 << (band_index & 7);
+
+ if ( *bptr & bmask )
+ { /* Already known. Just set the index. */
+ if ( pcls->tile_index == loc.index )
+ return 0;
+ cmd_put_tile_index_inline(cldev, pcls, loc.index);
+ }
+ else
+ { /* Not known yet. Output the bits. */
+ /* Note that the offset we write is the one used by */
+ /* the reading phase, not the writing phase. */
+ ulong offset = (byte *)loc.tile - cldev->chunk.data;
+ uint rsize = 2 + cmd_size_w(loc.tile->width) +
+ cmd_size_w(loc.tile->height) + cmd_size_w(loc.index) +
+ cmd_size_w(offset);
+ byte *dp;
+ uint csize;
+ gx_clist_state *bit_pcls = pcls;
+ int code;
+
+ if ( loc.tile->num_bands == CHAR_ALL_BANDS_COUNT )
+ bit_pcls = NULL;
+ code = cmd_put_bits(cldev, bit_pcls, ts_bits(cldev, loc.tile),
+ loc.tile->width * depth,
+ loc.tile->height, loc.tile->cb_raster,
+ rsize,
+ (1 << cmd_compress_cfe) | decompress_elsewhere,
+ &dp, &csize);
+
+ if ( code < 0 )
+ return code;
+ *dp = cmd_count_op(cmd_opv_set_bits, csize);
+ dp[1] = (depth << 2) + code;
+ dp += 2;
+ dp = cmd_put_w(loc.tile->width, dp);
+ dp = cmd_put_w(loc.tile->height, dp);
+ dp = cmd_put_w(loc.index, dp);
+ cmd_put_w(offset, dp);
+ if ( bit_pcls == NULL )
+ { memset(ts_mask(loc.tile), 0xff,
+ cldev->tile_band_mask_size);
+ loc.tile->num_bands = cldev->nbands;
+ }
+ else
+ { *bptr |= bmask;
+ loc.tile->num_bands++;
+ }
+ }
+ pcls->tile_index = loc.index;
+ pcls->tile_id = loc.tile->id;
+ return 0;
+ }
+ /* The tile is not in the cache. */
+ code = clist_add_tile(cldev, tiles, tiles->raster, depth);
+ if ( code < 0 )
+ return code;
+ goto top;
+}
diff --git a/pstoraster/gxcldev.h b/pstoraster/gxcldev.h
new file mode 100644
index 000000000..a67406f11
--- /dev/null
+++ b/pstoraster/gxcldev.h
@@ -0,0 +1,484 @@
+/* Copyright (C) 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxcldev.h */
+/* Internal definitions for Ghostscript command lists. */
+#include "gxclist.h"
+#include "gsropt.h"
+#include "gxht.h" /* for gxdht.h */
+#include "gxtmap.h" /* ditto */
+#include "gxdht.h" /* for halftones */
+#include "strimpl.h" /* for compressed bitmaps */
+#include "scfx.h" /* ditto */
+#include "srlx.h" /* ditto */
+
+/* The implementation files define cdev as either crdev or cwdev. */
+#define ccdev (&((gx_device_clist *)dev)->common)
+#define cwdev (&((gx_device_clist *)dev)->writer)
+#define crdev (&((gx_device_clist *)dev)->reader)
+
+/* ---------------- Commands ---------------- */
+
+/* Define the compression modes for bitmaps. */
+/*#define cmd_compress_none 0*/ /* (implicit) */
+#define cmd_compress_rle 1
+#define clist_rle_init(ss)\
+ { s_RLE_set_defaults_inline(ss);\
+ s_RLE_init_inline(ss);\
+ }
+#define clist_rld_init(ss)\
+ { s_RLD_set_defaults_inline(ss);\
+ s_RLD_init_inline(ss);\
+ }
+#define cmd_compress_cfe 2
+#define clist_cf_init(ss, width)\
+ { (ss)->memory = &gs_memory_default;\
+ (ss)->K = -1;\
+ (ss)->Columns = (width);\
+ (ss)->EndOfBlock = false;\
+ (ss)->BlackIs1 = true;\
+ (ss)->DecodedByteAlign = align_bitmap_mod;\
+ }
+#define clist_cfe_init(ss, width)\
+ { s_CFE_set_defaults_inline(ss);\
+ clist_cf_init(ss, width);\
+ (*s_CFE_template.init)((stream_state *)(ss));\
+ }
+#define clist_cfd_init(ss, width, height)\
+ { (*s_CFD_template.set_defaults)((stream_state *)ss);\
+ clist_cf_init(ss, width);\
+ (ss)->Rows = (height);\
+ (*s_CFD_template.init)((stream_state *)(ss));\
+ }
+#define cmd_mask_compress_any\
+ ((1 << cmd_compress_rle) | (1 << cmd_compress_cfe))
+
+/*
+ * A command always consists of an operation followed by operands;
+ * the syntax of the operands depends on the operation.
+ * In the operation definitions below:
+ * + (prefixed) means the operand is in the low 4 bits of the opcode.
+ * # means a variable-size operand encoded with the variable-size
+ * integer encoding.
+ * % means a variable-size operand encoded with the variable-size
+ * fixed coordinate encoding.
+ * $ means a color sized according to the device depth.
+ * <> means the operand size depends on other state information
+ * and/or previous operands.
+ */
+typedef enum {
+ cmd_op_misc = 0x00, /* (see below) */
+ cmd_opv_end_run = 0x00, /* (nothing) */
+ cmd_opv_set_tile_size = 0x01, /* depth, width#, height#, */
+ /* rep_width#, rep_height#, */
+ /* rep_shift# */
+ cmd_opv_set_tile_phase = 0x02, /* x#, y# */
+ cmd_opv_set_tile_bits = 0x03, /* index#, offset#, <bits> */
+ cmd_opv_set_bits = 0x04, /* depth*4+compress, width#, height#, */
+ /* index#, offset#, <bits> */
+ cmd_opv_set_tile_color = 0x05, /* (nothing; next set/delta_color */
+ /* refers to tile) */
+ cmd_opv_set_lop = 0x06, /* lop# */
+ cmd_opv_enable_lop = 0x07, /* (nothing) */
+ cmd_opv_disable_lop = 0x08, /* (nothing) */
+ cmd_opv_set_ht_size = 0x09, /* offset#, */
+ /* width#, height#, raster#, */
+ /* shift#, num_levels#, num_bits# */
+ cmd_opv_set_ht_data = 0x0a, /* n, n x (uint|gx_ht_bit) */
+ cmd_opv_next_data_x = 0x0b, /* data_x */
+ cmd_opv_delta2_color0 = 0x0c, /* dr5dg6db5 or dc4dm4dy4dk4 */
+#define cmd_delta2_24_bias 0x00102010
+#define cmd_delta2_24_mask 0x001f3f1f
+#define cmd_delta2_32_bias 0x08080808
+#define cmd_delta2_32_mask 0x0f0f0f0f
+ cmd_opv_delta2_color1 = 0x0d, /* <<same as color0>> */
+ cmd_opv_set_copy_color = 0x0e, /* (nothing) */
+ cmd_opv_set_copy_alpha = 0x0f, /* (nothing) */
+ cmd_op_set_color0 = 0x10, /* +15 = transparent | */
+ /* +0, color$ | +dcolor+8 | */
+ /* +dr4, dg4db4 | */
+ /* +dc3dm1, dm2dy3dk3 */
+ cmd_op_set_color1 = 0x20, /* <<same as color0>> */
+#define cmd_delta1_24_bias 0x00080808
+#define cmd_delta1_24_mask 0x000f0f0f
+#define cmd_delta1_32_bias 0x04040404
+#define cmd_delta1_32_mask 0x07070707
+ cmd_op_fill_rect = 0x30, /* +dy2dh2, x#, w# | +0, rect# */
+ cmd_op_fill_rect_short = 0x40, /* +dh, dx, dw | +0, rect_short */
+ cmd_op_fill_rect_tiny = 0x50, /* +dw+0, rect_tiny | +dw+8 */
+ cmd_op_tile_rect = 0x60, /* +dy2dh2, x#, w# | +0, rect# */
+ cmd_op_tile_rect_short = 0x70, /* +dh, dx, dw | +0, rect_short */
+ cmd_op_tile_rect_tiny = 0x80, /* +dw+0, rect_tiny | +dw+8 */
+ cmd_op_copy_mono = 0x90, /* +compress, x#, y#, w#, h#, */
+ /* <bits> | */
+#define cmd_copy_ht_color 4
+ /* +4+compress, x#, y#, w#, h#, */
+ /* <bits> | */
+#define cmd_copy_use_tile 8
+ /* +8 (use tile), x#, y# | */
+ /* +12 (use tile), x#, y# */
+ cmd_op_copy_color_alpha = 0xa0, /* (same as copy_mono, except: */
+ /* if color, ignore ht_color; */
+ /* if alpha & !use_tile, depth is */
+ /* first operand) */
+ cmd_op_delta_tile_index = 0xb0, /* +delta+8 */
+ cmd_op_set_tile_index = 0xc0 /* +index[11:8], index[7:0] */
+} gx_cmd_op;
+
+#define cmd_op_name_strings\
+ "(misc)", "set_color[0]", "set_color[1]", "fill_rect",\
+ "fill_rect_short", "fill_rect_tiny", "tile_rect", "tile_rect_short",\
+ "tile_rect_tiny", "copy_mono", "copy_color_alpha", "delta_tile_index",\
+ "set_tile_index", "?dx?", "?ex?", "?fx?"
+
+#define cmd_misc_op_name_strings\
+ "end_run", "set_tile_size", "set_tile_phase", "set_tile_bits",\
+ "set_bits", "set_tile_color", "set_lop", "enable_lop",\
+ "disable_lop", "set_ht_size", "set_ht_data", "next_data_x",\
+ "delta2_color0", "delta2_color1", "set_copy_color", "set_copy_alpha",
+
+#ifdef DEBUG
+extern const char *cmd_op_names[16];
+extern const char **cmd_sub_op_names[16];
+#endif
+
+/*
+ * Define the size of the largest command, not counting any bitmap or
+ * similar variable-length operands.
+ * The variable-size integer encoding is little-endian. The low 7 bits
+ * of each byte contain data; the top bit is 1 for all but the last byte.
+ */
+#define cmd_max_intsize(siz)\
+ (((siz) * 8 + 6) / 7)
+#define cmd_largest_size\
+ (2 + (1 + cmd_max_dash) * sizeof(float))
+
+/* ---------------- Command parameters ---------------- */
+
+/* Rectangle */
+typedef struct {
+ int x, y, width, height;
+} gx_cmd_rect;
+/* Short rectangle */
+typedef struct {
+ byte dx, dwidth, dy, dheight; /* dy and dheight are optional */
+} gx_cmd_rect_short;
+#define cmd_min_short (-128)
+#define cmd_max_short 127
+/* Tiny rectangle */
+#define cmd_min_dw_tiny (-4)
+#define cmd_max_dw_tiny 3
+typedef struct {
+ unsigned dx : 4;
+ unsigned dy : 4;
+} gx_cmd_rect_tiny;
+#define cmd_min_dxy_tiny (-8)
+#define cmd_max_dxy_tiny 7
+
+/*
+ * When we write bitmaps, we remove raster padding selectively:
+ * - If the width is <= 6 bytes, we remove all the padding;
+ * - If the bitmap is only 1 scan line high, we remove the padding;
+ * - If the bitmap is uncompressed, we remove the padding from the
+ * last scan line;
+ * - Otherwise, we don't remove any padding.
+ * Currently, this information is embodied in the code that writes bitmaps
+ * (cmd_put_bits) and in the code that reads bitmaps (set_[tile_]bits and
+ * copy_xxx opcodes).
+ *
+ * For halftone cells, we always write an unreplicated bitmap, but we
+ * reserve cache space for the reading pass based on the replicated size.
+ * See the clist_change_tile procedure for the algorithm that chooses the
+ * replication factors.
+ */
+#define cmd_max_short_width_bytes 6
+#define cmd_max_short_width_bits (cmd_max_short_width_bytes * 8)
+
+/* ---------------- Block file entries ---------------- */
+
+typedef struct cmd_block_s {
+ int band;
+#define cmd_band_all (-1) /* do in all bands */
+#define cmd_band_end (-2) /* end of band file */
+ long pos; /* starting position in cfile */
+} cmd_block;
+
+/* ---------------- Band state ---------------- */
+
+/* Remember the current state of one band when writing or reading. */
+struct gx_clist_state_s {
+ gx_color_index colors[2]; /* most recent colors */
+ uint tile_index; /* most recent tile index */
+ gx_bitmap_id tile_id; /* most recent tile id */
+/* Since tile table entries may be deleted and/or moved at any time, */
+/* the following is the only reliable way to check whether tile_index */
+/* references a particular tile id: */
+#define cls_has_tile_id(cldev, pcls, tid, offset_temp)\
+ ((pcls)->tile_id == (tid) &&\
+ (offset_temp = cldev->tile_table[(pcls)->tile_index].offset) != 0 &&\
+ ((tile_slot *)(cldev->data + offset_temp))->id == (tid))
+ gs_int_point tile_phase; /* most recent tile phase */
+ gx_color_index tile_colors[2]; /* most recent tile colors */
+ gx_cmd_rect rect; /* most recent rectangle */
+ gs_logical_operation_t lop; /* most recent logical op */
+ short lop_enabled; /* 0 = don't use lop, 1 = use lop, */
+ /* -1 is used internally */
+ short clip_enabled; /* 0 = don't clip, 1 = do clip, */
+ /* -1 is used internally */
+ ushort color_is_alpha; /* (Boolean) for copy_color_alpha */
+ ushort known; /* flags for whether this band */
+ /* knows various misc. parameters */
+/****** THESE SHOULD BE MOVED TO gxclpath.h ******/
+#define flatness_known (1<<0)
+#define fill_adjust_known (1<<1)
+#define ctm_known (1<<2)
+#define line_width_known (1<<3)
+#define miter_limit_known (1<<4)
+#define misc_known (1<<5)
+#define dash_known (1<<6)
+#define clip_path_known (1<<7)
+#define stroke_all_known ((1<<8)-1)
+#define color_space_known (1<<8)
+#define all_known ((1<<9)-1)
+ /* Following are only used when writing */
+ cmd_list list; /* list of commands for band */
+};
+
+/* The initial values for a band state */
+/*static const gx_clist_state cls_initial*/
+#define cls_initial_values\
+ { gx_no_color_index, gx_no_color_index },\
+ 0, gx_no_bitmap_id,\
+ { 0, 0 }, { gx_no_color_index, gx_no_color_index },\
+ { 0, 0, 0, 0 }, lop_default, 0, 0, 0, all_known,\
+ { 0, 0 }
+
+/* Define the size of the command buffer used for reading. */
+/* This is needed to split up operations with a large amount of data, */
+/* primarily large copy_ operations. */
+#define cbuf_size 800
+
+/* ---------------- Driver procedure support ---------------- */
+
+/* The procedures and macros defined here are used when writing */
+/* (gxclist.c, gxclbits.c, gxclpath.c). */
+
+/* ------ Exported by gxclist.c ------ */
+
+/* Conditionally keep command statistics. */
+#ifdef DEBUG
+int cmd_count_op(P2(int op, uint size));
+void cmd_uncount_op(P2(int op, uint size));
+# define cmd_count_add1(v) (v++)
+#else
+# define cmd_count_op(op, size) (op)
+# define cmd_uncount_op(op, size) DO_NOTHING
+# define cmd_count_add1(v) DO_NOTHING
+#endif
+
+/* Add a command to the appropriate band list, */
+/* and allocate space for its data. */
+byte *cmd_put_list_op(P3(gx_device_clist_writer *cldev, cmd_list *pcl, uint size));
+#ifdef DEBUG
+byte *cmd_put_op(P3(gx_device_clist_writer *cldev, gx_clist_state *pcls, uint size));
+#else
+# define cmd_put_op(cldev, pcls, size)\
+ cmd_put_list_op(cldev, &(pcls)->list, size)
+#endif
+/* Call cmd_put_op and return properly if an error occurs. */
+#define set_cmd_put_op(dp, cldev, pcls, op, csize)\
+ do {\
+ if ( (dp = cmd_put_op(cldev, pcls, csize)) == 0 )\
+ return (cldev)->error_code;\
+ *dp = cmd_count_op(op, csize);\
+ } while ( 0 )
+
+/* Add a command for all bands. */
+byte *cmd_put_all_op(P2(gx_device_clist_writer *cldev, uint size));
+/* Call cmd_put_all_op and return properly if an error occurs. */
+#define set_cmd_put_all_op(dp, cldev, op, csize)\
+ do {\
+ if ( (dp = cmd_put_all_op(cldev, csize)) == 0 )\
+ return (cldev)->error_code;\
+ *dp = cmd_count_op(op, csize);\
+ } while ( 0 )
+
+/* Shorten the last allocated command. */
+/* Note that this does not adjust the statistics. */
+#define cmd_shorten_list_op(cldev, pcls, delta)\
+ ((pcls)->tail->size -= (delta), (cldev)->cnext -= (delta))
+#define cmd_shorten_op(cldev, pcls, delta)\
+ cmd_shorten_list_op(cldev, &(pcls)->list, delta)
+
+/* Flush the buffer. */
+int clist_flush_buffer(P1(gx_device_clist_writer *));
+
+/* Compute the # of bytes required to represent a variable-size integer. */
+/* (This works for negative integers also; they are written as though */
+/* they were unsigned.) */
+int cmd_size_w(P1(uint));
+#define w1byte(w) (!((w) & ~0x7f))
+#define w2byte(w) (!((w) & ~0x3fff))
+#define cmd_sizew(w)\
+ (w1byte(w) ? 1 : w2byte(w) ? 2 : cmd_size_w((uint)(w)))
+#define cmd_size2w(wx,wy)\
+ (w1byte((wx) | (wy)) ? 2 :\
+ cmd_size_w((uint)(wx)) + cmd_size_w((uint)(wy)))
+#define cmd_sizexy(xy) cmd_size2w((xy).x, (xy).y)
+int cmd_size_rect(P1(const gx_cmd_rect *));
+#define cmd_sizew_max ((sizeof(uint) * 8 + 6) / 7)
+
+/* Put a variable-size integer in the buffer. */
+byte *cmd_put_w(P2(uint, byte *));
+#define cmd_putw(w,dp)\
+ (w1byte(w) ? (*dp = w, ++dp) :\
+ w2byte(w) ? (*dp = (w) | 0x80, dp[1] = (w) >> 7, dp += 2) :\
+ (dp = cmd_put_w((uint)(w), dp)))
+#define cmd_put2w(wx,wy,dp)\
+ (w1byte((wx) | (wy)) ? (dp[0] = (wx), dp[1] = (wy), dp += 2) :\
+ (dp = cmd_put_w((uint)(wy), cmd_put_w((uint)(wx), dp))))
+#define cmd_putxy(xy,dp) cmd_put2w((xy).x, (xy).y, dp)
+
+/* Put out a command to set a color. */
+typedef struct {
+ byte set_op;
+ byte delta2_op;
+ bool tile_color;
+} clist_select_color_t;
+extern const clist_select_color_t
+ clist_select_color0, clist_select_color1,
+ clist_select_tile_color0, clist_select_tile_color1;
+int cmd_put_color(P5(gx_device_clist_writer *cldev, gx_clist_state *pcls,
+ const clist_select_color_t *select,
+ gx_color_index color, gx_color_index *pcolor));
+#define cmd_set_color0(dev, pcls, color0)\
+ cmd_put_color(dev, pcls, &clist_select_color0, color0, &(pcls)->colors[0])
+#define cmd_set_color1(dev, pcls, color1)\
+ cmd_put_color(dev, pcls, &clist_select_color1, color1, &(pcls)->colors[1])
+
+/* Put out a command to set the tile colors. */
+int cmd_set_tile_colors(P4(gx_device_clist_writer *cldev, gx_clist_state *pcls,
+ gx_color_index color0, gx_color_index color1));
+
+/* Put out a command to set the tile phase. */
+int cmd_set_tile_phase(P4(gx_device_clist_writer *cldev, gx_clist_state *pcls,
+ int px, int py));
+
+/* Enable or disable the logical operation. */
+int cmd_put_enable_lop(P3(gx_device_clist_writer *, gx_clist_state *, int));
+#define cmd_do_enable_lop(cldev, pcls, enable)\
+ if ( (pcls)->lop_enabled == ((enable) ^ 1) &&\
+ cmd_put_enable_lop(cldev, pcls, enable) < 0\
+ )\
+ return (cldev)->error_code
+#define cmd_enable_lop(cldev, pcls)\
+ cmd_do_enable_lop(cldev, pcls, 1)
+#define cmd_disable_lop(cldev, pcls)\
+ cmd_do_enable_lop(cldev, pcls, 0)
+
+/* Enable or disable clipping. */
+extern byte cmd_opvar_enable_clip, cmd_opvar_disable_clip;
+int cmd_put_enable_clip(P3(gx_device_clist_writer *, gx_clist_state *, int));
+#define cmd_do_enable_clip(cldev, pcls, enable)\
+ if ( (pcls)->clip_enabled == ((enable) ^ 1) &&\
+ cmd_put_enable_clip(cldev, pcls, enable) < 0\
+ )\
+ return (cldev)->error_code
+#define cmd_enable_clip(cldev, pcls)\
+ cmd_do_enable_clip(cldev, pcls, 1)
+#define cmd_disable_clip(cldev, pcls)\
+ cmd_do_enable_clip(cldev, pcls, 0)
+
+/* Write a command to set the logical operation. */
+int cmd_set_lop(P3(gx_device_clist_writer *, gx_clist_state *,
+ gs_logical_operation_t));
+
+/* Put out a fill or tile rectangle command. */
+int cmd_write_rect_cmd(P7(gx_device_clist_writer *cldev, gx_clist_state *pcls,
+ int op, int x, int y, int width, int height));
+
+/*
+ * Define macros for dividing up an operation into bands.
+ * Note that BEGIN_RECT resets y and height. It is OK for the code that
+ * processes each band to reset height to a smaller (positive) value;
+ * the vertical subdivision code in copy_mono, copy_color, and copy_alpha
+ * makes use of this. The band processing code may `continue' (to reduce
+ * nesting of conditionals).
+ */
+#define BEGIN_RECT\
+ { int yend = y + height;\
+ int band_height = cdev->band_height;\
+ do\
+ { int band = y / band_height;\
+ gx_clist_state *pcls = cdev->states + band;\
+ int band_end = (band + 1) * band_height;\
+ height = min(band_end, yend) - y;\
+ {
+#define END_RECT\
+ }\
+ }\
+ while ( (y += height) < yend );\
+ }
+
+/* ------ Exported by gxclbits.c ------ */
+
+/*
+ * Put a bitmap in the buffer, compressing if appropriate.
+ * pcls == 0 means put the bitmap in all bands.
+ * Return <0 if error, otherwise the compression method.
+ * A return value of gs_error_limitcheck means that the bitmap was too big
+ * to fit in the command reading buffer.
+ * Note that this leaves room for the command and initial arguments,
+ * but doesn't fill them in.
+ *
+ * If decompress_elsewhere is set in the compression_mask, it is OK
+ * to write out a compressed bitmap whose decompressed size is too large
+ * to fit in the command reading buffer. (This is OK when reading a
+ * cached bitmap, but not a bitmap for a one-time copy operation.)
+ */
+#define decompress_elsewhere 0x100
+/*
+ * If decompress_spread is set, the decompressed data will be spread out
+ * for replication, so we drop all the padding even if the width is
+ * greater than cmd_max_short_width_bytes (see above).
+ */
+#define decompress_spread 0x200
+
+int cmd_put_bits(P10(gx_device_clist_writer *cldev, gx_clist_state *pcls,
+ const byte *data, uint width_bits, uint height,
+ uint raster, int op_size, int compression_mask,
+ byte **pdp, uint *psize));
+
+/*
+ * Change tiles for clist_tile_rectangle. (We make this a separate
+ * procedure primarily for readability.)
+ */
+int clist_change_tile(P4(gx_device_clist_writer *cldev, gx_clist_state *pcls,
+ const gx_strip_bitmap *tiles, int depth));
+
+/*
+ * Change "tile" for clist_copy_*. Only uses tiles->{data, id, raster,
+ * rep_width, rep_height}. tiles->[rep_]shift must be zero.
+ */
+int clist_change_bits(P4(gx_device_clist_writer *cldev, gx_clist_state *pcls,
+ const gx_strip_bitmap *tiles, int depth));
diff --git a/pstoraster/gxclfile.c b/pstoraster/gxclfile.c
new file mode 100644
index 000000000..987bd68a2
--- /dev/null
+++ b/pstoraster/gxclfile.c
@@ -0,0 +1,117 @@
+/* Copyright (C) 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxclfile.c */
+/* File-based command list implementation */
+#include "stdio_.h"
+#include "string_.h"
+#include "gserror.h"
+#include "gserrors.h"
+#include "gsmemory.h"
+#include "gp.h"
+#include "gxclio.h"
+
+/* This is an implementation of the command list I/O interface */
+/* that uses the file system for storage. */
+
+/* ------ Open/close/unlink ------ */
+
+int
+clist_open_scratch(char *fname, clist_file_ptr *pcf, gs_memory_t *mem,
+ bool ok_to_compress)
+{ char fmode[4];
+ strcpy(fmode, "w+");
+ strcat(fmode, gp_fmode_binary_suffix);
+ *pcf =
+ (clist_file_ptr)gp_open_scratch_file(gp_scratch_file_name_prefix,
+ fname, fmode);
+ if ( *pcf == NULL )
+ { eprintf1("Could not open the scratch file %s.\n", fname);
+ return_error(gs_error_invalidfileaccess);
+ }
+ return 0;
+}
+
+void
+clist_fclose_and_unlink(clist_file_ptr cf, const char *fname)
+{ fclose((FILE *)cf);
+ unlink(fname);
+}
+
+/* ------ Writing ------ */
+
+long
+clist_space_available(long requested)
+{ return requested;
+}
+
+int
+clist_fwrite_chars(const void *data, uint len, clist_file_ptr cf)
+{ return fwrite(data, 1, len, (FILE *)cf);
+}
+
+/* ------ Reading ------ */
+
+int
+clist_fread_chars(void *data, uint len, clist_file_ptr cf)
+{ FILE *f = (FILE *)cf;
+ byte *str = data;
+ /* The typical implementation of fread */
+ /* is extremely inefficient for small counts, */
+ /* so we just use straight-line code instead. */
+ switch ( len )
+ {
+ default: return fread(str, 1, len, f);
+ case 8: *str++ = (byte)getc(f);
+ case 7: *str++ = (byte)getc(f);
+ case 6: *str++ = (byte)getc(f);
+ case 5: *str++ = (byte)getc(f);
+ case 4: *str++ = (byte)getc(f);
+ case 3: *str++ = (byte)getc(f);
+ case 2: *str++ = (byte)getc(f);
+ case 1: *str = (byte)getc(f);
+ }
+ return len;
+}
+
+/* ------ Position/status ------ */
+
+int
+clist_ferror_code(clist_file_ptr cf)
+{ return (ferror((FILE *)cf) ? gs_error_ioerror : 0);
+}
+
+long
+clist_ftell(clist_file_ptr cf)
+{ return ftell((FILE *)cf);
+}
+
+void
+clist_rewind(clist_file_ptr cf, bool discard_data)
+{ rewind((FILE *)cf);
+}
+
+int
+clist_fseek(clist_file_ptr cf, long offset, int mode)
+{ return fseek((FILE *)cf, offset, mode);
+}
diff --git a/pstoraster/gxclimag.c b/pstoraster/gxclimag.c
new file mode 100644
index 000000000..e83f01c1e
--- /dev/null
+++ b/pstoraster/gxclimag.c
@@ -0,0 +1,626 @@
+/* Copyright (C) 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxclimag.c */
+/* Higher-level image operations for band lists */
+#include "math_.h"
+#include "memory_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gscspace.h"
+#include "gxarith.h"
+#include "gxdevice.h"
+#include "gxdevmem.h" /* must precede gxcldev.h */
+#include "gxcldev.h"
+#include "gxclpath.h"
+
+/* Define whether we can handle high-level images yet. */
+/*#define USE_HL_IMAGES*/
+
+#define cdev cwdev
+
+extern int igcd(P2(int, int));
+
+/* Driver procedures */
+private dev_proc_fill_mask(clist_fill_mask);
+private dev_proc_begin_image(clist_begin_image);
+private dev_proc_image_data(clist_image_data);
+private dev_proc_end_image(clist_end_image);
+
+/* Initialize the extensions to the command set. */
+void
+gs_climag_init(gs_memory_t *mem)
+{
+ gs_clist_device_procs.fill_mask = clist_fill_mask;
+ gs_clist_device_procs.begin_image = clist_begin_image;
+ gs_clist_device_procs.image_data = clist_image_data;
+ gs_clist_device_procs.end_image = clist_end_image;
+}
+
+/* ------ Driver procedures ------ */
+
+private int
+clist_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)
+{ int log2_depth = depth >> 1; /* works for 1,2,4 */
+ int y0;
+ int data_x_bit;
+ gx_bitmap_id orig_id = id;
+ byte copy_op =
+ (depth > 1 ? cmd_op_copy_color_alpha :
+ gx_dc_is_pure(pdcolor) ? cmd_op_copy_mono :
+ cmd_op_copy_mono + cmd_copy_ht_color);
+
+ fit_copy(dev, data, data_x, raster, id, x, y, width, height);
+ if ( cmd_check_clip_path(cdev, pcpath) )
+ cmd_clear_known(cdev, clip_path_known);
+ y0 = y;
+ data_x_bit = data_x << log2_depth;
+ BEGIN_RECT
+ int dx = (data_x_bit & 7) >> log2_depth;
+ int w1 = dx + width;
+ const byte *row = data + (y - y0) * raster + (data_x_bit >> 3);
+ int code;
+
+ if ( lop == lop_default )
+ { cmd_disable_lop(cdev, pcls);
+ }
+ else
+ { if ( lop != pcls->lop )
+ { code = cmd_set_lop(cdev, pcls, lop);
+ if ( code < 0 )
+ return code;
+ }
+ cmd_enable_lop(cdev, pcls);
+ }
+ if ( depth > 1 && !pcls->color_is_alpha )
+ { byte *dp;
+ set_cmd_put_op(dp, cdev, pcls, cmd_opv_set_copy_alpha, 1);
+ pcls->color_is_alpha = 1;
+ }
+ cmd_do_write_unknown(cdev, pcls, clip_path_known);
+ cmd_do_enable_clip(cdev, pcls, pcpath != NULL);
+ code = cmd_put_drawing_color(cdev, pcls, pdcolor);
+ if ( code < 0 )
+ return code;
+ /*
+ * Unfortunately, painting a character with a halftone requires the
+ * use of two bitmaps, a situation that we can neither represent in
+ * the band list nor guarantee will both be present in the tile
+ * cache; in this case, we always write the bits of the character.
+ */
+ if ( id != gx_no_bitmap_id && gx_dc_is_pure(pdcolor) )
+ { /* This is a character. ****** WRONG IF HALFTONE CELL. ******/
+ /* Put it in the cache if possible. */
+ ulong offset_temp;
+ if ( !cls_has_tile_id(cdev, pcls, id, offset_temp) )
+ { gx_strip_bitmap tile;
+ tile.data = (byte *)data; /* actually const */
+ tile.raster = raster;
+ tile.size.x = tile.rep_width = width;
+ tile.size.y = tile.rep_height = yend - y0; /* full height */
+ tile.rep_shift = tile.shift = 0;
+ tile.id = id;
+ if ( clist_change_bits(cdev, pcls, &tile, depth) < 0 )
+ { /* Something went wrong; just copy the bits. */
+ goto copy;
+ }
+ }
+ { gx_cmd_rect rect;
+ int rsize;
+ byte op = copy_op + cmd_copy_use_tile;
+ byte *dp;
+
+ /* Output a command to copy the entire character. */
+ /* It will be truncated properly per band. */
+ rect.x = x, rect.y = y0;
+ rect.width = w1, rect.height = yend - y0;
+ rsize = 1 + cmd_sizexy(rect);
+ if ( dx )
+ { set_cmd_put_op(dp, cdev, pcls, cmd_opv_next_data_x, 2);
+ dp[1] = dx;
+ }
+ set_cmd_put_op(dp, cdev, pcls, op, rsize);
+ dp++;
+ cmd_putxy(rect, dp);
+ pcls->rect = rect;
+ goto end;
+ }
+ }
+copy: { gx_cmd_rect rect;
+ int rsize;
+ byte *dp;
+ uint csize;
+ int code;
+
+ rect.x = x, rect.y = y;
+ rect.width = w1, rect.height = height;
+ rsize = (dx ? 3 : 1) + (depth > 1 ? 1 : 0 ) +
+ cmd_size_rect(&rect);
+ code = cmd_put_bits(cdev, pcls, row, w1 << log2_depth,
+ height, raster, rsize,
+ (orig_id == gx_no_bitmap_id || depth > 1 ?
+ 1 << cmd_compress_rle :
+ cmd_mask_compress_any),
+ &dp, &csize);
+ if ( code < 0 )
+ { if ( code != gs_error_limitcheck )
+ return code;
+ /* The bitmap was too large; split up the transfer. */
+ if ( height > 1 )
+ { /* Split the transfer by reducing the */
+ /* height. See the comment above BEGIN_RECT. */
+ height >>= 1;
+ goto copy;
+ }
+ { /* Split a single (very long) row. */
+ int w2 = w1 >> 1;
+ code = clist_fill_mask(dev, row, dx + w2,
+ raster, gx_no_bitmap_id, x + w2, y,
+ w1 - w2, 1, pdcolor, depth, lop, pcpath);
+ if ( code < 0 )
+ return code;
+ w1 = w2;
+ goto copy;
+ }
+ }
+ if ( dx )
+ { *dp++ = cmd_count_op(cmd_opv_next_data_x, 2);
+ *dp++ = dx;
+ }
+ *dp++ = cmd_count_op(copy_op + code, csize);
+ if ( depth > 1 )
+ *dp++ = depth;
+ cmd_put2w(x, y, dp);
+ cmd_put2w(w1, height, dp);
+ pcls->rect = rect;
+ }
+end: ;
+ END_RECT
+ return 0;
+}
+
+/* ------ Bitmap image driver procedures ------ */
+
+/* Define the structure for keeping track of progress through an image. */
+typedef struct clist_image_enum_s {
+ /* Arguments of begin_image */
+ gs_memory_t *memory;
+ gs_image_t image;
+ gx_drawing_color dcolor;
+ const gs_imager_state *pis;
+ const gx_clip_path *pcpath;
+ /* Set at creation time */
+ void *default_info;
+ bool multi;
+ int num_planes;
+ int bits_per_pixel; /* bits per pixel (per plane) */
+ uint bytes_mod; /* min # of bytes (per plane) for integral */
+ /* number of pixels */
+ gs_matrix matrix; /* image space -> device space */
+ byte color_space;
+ int ymin, ymax;
+ /* Updated dynamically */
+ int xywh[4]; /* most recent image_data parameters */
+} clist_image_enum;
+/* We can disregard the pointers in the writer by allocating */
+/* the image enumerator as immovable. This is a hack, of course. */
+gs_private_st_ptrs1(st_clist_image_enum, clist_image_enum, "clist_image_enum",
+ clist_image_enum_enum_ptrs, clist_image_enum_reloc_ptrs, default_info);
+
+/* Forward declarations */
+private int cmd_image_data(P10(gx_device_clist_writer *cldev,
+ gx_clist_state *pcls, clist_image_enum *pie,
+ const byte *data, uint raster, uint nbytes,
+ int x, int y, int w, int h));
+
+/* Start processing an image. */
+private int
+clist_begin_image(gx_device *dev,
+ const gs_imager_state *pis, const gs_image_t *pim,
+ gs_image_format_t format, gs_image_shape_t shape,
+ const gx_drawing_color *pdcolor, const gx_clip_path *pcpath,
+ gs_memory_t *mem, void **pinfo)
+{ clist_image_enum *pie;
+ int base_index;
+ bool indexed;
+ int num_components;
+ gs_matrix mat;
+ int code;
+
+ /* See above for why we allocate the enumerator as immovable. */
+ pie = gs_alloc_struct_immovable(mem, clist_image_enum,
+ &st_clist_image_enum,
+ "clist_begin_image");
+ if ( pie == 0 )
+ return_error(gs_error_VMerror);
+ pie->memory = mem;
+ *pinfo = pie;
+ if ( pim->ImageMask )
+ { base_index = gs_color_space_index_DeviceGray; /* arbitrary */
+ indexed = false;
+ num_components = 1;
+ }
+ else
+ { const gs_color_space *pcs = pim->ColorSpace;
+ base_index = gs_color_space_get_index(pcs);
+ if ( base_index == gs_color_space_index_Indexed )
+ { const gs_color_space *pbcs =
+ gs_color_space_indexed_base_space(pcs);
+ indexed = true;
+ base_index = gs_color_space_get_index(pbcs);
+ num_components = 1;
+ }
+ else
+ { indexed = false;
+ num_components = gs_color_space_num_components(pcs);
+ }
+ }
+ if ( /****** CAN'T HANDLE CIE COLOR YET ******/
+ base_index > gs_color_space_index_DeviceCMYK ||
+ (code = gs_matrix_invert(&pim->ImageMatrix, &mat)) < 0 ||
+ (code = gs_matrix_multiply(&mat, &ctm_only(pis), &mat)) < 0
+ /****** CAN'T HANDLE NON-PURE-COLOR MASKS YET ******/
+ || (pim->ImageMask && !gx_dc_is_pure(pdcolor))
+ /****** CAN'T HANDLE MULTI-PLANE IMAGES YET ******/
+ /****** (requires flipping in cmd_image_data) ******/
+ || format != gs_image_format_chunky
+#ifndef USE_HL_IMAGES
+ || 1 /* Always use the default. */
+#endif
+ )
+ { int code = gx_default_begin_image(dev, pis, pim, format, shape,
+ pdcolor, pcpath, mem,
+ &pie->default_info);
+ if ( code < 0 )
+ gs_free_object(mem, pie, "clist_begin_image");
+ return code;
+ }
+ pie->default_info = 0;
+ pie->image = *pim;
+ pie->dcolor = *pdcolor;
+ pie->pis = pis;
+ pie->pcpath = pcpath;
+ pie->multi = (format != gs_image_format_chunky);
+ pie->num_planes = (pie->multi ? num_components : 1);
+ pie->bits_per_pixel =
+ pim->BitsPerComponent * num_components / pie->num_planes;
+ pie->bytes_mod = pie->bits_per_pixel / igcd(pie->bits_per_pixel, 8);
+ pie->matrix = mat;
+ pie->color_space = (base_index << 4) | (indexed ? 8 : 0);
+ /* Write out the begin_image command. */
+ { gs_rect sbox, dbox;
+ int y, height;
+ byte cbuf[2 + 14 * sizeof(float)];
+ byte *cp = cbuf;
+ byte b;
+ uint len, total_len;
+
+ if ( indexed )
+ b = 0;
+ else switch ( pim->BitsPerComponent )
+ {
+ case 1: b = 1 << 5; break;
+ case 2: b = 2 << 5; break;
+ case 4: b = 3 << 5; break;
+ case 8: b = 4 << 5; break;
+ case 12: b = 5 << 5; break;
+ default: return_error(gs_error_rangecheck);
+ }
+ if ( pim->Interpolate )
+ b |= 1 << 4;
+ if ( !(pim->ImageMatrix.xx == pim->Width &&
+ pim->ImageMatrix.xy == 0 &&
+ pim->ImageMatrix.yx == 0 &&
+ pim->ImageMatrix.yy == -pim->Height &&
+ pim->ImageMatrix.tx == 0 &&
+ pim->ImageMatrix.ty == pim->Height
+ )
+ )
+ { b |= 1 << 3;
+ cp = cmd_for_matrix(cp, &pim->ImageMatrix);
+ }
+ { static const float base_decode[8] = {0, 1, 0, 1, 0, 1, 0, 1};
+ float indexed_decode[2];
+ const float *default_decode = base_decode;
+ int num_decode = num_components * 2;
+ int i;
+
+ if ( indexed )
+ { indexed_decode[0] = 0;
+ indexed_decode[1] = (1 << pim->BitsPerComponent) - 1;
+ default_decode = indexed_decode;
+ }
+ for ( i = 0; i < num_decode; ++i )
+ if ( pim->Decode[i] != default_decode[i] )
+ break;
+ if ( i != num_decode )
+ { byte *pdb = cp++;
+ byte dflags = 0;
+
+ b |= 1 << 2;
+ for ( i = 0; i < num_decode; i += 2 )
+ { float u = pim->Decode[i], v = pim->Decode[i+1];
+ dflags <<= 2;
+ if ( u == 0 && v == default_decode[i+1] )
+ ;
+ else if ( u == default_decode[i+1] && v == 0 )
+ dflags += 1;
+ else
+ { if ( u != 0 )
+ { dflags++;
+ memcpy(cp, &u, sizeof(float));
+ cp += sizeof(float);
+ }
+ dflags += 2;
+ memcpy(cp, &v, sizeof(float));
+ cp += sizeof(float);
+ }
+ }
+ *pdb = dflags;
+ }
+ }
+ if ( (indexed ? pim->adjust : pim->CombineWithColor) )
+ b |= 1 << 1;
+ len = cp - cbuf;
+ total_len = 3 + len + cmd_size2w(pim->Width, pim->Height);
+ sbox.p.x = 0;
+ sbox.p.y = 0;
+ sbox.q.x = pim->Width;
+ sbox.q.y = pim->Height;
+ gs_bbox_transform(&sbox, &pie->matrix, &dbox);
+ y = pie->ymin = (int)floor(dbox.p.y);
+ height = (pie->ymax = (int)ceil(dbox.q.y)) - y;
+ BEGIN_RECT
+ byte *dp;
+
+ set_cmd_put_op(dp, cdev, pcls, cmd_opv_begin_image, total_len);
+ dp[1] = b;
+ /****** ADD split_row IF NECESSARY ******/
+ dp[2] = shape |
+ gs_image_shape_clip_top | gs_image_shape_clip_bottom |
+ gs_image_shape_rows;
+ dp += 3;
+ cmd_put2w(pim->Width, pim->Height, dp);
+ memcpy(dp, cbuf, len);
+ END_RECT
+ }
+ pie->xywh[0] = 0; /* x */
+ pie->xywh[1] = 0; /* y */
+ pie->xywh[2] = pim->Width; /* w */
+ pie->xywh[3] = 1; /* h */
+ return 0;
+}
+
+/* Process the next piece of an image. */
+/* We rely on the caller to provide the data in the right order & size. */
+private int
+clist_image_data(gx_device *dev,
+ void *info, const byte **planes, uint raster,
+ int bx, int by, int bwidth, int bheight)
+{ clist_image_enum *pie = info;
+ const gs_imager_state *pis = pie->pis;
+ const gx_clip_path *pcpath = pie->pcpath;
+ gs_logical_operation_t lop;
+ gs_rect sbox, dbox;
+ int y, height; /* for BEGIN/END_RECT */
+ uint unknown = 0;
+
+ if ( pie->default_info )
+ return gx_default_image_data(dev, pie->default_info, planes, raster,
+ bx, by, bwidth, bheight);
+ lop = pis->log_op;
+ if ( state_neq(ctm.xx) || state_neq(ctm.xy) ||
+ state_neq(ctm.yx) || state_neq(ctm.yy) ||
+ state_neq(ctm.tx) || state_neq(ctm.ty)
+ )
+ { unknown |= ctm_known;
+ state_update(ctm);
+ }
+ if ( cmd_check_clip_path(cdev, pcpath) )
+ unknown |= clip_path_known;
+ if ( cdev->color_space != pie->color_space ||
+ ((cdev->color_space & 8) != 0 &&
+ cdev->indexed_hival !=
+ pie->image.ColorSpace->params.indexed.hival)
+ )
+ { unknown |= color_space_known;
+ cdev->color_space = pie->color_space;
+ if ( cdev->color_space & 8 )
+ cdev->indexed_hival =
+ pie->image.ColorSpace->params.indexed.hival;
+ }
+ if ( unknown )
+ cmd_clear_known(cdev, unknown);
+ sbox.p.x = bx;
+ sbox.p.y = by;
+ sbox.q.x = bx + bwidth;
+ sbox.q.y = by + bheight;
+ gs_bbox_transform(&sbox, &pie->matrix, &dbox);
+ y = (int)floor(dbox.p.y);
+ height = (int)ceil(dbox.q.y) - y;
+ BEGIN_RECT
+ int code;
+
+ cmd_do_write_unknown(cdev, pcls,
+ ctm_known | clip_path_known | color_space_known);
+ cmd_do_enable_clip(cdev, pcls, pie->pcpath != NULL);
+ if ( lop == lop_default )
+ { cmd_disable_lop(cdev, pcls);
+ }
+ else
+ { if ( lop != pcls->lop )
+ { byte *dp;
+ set_cmd_put_op(dp, cdev, pcls,
+ cmd_opv_set_lop, 1 + cmd_size_w(lop));
+ ++dp;
+ cmd_put_w(lop, dp);
+ pcls->lop = lop;
+ }
+ cmd_enable_lop(cdev, pcls);
+ }
+ if ( pie->image.ImageMask )
+ { code = cmd_put_drawing_color(cdev, pcls, &pie->dcolor);
+ if ( code < 0 )
+ return code;
+ }
+ /*
+ * Currently we only clip unrotated images; for others, we transmit
+ * the entire block of data in every band it may overlap.
+ * (Since blocks tend to be small, we don't transmit much twice.)
+ */
+ { int iy, ih;
+ uint bytes_per_row;
+ long offset;
+
+#if 0
+ if ( pie->matrix.xy == 0 && pie->matrix.yx == 0 )
+ { /* Unrotated image, clip the data. */
+ /****** NOT IMPLEMENTED YET ******/
+ }
+ else
+#endif
+ { iy = by;
+ ih = bheight;
+ offset = 0;
+ }
+ bytes_per_row = (bwidth * pie->bits_per_pixel + 7) >> 3;
+ if ( bytes_per_row <= cbuf_size - cmd_largest_size )
+ { /* Transmit multiple complete rows. */
+ uint rows_per_cmd =
+ (cbuf_size - cmd_largest_size) / bytes_per_row;
+ int nrows;
+
+ for ( ; ih > 0;
+ iy += nrows, ih -= nrows, offset += raster * nrows
+ )
+ { nrows = min(ih, rows_per_cmd);
+ code = cmd_image_data(cdev, pcls, pie,
+ planes[0] + offset,
+ raster, bytes_per_row,
+ bx, iy, bwidth, nrows);
+ if ( code < 0 )
+ return code;
+ }
+ }
+ else
+ { /* Transmit partial rows. */
+ uint bytes_per_cmd =
+ ((cbuf_size - cmd_largest_size) / pie->bytes_mod) *
+ pie->bytes_mod;
+ int width_per_cmd = bytes_per_cmd * 8 / pie->bits_per_pixel;
+
+ for ( ; ih > 0; ++iy, --ih, offset += raster )
+ { uint xoff = 0, xbytes;
+ int dx = 0, w;
+
+ for ( ; xoff < bytes_per_row; xoff += xbytes, dx += w )
+ { xbytes = min(bytes_per_cmd, bytes_per_row - xoff);
+ w = min(width_per_cmd, bwidth - dx);
+ code = cmd_image_data(cdev, pcls, pie,
+ planes[0] + offset + xoff,
+ raster, xbytes,
+ bx + dx, w, iy, 1);
+ if ( code < 0 )
+ return code;
+ }
+ }
+ }
+ }
+
+
+ END_RECT
+ return 0;
+}
+
+/* Clean up by releasing the buffers. */
+private int
+clist_end_image(gx_device *dev, void *info, bool draw_last)
+{ clist_image_enum *pie = info;
+ int code;
+
+ if ( pie->default_info )
+ code = gx_default_end_image(dev, pie->default_info, draw_last);
+ else
+ { int y = pie->ymin;
+ int height = pie->ymax - y;
+ BEGIN_RECT
+ byte *dp;
+ set_cmd_put_op(dp, cdev, pcls, cmd_opv_image_data, 2);
+ dp[1] = 0; /* EOD */
+ END_RECT
+ code = 0;
+ }
+ gs_free_object(pie->memory, pie, "clist_end_image");
+ return code;
+}
+
+/* ------ Utilities ------ */
+
+/* Write data for a partial image. */
+private int
+cmd_image_data(gx_device_clist_writer *cldev, gx_clist_state *pcls,
+ clist_image_enum *pie, const byte *data, uint raster, uint nbytes,
+ int x, int y, int w, int h)
+{ byte b = 0;
+ uint len = 2 + cmd_sizew(nbytes) + h * nbytes;
+ int xywh[4];
+ int i;
+ byte *dp;
+
+ xywh[0] = x, xywh[1] = y, xywh[2] = w, xywh[3] = h;
+ for ( i = 0; i < 4; ++i )
+ { int diff = xywh[i] - pie->xywh[i];
+ b <<= 2;
+ if ( diff == 0 )
+ ;
+ else if ( diff == 1 )
+ b += 1;
+ else if ( diff > 0 )
+ { b += 2;
+ len += cmd_sizew(diff);
+ }
+ else
+ { b += 3;
+ len += cmd_sizew(-diff);
+ }
+ pie->xywh[i] += diff;
+ xywh[i] = diff;
+ }
+ set_cmd_put_op(dp, cldev, pcls, cmd_opv_image_data, len);
+ cmd_putw(nbytes, dp);
+ *dp++ = b;
+ for ( i = 0; i < 4; ++i )
+ { int diff = xywh[i];
+ if ( diff & ~1 )
+ dp = cmd_putw((diff < 0 ? -diff : diff), dp);
+ }
+ for ( i = 0; i < h; ++i )
+ { memcpy(dp, data + i * raster, nbytes);
+ dp += nbytes;
+ }
+ return 0;
+}
diff --git a/pstoraster/gxclio.h b/pstoraster/gxclio.h
new file mode 100644
index 000000000..7fc01f831
--- /dev/null
+++ b/pstoraster/gxclio.h
@@ -0,0 +1,70 @@
+/* Copyright (C) 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxclio.h */
+/* I/O interface for command lists */
+
+#ifndef gxclio_INCLUDED
+# define gxclio_INCLUDED
+
+/*
+ * We intend that there be two implementations of the I/O interface for
+ * command lists -- one suitable for embedded systems, which stores the
+ * "files" in RAM, and one suitable for other systems, which uses an
+ * external file system -- with the choice made at compile/link time.
+ * This header file defines the API between the command list code proper
+ * and its I/O interface.
+ */
+
+typedef void *clist_file_ptr; /* We can't do any better than this. */
+
+/* ---------------- Open/close/unlink ---------------- */
+
+int clist_open_scratch(P4(char *fname, clist_file_ptr *pcf, gs_memory_t *mem,
+ bool ok_to_compress));
+
+void clist_fclose_and_unlink(P2(clist_file_ptr cf, const char *fname));
+
+/* ---------------- Writing ---------------- */
+
+/* clist_space_available returns min(requested, available). */
+long clist_space_available(P1(long requested));
+
+int clist_fwrite_chars(P3(const void *data, uint len, clist_file_ptr cf));
+
+/* ---------------- Reading ---------------- */
+
+int clist_fread_chars(P3(void *data, uint len, clist_file_ptr cf));
+
+/* ---------------- Position/status ---------------- */
+
+/* clist_ferror_code returns an error code per gserrors.h, not a Boolean. */
+int clist_ferror_code(P1(clist_file_ptr cf));
+
+long clist_ftell(P1(clist_file_ptr cf));
+
+void clist_rewind(P2(clist_file_ptr cf, bool discard_data));
+
+int clist_fseek(P3(clist_file_ptr cf, long offset, int mode));
+
+#endif /* gxclio_INCLUDED */
diff --git a/pstoraster/gxclip2.c b/pstoraster/gxclip2.c
new file mode 100644
index 000000000..303a812d1
--- /dev/null
+++ b/pstoraster/gxclip2.c
@@ -0,0 +1,362 @@
+/* Copyright (C) 1993, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxclip2.c */
+/* Mask clipping for patterns */
+#include "memory_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gsstruct.h"
+#include "gxdevice.h"
+#include "gxdevmem.h"
+#include "gxclip2.h"
+
+private_st_device_tile_clip();
+
+/* Device procedures */
+private dev_proc_fill_rectangle(tile_clip_fill_rectangle);
+private dev_proc_copy_mono(tile_clip_copy_mono);
+private dev_proc_copy_color(tile_clip_copy_color);
+private dev_proc_copy_alpha(tile_clip_copy_alpha);
+private dev_proc_strip_copy_rop(tile_clip_strip_copy_rop);
+
+/* The device descriptor. */
+private const gx_device_tile_clip gs_tile_clip_device =
+{ std_device_std_body_open(gx_device_tile_clip, 0, "tile clipper",
+ 0, 0, 1, 1),
+ { gx_default_open_device,
+ gx_forward_get_initial_matrix,
+ gx_default_sync_output,
+ gx_default_output_page,
+ gx_default_close_device,
+ gx_forward_map_rgb_color,
+ gx_forward_map_color_rgb,
+ tile_clip_fill_rectangle,
+ gx_default_tile_rectangle,
+ tile_clip_copy_mono,
+ tile_clip_copy_color,
+ gx_default_draw_line,
+ gx_default_get_bits,
+ gx_forward_get_params,
+ gx_forward_put_params,
+ gx_forward_map_cmyk_color,
+ gx_forward_get_xfont_procs,
+ gx_forward_get_xfont_device,
+ gx_forward_map_rgb_alpha_color,
+ gx_forward_get_page_device,
+ gx_forward_get_alpha_bits,
+ tile_clip_copy_alpha,
+ gx_forward_get_band,
+ gx_default_copy_rop,
+ gx_default_fill_path,
+ gx_default_stroke_path,
+ gx_default_fill_mask,
+ gx_default_fill_trapezoid,
+ gx_default_fill_parallelogram,
+ gx_default_fill_triangle,
+ gx_default_draw_thin_line,
+ gx_default_begin_image,
+ gx_default_image_data,
+ gx_default_end_image,
+ gx_default_strip_tile_rectangle,
+ tile_clip_strip_copy_rop,
+ }
+};
+
+/* Initialize a tile clipping device from a mask. */
+int
+tile_clip_initialize(gx_device_tile_clip *cdev, const gx_strip_bitmap *tiles,
+ gx_device *idev, int px, int py)
+{ int buffer_width = tiles->size.x;
+ int buffer_height =
+ tile_clip_buffer_size / (tiles->raster + sizeof(byte *));
+
+ *cdev = gs_tile_clip_device;
+ cdev->width = idev->width;
+ cdev->height = idev->height;
+ cdev->color_info = idev->color_info;
+ cdev->target = idev;
+ cdev->tiles = *tiles;
+ tile_clip_set_phase(cdev, px, py);
+ if ( buffer_height > tiles->size.y )
+ buffer_height = tiles->size.y;
+ gs_make_mem_mono_device(&cdev->mdev, 0, 0);
+ for ( ; ; )
+ { if ( buffer_height <= 0 )
+ return_error(gs_error_rangecheck);
+ cdev->mdev.width = buffer_width;
+ cdev->mdev.height = buffer_height;
+ if ( gdev_mem_bitmap_size(&cdev->mdev) <= tile_clip_buffer_size )
+ break;
+ buffer_height--;
+ }
+ cdev->mdev.base = cdev->buffer.bytes;
+ return (*dev_proc(&cdev->mdev, open_device))((gx_device *)&cdev->mdev);
+}
+
+/* Set the phase of the tile. */
+void
+tile_clip_set_phase(gx_device_tile_clip *cdev, int px, int py)
+{ cdev->phase.x = px;
+ cdev->phase.y = py;
+}
+
+#define cdev ((gx_device_tile_clip *)dev)
+
+/* Fill a rectangle by tiling with the mask. */
+private int
+tile_clip_fill_rectangle(gx_device *dev, int x, int y, int w, int h,
+ gx_color_index color)
+{ gx_device *tdev = cdev->target;
+ return (*dev_proc(tdev, strip_tile_rectangle))(tdev, &cdev->tiles,
+ x, y, w, h,
+ gx_no_color_index, color, cdev->phase.x, cdev->phase.y);
+}
+
+/* Calculate the X offset corresponding to a given Y, taking the phase */
+/* and shift into account. */
+#define x_offset(ty, cdev)\
+ ((cdev)->phase.x + (((ty) + (cdev)->phase.y) / (cdev)->tiles.rep_height) *\
+ (cdev)->tiles.rep_shift)
+
+/* Copy a monochrome bitmap. We divide it up into maximal chunks */
+/* that line up with a single tile, and then do the obvious Boolean */
+/* combination of the tile mask and the source. */
+private int
+tile_clip_copy_mono(gx_device *dev,
+ const byte *data, int sourcex, int raster, gx_bitmap_id id,
+ int x, int y, int w, int h,
+ gx_color_index color0, gx_color_index color1)
+{ gx_color_index color, mcolor0, mcolor1;
+ int ty, ny;
+ int code;
+ if ( color1 != gx_no_color_index )
+ { if ( color0 != gx_no_color_index )
+ { /* Pre-fill with color0. */
+ code = tile_clip_fill_rectangle(dev, x, y, w, h, color0);
+ if ( code < 0 )
+ return code;
+ }
+ color = color1;
+ mcolor0 = 0, mcolor1 = gx_no_color_index;
+ }
+ else if ( color0 != gx_no_color_index )
+ { color = color0;
+ mcolor0 = gx_no_color_index, mcolor1 = 0;
+ }
+ else
+ return 0;
+ for ( ty = y; ty < y + h; ty += ny )
+ { int tx, nx;
+ int cy = (ty + cdev->phase.y) % cdev->tiles.rep_height;
+ int xoff = x_offset(ty, cdev);
+
+ ny = min(y + h - ty, cdev->tiles.size.y - cy);
+ if ( ny > cdev->mdev.height )
+ ny = cdev->mdev.height;
+ for ( tx = x; tx < x + w; tx += nx )
+ { int cx = (tx + xoff) % cdev->tiles.rep_width;
+ nx = min(x + w - tx, cdev->tiles.size.x - cx);
+ /* Copy a tile slice to the memory device buffer. */
+ memcpy(cdev->buffer.bytes,
+ cdev->tiles.data + cy * cdev->tiles.raster,
+ cdev->tiles.raster * ny);
+ /* Intersect the tile with the source data. */
+ /* mcolor0 and mcolor1 invert the data if needed. */
+ /* This call can't fail. */
+ (*dev_proc(&cdev->mdev, copy_mono))((gx_device *)&cdev->mdev,
+ data + (ty - y) * raster, sourcex + tx - x,
+ raster, gx_no_bitmap_id,
+ cx, 0, nx, ny, mcolor0, mcolor1);
+ /* Now copy the color through the double mask. */
+ code = (*dev_proc(cdev->target, copy_mono))(cdev->target,
+ cdev->buffer.bytes, cx, cdev->tiles.raster,
+ gx_no_bitmap_id,
+ tx, ty, nx, ny, gx_no_color_index, color);
+ if ( code < 0 )
+ return code;
+ }
+ }
+ return 0;
+}
+
+/* Copy a color rectangle. We can't use the BitBlt tricks; we have to */
+/* scan for runs of 1s. There are many obvious ways to speed this up; */
+/* we'll implement some if we need to. */
+private int
+tile_clip_copy_color(gx_device *dev,
+ const byte *data, int sourcex, int raster, gx_bitmap_id id,
+ int x, int y, int w, int h)
+{ const byte *data_row = data;
+ int cy = (y + cdev->phase.y) % cdev->tiles.rep_height;
+ const byte *tile_row = cdev->tiles.data + cy * cdev->tiles.raster;
+ int ty;
+
+ for ( ty = y; ty < y + h; ty++, data_row += raster )
+ { int cx = (x + x_offset(ty, cdev)) % cdev->tiles.rep_width;
+ const byte *tp = tile_row + (cx >> 3);
+ byte tbit = 0x80 >> (cx & 7);
+ int tx;
+ int code;
+
+ for ( tx = x; tx < x + w; )
+ { int tx1;
+#define t_next()\
+ if ( ++cx == cdev->tiles.size.x )\
+ cx = 0, tp = tile_row, tbit = 0x80;\
+ else if ( (tbit >>= 1) == 0 )\
+ tp++, tbit = 0x80;\
+ tx++
+ /* Skip a run of 0s. */
+ while ( tx < x + w && (*tp & tbit) == 0 )
+ { t_next();
+ }
+ if ( tx == x + w )
+ break;
+ /* Scan a run of 1s. */
+ tx1 = tx;
+ do
+ { t_next();
+ }
+ while ( tx < x + w && (*tp & tbit) != 0 );
+ /* Copy the run. */
+ code = (*dev_proc(cdev->target, copy_color))(cdev->target,
+ data_row, sourcex + tx1 - x, raster,
+ gx_no_bitmap_id, tx1, ty, tx - tx1, 1);
+ if ( code < 0 )
+ return code;
+ }
+ if ( ++cy == cdev->tiles.size.y )
+ cy = 0, tile_row = cdev->tiles.data;
+ else
+ tile_row += cdev->tiles.raster;
+ }
+
+ return 0;
+}
+
+/* Copy an alpha rectangle similarly. */
+private int
+tile_clip_copy_alpha(gx_device *dev,
+ const byte *data, int sourcex, int raster, gx_bitmap_id id,
+ int x, int y, int w, int h, gx_color_index color, int depth)
+{ const byte *data_row = data;
+ int ty;
+
+ for ( ty = y; ty < y + h; ty++, data_row += raster )
+ { const byte *tile_row = cdev->tiles.data +
+ ((ty + cdev->phase.y) % cdev->tiles.rep_height) *
+ cdev->tiles.raster;
+ int cx = (x + x_offset(ty, cdev)) % cdev->tiles.rep_width;
+ const byte *tp = tile_row + (cx >> 3);
+ byte tbit = 0x80 >> (cx & 7);
+ int tx;
+ int code;
+
+ for ( tx = x; tx < x + w; )
+ { int tx1;
+#define t_next()\
+ if ( ++cx == cdev->tiles.size.x )\
+ cx = 0, tp = tile_row, tbit = 0x80;\
+ else if ( (tbit >>= 1) == 0 )\
+ tp++, tbit = 0x80;\
+ tx++
+ /* Skip a run of 0s. */
+ while ( tx < x + w && (*tp & tbit) == 0 )
+ { t_next();
+ }
+ if ( tx == x + w )
+ break;
+ /* Scan a run of 1s. */
+ tx1 = tx;
+ do
+ { t_next();
+ }
+ while ( tx < x + w && (*tp & tbit) != 0 );
+ /* Copy the run. */
+ code = (*dev_proc(cdev->target, copy_alpha))(cdev->target,
+ data_row, sourcex + tx1 - x, raster,
+ gx_no_bitmap_id, tx1, ty, tx - tx1, 1,
+ color, depth);
+ if ( code < 0 )
+ return code;
+ }
+ }
+
+ return 0;
+}
+
+/* Copy a RasterOp rectangle similarly. */
+private int
+tile_clip_strip_copy_rop(gx_device *dev,
+ const byte *data, int sourcex, uint raster, 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)
+{ const byte *data_row = data;
+ int ty;
+
+ for ( ty = y; ty < y + h; ty++, data_row += raster )
+ { const byte *tile_row = cdev->tiles.data +
+ ((ty + cdev->phase.y) % cdev->tiles.rep_height) *
+ cdev->tiles.raster;
+ int cx = (x + x_offset(ty, cdev)) % cdev->tiles.rep_width;
+ const byte *tp = tile_row + (cx >> 3);
+ byte tbit = 0x80 >> (cx & 7);
+ int tx;
+ int code;
+
+ for ( tx = x; tx < x + w; )
+ { int tx1;
+#define t_next()\
+ if ( ++cx == cdev->tiles.size.x )\
+ cx = 0, tp = tile_row, tbit = 0x80;\
+ else if ( (tbit >>= 1) == 0 )\
+ tp++, tbit = 0x80;\
+ tx++
+ /* Skip a run of 0s. */
+ while ( tx < x + w && (*tp & tbit) == 0 )
+ { t_next();
+ }
+ if ( tx == x + w )
+ break;
+ /* Scan a run of 1s. */
+ tx1 = tx;
+ do
+ { t_next();
+ }
+ while ( tx < x + w && (*tp & tbit) != 0 );
+ /* Copy the run. */
+ code = (*dev_proc(cdev->target, strip_copy_rop))
+ (cdev->target,
+ data_row, sourcex + tx1 - x, raster,
+ gx_no_bitmap_id, scolors, textures, tcolors,
+ tx1, ty, tx - tx1, 1, phase_x, phase_y, lop);
+ if ( code < 0 )
+ return code;
+ }
+ }
+
+ return 0;
+}
diff --git a/pstoraster/gxclip2.h b/pstoraster/gxclip2.h
new file mode 100644
index 000000000..626777295
--- /dev/null
+++ b/pstoraster/gxclip2.h
@@ -0,0 +1,60 @@
+/* Copyright (C) 1993, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxclip2.h */
+/* Mask clipping device and interface */
+/* Requires gxdevice.h, gxdevmem.h */
+
+/*
+ * Patterns that don't completely fill their bounding boxes require
+ * the ability to clip against a tiled mask. For now, we only support
+ * tiling parallel to the axes.
+ */
+
+#define tile_clip_buffer_request 128
+#define tile_clip_buffer_size\
+ ((tile_clip_buffer_request / arch_sizeof_long) * arch_sizeof_long)
+typedef struct gx_device_tile_clip_s {
+ gx_device_forward_common; /* target is set by client */
+ gx_strip_bitmap tiles;
+ gx_device_memory mdev; /* for tile buffer for copy_mono */
+ gs_int_point phase; /* device space origin relative */
+ /* to tile (backwards from gstate phase) */
+ /* Ensure that the buffer is long-aligned. */
+ union _b {
+ byte bytes[tile_clip_buffer_size];
+ ulong longs[tile_clip_buffer_size / arch_sizeof_long];
+ } buffer;
+} gx_device_tile_clip;
+#define private_st_device_tile_clip() /* in gxclip2.c */\
+ gs_private_st_simple(st_device_tile_clip, gx_device_tile_clip,\
+ "gx_device_tile_clip")
+
+/* Initialize a tile clipping device from a mask. */
+/* We supply an explicit phase. */
+int tile_clip_initialize(P5(gx_device_tile_clip *, const gx_strip_bitmap *,
+ gx_device *, int, int));
+
+/* Set the phase of the tile -- used in the tiling loop when */
+/* the tile doesn't simply fill the plane. */
+void tile_clip_set_phase(P3(gx_device_tile_clip *, int, int));
diff --git a/pstoraster/gxclist.c b/pstoraster/gxclist.c
new file mode 100644
index 000000000..8725db3ad
--- /dev/null
+++ b/pstoraster/gxclist.c
@@ -0,0 +1,1138 @@
+/*
+ Copyright 1993-1999 by Easy Software Products.
+ Copyright (C) 1991, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxclist.c */
+/* Command list writing for Ghostscript. */
+#include "memory_.h"
+#include "gx.h"
+#include "gpcheck.h"
+#include "gserrors.h"
+#include "gsbitops.h"
+#include "gsutil.h" /* for gs_next_ids */
+#include "gxdevice.h"
+#include "gxdevmem.h" /* must precede gxcldev.h */
+#include "gxcldev.h"
+
+#define cdev cwdev
+
+/* Forward declarations of procedures */
+private dev_proc_open_device(clist_open);
+private dev_proc_output_page(clist_output_page);
+private dev_proc_fill_rectangle(clist_fill_rectangle);
+private dev_proc_copy_mono(clist_copy_mono);
+private dev_proc_copy_color(clist_copy_color);
+private dev_proc_copy_alpha(clist_copy_alpha);
+extern dev_proc_get_bits(clist_get_bits); /* in gxclread.c */
+private dev_proc_get_band(clist_get_band);
+private dev_proc_strip_tile_rectangle(clist_strip_tile_rectangle);
+private dev_proc_strip_copy_rop(clist_strip_copy_rop);
+
+/* The device procedures */
+gx_device_procs gs_clist_device_procs =
+{ clist_open,
+ gx_forward_get_initial_matrix,
+ gx_default_sync_output,
+ clist_output_page,
+ gx_default_close_device,
+ gx_forward_map_rgb_color,
+ gx_forward_map_color_rgb,
+ clist_fill_rectangle,
+ gx_default_tile_rectangle,
+ clist_copy_mono,
+ clist_copy_color,
+ gx_default_draw_line,
+ clist_get_bits,
+ gx_forward_get_params,
+ gx_forward_put_params,
+ gx_forward_map_cmyk_color,
+ gx_forward_get_xfont_procs,
+ gx_forward_get_xfont_device,
+ gx_forward_map_rgb_alpha_color,
+ gx_forward_get_page_device,
+ gx_forward_get_alpha_bits,
+ clist_copy_alpha,
+ clist_get_band,
+ gx_default_copy_rop,
+ gx_default_fill_path,
+ gx_default_stroke_path,
+ gx_default_fill_mask,
+ gx_default_fill_trapezoid,
+ gx_default_fill_parallelogram,
+ gx_default_fill_triangle,
+ gx_default_draw_thin_line,
+ gx_default_begin_image,
+ gx_default_image_data,
+ gx_default_end_image,
+ clist_strip_tile_rectangle,
+ clist_strip_copy_rop,
+};
+
+/* ------ Define the command set and syntax ------ */
+
+/* Define the clipping enable/disable opcodes. */
+/* The path extensions initialize these to their proper values. */
+byte cmd_opvar_disable_clip = 0xff;
+byte cmd_opvar_enable_clip = 0xff;
+
+#ifdef DEBUG
+const char *cmd_op_names[16] = { cmd_op_name_strings };
+private const char *cmd_misc_op_names[16] = { cmd_misc_op_name_strings };
+const char **cmd_sub_op_names[16] =
+{ cmd_misc_op_names, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0
+};
+private ulong far_data cmd_op_counts[256];
+private ulong far_data cmd_op_sizes[256];
+private ulong cmd_tile_reset, cmd_tile_found, cmd_tile_added;
+extern ulong cmd_diffs[5]; /* in gxclpath.c */
+private ulong cmd_same_band, cmd_other_band;
+int
+cmd_count_op(int op, uint size)
+{ cmd_op_counts[op]++;
+ cmd_op_sizes[op] += size;
+ if ( gs_debug_c('L') )
+ { const char **sub = cmd_sub_op_names[op >> 4];
+ if ( sub )
+ dprintf2(", %s(%u)\n", sub[op & 0xf], size);
+ else
+ dprintf3(", %s %d(%u)\n", cmd_op_names[op >> 4], op & 0xf, size);
+ fflush(dstderr);
+ }
+ return op;
+}
+void
+cmd_uncount_op(int op, uint size)
+{ cmd_op_counts[op]--;
+ cmd_op_sizes[op] -= size;
+}
+#endif
+
+/* Initialization for imager state. */
+/* The initial scale is arbitrary. */
+const gs_imager_state clist_imager_state_initial =
+ { gs_imager_state_initial(300.0 / 72.0) };
+
+/* Initialize the device state */
+private int
+clist_open(gx_device *dev)
+{ /*
+ * The buffer area (data, data_size) holds a bitmap cache when
+ * both writing and reading. The rest of the space is used for
+ * the command buffer and band state bookkeeping when writing,
+ * and for the rendering buffer (image device) when reading.
+ * For the moment, we divide the space up arbitrarily, except that
+ * we allocate less space for the bitmap cache if the device
+ * doesn't need halftoning.
+ *
+ * This routine requires only data, data_size, and target
+ * to have been set in the device structure, and is idempotent,
+ * so it can be used to check whether a given-size buffer
+ * is large enough.
+ */
+ byte *data = cdev->data;
+ uint size = cdev->data_size;
+/**** MRS - 64-bit align all data structures! ****/
+#define alloc_data(n) data += ((n) + 7) & ~7, size -= ((n) + 7) & ~7
+ gx_device *target = cdev->target;
+ uint raster, nbands, band;
+ gx_clist_state *states;
+ ulong state_size;
+ static const gx_clist_state cls_initial = { cls_initial_values };
+
+ cdev->ymin = cdev->ymax = -1; /* render_init not done yet */
+ { uint bits_size = (size / 5) & -align_cached_bits_mod; /* arbitrary */
+ uint hc;
+ if ( (gx_device_has_color(target) ? target->color_info.max_color :
+ target->color_info.max_gray) >= 31
+ )
+ { /* No halftones -- cache holds only Patterns & characters. */
+ bits_size -= bits_size >> 2;
+ }
+#define min_bits_size 1024
+ if ( bits_size < min_bits_size )
+ bits_size = min_bits_size;
+ /* Partition the bits area between the hash table and the */
+ /* actual bitmaps. The per-bitmap overhead is about 24 bytes; */
+ /* if the average character size is 10 points, its bitmap takes */
+ /* about 24 + 0.5 * 10/72 * xdpi * 10/72 * ydpi / 8 bytes */
+ /* (the 0.5 being a fudge factor to account for characters */
+ /* being narrower than they are tall), which gives us */
+ /* a guideline for the size of the hash table. */
+ { uint avg_char_size =
+ (uint)(dev->x_pixels_per_inch * dev->y_pixels_per_inch *
+ (0.5 * 10/72 * 10/72 / 8)) + 24;
+ hc = bits_size / avg_char_size;
+ }
+ while ( (hc + 1) & hc )
+ hc |= hc >> 1; /* make mask (power of 2 - 1) */
+ if ( hc < 0xff )
+ hc = 0xff; /* make allowance for halftone tiles */
+ else if ( hc > 0xfff )
+ hc = 0xfff; /* cmd_op_set_tile_index has 12-bit operand */
+ cdev->tile_hash_mask = hc;
+ cdev->tile_max_count = hc - (hc >> 2);
+ hc = (hc + 1) * sizeof(tile_hash);
+ cdev->tile_table = (tile_hash *)data;
+ memset(data, 0, hc);
+ alloc_data(hc);
+ bits_size -= hc;
+ gx_bits_cache_chunk_init(&cdev->chunk, data, bits_size);
+ alloc_data(bits_size);
+ gx_bits_cache_init(&cdev->bits, &cdev->chunk);
+ }
+ raster = gx_device_raster(target, 1) + sizeof(byte *);
+ cdev->band_height = size / raster;
+ if ( cdev->band_height == 0 ) /* can't even fit one scan line */
+ return_error(gs_error_limitcheck);
+ nbands = target->height / cdev->band_height + 1;
+ cdev->nbands = nbands;
+ if_debug4('l', "[l]width=%d, raster=%d, band_height=%d, nbands=%d\n",
+ target->width, raster, cdev->band_height, cdev->nbands);
+ state_size = nbands * (ulong)sizeof(gx_clist_state);
+ if ( state_size + sizeof(cmd_prefix) + cmd_largest_size + raster + 4 > size ) /* not enough room */
+ return_error(gs_error_limitcheck);
+ cdev->mdata = data;
+ cdev->states = states = (gx_clist_state *)data;
+ alloc_data((uint)state_size);
+ cdev->cbuf = data;
+ cdev->cnext = data;
+ cdev->cend = data + size;
+ cdev->ccl = 0;
+ cdev->all_band_list.head = cdev->all_band_list.tail = 0;
+ for ( band = 0; band < nbands; band++)
+ *states++ = cls_initial;
+#undef alloc_data
+ /* Round up the size of the band mask so that */
+ /* the bits, which follow it, stay aligned. */
+ cdev->tile_band_mask_size =
+ ((nbands + (align_bitmap_mod * 8 - 1)) >> 3) &
+ ~(align_bitmap_mod - 1);
+ /* Initialize the all-band parameters to impossible values, */
+ /* to force them to be written the first time they are used. */
+ memset(&cdev->tile_params, 0, sizeof(cdev->tile_params));
+ cdev->tile_depth = 0;
+ cdev->imager_state = clist_imager_state_initial;
+ cdev->clip_path = NULL;
+ cdev->clip_path_id = gx_no_bitmap_id;
+ cdev->color_space = 0;
+ return 0;
+}
+
+/* Clean up after rendering a page. */
+private int
+clist_output_page(gx_device *dev, int num_copies, int flush)
+{ if ( flush )
+ { clist_rewind(cdev->cfile, true);
+ clist_rewind(cdev->bfile, true);
+ cdev->bfile_end_pos = 0;
+ }
+ else
+ { clist_fseek(cdev->cfile, 0L, SEEK_END);
+ clist_fseek(cdev->bfile, 0L, SEEK_END);
+ }
+ return clist_open(dev); /* reinitialize */
+}
+
+/* Print statistics. */
+#ifdef DEBUG
+void
+cmd_print_stats(void)
+{ int ci, cj;
+ dprintf3("[l]counts: reset = %lu, found = %lu, added = %lu\n",
+ cmd_tile_reset, cmd_tile_found, cmd_tile_added);
+ dprintf5(" diff 2.5 = %lu, 3 = %lu, 4 = %lu, 2 = %lu, >4 = %lu\n",
+ cmd_diffs[0], cmd_diffs[1], cmd_diffs[2], cmd_diffs[3],
+ cmd_diffs[4]);
+ dprintf2(" same_band = %lu, other_band = %lu\n",
+ cmd_same_band, cmd_other_band);
+ for ( ci = 0; ci < 0x100; ci += 0x10 )
+ { const char **sub = cmd_sub_op_names[ci >> 4];
+ if ( sub != 0 )
+ { dprintf1("[l] %s =", cmd_op_names[ci >> 4]);
+ for ( cj = ci; cj < ci + 0x10; cj += 2 )
+ dprintf6("\n\t%s = %lu(%lu), %s = %lu(%lu)",
+ sub[cj-ci],
+ cmd_op_counts[cj], cmd_op_sizes[cj],
+ sub[cj-ci+1],
+ cmd_op_counts[cj+1], cmd_op_sizes[cj+1]);
+ }
+ else
+ { ulong tcounts = 0, tsizes = 0;
+ for ( cj = ci; cj < ci + 0x10; cj++ )
+ tcounts += cmd_op_counts[cj],
+ tsizes += cmd_op_sizes[cj];
+ dprintf3("[l] %s (%lu,%lu) =\n\t",
+ cmd_op_names[ci >> 4], tcounts, tsizes);
+ for ( cj = ci; cj < ci + 0x10; cj++ )
+ if ( cmd_op_counts[cj] == 0 )
+ dputs(" -");
+ else
+ dprintf2(" %lu(%lu)", cmd_op_counts[cj],
+ cmd_op_sizes[cj]);
+ }
+ dputs("\n");
+ }
+}
+#endif /* DEBUG */
+
+/* ------ Writing ------ */
+
+/* Utilities */
+
+#define cmd_set_rect(rect)\
+ ((rect).x = x, (rect).y = y,\
+ (rect).width = width, (rect).height = height)
+
+/* Write the commands for one band. */
+private int
+cmd_write_band(gx_device_clist_writer *cldev, int band, cmd_list *pcl)
+{ const cmd_prefix *cp = pcl->head;
+
+ if ( cp != 0 )
+ { clist_file_ptr cfile = cldev->cfile;
+ clist_file_ptr bfile = cldev->bfile;
+ cmd_block cb;
+ char end = cmd_count_op(cmd_opv_end_run, 1);
+ int code;
+
+ cb.band = band;
+ cb.pos = clist_ftell(cfile);
+ if_debug2('l', "[l]writing for band %d at %ld\n",
+ band, cb.pos);
+ clist_fwrite_chars(&cb, sizeof(cb), bfile);
+ pcl->tail->next = 0; /* terminate the list */
+ for ( ; cp != 0; cp = cp->next )
+ {
+#ifdef DEBUG
+ if ( (const byte *)cp < cldev->cbuf ||
+ (const byte *)cp >= cldev->cend ||
+ cp->size > cldev->cend - (const byte *)cp
+ )
+ { lprintf1("cmd_write_band error at 0x%lx\n", (ulong)cp);
+ return_error(gs_error_Fatal);
+ }
+#endif
+ clist_fwrite_chars(cp + 1, cp->size, cfile);
+ }
+ pcl->head = pcl->tail = 0;
+ clist_fwrite_chars(&end, 1, cfile);
+ process_interrupts();
+ if ( (code = clist_ferror_code(bfile)) < 0 ||
+ (code = clist_ferror_code(cfile)) < 0
+ )
+ return_error(code);
+ }
+ return 0;
+}
+
+/* Write out the buffered commands, and reset the buffer. */
+private int
+cmd_write_buffer(gx_device_clist_writer *cldev)
+{ int nbands = cldev->nbands;
+ gx_clist_state *pcls;
+ int band;
+ int code = cmd_write_band(cldev, cmd_band_all, &cldev->all_band_list);
+
+ for ( band = 0, pcls = cldev->states;
+ code >= 0 && band < nbands; band++, pcls++
+ )
+ code = cmd_write_band(cldev, band, &pcls->list);
+ cldev->cnext = cldev->cbuf;
+ cldev->ccl = 0;
+ cldev->all_band_list.head = cldev->all_band_list.tail = 0;
+#ifdef DEBUG
+ if ( gs_debug_c('l') )
+ cmd_print_stats();
+#endif
+ return_check_interrupt(code);
+}
+/* Export under a different name for gxclread.c */
+int
+clist_flush_buffer(gx_device_clist_writer *cldev)
+{ return cmd_write_buffer(cldev);
+}
+
+/* Add a command to the appropriate band list, */
+/* and allocate space for its data. */
+/* Return the pointer to the data area. */
+/* If an error occurs, set cldev->error_code and return 0. */
+#define cmd_headroom (sizeof(cmd_prefix) + arch_align_ptr_mod)
+byte *
+cmd_put_list_op(gx_device_clist_writer *cldev, cmd_list *pcl, uint size)
+{ byte *dp = cldev->cnext;
+ if ( size + cmd_headroom > cldev->cend - dp )
+ { int code = cldev->error_code = cmd_write_buffer(cldev);
+ if ( code < 0 )
+ return 0;
+ return cmd_put_list_op(cldev, pcl, size);
+ }
+ if ( cldev->ccl == pcl )
+ { /* We're adding another command for the same band. */
+ /* Tack it onto the end of the previous one. */
+ cmd_count_add1(cmd_same_band);
+#ifdef DEBUG
+ if ( pcl->tail->size > dp - (byte *)(pcl->tail + 1) )
+ { lprintf1("cmd_put_list_op error at 0x%lx\n", (ulong)pcl->tail);
+ }
+#endif
+ pcl->tail->size += size;
+ }
+ else
+ { /* Skip to an appropriate alignment boundary. */
+ /* (We assume the command buffer itself is aligned.) */
+ cmd_prefix *cp =
+ (cmd_prefix *)(dp +
+ ((cldev->cbuf - dp) & (arch_align_ptr_mod - 1)));
+ cmd_count_add1(cmd_other_band);
+ dp = (byte *)(cp + 1);
+ if ( pcl->tail != 0 )
+ {
+#ifdef DEBUG
+ if ( pcl->tail < pcl->head ||
+ pcl->tail->size > dp - (byte *)(pcl->tail + 1)
+ )
+ { lprintf1("cmd_put_list_op error at 0x%lx\n",
+ (ulong)pcl->tail);
+ }
+#endif
+ pcl->tail->next = cp;
+ }
+ else
+ pcl->head = cp;
+ pcl->tail = cp;
+ cldev->ccl = pcl;
+ cp->size = size;
+ }
+ cldev->cnext = dp + size;
+ return dp;
+}
+#ifdef DEBUG
+byte *
+cmd_put_op(gx_device_clist_writer *cldev, gx_clist_state *pcls, uint size)
+{ if_debug3('L', "[L]band %d: size=%u, left=%u",
+ (int)(pcls - cldev->states),
+ size, (uint)(cldev->cend - cldev->cnext));
+ return cmd_put_list_op(cldev, &pcls->list, size);
+}
+#endif
+
+/* Add a command for all bands. */
+byte *
+cmd_put_all_op(gx_device_clist_writer *cldev, uint size)
+{ cmd_prefix *tail;
+ if_debug2('L', "[L]all-band: size=%u, left=%u",
+ size, (uint)(cldev->cend - cldev->cnext));
+ if ( cldev->all_band_list.head == 0 ||
+ (tail = cldev->all_band_list.tail,
+ cldev->cnext != (byte *)(tail + 1) + tail->size)
+ )
+ { if ( (cldev->error_code = cmd_write_buffer(cldev)) < 0 )
+ return 0;
+ }
+ return cmd_put_list_op(cldev, &cldev->all_band_list, size);
+}
+
+/* Write a variable-size positive integer. */
+int
+cmd_size_w(register uint w)
+{ register int size = 1;
+ while ( w > 0x7f ) w >>= 7, size++;
+ return size;
+}
+byte *
+cmd_put_w(register uint w, register byte *dp)
+{ while ( w > 0x7f ) *dp++ = w | 0x80, w >>= 7;
+ *dp = w;
+ return dp + 1;
+}
+
+/* Write a rectangle. */
+int
+cmd_size_rect(register const gx_cmd_rect *prect)
+{ return
+ cmd_sizew(prect->x) + cmd_sizew(prect->y) +
+ cmd_sizew(prect->width) + cmd_sizew(prect->height);
+}
+private byte *
+cmd_put_rect(register const gx_cmd_rect *prect, register byte *dp)
+{ cmd_putw(prect->x, dp);
+ cmd_putw(prect->y, dp);
+ cmd_putw(prect->width, dp);
+ cmd_putw(prect->height, dp);
+ return dp;
+}
+
+int
+cmd_write_rect_cmd(gx_device_clist_writer *cldev, gx_clist_state *pcls,
+ int op, int x, int y, int width, int height)
+{ int dx = x - pcls->rect.x;
+ int dy = y - pcls->rect.y;
+ int dwidth = width - pcls->rect.width;
+ int dheight = height - pcls->rect.height;
+#define check_range_xy(rmin, rmax)\
+ ((unsigned)(dx - rmin) <= (rmax - rmin) &&\
+ (unsigned)(dy - rmin) <= (rmax - rmin))
+#define check_range_w(rmin, rmax)\
+ ((unsigned)(dwidth - rmin) <= (rmax - rmin))
+#define check_ranges(rmin, rmax)\
+ (check_range_xy(rmin, rmax) && check_range_w(rmin, rmax) &&\
+ (unsigned)(dheight - rmin) <= (rmax - rmin))
+ cmd_set_rect(pcls->rect);
+ if ( dheight == 0 && check_range_w(cmd_min_dw_tiny, cmd_max_dw_tiny) &&
+ check_range_xy(cmd_min_dxy_tiny, cmd_max_dxy_tiny)
+ )
+ { byte op_tiny = op + 0x20 + dwidth - cmd_min_dw_tiny;
+ byte *dp;
+
+ if ( dx == width - dwidth && dy == 0 )
+ { set_cmd_put_op(dp, cldev, pcls, op_tiny + 8, 1);
+ }
+ else
+ { set_cmd_put_op(dp, cldev, pcls, op_tiny, 2);
+ dp[1] = (dx << 4) + dy - (cmd_min_dxy_tiny * 0x11);
+ }
+ }
+#define rmin cmd_min_short
+#define rmax cmd_max_short
+ else if ( check_ranges(rmin, rmax) )
+ { int dh = dheight - cmd_min_dxy_tiny;
+ byte *dp;
+ if ( (unsigned)dh <= cmd_max_dxy_tiny - cmd_min_dxy_tiny &&
+ dh != 0 && dy == 0
+ )
+ { op += dh;
+ set_cmd_put_op(dp, cldev, pcls, op + 0x10, 3);
+ if_debug3('L', " rs2:%d,%d,0,%d\n",
+ dx, dwidth, dheight);
+ }
+ else
+ { set_cmd_put_op(dp, cldev, pcls, op + 0x10, 5);
+ if_debug4('L', " rs4:%d,%d,%d,%d\n",
+ dx, dwidth, dy, dheight);
+ dp[3] = dy - rmin;
+ dp[4] = dheight - rmin;
+ }
+ dp[1] = dx - rmin;
+ dp[2] = dwidth - rmin;
+ }
+#undef rmin
+#undef rmax
+ else if ( dy >= -2 && dy <= 1 && dheight >= -2 && dheight <= 1 &&
+ (dy + dheight) != -4
+ )
+ { byte *dp;
+ int rcsize = 1 + cmd_sizew(x) + cmd_sizew(width);
+ set_cmd_put_op(dp, cldev, pcls,
+ op + ((dy + 2) << 2) + dheight + 2, rcsize);
+ ++dp;
+ cmd_put2w(x, width, dp);
+ }
+ else
+ { byte *dp;
+ int rcsize = 1 + cmd_size_rect(&pcls->rect);
+ set_cmd_put_op(dp, cldev, pcls, op, rcsize);
+ if_debug5('L', " r%d:%d,%d,%d,%d\n",
+ rcsize - 1, dx, dwidth, dy, dheight);
+ cmd_put_rect(&pcls->rect, dp + 1);
+ }
+ return 0;
+}
+
+/* Define the encodings of the different settable colors. */
+const clist_select_color_t
+ clist_select_color0 = {cmd_op_set_color0, cmd_opv_delta2_color0, 0},
+ clist_select_color1 = {cmd_op_set_color1, cmd_opv_delta2_color1, 0},
+ clist_select_tile_color0 = {cmd_op_set_color0, cmd_opv_delta2_color0, 1},
+ clist_select_tile_color1 = {cmd_op_set_color1, cmd_opv_delta2_color1, 1};
+int
+cmd_put_color(gx_device_clist_writer *cldev, gx_clist_state *pcls,
+ const clist_select_color_t *select,
+ gx_color_index color, gx_color_index *pcolor)
+{ byte *dp;
+ long diff = (long)color - (long)(*pcolor);
+ byte op, op_delta2;
+
+ if ( diff == 0 )
+ return 0;
+ if ( select->tile_color )
+ set_cmd_put_op(dp, cldev, pcls, cmd_opv_set_tile_color, 1);
+ op = select->set_op;
+ op_delta2 = select->delta2_op;
+ if ( color == gx_no_color_index )
+ { /*
+ * We must handle this specially, because it may take more
+ * bytes than the color depth.
+ */
+ set_cmd_put_op(dp, cldev, pcls, op + 15, 1);
+ }
+ else
+ { long delta;
+ byte operand;
+
+ switch ( (cldev->color_info.depth + 15) >> 3 )
+ {
+ case 5:
+ if ( !((delta = diff + cmd_delta1_32_bias) &
+ ~cmd_delta1_32_mask) &&
+ (operand =
+ (byte)((delta >> 23) + ((delta >> 18) & 1))) != 0 &&
+ operand != 15
+ )
+ { set_cmd_put_op(dp, cldev, pcls,
+ (byte)(op + operand), 2);
+ dp[1] = (byte)(((delta >> 10) & 0300) +
+ (delta >> 5) + delta);
+ break;
+ }
+ if ( !((delta = diff + cmd_delta2_32_bias) &
+ ~cmd_delta2_32_mask)
+ )
+ { set_cmd_put_op(dp, cldev, pcls, op_delta2, 3);
+ dp[1] = (byte)((delta >> 20) + (delta >> 16));
+ dp[2] = (byte)((delta >> 4) + delta);
+ break;
+ }
+ set_cmd_put_op(dp, cldev, pcls, op, 5);
+ *++dp = (byte)(color >> 24);
+ goto b3;
+ case 4:
+ if ( !((delta = diff + cmd_delta1_24_bias) &
+ ~cmd_delta1_24_mask) &&
+ (operand = (byte)(delta >> 16)) != 0 &&
+ operand != 15
+ )
+ { set_cmd_put_op(dp, cldev, pcls,
+ (byte)(op + operand), 2);
+ dp[1] = (byte)((delta >> 4) + delta);
+ break;
+ }
+ if ( !((delta = diff + cmd_delta2_24_bias) &
+ ~cmd_delta2_24_mask)
+ )
+ { set_cmd_put_op(dp, cldev, pcls, op_delta2, 3);
+ dp[1] = ((byte)(delta >> 13) & 0xf8) +
+ ((byte)(delta >> 11) & 7);
+ dp[2] = (byte)(((delta >> 3) & 0xe0) + delta);
+ break;
+ }
+ set_cmd_put_op(dp, cldev, pcls, op, 4);
+b3: *++dp = (byte)(color >> 16);
+ goto b2;
+ case 3:
+ set_cmd_put_op(dp, cldev, pcls, op, 3);
+b2: *++dp = (byte)(color >> 8);
+ goto b1;
+ case 2:
+ if ( diff >= -7 && diff < 7 )
+ { set_cmd_put_op(dp, cldev, pcls,
+ op + (int)diff + 8, 1);
+ break;
+ }
+ set_cmd_put_op(dp, cldev, pcls, op, 2);
+b1: dp[1] = (byte)color;
+ }
+ }
+ *pcolor = color;
+ return 0;
+}
+
+/* Put out a command to set the tile colors. */
+int
+cmd_set_tile_colors(gx_device_clist_writer *cldev, gx_clist_state *pcls,
+ gx_color_index color0, gx_color_index color1)
+{ if ( color0 != pcls->tile_colors[0] )
+ { int code = cmd_put_color(cldev, pcls,
+ &clist_select_tile_color0,
+ color0, &pcls->tile_colors[0]);
+ if ( code < 0 )
+ return code;
+ }
+ if ( color1 != pcls->tile_colors[1] )
+ { int code = cmd_put_color(cldev, pcls,
+ &clist_select_tile_color1,
+ color1, &pcls->tile_colors[1]);
+ if ( code < 0 )
+ return code;
+ }
+ return 0;
+}
+
+/* Put out a command to set the tile phase. */
+int
+cmd_set_tile_phase(gx_device_clist_writer *cldev, gx_clist_state *pcls,
+ int px, int py)
+{ int pcsize;
+ byte *dp;
+
+ pcls->tile_phase.x = px;
+ pcls->tile_phase.y = py;
+ pcsize = 1 + cmd_sizexy(pcls->tile_phase);
+ set_cmd_put_op(dp, cldev, pcls, (byte)cmd_opv_set_tile_phase, pcsize);
+ ++dp;
+ cmd_putxy(pcls->tile_phase, dp);
+ return 0;
+}
+
+/* Write a command to enable or disable the logical operation. */
+int
+cmd_put_enable_lop(gx_device_clist_writer *cldev, gx_clist_state *pcls,
+ int enable)
+{ byte *dp;
+ set_cmd_put_op(dp, cldev, pcls,
+ (byte)(enable ? cmd_opv_enable_lop :
+ cmd_opv_disable_lop),
+ 1);
+ pcls->lop_enabled = enable;
+ return 0;
+}
+
+/* Write a command to enable or disable clipping. */
+/* This routine is only called if the path extensions are included. */
+int
+cmd_put_enable_clip(gx_device_clist_writer *cldev, gx_clist_state *pcls,
+ int enable)
+{ byte *dp;
+ set_cmd_put_op(dp, cldev, pcls,
+ (byte)(enable ? cmd_opvar_enable_clip :
+ cmd_opvar_disable_clip),
+ 1);
+ pcls->clip_enabled = enable;
+ return 0;
+}
+
+/* Write a command to set the logical operation. */
+int
+cmd_set_lop(gx_device_clist_writer *cldev, gx_clist_state *pcls,
+ gs_logical_operation_t lop)
+{ byte *dp;
+ set_cmd_put_op(dp, cldev, pcls,
+ cmd_opv_set_lop, 1 + cmd_sizew(lop));
+ ++dp;
+ cmd_putw(lop, dp);
+ pcls->lop = lop;
+ return 0;
+}
+
+/* ---------------- Driver interface ---------------- */
+
+private int
+clist_fill_rectangle(gx_device *dev, int x, int y, int width, int height,
+ gx_color_index color)
+{ fit_fill(dev, x, y, width, height);
+ BEGIN_RECT
+ cmd_disable_lop(cdev, pcls);
+ if ( color != pcls->colors[1] )
+ { int code = cmd_put_color(cdev, pcls, &clist_select_color1,
+ color, &pcls->colors[1]);
+ if ( code < 0 )
+ return code;
+ }
+ { int code = cmd_write_rect_cmd(cdev, pcls, cmd_op_fill_rect, x, y,
+ width, height);
+ if ( code < 0 )
+ return code;
+ }
+ END_RECT
+ return 0;
+}
+
+private int
+clist_strip_tile_rectangle(gx_device *dev, const gx_strip_bitmap *tile,
+ int x, int y, int width, int height,
+ gx_color_index color0, gx_color_index color1, int px, int py)
+{ int depth =
+ (color1 == gx_no_color_index && color0 == gx_no_color_index ?
+ dev->color_info.depth : 1);
+ fit_fill(dev, x, y, width, height);
+ BEGIN_RECT
+ ulong offset_temp;
+
+ cmd_disable_lop(cdev, pcls);
+ if ( !cls_has_tile_id(cdev, pcls, tile->id, offset_temp) )
+ { if ( tile->id == gx_no_bitmap_id ||
+ clist_change_tile(cdev, pcls, tile, depth) < 0
+ )
+ { int code =
+ gx_default_strip_tile_rectangle(dev, tile,
+ x, y, width, height,
+ color0, color1, px, py);
+ if ( code < 0 )
+ return code;
+ goto endr;
+ }
+ }
+ if ( color0 != pcls->tile_colors[0] || color1 != pcls->tile_colors[1] )
+ { int code = cmd_set_tile_colors(cdev, pcls, color0, color1);
+ if ( code < 0 )
+ return code;
+ }
+ if ( px != pcls->tile_phase.x || py != pcls->tile_phase.y )
+ { int code = cmd_set_tile_phase(cdev, pcls, px, py);
+ if ( code < 0 )
+ return code;
+ }
+ { int code = cmd_write_rect_cmd(cdev, pcls, cmd_op_tile_rect, x, y,
+ width, height);
+ if ( code < 0 )
+ return code;
+ }
+endr: ;
+ END_RECT
+ return 0;
+}
+
+private int
+clist_copy_mono(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 color0, gx_color_index color1)
+{ int y0;
+ gx_bitmap_id orig_id = id;
+
+ fit_copy(dev, data, data_x, raster, id, x, y, width, height);
+ y0 = y;
+ BEGIN_RECT
+ int dx = data_x & 7;
+ int w1 = dx + width;
+ const byte *row = data + (y - y0) * raster + (data_x >> 3);
+ int code;
+
+ cmd_disable_lop(cdev, pcls);
+ cmd_disable_clip(cdev, pcls);
+ if ( color0 != pcls->colors[0] )
+ { code = cmd_set_color0(cdev, pcls, color0);
+ if ( code < 0 )
+ return code;
+ }
+ if ( color1 != pcls->colors[1] )
+ { code = cmd_set_color1(cdev, pcls, color1);
+ if ( code < 0 )
+ return code;
+ }
+ /* Don't bother to check for a possible cache hit: */
+ /* tile_rectangle and fill_mask handle those cases. */
+copy: { gx_cmd_rect rect;
+ int rsize;
+ byte op = (byte)cmd_op_copy_mono;
+ byte *dp;
+ uint csize;
+ int code;
+
+ rect.x = x, rect.y = y;
+ rect.width = w1, rect.height = height;
+ rsize = (dx ? 3 : 1) + cmd_size_rect(&rect);
+ code = cmd_put_bits(cdev, pcls, row, w1, height, raster,
+ rsize, (orig_id == gx_no_bitmap_id ?
+ 1 << cmd_compress_rle :
+ cmd_mask_compress_any),
+ &dp, &csize);
+ if ( code < 0 )
+ { if ( code != gs_error_limitcheck )
+ return code;
+ /* The bitmap was too large; split up the transfer. */
+ if ( height > 1 )
+ { /* Split the transfer by reducing the height.
+ * See the comment above BEGIN_RECT in gxcldev.h.
+ */
+ height >>= 1;
+ goto copy;
+ }
+ { /* Split a single (very long) row. */
+ int w2 = w1 >> 1;
+ code = clist_copy_mono(dev, row, dx + w2,
+ raster, gx_no_bitmap_id, x + w2, y,
+ w1 - w2, 1, color0, color1);
+ if ( code < 0 )
+ return code;
+ w1 = w2;
+ goto copy;
+ }
+ }
+ op += code;
+ if ( dx )
+ { *dp++ = cmd_count_op(cmd_opv_next_data_x, 2);
+ *dp++ = dx;
+ }
+ *dp++ = cmd_count_op(op, csize);
+ cmd_put2w(x, y, dp);
+ cmd_put2w(w1, height, dp);
+ pcls->rect = rect;
+ }
+ END_RECT
+ return 0;
+}
+
+private int
+clist_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)
+{ int depth = dev->color_info.depth;
+ int y0;
+ int data_x_bit;
+
+ fit_copy(dev, data, data_x, raster, id, x, y, width, height);
+ y0 = y;
+ data_x_bit = data_x * depth;
+ BEGIN_RECT
+ int dx = (data_x_bit & 7) / depth;
+ int w1 = dx + width;
+ const byte *row = data + (y - y0) * raster + (data_x_bit >> 3);
+ int code;
+
+ cmd_disable_lop(cdev, pcls);
+ cmd_disable_clip(cdev, pcls);
+ if ( pcls->color_is_alpha )
+ { byte *dp;
+ set_cmd_put_op(dp, cdev, pcls, cmd_opv_set_copy_color, 1);
+ pcls->color_is_alpha = 0;
+ }
+copy: { gx_cmd_rect rect;
+ int rsize;
+ byte op = (byte)cmd_op_copy_color_alpha;
+ byte *dp;
+ uint csize;
+
+ rect.x = x, rect.y = y;
+ rect.width = w1, rect.height = height;
+ rsize = (dx ? 3 : 1) + cmd_size_rect(&rect);
+ code = cmd_put_bits(cdev, pcls, row, w1 * depth,
+ height, raster, rsize,
+ 1 << cmd_compress_rle, &dp, &csize);
+ if ( code < 0 )
+ { if ( code != gs_error_limitcheck )
+ return code;
+ /* The bitmap was too large; split up the transfer. */
+ if ( height > 1 )
+ { /* Split the transfer by reducing the height.
+ * See the comment above BEGIN_RECT in gxcldev.h.
+ */
+ height >>= 1;
+ goto copy;
+ }
+ { /* Split a single (very long) row. */
+ int w2 = w1 >> 1;
+ code = clist_copy_color(dev, row, dx + w2,
+ raster, gx_no_bitmap_id, x + w2, y,
+ w1 - w2, 1);
+ if ( code < 0 )
+ return code;
+ w1 = w2;
+ goto copy;
+ }
+ }
+ op += code;
+ if ( dx )
+ { *dp++ = cmd_count_op(cmd_opv_next_data_x, 2);
+ *dp++ = dx;
+ }
+ *dp++ = cmd_count_op(op, csize);
+ cmd_put2w(x, y, dp);
+ cmd_put2w(w1, height, dp);
+ pcls->rect = rect;
+
+ }
+ END_RECT
+ return 0;
+}
+
+private int
+clist_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)
+{ /* I don't like copying the entire body of clist_copy_color */
+ /* just to change 2 arguments and 1 opcode, */
+ /* but I don't see any alternative that doesn't require */
+ /* another level of procedure call even in the common case. */
+ int log2_depth = depth >> 1; /* works for 1,2,4 */
+ int y0;
+ int data_x_bit;
+
+ fit_copy(dev, data, data_x, raster, id, x, y, width, height);
+ y0 = y;
+ data_x_bit = data_x << log2_depth;
+ BEGIN_RECT
+ int dx = (data_x_bit & 7) >> log2_depth;
+ int w1 = dx + width;
+ const byte *row = data + (y - y0) * raster + (data_x_bit >> 3);
+ int code;
+
+ cmd_disable_lop(cdev, pcls);
+ cmd_disable_clip(cdev, pcls);
+ if ( !pcls->color_is_alpha )
+ { byte *dp;
+ set_cmd_put_op(dp, cdev, pcls, cmd_opv_set_copy_alpha, 1);
+ pcls->color_is_alpha = 1;
+ }
+ if ( color != pcls->colors[1] )
+ { int code = cmd_set_color1(cdev, pcls, color);
+ if ( code < 0 )
+ return code;
+ }
+copy: { gx_cmd_rect rect;
+ int rsize;
+ byte op = (byte)cmd_op_copy_color_alpha;
+ byte *dp;
+ uint csize;
+
+ rect.x = x, rect.y = y;
+ rect.width = w1, rect.height = height;
+ rsize = (dx ? 4 : 2) + cmd_size_rect(&rect);
+ code = cmd_put_bits(cdev, pcls, row, w1 << log2_depth,
+ height, raster, rsize,
+ 1 << cmd_compress_rle, &dp, &csize);
+ if ( code < 0 )
+ { if ( code != gs_error_limitcheck )
+ return code;
+ /* The bitmap was too large; split up the transfer. */
+ if ( height > 1 )
+ { /* Split the transfer by reducing the height.
+ * See the comment above BEGIN_RECT in gxcldev.h.
+ */
+ height >>= 1;
+ goto copy;
+ }
+ { /* Split a single (very long) row. */
+ int w2 = w1 >> 1;
+ code = clist_copy_alpha(dev, row, dx + w2,
+ raster, gx_no_bitmap_id, x + w2, y,
+ w1 - w2, 1, color, depth);
+ if ( code < 0 )
+ return code;
+ w1 = w2;
+ goto copy;
+ }
+ }
+ op += code;
+ if ( dx )
+ { *dp++ = cmd_count_op(cmd_opv_next_data_x, 2);
+ *dp++ = dx;
+ }
+ *dp++ = cmd_count_op(op, csize);
+ *dp++ = depth;
+ cmd_put2w(x, y, dp);
+ cmd_put2w(w1, height, dp);
+ pcls->rect = rect;
+ }
+ END_RECT
+ return 0;
+}
+
+private int
+clist_get_band(gx_device *dev, int y, int *band_start)
+{ int start;
+ if ( y < 0 )
+ y = 0;
+ else if ( y >= dev->height )
+ y = dev->height;
+ *band_start = start = y - y % cdev->band_height;
+ return min(dev->height - start, cdev->band_height);
+}
+
+private int
+clist_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)
+{ gs_rop3_t rop = (gs_rop3_t)(lop & lop_rop_mask);
+ gx_strip_bitmap tile_with_id;
+ const gx_strip_bitmap *tiles = textures;
+
+ if ( scolors != 0 && scolors[0] != scolors[1] )
+ { fit_fill(dev, x, y, width, height);
+ }
+ else
+ { fit_copy(dev, sdata, sourcex, sraster, id,
+ x, y, width, height);
+ }
+ /*
+ * We shouldn't need to put the logic below inside BEGIN/END_RECT,
+ * but the lop_enabled flags are per-band.
+ */
+ BEGIN_RECT
+ int code;
+
+ if ( lop != pcls->lop )
+ { code = cmd_set_lop(cdev, pcls, lop);
+ if ( code < 0 )
+ return code;
+ }
+ cmd_enable_lop(cdev, pcls);
+ if ( rop3_uses_T(rop) )
+ { if ( tcolors == 0 || tcolors[0] != tcolors[1] )
+ { ulong offset_temp;
+ if ( !cls_has_tile_id(cdev, pcls, tiles->id, offset_temp) )
+ { /* Change tile. If there is no id, generate one. */
+ if ( tiles->id == gx_no_bitmap_id )
+ { tile_with_id = *tiles;
+ tile_with_id.id = gs_next_ids(1);
+ tiles = &tile_with_id;
+ }
+ code = clist_change_tile(cdev, pcls, tiles,
+ (tcolors != 0 ? 1 :
+ dev->color_info.depth));
+ if ( code < 0 )
+ return code;
+ if ( phase_x != pcls->tile_phase.x ||
+ phase_y != pcls->tile_phase.y
+ )
+ { code = cmd_set_tile_phase(cdev, pcls, phase_x,
+ phase_y);
+ if ( code < 0 )
+ return code;
+ }
+ }
+ }
+ /* Set the tile colors. */
+ code = (tcolors != 0 ?
+ cmd_set_tile_colors(cdev, pcls, tcolors[0],
+ tcolors[1]) :
+ cmd_set_tile_colors(cdev, pcls, gx_no_color_index,
+ gx_no_color_index));
+ if ( code < 0 )
+ return code;
+ }
+ /* Set lop_enabled to -1 so that fill_rectangle / copy_* */
+ /* won't attempt to set it to 0. */
+ pcls->lop_enabled = -1;
+ if ( scolors != 0 )
+ { if ( scolors[0] == scolors[1] )
+ code = clist_fill_rectangle(dev, x, y, width, height,
+ scolors[1]);
+ else
+ code = clist_copy_mono(dev, sdata, sourcex, sraster, id,
+ x, y, width, height,
+ scolors[0], scolors[1]);
+ }
+ else
+ code = clist_copy_color(dev, sdata, sourcex, sraster, id,
+ x, y, width, height);
+ pcls->lop_enabled = 1;
+ if ( code < 0 )
+ return 0;
+ END_RECT
+ return 0;
+}
diff --git a/pstoraster/gxclist.h b/pstoraster/gxclist.h
new file mode 100644
index 000000000..b8b363893
--- /dev/null
+++ b/pstoraster/gxclist.h
@@ -0,0 +1,187 @@
+/* Copyright (C) 1991, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxclist.h */
+/* Command list definitions for Ghostscript. */
+/* Requires gxdevice.h and gxdevmem.h */
+#include "gxbcache.h"
+#include "gxclio.h"
+#include "gxistate.h"
+
+/*
+ * A command list is essentially a compressed list of driver calls.
+ * Command lists are used to record an image that must be rendered in bands
+ * for high-resolution and/or limited-memory printers.
+ *
+ * Command lists work in two phases. The first phase records driver calls,
+ * sorting them according to the band(s) they affect. The second phase
+ * reads back the commands band-by-band to create the bitmap images.
+ * When opened, a command list is in the recording state; it switches
+ * automatically from recording to reading when its get_bits procedure
+ * is called. Currently, there is a hack to reopen the device after
+ * each page is processed, in order to switch back to recording.
+ */
+
+/*
+ * The command list contains both commands for particular bands (the vast
+ * majority) and commands that apply to all bands. In order to synchronize
+ * the two, we maintain the following invariant for buffered commands:
+ *
+ * If there are any all-band commands in the buffer, they are the
+ * first commands in the buffer, before any specific-band commands.
+ *
+ * To maintain this invariant, whenever we are about to put an all-band
+ * command in the buffer, we check to see if the buffer already has any
+ * all-band commands in it, and if so, whether they are the last commands
+ * in the buffer; if the answer to either question is negative, we flush
+ * the buffer.
+ */
+
+/*
+ * Currently, halftoning occurs during the first phase, producing calls
+ * to tile_rectangle. Both phases keep a cache of recently seen bitmaps
+ * (halftone cells and characters), which allows writing only a short cache
+ * index in the command list rather than the entire bitmap.
+ *
+ * We keep only a single cache for all bands, but since the second phase
+ * reads the command lists for each band separately, we have to keep track
+ * for each cache entry E and band B whether the definition of E has been
+ * written into B's list. We do this with a bit mask in each entry.
+ *
+ * Eventually, we will replace this entire arrangement with one in which
+ * we pass the actual halftone screen (whitening order) to all bands
+ * through the command list, and halftoning occurs on the second phase.
+ * This not only will shrink the command list, but will allow us to apply
+ * other rendering algorithms such as error diffusion in the second phase.
+ */
+typedef struct {
+ ulong offset; /* writing: offset from cdev->data, */
+ /* 0 means unused */
+ /* reading: offset from cdev->chunk.data */
+} tile_hash;
+typedef struct {
+ gx_cached_bits_common;
+ ushort index; /* index in table (hash table when writing) */
+ ushort num_bands; /* # of 1-bits in the band mask */
+ /* byte band_mask[]; */
+#define ts_mask(pts) (byte *)((pts) + 1)
+ /* byte bits[]; */
+#define ts_bits(cldev,pts) (ts_mask(pts) + (cldev)->tile_band_mask_size)
+} tile_slot;
+
+/* Define the prefix on each command run in the writing buffer. */
+typedef struct cmd_prefix_s cmd_prefix;
+struct cmd_prefix_s {
+ cmd_prefix *next;
+ uint size;
+};
+
+/* Define the pointers for managing a list of command runs in the buffer. */
+/* There is one of these for each band, plus one for all-band commands. */
+typedef struct cmd_list_s {
+ cmd_prefix *head, *tail; /* list of commands for band */
+} cmd_list;
+
+/*
+ * In order to keep the per-band state down to a reasonable size,
+ * we store only a single set of the imager state parameters;
+ * for each parameter, each band has a flag that says whether that band
+ * 'knows' the current value of the parameters.
+ */
+extern const gs_imager_state clist_imager_state_initial;
+
+/*
+ * Define the main structure for holding command list state.
+ * Unless otherwise noted, all elements are used in both the writing (first)
+ * and reading (second) phase.
+ */
+typedef struct gx_clist_state_s gx_clist_state;
+#define gx_device_clist_common\
+ gx_device_forward_common; /* (see gxdevice.h) */\
+ /* Following must be set before writing or reading. */\
+ /* gx_device *target; */ /* device for which commands */\
+ /* are being buffered */\
+ dev_proc_make_buffer_device((*make_buffer_device));\
+ byte *data; /* buffer area */\
+ uint data_size; /* size of buffer */\
+ clist_file_ptr cfile; /* command list file */\
+ clist_file_ptr bfile; /* command list block file */\
+ /* Following are used for both writing and reading. */\
+ gx_bits_cache_chunk chunk; /* the only chunk of bits */\
+ gx_bits_cache bits;\
+ uint tile_hash_mask; /* size of tile hash table -1 */\
+ uint tile_band_mask_size; /* size of band mask preceding */\
+ /* each tile in the cache */\
+ tile_hash *tile_table; /* table for tile cache: */\
+ /* see tile_hash above */\
+ /* (a hash table when writing) */\
+ int ymin, ymax; /* current band, <0 when writing */\
+ /* Following are set when writing, read when reading. */\
+ byte *mdata; /* start of memory device data */\
+ int band_height; /* height of each band */\
+ int nbands; /* # of bands */\
+ long bfile_end_pos /* ftell at end of bfile */
+
+/* Define the length of the longest dash pattern we are willing to store. */
+/* (Strokes with longer patterns are converted to fills.) */
+#define cmd_max_dash 11
+
+/* Define the state of a band list when writing. */
+typedef struct gx_device_clist_writer_s {
+ gx_device_clist_common; /* (must be first) */
+ int error_code; /* error returned by cmd_put_op */
+ gx_clist_state *states; /* current state of each band */
+ byte *cbuf; /* start of command buffer */
+ byte *cnext; /* next slot in command buffer */
+ byte *cend; /* end of command buffer */
+ cmd_list *ccl; /* &clist_state.list of last command */
+ cmd_list all_band_list; /* list of all-band commands */
+ uint tile_max_size; /* max size of a single tile (bytes) */
+ uint tile_max_count; /* max # of hash table entries */
+ gx_strip_bitmap tile_params; /* current tile parameters */
+ int tile_depth; /* current tile depth */
+ gs_imager_state imager_state; /* current values of imager params */
+ float dash_pattern[cmd_max_dash]; /* current dash pattern */
+ const gx_clip_path *clip_path; /* current clip path */
+ gx_bitmap_id clip_path_id; /* id of current clip path */
+ byte color_space; /* current color space identifier */
+ /* (only used for images) */
+ int indexed_hival; /* current indexed space hival */
+ /* (ditto) */
+} gx_device_clist_writer;
+
+/* Define the state of a band list when reading. */
+typedef struct gx_device_clist_reader_s {
+ gx_device_clist_common; /* (must be first) */
+} gx_device_clist_reader;
+
+typedef union gx_device_clist_s {
+ struct _clc {
+ gx_device_clist_common;
+ } common;
+ gx_device_clist_reader reader;
+ gx_device_clist_writer writer;
+} gx_device_clist;
+
+/* The device template itself is never used, only the procs. */
+extern gx_device_procs gs_clist_device_procs;
diff --git a/pstoraster/gxclpath.c b/pstoraster/gxclpath.c
new file mode 100644
index 000000000..2ce2bd0d7
--- /dev/null
+++ b/pstoraster/gxclpath.c
@@ -0,0 +1,959 @@
+/* Copyright (C) 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxclpath.c */
+/* Higher-level path operations for band lists */
+#include "math_.h"
+#include "memory_.h"
+#include "gx.h"
+#include "gpcheck.h"
+#include "gserrors.h"
+#include "gxdevice.h"
+#include "gxdevmem.h" /* must precede gxcldev.h */
+#include "gxcldev.h"
+#include "gxclpath.h"
+#include "gxpaint.h" /* for gx_fill/stroke_params */
+#include "gzpath.h"
+#include "gzcpath.h"
+
+#define cdev cwdev
+
+/* Statistics */
+#ifdef DEBUG
+ulong cmd_diffs[5];
+#endif
+
+/* Forward declarations */
+private int cmd_put_path(P7(gx_device_clist_writer *cldev,
+ gx_clist_state *pcls, const gx_path *ppath, fixed ymin, fixed ymax, byte op,
+ bool implicit_close));
+/* Driver procedures */
+private dev_proc_fill_path(clist_fill_path);
+private dev_proc_stroke_path(clist_stroke_path);
+
+/* ------ Define the extensions to the command set ------ */
+
+#ifdef DEBUG
+private const char *cmd_misc2_op_names[16] = { cmd_misc2_op_name_strings };
+private const char *cmd_segment_op_names[16] = { cmd_segment_op_name_strings };
+private const char *cmd_path_op_names[16] = { cmd_path_op_name_strings };
+#endif
+
+/* Initialize the extensions to the command name table. */
+void
+gs_clpath_init(gs_memory_t *mem)
+{
+#ifdef DEBUG
+ cmd_op_names[cmd_op_misc2 >> 4] = "(misc2)";
+ cmd_sub_op_names[cmd_op_misc2 >> 4] = cmd_misc2_op_names;
+ cmd_op_names[cmd_op_segment >> 4] = "(segment)";
+ cmd_sub_op_names[cmd_op_segment >> 4] = cmd_segment_op_names;
+ cmd_op_names[cmd_op_path >> 4] = "(path)";
+ cmd_sub_op_names[cmd_op_path >> 4] = cmd_path_op_names;
+#endif
+ gs_clist_device_procs.fill_path = clist_fill_path;
+ gs_clist_device_procs.stroke_path = clist_stroke_path;
+ cmd_opvar_disable_clip = cmd_opv_disable_clip;
+ cmd_opvar_enable_clip = cmd_opv_enable_clip;
+}
+
+/* ------ Utilities ------ */
+
+/* Write out the color for filling, stroking, or masking. */
+/* We should be able to share this with clist_tile_rectangle, */
+/* but I don't see how to do it without adding a level of procedure. */
+int
+cmd_put_drawing_color(gx_device_clist_writer *cldev, gx_clist_state *pcls,
+ const gx_drawing_color *pdcolor)
+{ const gx_strip_bitmap *tile;
+ gx_color_index color0, color1;
+ ulong offset_temp;
+
+ if ( gx_dc_is_pure(pdcolor) )
+ { gx_color_index color1 = gx_dc_pure_color(pdcolor);
+ return (color1 == pcls->colors[1] ? 0 :
+ cmd_set_color1(cldev, pcls, color1));
+ }
+ else if ( gx_dc_is_binary_halftone(pdcolor) )
+ { tile = gx_dc_binary_tile(pdcolor);
+ color0 = gx_dc_binary_color0(pdcolor);
+ color1 = gx_dc_binary_color1(pdcolor);
+ }
+ else /* should handle colored halftones */
+ return -1;
+
+ /* Set up tile and colors as for clist_tile_rectangle. */
+ if ( !cls_has_tile_id(cldev, pcls, tile->id, offset_temp) )
+ { int depth =
+ (color1 == gx_no_color_index &&
+ color0 == gx_no_color_index ?
+ cldev->color_info.depth : 1);
+ if ( tile->id == gx_no_bitmap_id ||
+ clist_change_tile(cldev, pcls, tile, depth) < 0
+ )
+ return -1; /* can't cache tile */
+ }
+ if ( color1 != pcls->tile_colors[1] ||
+ color0 != pcls->tile_colors[0]
+ )
+ { int code = cmd_set_tile_colors(cldev, pcls, color0, color1);
+ if ( code < 0 )
+ return code;
+ }
+ { int px = pdcolor->phase.x, py = pdcolor->phase.y;
+ if ( px != pcls->tile_phase.x || py != pcls->tile_phase.y )
+ { int code = cmd_set_tile_phase(cldev, pcls, px, py);
+ if ( code < 0 )
+ return code;
+ }
+ }
+ return 0;
+}
+
+/* Clear (a) specific 'known' flag(s) for all bands. */
+/* We must do this whenever the value of a 'known' parameter changes. */
+void
+cmd_clear_known(gx_device_clist_writer *cldev, uint known)
+{ ushort unknown = ~known;
+ gx_clist_state *pcls = cldev->states;
+ int i;
+
+ for ( i = cldev->nbands; --i >= 0; ++pcls )
+ pcls->known &= unknown;
+}
+
+/* Check whether we need to change the clipping path in the device. */
+bool
+cmd_check_clip_path(gx_device_clist_writer *cldev, const gx_clip_path *pcpath)
+{ if ( pcpath == NULL )
+ return false;
+ /* The clip path might have moved in memory, so even if the */
+ /* ids match, update the pointer. */
+ cldev->clip_path = pcpath;
+ if ( pcpath->id == cldev->clip_path_id )
+ return false;
+ cldev->clip_path_id = pcpath->id;
+ return true;
+}
+
+/* Construct the parameters for writing out a matrix. */
+/* We need a buffer of at least 1 + 6 * sizeof(float) bytes. */
+byte *
+cmd_for_matrix(byte *cbuf, const gs_matrix *pmat)
+{ byte *cp = cbuf + 1;
+ byte b = 0;
+ float coeffs[6];
+ int i;
+
+ coeffs[0] = pmat->xx;
+ coeffs[1] = pmat->xy;
+ coeffs[2] = pmat->yx;
+ coeffs[3] = pmat->yy;
+ coeffs[4] = pmat->tx;
+ coeffs[5] = pmat->ty;
+ for ( i = 0; i < 4; i += 2 )
+ { float u = coeffs[i], v = coeffs[i^3];
+ b <<= 2;
+ if ( u != 0 || v != 0 )
+ { memcpy(cp, &u, sizeof(float));
+ cp += sizeof(float);
+ if ( v == u )
+ b += 1;
+ else if ( v == -u )
+ b += 2;
+ else
+ { b += 3;
+ memcpy(cp, &v, sizeof(float));
+ cp += sizeof(float);
+ }
+ }
+ }
+ for ( ; i < 6; ++i )
+ { float v = coeffs[i];
+ b <<= 1;
+ if ( v != 0 )
+ { ++b;
+ memcpy(cp, &v, sizeof(float));
+ cp += sizeof(float);
+ }
+ }
+ cbuf[0] = b << 2;
+ return cp;
+}
+
+/* Write out values of any unknown parameters. */
+int
+cmd_write_unknown(gx_device_clist_writer *cldev, gx_clist_state *pcls,
+ uint must_know)
+{ ushort unknown = ~pcls->known & must_know;
+
+ if ( unknown & flatness_known )
+ { byte *dp;
+ set_cmd_put_op(dp, cldev, pcls, cmd_opv_set_flatness,
+ 1 + sizeof(float));
+ memcpy(dp + 1, &cldev->imager_state.flatness, sizeof(float));
+ pcls->known |= flatness_known;
+ }
+ if ( unknown & fill_adjust_known )
+ { byte *dp;
+ set_cmd_put_op(dp, cldev, pcls, cmd_opv_set_fill_adjust,
+ 1 + sizeof(fixed) * 2);
+ memcpy(dp + 1, &cldev->imager_state.fill_adjust.x, sizeof(fixed));
+ memcpy(dp + 1 + sizeof(fixed), &cldev->imager_state.fill_adjust.y, sizeof(fixed));
+ pcls->known |= fill_adjust_known;
+ }
+ if ( unknown & ctm_known )
+ { byte cbuf[1 + 6 * sizeof(float)];
+ uint len =
+ cmd_for_matrix(cbuf,
+ (const gs_matrix *)&cldev->imager_state.ctm) -
+ cbuf;
+ byte *dp;
+
+ set_cmd_put_op(dp, cldev, pcls, cmd_opv_set_ctm, len + 1);
+ memcpy(dp + 1, cbuf, len);
+ pcls->known |= ctm_known;
+ }
+ if ( unknown & line_width_known )
+ { byte *dp;
+ float width =
+ gx_current_line_width(&cldev->imager_state.line_params);
+
+ set_cmd_put_op(dp, cldev, pcls, cmd_opv_set_line_width,
+ 1 + sizeof(width));
+ memcpy(dp + 1, &width, sizeof(width));
+ pcls->known |= line_width_known;
+ }
+ if ( unknown & miter_limit_known )
+ { byte *dp;
+ set_cmd_put_op(dp, cldev, pcls, cmd_opv_set_miter_limit,
+ 1 + sizeof(float));
+ memcpy(dp + 1, &cldev->imager_state.line_params.miter_limit, sizeof(float));
+ pcls->known |= miter_limit_known;
+ }
+ if ( unknown & misc_known )
+ { byte *dp;
+ set_cmd_put_op(dp, cldev, pcls, cmd_opv_set_misc, 2);
+ dp[1] =
+ (cldev->imager_state.overprint ? 0x80 : 0) +
+ (cldev->imager_state.stroke_adjust ? 0x40 : 0) +
+ (cldev->imager_state.line_params.cap << 3) +
+ cldev->imager_state.line_params.join;
+ pcls->known |= misc_known;
+ }
+ if ( unknown & dash_known )
+ { byte *dp;
+ int n = cldev->imager_state.line_params.dash.pattern_size;
+
+ set_cmd_put_op(dp, cldev, pcls, cmd_opv_set_dash,
+ 2 + (n + 1) * sizeof(float));
+ dp[1] = n;
+ memcpy(dp + 2, &cldev->imager_state.line_params.dash.offset,
+ sizeof(float));
+ if ( n != 0 )
+ memcpy(dp + 2 + sizeof(float),
+ cldev->imager_state.line_params.dash.pattern,
+ n * sizeof(float));
+ pcls->known |= dash_known;
+ }
+ if ( unknown & clip_path_known )
+ { /* We can write out the clipping path either as rectangles */
+ /* or as a real (filled) path. */
+ const gx_clip_path *pcpath = cldev->clip_path;
+ int ymin = (pcls - cldev->states) * cldev->band_height;
+ int ymax = min(ymin + cldev->band_height, cldev->height);
+ byte *dp;
+
+ set_cmd_put_op(dp, cldev, pcls, cmd_opv_begin_clip, 1);
+ if ( pcpath->segments_valid )
+ { /* Write out the path. */
+ int code = cmd_put_path(cldev, pcls, &pcpath->path,
+ int2fixed(ymin) - fixed_half,
+ int2fixed(ymax) + fixed_half,
+ (pcpath->rule == gx_rule_even_odd ?
+ cmd_opv_eofill : cmd_opv_fill),
+ true);
+ if ( code < 0 )
+ return code;
+ }
+ else
+ { /* Write out the rectangles. */
+ const gx_clip_rect *prect = pcpath->list.head;
+
+ if ( prect == 0 )
+ prect = &pcpath->list.single;
+ for ( ; prect != 0; prect = prect->next )
+ if ( prect->xmax > prect->xmin &&
+ prect->ymin < ymax && prect->ymax > ymin
+ )
+ { int code =
+ cmd_write_rect_cmd(cldev, pcls, cmd_op_fill_rect,
+ prect->xmin, prect->ymin,
+ prect->xmax - prect->xmin,
+ prect->ymax - prect->ymin);
+ if ( code < 0 )
+ return code;
+ }
+
+ }
+ set_cmd_put_op(dp, cldev, pcls, cmd_opv_end_clip, 2);
+ dp[1] = (gx_cpath_is_outside(pcpath) ? 1 : 0);
+ pcls->clip_enabled = 1;
+ pcls->known |= clip_path_known;
+ }
+ if ( unknown & color_space_known )
+ { byte *dp;
+
+ if ( cldev->color_space & 8 ) /* indexed */
+ { set_cmd_put_op(dp, cldev, pcls, cmd_opv_set_color_space,
+ 2 + cmd_sizew(cldev->indexed_hival));
+ cmd_put_w(cldev->indexed_hival, dp + 2);
+ }
+ else
+ { set_cmd_put_op(dp, cldev, pcls, cmd_opv_set_color_space, 2);
+ }
+ dp[1] = cldev->color_space;
+ pcls->known |= color_space_known;
+ }
+ return 0;
+}
+
+/* ------ Driver procedures ------ */
+
+private int
+clist_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)
+{ uint unknown = 0;
+ int y, height;
+ gs_logical_operation_t lop = pis->log_op;
+ byte op = (byte)
+ (params->rule == gx_rule_even_odd ?
+ (gx_dc_is_pure(pdcolor) ? cmd_opv_eofill : cmd_opv_hteofill) :
+ (gx_dc_is_pure(pdcolor) ? cmd_opv_fill : cmd_opv_htfill));
+ gs_fixed_point adjust;
+
+ adjust = params->adjust;
+ { gs_fixed_rect bbox;
+ gx_path_bbox(ppath, &bbox);
+ if ( params->fill_zero_width )
+ gx_adjust_if_empty(&bbox, &adjust);
+ y = fixed2int(bbox.p.y - adjust.y);
+ height = fixed2int_ceiling(bbox.q.y + adjust.y) - y;
+ fit_fill_yh(dev, y, height);
+ if ( height <= 0 )
+ return 0;
+ }
+ if ( cdev->imager_state.flatness != params->flatness )
+ { unknown |= flatness_known;
+ cdev->imager_state.flatness = params->flatness;
+ }
+ if ( cdev->imager_state.fill_adjust.x != adjust.x ||
+ cdev->imager_state.fill_adjust.y != adjust.y
+ )
+ { unknown |= fill_adjust_known;
+ cdev->imager_state.fill_adjust = adjust;
+ }
+ if ( cmd_check_clip_path(cdev, pcpath) )
+ unknown |= clip_path_known;
+ if ( unknown )
+ cmd_clear_known(cdev, unknown);
+ BEGIN_RECT
+ int code;
+
+ cmd_do_write_unknown(cdev, pcls,
+ flatness_known | fill_adjust_known |
+ clip_path_known);
+ cmd_do_enable_clip(cdev, pcls, pcpath != NULL);
+ if ( lop == lop_default )
+ { cmd_disable_lop(cdev, pcls);
+ }
+ else
+ { if ( lop != pcls->lop )
+ { byte *dp;
+ set_cmd_put_op(dp, cdev, pcls,
+ cmd_opv_set_lop, 1 + cmd_size_w(lop));
+ ++dp;
+ cmd_put_w(lop, dp);
+ pcls->lop = lop;
+ }
+ cmd_enable_lop(cdev, pcls);
+ }
+ code = cmd_put_drawing_color(cdev, pcls, pdcolor);
+ if ( code < 0 )
+ { /* Something went wrong, use the default implementation. */
+ return gx_default_fill_path(dev, pis, ppath, params, pdcolor,
+ pcpath);
+ }
+ code = cmd_put_path(cdev, pcls, ppath,
+ int2fixed(y) - params->adjust.y,
+ int2fixed(y + height) + params->adjust.y,
+ op, true);
+ if ( code < 0 )
+ return code;
+ END_RECT
+ return 0;
+}
+
+private int
+clist_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)
+{ int pattern_size = pis->line_params.dash.pattern_size;
+ uint unknown = 0;
+ gs_fixed_rect bbox;
+ gs_fixed_point expansion;
+ fixed adjust_y;
+ int y, height;
+ gs_logical_operation_t lop = pis->log_op;
+ byte op = (byte)
+ (gx_dc_is_pure(pdcolor) ? cmd_opv_stroke : cmd_opv_htstroke);
+
+ gx_path_bbox(ppath, &bbox);
+ /* We must use the supplied imager state, not our saved one, */
+ /* for computing the stroke expansion. */
+ gx_stroke_expansion(pis, &expansion);
+ adjust_y = expansion.y + pis->fill_adjust.y;
+ y = fixed2int(bbox.p.y - adjust_y);
+ height = fixed2int_ceiling(bbox.q.y + adjust_y) - y;
+ fit_fill_yh(dev, y, height);
+ if ( height <= 0 )
+ return 0;
+ /* Check the dash pattern, since we bail out if */
+ /* the pattern is too large. */
+ cdev->imager_state.line_params.dash.pattern = cdev->dash_pattern;
+ if ( cdev->imager_state.line_params.dash.pattern_size != pattern_size ||
+ (pattern_size != 0 &&
+ memcmp(cdev->imager_state.line_params.dash.pattern,
+ pis->line_params.dash.pattern,
+ pattern_size * sizeof(float)))
+ )
+ { /* Bail out if the dash pattern is too long. */
+ if ( pattern_size > cmd_max_dash )
+ return gx_default_stroke_path(dev, pis, ppath, params,
+ pdcolor, pcpath);
+ unknown |= dash_known;
+ gx_set_dash(&cdev->imager_state.line_params.dash,
+ pis->line_params.dash.pattern,
+ pis->line_params.dash.pattern_size,
+ pis->line_params.dash.offset, NULL);
+ }
+ if ( state_neq(flatness) )
+ { unknown |= flatness_known;
+ state_update(flatness);
+ }
+ if ( state_neq(fill_adjust.x) || state_neq(fill_adjust.y) )
+ { unknown |= fill_adjust_known;
+ state_update(fill_adjust);
+ }
+ if ( state_neq(ctm.xx) || state_neq(ctm.xy) ||
+ state_neq(ctm.yx) || state_neq(ctm.yy) ||
+ /* We don't actually need tx or ty, but we don't want to bother */
+ /* tracking them separately from the other coefficients. */
+ state_neq(ctm.tx) || state_neq(ctm.ty)
+ )
+ { unknown |= ctm_known;
+ state_update(ctm);
+ }
+ if ( state_neq(line_params.half_width) )
+ { unknown |= line_width_known;
+ state_update(line_params.half_width);
+ }
+ if ( state_neq(line_params.miter_limit) )
+ { unknown |= miter_limit_known;
+ gx_set_miter_limit(&cdev->imager_state.line_params,
+ pis->line_params.miter_limit);
+ }
+ if ( state_neq(line_params.cap) || state_neq(line_params.join) ||
+ state_neq(stroke_adjust)
+ )
+ { unknown |= misc_known;
+ state_update(line_params.cap);
+ state_update(line_params.join);
+ state_update(stroke_adjust);
+ }
+ if ( cmd_check_clip_path(cdev, pcpath) )
+ unknown |= clip_path_known;
+ if ( unknown )
+ cmd_clear_known(cdev, unknown);
+ BEGIN_RECT
+ int code;
+
+ cmd_do_write_unknown(cdev, pcls, stroke_all_known);
+ cmd_do_enable_clip(cdev, pcls, pcpath != NULL);
+ if ( lop == lop_default )
+ { cmd_disable_lop(cdev, pcls);
+ }
+ else
+ { if ( lop != pcls->lop )
+ { byte *dp;
+ set_cmd_put_op(dp, cdev, pcls,
+ cmd_opv_set_lop, 1 + cmd_size_w(lop));
+ ++dp;
+ cmd_put_w(lop, dp);
+ pcls->lop = lop;
+ }
+ cmd_enable_lop(cdev, pcls);
+ }
+ code = cmd_put_drawing_color(cdev, pcls, pdcolor);
+ if ( code < 0 )
+ { /* Something went wrong, use the default implementation. */
+ return gx_default_stroke_path(dev, pis, ppath, params, pdcolor,
+ pcpath);
+ }
+ { fixed ymin, ymax;
+ /* If a dash pattern is active, we can't skip segments */
+ /* outside the clipping region, because that would throw off */
+ /* the pattern. */
+ if ( pattern_size == 0 )
+ ymin = int2fixed(y) - adjust_y,
+ ymax = int2fixed(y + height) + adjust_y;
+ else
+ ymin = min_fixed,
+ ymax = max_fixed;
+ code = cmd_put_path(cdev, pcls, ppath, ymin, ymax, op, false);
+ if ( code < 0 )
+ return code;
+ }
+ END_RECT
+ return 0;
+}
+
+/* ------ Path utilities ------ */
+
+/* Define the state bookkeeping for writing path segments. */
+typedef struct cmd_segment_writer_s {
+ /* Set at initialization */
+ gx_device_clist_writer *cldev;
+ gx_clist_state *pcls;
+ /* Updated dynamically */
+ byte *dp;
+ int len;
+ gs_fixed_point delta_first;
+ byte cmd[6 * (1 + sizeof(fixed))];
+} cmd_segment_writer;
+
+/* Put out a path segment command. */
+private int near
+cmd_put_segment(cmd_segment_writer _ss *psw, byte op,
+ const fixed _ss *operands)
+{ const fixed _ss *optr = operands;
+ /* Fetch num_operands before possible command merging. */
+ int i = clist_segment_op_num_operands[op & 0xf];
+ byte *q = psw->cmd - 1;
+
+#ifdef DEBUG
+ if ( gs_debug_c('L') )
+ { int j;
+ dprintf1("[L] %s:", cmd_segment_op_names[op & 0xf]);
+ for ( j = 0; j < i; ++j )
+ dprintf1(" %g", fixed2float(operands[j]));
+ dputs("\n");
+ }
+#endif
+
+ /* Merge or shorten commands if possible. */
+ if ( op == cmd_opv_rlineto )
+ { if ( operands[0] == 0 )
+ op = cmd_opv_vlineto, optr = ++operands, i = 1;
+ else if ( operands[1] == 0 )
+ op = cmd_opv_hlineto, i = 1;
+ else
+ switch ( *psw->dp )
+ {
+ case cmd_opv_rmoveto:
+ psw->delta_first.x = operands[0];
+ psw->delta_first.y = operands[1];
+ op = cmd_opv_rmlineto;
+merge: cmd_uncount_op(*psw->dp, psw->len);
+ cmd_shorten_op(psw->cldev, psw->pcls, psw->len); /* delete it */
+ q += psw->len - 1;
+ break;
+ case cmd_opv_rmlineto:
+ op = cmd_opv_rm2lineto;
+ goto merge;
+ case cmd_opv_rm2lineto:
+ if ( operands[0] == -psw->delta_first.x &&
+ operands[1] == -psw->delta_first.y
+ )
+ { cmd_uncount_op(cmd_opv_rm2lineto, psw->len);
+ *psw->dp = cmd_count_op(cmd_opv_rm3lineto, psw->len);
+ return 0;
+ }
+ break;
+ default:
+ ;
+ }
+ }
+
+ for ( ; --i >= 0; ++optr )
+ { fixed d = *optr, d2;
+ if ( is_bits(d, _fixed_shift + 11) &&
+ !(d & (float2fixed(0.25) - 1))
+ )
+ { cmd_count_add1(cmd_diffs[3]);
+ d = ((d >> (_fixed_shift - 2)) & 0x1fff) + 0xc000;
+ q += 2;
+ }
+ else if ( is_bits(d, 19) && i > 0 && is_bits(d2 = optr[1], 19) )
+ { cmd_count_add1(cmd_diffs[0]);
+ q[1] = (d >> 13) & 0x3f;
+ q[2] = (byte)(d >> 5);
+ q[3] = (byte)((d << 3) + ((d2 >> 16) & 7));
+ q[4] = (byte)(d2 >> 8);
+ q[5] = (byte)d2;
+ q += 5;
+ --i, ++optr;
+ continue;
+ }
+ else if ( is_bits(d, 22) )
+ { cmd_count_add1(cmd_diffs[1]);
+ q[1] = ((d >> 16) & 0x3f) + 0x40;
+ q += 3;
+ }
+ else if ( is_bits(d, 30) )
+ { cmd_count_add1(cmd_diffs[2]);
+ q[1] = ((d >> 24) & 0x3f) + 0x80;
+ q[2] = (byte)(d >> 16);
+ q += 4;
+ }
+ else
+ { int b;
+ cmd_count_add1(cmd_diffs[4]);
+ *++q = 0xe0;
+ for ( b = sizeof(fixed) - 1; b > 1; --b )
+ *++q = (byte)(d >> (b * 8));
+ q += 2;
+ }
+ q[-1] = (byte)(d >> 8);
+ *q = (byte)d;
+ }
+ { int len = q + 2 - psw->cmd;
+ byte *dp;
+
+ set_cmd_put_op(dp, psw->cldev, psw->pcls, op, len);
+ memcpy(dp + 1, psw->cmd, len - 1);
+ psw->len = len;
+ psw->dp = dp;
+ }
+ return 0;
+}
+/* Put out a line segment command. */
+#define cmd_put_rmoveto(psw, operands)\
+ cmd_put_segment(psw, cmd_opv_rmoveto, operands)
+#define cmd_put_rlineto(psw, operands)\
+ cmd_put_segment(psw, cmd_opv_rlineto, operands)
+
+/*
+ * Write a path. We go to a lot of trouble to omit segments that are
+ * entirely outside the band.
+ */
+private int
+cmd_put_path(gx_device_clist_writer *cldev, gx_clist_state *pcls,
+ const gx_path *ppath, fixed ymin, fixed ymax, byte path_op,
+ bool implicit_close)
+{ gs_path_enum cenum;
+ cmd_segment_writer writer;
+ static byte initial_op = { cmd_opv_end_run };
+ /*
+ * We define the 'side' of a point according to its Y value as
+ * follows:
+ */
+#define which_side(y) ((y) < ymin ? -1 : (y) >= ymax ? 1 : 0)
+
+ /*
+ * While writing a subpath, we need to keep track of any segments
+ * skipped at the beginning of the subpath and any segments skipped
+ * just before the current segment. We do this with two sets of
+ * state variables, one that tracks the actual path segments and one
+ * that tracks the emitted segments.
+ *
+ * The following track the actual segments:
+ */
+
+ /*
+ * The point and side of the last moveto (skipped if
+ * start_side != 0):
+ */
+ gs_fixed_point start;
+ int start_side;
+ /*
+ * Whether any lines or curves were skipped immediately
+ * following the moveto:
+ */
+ bool start_skip;
+ /* The side of the last point: */
+ int side;
+ /* The last point with side != 0: */
+ gs_fixed_point out;
+
+ /*
+ * The following track the emitted segments:
+ */
+
+ /* The last point emitted: */
+ fixed px = int2fixed(pcls->rect.x);
+ fixed py = int2fixed(pcls->rect.y);
+ /* The point of the last emitted moveto: */
+ gs_fixed_point first;
+ /* Information about the last emitted operation: */
+ int open = 0; /* -1 if last was moveto, 1 if line/curveto, */
+ /* 0 if newpath/closepath */
+
+ if_debug4('p', "[p]initial (%g,%g), clip [%g..%g)\n",
+ fixed2float(px), fixed2float(py),
+ fixed2float(ymin), fixed2float(ymax));
+ gx_path_enum_init(&cenum, ppath);
+ writer.cldev = cldev;
+ writer.pcls = pcls;
+ writer.dp = &initial_op;
+#define first_point() ((writer).dp == &initial_op)
+ for ( ; ; )
+ { fixed vs[6];
+#define A vs[0]
+#define B vs[1]
+#define C vs[2]
+#define D vs[3]
+#define E vs[4]
+#define F vs[5]
+ int pe_op = gx_path_enum_next(&cenum, (gs_fixed_point *)vs);
+ byte *dp;
+ int code;
+
+ switch ( pe_op )
+ {
+ case 0:
+ /* If the path is open and needs an implicit close, */
+ /* do the close and then come here again. */
+ if ( open > 0 && implicit_close )
+ goto close;
+ /* All done. */
+ pcls->rect.x = fixed2int_var(px);
+ pcls->rect.y = fixed2int_var(py);
+ if_debug2('p', "[p]final (%d,%d)\n",
+ pcls->rect.x, pcls->rect.y);
+ set_cmd_put_op(dp, cldev, pcls, path_op, 1);
+ return 0;
+ case gs_pe_moveto:
+ /* If the path is open and needs an implicit close, */
+ /* do a closepath and then redo the moveto. */
+ if ( open > 0 && implicit_close )
+ { gx_path_enum_backup(&cenum);
+ goto close;
+ }
+ open = -1;
+ start.x = A, start.y = B;
+ start_skip = false;
+ if ( (start_side = side = which_side(B)) != 0 )
+ { out.x = A, out.y = B;
+ if_debug3('p', "[p]skip moveto (%g,%g) side %d\n",
+ fixed2float(out.x), fixed2float(out.y),
+ side);
+ continue;
+ }
+ C = A - px, D = B - py;
+ first.x = px = A, first.y = py = B;
+ code = cmd_put_rmoveto(&writer, &C);
+ if_debug2('p', "[p]moveto (%g,%g)\n",
+ fixed2float(px), fixed2float(py));
+ break;
+ case gs_pe_lineto:
+ { int next_side = which_side(B);
+ if ( next_side == side && side != 0 )
+ { /* Skip a line completely outside the clip region. */
+ if ( open < 0 )
+ start_skip = true;
+ out.x = A, out.y = B;
+ if_debug3('p', "[p]skip lineto (%g,%g) side %d\n",
+ fixed2float(out.x), fixed2float(out.y),
+ side);
+ continue;
+ }
+ /* If we skipped any segments, put out a moveto/lineto. */
+ if ( side && (px != out.x || py != out.y || first_point()) )
+ { C = out.x - px, D = out.y - py;
+ if ( open < 0 )
+ { first = out;
+ code = cmd_put_rmoveto(&writer, &C);
+ }
+ else
+ code = cmd_put_rlineto(&writer, &C);
+ if ( code < 0 )
+ return code;
+ px = out.x, py = out.y;
+ if_debug3('p', "[p]catchup %s (%g,%g) for line\n",
+ (open < 0 ? "moveto" : "lineto"),
+ fixed2float(px), fixed2float(py));
+ }
+ if ( (side = next_side) != 0 )
+ { /* Note a vertex going outside the clip region. */
+ out.x = A, out.y = B;
+ }
+ }
+ C = A - px, D = B - py;
+ px = A, py = B;
+ open = 1;
+ code = cmd_put_rlineto(&writer, &C);
+ if_debug3('p', "[p]lineto (%g,%g) side %d\n",
+ fixed2float(px), fixed2float(py), side);
+ break;
+ case gs_pe_closepath:
+#ifdef DEBUG
+ { gs_path_enum cpenum;
+ gs_fixed_point cvs[3];
+ int op;
+ cpenum = cenum;
+ switch ( op = gx_path_enum_next(&cpenum, cvs) )
+ {
+ case 0: case gs_pe_moveto:
+ break;
+ default:
+ lprintf1("closepath followed by %d, not end/moveto!\n",
+ op);
+ }
+ }
+#endif
+ /* A closepath may require drawing an explicit line if */
+ /* we skipped any segments at the beginning of the path. */
+close: if ( side != start_side )
+ { /* If we skipped any segments, put out a moveto/lineto. */
+ if ( side && (px != out.x || py != out.y || first_point()) )
+ { C = out.x - px, D = out.y - py;
+ code = cmd_put_rlineto(&writer, &C);
+ if ( code < 0 )
+ return code;
+ px = out.x, py = out.y;
+ if_debug2('p', "[p]catchup line (%g,%g) for close\n",
+ fixed2float(px), fixed2float(py));
+ }
+ if ( open > 0 && start_skip )
+ { /* Draw the closing line back to the start. */
+ C = start.x - px, D = start.y - py;
+ code = cmd_put_rlineto(&writer, &C);
+ if ( code < 0 )
+ return code;
+ px = start.x, py = start.y;
+ if_debug2('p', "[p]draw close to (%g,%g)\n",
+ fixed2float(px), fixed2float(py));
+ }
+ }
+ /*
+ * We don't bother to update side because we know that the
+ * next element after a closepath, if any, must be a moveto.
+ * We must handle explicitly the possibility that the entire
+ * subpath was skipped.
+ */
+ if ( implicit_close || open <= 0 )
+ { open = 0;
+ continue;
+ }
+ open = 0;
+ px = first.x, py = first.y;
+ code = cmd_put_segment(&writer, cmd_opv_closepath, &A);
+ if_debug0('p', "[p]close\n");
+ break;
+ case gs_pe_curveto:
+ { fixed bpy, bqy;
+ int all_side, out_side;
+
+ /* Compute the Y bounds for the clipping check. */
+ if ( B < D ) bpy = B, bqy = D;
+ else bpy = D, bqy = B;
+ if ( F < bpy ) bpy = F;
+ else if ( F > bqy ) bqy = F;
+ all_side = (bqy < ymin ? -1 : bpy > ymax ? 1 : 0);
+ if ( all_side != 0 )
+ { if ( all_side == side )
+ { /* Skip a curve entirely outside the clip region. */
+ if ( open < 0 )
+ start_skip = true;
+ out.x = E, out.y = F;
+ if_debug3('p', "[p]skip curveto (%g,%g) side %d\n",
+ fixed2float(out.x), fixed2float(out.y),
+ side);
+ continue;
+ }
+ out_side = all_side;
+ }
+ else
+ out_side = which_side(F);
+ /* If we skipped any segments, put out a moveto/lineto. */
+ if ( side && (px != out.x || py != out.y || first_point()) )
+ { fixed diff[2];
+ diff[0] = out.x - px, diff[1] = out.y - py;
+ if ( open < 0 )
+ { first = out;
+ code = cmd_put_rmoveto(&writer, diff);
+ }
+ else
+ code = cmd_put_rlineto(&writer, diff);
+ if ( code < 0 )
+ return code;
+ px = out.x, py = out.y;
+ if_debug3('p', "[p]catchup %s (%g,%g) for curve\n",
+ (open < 0 ? "moveto" : "lineto"),
+ fixed2float(px), fixed2float(py));
+ }
+ if ( (side = out_side) != 0 )
+ { /* Note a vertex going outside the clip region. */
+ out.x = E, out.y = F;
+ }
+ }
+ { fixed nx = E, ny = F;
+ const fixed _ss *optr = vs;
+ byte op;
+
+ if_debug7('p', "[p]curveto (%g,%g; %g,%g; %g,%g) side %d\n",
+ fixed2float(A), fixed2float(B),
+ fixed2float(C), fixed2float(D),
+ fixed2float(E), fixed2float(F), side);
+ E -= C, F -= D;
+ C -= A, D -= B;
+ A -= px, B -= py;
+ if ( B == 0 && E == 0 )
+ B = A, E = F, optr++, op = cmd_opv_hvcurveto;
+ else if ( A == 0 && F == 0 )
+ optr++, op = cmd_opv_vhcurveto;
+ else if ( A == 0 && B == 0 )
+ optr += 2, op = cmd_opv_nrcurveto;
+ else if ( E == 0 && F == 0 )
+ op = cmd_opv_rncurveto;
+ else
+ op = cmd_opv_rrcurveto;
+ px = nx, py = ny;
+ open = 1;
+ code = cmd_put_segment(&writer, op, optr);
+ } break;
+ default:
+ return_error(gs_error_rangecheck);
+ }
+ if ( code < 0 )
+ return code;
+#undef A
+#undef B
+#undef C
+#undef D
+#undef E
+#undef F
+ }
+}
diff --git a/pstoraster/gxclpath.h b/pstoraster/gxclpath.h
new file mode 100644
index 000000000..ee128ff33
--- /dev/null
+++ b/pstoraster/gxclpath.h
@@ -0,0 +1,155 @@
+/* Copyright (C) 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxclpath.h */
+/* Internal definitions for higher-level command list facilities. */
+/* Extends (requires) gxcldev.h */
+#include "gxfixed.h" /* for gzpath.h */
+
+/* Extend the command set. See gxcldev.h for more information. */
+typedef enum {
+ cmd_op_misc2 = 0xd0, /* (see below) */
+ cmd_opv_set_flatness = 0xd0, /* flatness(float) */
+ cmd_opv_set_fill_adjust = 0xd1, /* adjust_x/y(fixed) */
+ cmd_opv_set_ctm = 0xd2, /* (0=0,0, 1=V,V, 2=V,-V, 3=U,V)x */
+ /* (xx+yy,xy+yx)(0=0, 1=V)x(tx,ty), */
+ /* 0..5 x coeff(float) */
+ cmd_opv_set_line_width = 0xd3, /* width(float) */
+ cmd_opv_set_misc = 0xd4, /* overprint(1)stroke_adj(1) */
+ /* cap(3)join(3) */
+ cmd_opv_set_miter_limit = 0xd5, /* miter limit(float) */
+ cmd_opv_set_dash = 0xd6, /* n, offset(float), n x (float) */
+ cmd_opv_enable_clip = 0xd7, /* (nothing) */
+ cmd_opv_disable_clip = 0xd8, /* (nothing) */
+ cmd_opv_begin_clip = 0xd9, /* (nothing) */
+ cmd_opv_end_clip = 0xda, /* outside? */
+ cmd_opv_set_color_space = 0xdb, /* base(4)Indexed?(1)0(3) */
+ /* [, hival#] */
+ cmd_opv_set_color_mapping = 0xdc, /******NYI******/
+ cmd_opv_begin_image = 0xdd, /* BPCi(3)(0=mask)Interpolate(1) */
+ /* Matrix?(1)Decode?(1) */
+ /* adjust/CombineWithColor(1)0(1), */
+ /* shape, width#, height#, */
+ /* [, aabbcd00, 0..6 x coeff(float)] */
+ /* [, (0=default, 1=swapped default, */
+ /* 2=0,V, 3=U,V)x4, */
+ /* 0..8 x decode(float)] */
+ cmd_opv_image_data = 0xde, /* nbytes# (premature EOD if 0), */
+ /* (0=same, 1=+1, 2=+d, 3=-d)x */
+ /* (x,y,w,h), [dx#,] [dy#,] [dw#,] */
+ /* [dh#,] <data> */
+ cmd_op_segment = 0xe0, /* (see below) */
+ cmd_opv_rmoveto = 0xe0, /* dx%, dy% */
+ cmd_opv_rlineto = 0xe1, /* dx%, dy% */
+ cmd_opv_hlineto = 0xe2, /* dx% */
+ cmd_opv_vlineto = 0xe3, /* dy% */
+ cmd_opv_rrcurveto = 0xe4, /* dx1%,dy1%, dx2%,dy2%, dx3%,dy3% */
+ cmd_opv_hvcurveto = 0xe5, /* dx1%, dx2%,dy2%, dy3% */
+ cmd_opv_vhcurveto = 0xe6, /* dy1%, dx2%,dy2%, dx3% */
+ cmd_opv_nrcurveto = 0xe7, /* dx2%,dy2%, dx3%,dy3% */
+ cmd_opv_rncurveto = 0xe8, /* dx1%,dy1%, dx2%,dy2% */
+ cmd_opv_rmlineto = 0xe9, /* dx1%,dy1%, dx2%,dy2% */
+ cmd_opv_rm2lineto = 0xea, /* dx1%,dy1%, dx2%,dy2%, dx3%,dy3% */
+ cmd_opv_rm3lineto = 0xeb, /* dx1%,dy1%, dx2%,dy2%, dx3%,dy3%, */
+ /* [-dx2,-dy2 implicit] */
+ cmd_opv_closepath = 0xec, /* (nothing) */
+ cmd_op_path = 0xf0, /* (see below) */
+ cmd_opv_fill = 0xf0,
+ cmd_opv_eofill = 0xf1,
+ cmd_opv_stroke = 0xf2,
+ cmd_opv_htfill = 0xf3,
+ cmd_opv_hteofill = 0xf4,
+ cmd_opv_htstroke = 0xf5
+} gx_cmd_xop;
+
+static const byte clist_segment_op_num_operands[] =
+ { 2, 2, 1, 1, 6, 4, 4, 4, 4, 4, 6, 6, 0
+ };
+
+#define cmd_misc2_op_name_strings\
+ "set_flatness", "set_fill_adjust", "set_ctm", "set_line_width",\
+ "set_misc", "set_miter_limit", "set_dash", "enable_clip",\
+ "disable_clip", "begin_clip", "end_clip", "set_color_space",\
+ "set_color_mapping", "begin_image", "image_data", "?df?"
+
+#define cmd_segment_op_name_strings\
+ "rmoveto", "rlineto", "hlineto", "vlineto",\
+ "rrcurveto", "hvcurveto", "vhcurveto", "nrcurveto",\
+ "rncurveto", "rmlineto", "rm2lineto", "rm3lineto",\
+ "closepath", "?ed?", "?ee?", "?ef?"
+
+#define cmd_path_op_name_strings\
+ "fill", "eofill", "stroke", "htfill",\
+ "hteofill", "htstroke", "?f6?", "?f7?",\
+ "?f8?", "?f9?", "?fa?", "?fb?",\
+ "?fc?", "?fd?", "?fe?", "?ff?"
+
+/*
+ * We represent path coordinates as 'fixed' values in a variable-length,
+ * relative form (s/t = sign, x/y = integer, f/g = fraction):
+ * 00sxxxxx xfffffff ffffftyy yyyygggg gggggggg
+ * 01sxxxxx xxxxffff ffffffff
+ * 10sxxxxx xxxxxxxx xxxxffff ffffffff
+ * 110sxxxx xxxxxxff
+ * 111----- (a full-size `fixed' value)
+ */
+#define is_bits(d, n) !(((d) + ((fixed)1 << ((n) - 1))) & (-(fixed)1 << (n)))
+
+/* ---------------- Driver procedure support ---------------- */
+
+/* The procedures and macros defined here are used when writing */
+/* (gxclimag.c, gxclpath.c). */
+
+/* Compare and update members of the imager state. */
+#define state_neq(member)\
+ cdev->imager_state.member != pis->member
+#define state_update(member)\
+ cdev->imager_state.member = pis->member
+
+/* ------ Exported by gxclpath.c ------ */
+
+/* Write out the color for filling, stroking, or masking. */
+int cmd_put_drawing_color(P3(gx_device_clist_writer *cldev,
+ gx_clist_state *pcls,
+ const gx_drawing_color *pdcolor));
+
+/* Clear (a) specific 'known' flag(s) for all bands. */
+/* We must do this whenever the value of a 'known' parameter changes. */
+void cmd_clear_known(P2(gx_device_clist_writer *cldev, uint known));
+
+/* Check whether we need to change the clipping path in the device. */
+bool cmd_check_clip_path(P2(gx_device_clist_writer *cldev,
+ const gx_clip_path *pcpath));
+
+/* Construct the parameters for writing out a matrix. */
+/* We need a buffer of at least 1 + 6 * sizeof(float) bytes. */
+byte *cmd_for_matrix(P2(byte *cbuf, const gs_matrix *pmat));
+
+/* Write out values of any unknown parameters. */
+#define cmd_do_write_unknown(cldev, pcls, must_know)\
+ if ( ~(pcls)->known & (must_know) )\
+ { int code = cmd_write_unknown(cldev, pcls, must_know);\
+ if ( code < 0 ) return code;\
+ }
+int cmd_write_unknown(P3(gx_device_clist_writer *cldev, gx_clist_state *pcls,
+ uint must_know));
diff --git a/pstoraster/gxclread.c b/pstoraster/gxclread.c
new file mode 100644
index 000000000..eb6b0c85b
--- /dev/null
+++ b/pstoraster/gxclread.c
@@ -0,0 +1,1523 @@
+/*
+ Copyright 1993-1999 by Easy Software Products.
+ Copyright (C) 1991, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxclread.c */
+/* Command list reading for Ghostscript. */
+#include "memory_.h"
+#include "gx.h"
+#include "gpcheck.h"
+#include "gserrors.h"
+#include "gsbitops.h"
+#include "gscspace.h"
+#include "gsdcolor.h"
+#include "gxdevice.h"
+#include "gsdevice.h" /* for gs_deviceinitialmatrix */
+#include "gxdevmem.h" /* must precede gxcldev.h */
+#include "gxcldev.h"
+#include "gxclpath.h"
+#include "gxpaint.h" /* for gx_fill/stroke_params */
+#include "gxhttile.h"
+#include "gdevht.h"
+#include "gzpath.h"
+#include "gzcpath.h"
+#include "gzacpath.h"
+#include "stream.h"
+#include "strimpl.h"
+
+#define cdev crdev
+
+/* Print a bitmap for tracing */
+#ifdef DEBUG
+private void
+cmd_print_bits(const byte *data, int width, int height, int raster)
+{ int i, j;
+ dprintf3("[L]width=%d, height=%d, raster=%d\n",
+ width, height, raster);
+ for ( i = 0; i < height; i++ )
+ { const byte *row = data + i * raster;
+ dprintf("[L]");
+ for ( j = 0; j < raster; j++ )
+ dprintf1(" %02x", row[j]);
+ dputc('\n');
+ }
+}
+#else
+# define cmd_print_bits(data, width, height, raster) DO_NOTHING
+#endif
+
+/* ------ Band file reading stream ------ */
+
+/*
+ * To separate banding per se from command list interpretation,
+ * we make the command list interpreter simply read from a stream.
+ * When we are actually doing banding, the stream filters the band file
+ * and only passes through the commands for the current band (or all bands).
+ */
+typedef struct stream_band_read_state_s {
+ stream_state_common;
+ clist_file_ptr cfile;
+ clist_file_ptr bfile;
+ long bfile_end_pos;
+ int band;
+ uint left; /* amount of data left in this run */
+ cmd_block b_this;
+} stream_band_read_state;
+
+#define ss ((stream_band_read_state *)st)
+
+private int
+s_band_read_init(stream_state *st)
+{ ss->left = 0;
+ ss->b_this.band = 0;
+ ss->b_this.pos = 0;
+ clist_rewind(ss->bfile, false);
+ return 0;
+}
+
+private int
+s_band_read_process(stream_state *st, stream_cursor_read *ignore_pr,
+ stream_cursor_write *pw, bool last)
+{ register byte *q = pw->ptr;
+ byte *wlimit = pw->limit;
+ clist_file_ptr cfile = ss->cfile;
+ clist_file_ptr bfile = ss->bfile;
+ uint left = ss->left;
+ int status = 1;
+ uint count;
+
+ while ( (count = wlimit - q) != 0 )
+ { if ( left )
+ { /* Read more data for the current run. */
+ if ( count > left )
+ count = left;
+ clist_fread_chars(q + 1, count, cfile);
+ /****** CHECK FOR ferror ******/
+ q += count;
+ left -= count;
+ process_interrupts();
+ continue;
+ }
+rb: /* Scan for the next run for this band (or all bands). */
+ if ( ss->b_this.band == cmd_band_end &&
+ clist_ftell(bfile) == ss->bfile_end_pos
+ )
+ { status = EOFC;
+ break;
+ }
+ { int band = ss->b_this.band;
+ long pos = ss->b_this.pos;
+ clist_fread_chars(&ss->b_this, sizeof(ss->b_this), bfile);
+ if ( !(band == ss->band || band == cmd_band_all) )
+ goto rb;
+ clist_fseek(cfile, pos, SEEK_SET);
+ left = (uint)(ss->b_this.pos - pos);
+ }
+ }
+ pw->ptr = q;
+ ss->left = left;
+ return status;
+}
+
+#undef ss
+
+/* Stream template */
+const stream_template s_band_read_template =
+{ &st_stream_state, s_band_read_init, s_band_read_process, 1, cbuf_size
+};
+
+
+/* ------ Reading/rendering ------ */
+
+private int clist_render_init(P2(gx_device_clist *, gx_device_ht *));
+private int clist_render_band(P6(gx_device_clist_reader *, stream *, gx_device *, int, int, gs_memory_t *));
+
+/* Copy a scan line to the client. This is where rendering gets done. */
+int
+clist_get_bits(gx_device *dev, int y, byte *str, byte **actual_data)
+{ gx_device *target = cdev->target;
+ uint raster = gx_device_raster(target, 1);
+ gx_device_memory mdev;
+ gx_device_ht hdev;
+ gx_device *tdev = (gx_device *)&mdev;
+
+ /* Initialize for rendering if we haven't done so yet. */
+ if ( cdev->ymin < 0 )
+ { int code = clist_render_init((gx_device_clist *)dev, &hdev);
+ if ( code < 0 )
+ return code;
+ if ( code != 0 )
+ { hdev.target = tdev;
+ tdev = (gx_device *)&hdev;
+ }
+ }
+ /* Render a band if necessary, and copy it incrementally. */
+ if ( !(y >= cdev->ymin && y < cdev->ymax) )
+ { int band = y / cdev->band_height;
+ stream s;
+ stream_band_read_state rs;
+ byte sbuf[cbuf_size];
+ int code;
+ private const stream_procs no_procs =
+ { s_std_noavailable, s_std_noseek, s_std_read_reset,
+ s_std_read_flush, s_std_close, s_band_read_process
+ };
+
+ if ( y < 0 || y > dev->height )
+ return_error(gs_error_rangecheck);
+ code = (*cdev->make_buffer_device)
+ (&mdev, target, 0, true);
+ if ( code < 0 )
+ return code;
+ mdev.base = cdev->mdata;
+ rs.template = &s_band_read_template;
+ rs.memory = 0;
+ rs.cfile = cdev->cfile;
+ rs.bfile = cdev->bfile;
+ rs.bfile_end_pos = cdev->bfile_end_pos;
+ rs.band = band;
+ s_band_read_init((stream_state *)&rs);
+ s_std_init(&s, sbuf, cbuf_size, &no_procs, s_mode_read);
+ s.foreign = 1;
+ s.state = (stream_state *)&rs;
+ /*
+ * The matrix in the memory device is irrelevant,
+ * because all we do with the device is call the device-level
+ * output procedures, but we may as well set it to
+ * something halfway reasonable.
+ */
+ gs_deviceinitialmatrix(target, &mdev.initial_matrix);
+ mdev.width = target->width;
+ mdev.height = cdev->band_height;
+ mdev.raster = raster;
+ (*dev_proc(&mdev, open_device))((gx_device *)&mdev);
+ /* We have to pick some allocator for rendering.... */
+ if_debug1('l', "[l]rendering band %d\n", band);
+ code = clist_render_band(cdev, &s, tdev, 0, band * mdev.height,
+ (cdev->memory != 0 ? cdev->memory :
+ &gs_memory_default));
+ /* Reset the band boundaries now, so that we don't get */
+ /* an infinite loop. */
+ cdev->ymin = band * mdev.height;
+ cdev->ymax = cdev->ymin + mdev.height;
+ if ( cdev->ymax > dev->height )
+ cdev->ymax = dev->height;
+ if ( code < 0 )
+ return code;
+ }
+ { byte *src = cdev->mdata + (y - cdev->ymin) * raster;
+ if ( actual_data == 0 )
+ memcpy(str, src, gx_device_raster(dev, 0));
+ else
+ *actual_data = src;
+ }
+ return 0;
+}
+
+/* Initialize for reading. */
+private int
+clist_render_init(gx_device_clist *dev, gx_device_ht *hdev)
+{ int code = clist_flush_buffer(&dev->writer);
+ if ( code < 0 )
+ return code;
+ /* Write the terminating entry in the block file. */
+ /* Note that because of copypage, there may be many such entries. */
+ { cmd_block cb;
+ cb.band = cmd_band_end;
+ cb.pos = clist_ftell(cdev->cfile);
+ clist_fwrite_chars(&cb, sizeof(cb), cdev->bfile);
+ cdev->bfile_end_pos = clist_ftell(cdev->bfile);
+ if_debug2('l', "[l]clist_render_init at cfile=%ld, bfile=%ld\n",
+ cb.pos, cdev->bfile_end_pos);
+ }
+ cdev->ymin = cdev->ymax = 0;
+ return 0;
+}
+
+#undef cdev
+
+/* Get a variable-length integer operand. */
+#define cmd_getw(var, p)\
+ do\
+ { if ( *p < 0x80 ) var = *p++;\
+ else { const byte *_cbp; var = cmd_get_w(p, &_cbp); p = _cbp; }\
+ }\
+ while (0)
+private long near
+cmd_get_w(const byte *p, const byte **rp)
+{ long val = *p++ & 0x7f;
+ int shift = 7;
+ for ( ; val += (long)(*p & 0x7f) << shift, *p++ > 0x7f; shift += 7 )
+ ;
+ *rp = p;
+ return val;
+}
+
+/* Render one band to a specified target device. */
+private const byte *cmd_read_rect(P3(int, gx_cmd_rect *, const byte *));
+private const byte *cmd_read_matrix(P2(gs_matrix *, const byte *));
+private void clist_unpack_short_bits(P5(byte *, const byte *, int, int, uint));
+private int clist_decode_segment(P6(gx_path *, int, fixed [6],
+ gs_fixed_point *, int, int));
+private int
+clist_render_band(gx_device_clist_reader *cdev, stream *s, gx_device *target,
+ int x0, int y0, gs_memory_t *mem)
+{ byte cbuf[cbuf_size];
+ /* data_bits is for short copy_* bits and copy_* compressed, */
+ /* must be aligned */
+#define data_bits_size cbuf_size
+ byte *data_bits;
+ register const byte *cbp;
+ const byte *cb_limit;
+ const byte *cb_end;
+ int end_status = 0;
+ int dev_depth = cdev->color_info.depth;
+ int dev_depth_bytes = (dev_depth + 7) >> 3;
+ gx_device *tdev = target;
+ gx_clist_state state;
+ gx_color_index _ss *set_colors = state.colors;
+ tile_slot *state_slot;
+ tile_slot tile_bits; /* current tile parameters */
+ tile_slot bits; /* for reading bits */
+ gx_strip_bitmap state_tile;
+ gs_int_point tile_phase;
+ gx_path path;
+ bool in_path;
+ gs_fixed_point ppos;
+ gx_clip_path clip_path;
+ gx_clip_path *pcpath = NULL;
+ gx_device_cpath_accum clip_accum;
+ struct _cas {
+ bool lop_enabled;
+ gs_fixed_point fill_adjust;
+ } clip_save;
+ gs_imager_state imager_state;
+ float dash_pattern[cmd_max_dash];
+ gx_fill_params fill_params;
+ gx_stroke_params stroke_params;
+ gx_ht_order order;
+ uint ht_data_index;
+ gs_color_space color_space; /* only used for indexed spaces */
+ const gs_color_space *pcs;
+ void *image_info;
+ int image_xywh[4];
+ int data_x = 0;
+ int code = 0;
+
+#define cmd_get_value(var, cbp)\
+ memcpy(&var, cbp, sizeof(var));\
+ cbp += sizeof(var)
+#define cmd_read(ptr, rsize, cbp)\
+ if ( cb_end - cbp >= (rsize) )\
+ memcpy(ptr, cbp, rsize), cbp += rsize;\
+ else\
+ { uint cleft = cb_end - cbp, rleft = (rsize) - cleft;\
+ memcpy(ptr, cbp, cleft);\
+ sgets(s, ptr + cleft, rleft, &rleft);\
+ cbp = cb_end;\
+ }
+#define cmd_read_short_bits(ptr, bw, ht, ras, cbp)\
+ cmd_read(ptr, (bw) * (ht), cbp);\
+ clist_unpack_short_bits(ptr, ptr, bw, ht, ras)
+
+ { static const gx_clist_state cls_initial = { cls_initial_values };
+ state = cls_initial;
+ }
+ state_tile.id = gx_no_bitmap_id;
+ state_tile.shift = state_tile.rep_shift = 0;
+ tile_phase.x = tile_phase.y = 0;
+ gx_path_init(&path, mem);
+ in_path = false;
+ /* Initialize the clipping region to the full band. */
+ { gs_fixed_rect cbox;
+ cbox.p.x = 0;
+ cbox.p.y = 0;
+ cbox.q.x = cdev->width;
+ cbox.q.y = cdev->height;
+ gx_cpath_from_rectangle(&clip_path, &cbox, mem);
+ }
+ imager_state = clist_imager_state_initial;
+ imager_state.line_params.dash.pattern = dash_pattern;
+ fill_params.fill_zero_width = false;
+ /****** SET order.cache ******/
+ order.transfer = 0;
+ pcs = gs_color_space_DeviceGray();
+ data_bits = gs_alloc_bytes(mem, data_bits_size,
+ "clist_render_band(data_bits)");
+ if ( data_bits == 0 )
+ { code = gs_note_error(gs_error_VMerror);
+ goto out;
+ }
+ cb_limit = cbuf + (cbuf_size - cmd_largest_size + 1);
+ cb_end = cbuf + cbuf_size;
+ cbp = cb_end;
+ while ( !code )
+ { int op;
+ int compress, depth, raster;
+ uint rep_width, rep_height;
+ byte *source;
+ gx_color_index colors[2];
+ gx_color_index _ss *pcolor;
+
+ /* Make sure the buffer contains a full command. */
+#define set_cb_end(p)\
+ cb_end = p;\
+ cb_limit = cbuf + (cbuf_size - cmd_largest_size + 1);\
+ if ( cb_limit > cb_end ) cb_limit = cb_end
+ if ( cbp >= cb_limit )
+ { if ( end_status < 0 )
+ { /* End of file or error. */
+ if ( cbp == cb_end )
+ { code = (end_status == EOFC ? 0 :
+ gs_note_error(gs_error_ioerror));
+ break;
+ }
+ }
+ else
+ { uint nread;
+ memmove(cbuf, cbp, cb_end - cbp);
+ cbp = cbuf + (cb_end - cbp);
+ nread = cb_end - cbp;
+ /* Cast to remove 'const'. */
+ end_status = sgets(s, (byte *)cbp,
+ nread, &nread);
+ set_cb_end(cbp + nread);
+ cbp = cbuf;
+ process_interrupts();
+ }
+ }
+ op = *cbp++;
+#ifdef DEBUG
+ if ( gs_debug_c('L') )
+ { const char **sub = cmd_sub_op_names[op >> 4];
+ if ( sub )
+ dprintf1("[L]%s:\n", sub[op & 0xf]);
+ else
+ dprintf2("[L]%s %d\n", cmd_op_names[op >> 4], op & 0xf);
+ }
+#endif
+ switch ( op >> 4 )
+ {
+ case cmd_op_misc >> 4:
+ switch ( op )
+ {
+ case cmd_opv_end_run:
+ continue;
+ case cmd_opv_set_tile_size:
+ tile_bits.cb_depth = *cbp++;
+ cmd_getw(tile_bits.width, cbp);
+ cmd_getw(tile_bits.height, cbp);
+ cmd_getw(state_tile.rep_width, cbp);
+ cmd_getw(state_tile.rep_height, cbp);
+ cmd_getw(tile_bits.shift, cbp);
+ state_tile.rep_shift = tile_bits.shift;
+ state_tile.shift =
+ (state_tile.rep_shift == 0 ? 0 :
+ (state_tile.rep_shift *
+ (tile_bits.height /
+ state_tile.rep_height))
+ % state_tile.rep_width);
+ tile_bits.cb_raster =
+ bitmap_raster(tile_bits.width *
+ tile_bits.cb_depth);
+ /* Set state_tile the same as tile_bits. */
+ state_tile.size.x = tile_bits.width;
+ state_tile.size.y = tile_bits.height;
+ state_tile.raster = tile_bits.cb_raster;
+ break;
+ case cmd_opv_set_tile_phase:
+ cmd_getw(state.tile_phase.x, cbp);
+ cmd_getw(state.tile_phase.y, cbp);
+ break;
+ case cmd_opv_set_tile_bits:
+ bits = tile_bits;
+ rep_width = state_tile.rep_width;
+ rep_height = state_tile.rep_height;
+ compress = 0;
+stb: { ulong offset;
+ uint width_bits = rep_width * bits.cb_depth;
+ uint width_bytes = (width_bits + 7) >> 3;
+ uint bytes =
+ (rep_height == 0 ? 0 :
+ rep_height == 1 ? width_bytes :
+ compress ? bits.cb_raster * rep_height :
+ bits.cb_raster * (rep_height - 1) +
+ width_bytes);
+ byte *data;
+
+ cmd_getw(state.tile_index, cbp);
+ cmd_getw(offset, cbp);
+ cdev->tile_table[state.tile_index].offset =
+ offset;
+ state_slot =
+ (tile_slot *)(cdev->chunk.data + offset);
+ state_slot->cb_depth = bits.cb_depth;
+ state_slot->width = bits.width;
+ state_slot->height = bits.height;
+ state_slot->cb_raster = bits.cb_raster;
+ state_tile.data = data =
+ (byte *)(state_slot + 1);
+#ifdef DEBUG
+ state_slot->index = state.tile_index;
+#endif
+ if ( compress )
+ { /* Decompress the image data. */
+ /* We'd like to share this code */
+ /* with the similar code in copy_*, */
+ /* but right now we don't see how. */
+ stream_cursor_read r;
+ stream_cursor_write w;
+ /* We don't know the data length a */
+ /* priori, so to be conservative, */
+ /* we read the uncompressed size. */
+ uint cleft = cb_end - cbp;
+
+ if ( cleft < bytes )
+ { uint nread = cbuf_size - cleft;
+ memmove(cbuf, cbp, cleft);
+ end_status = sgets(s, cbuf + cleft, nread, &nread);
+ set_cb_end(cbuf + cleft + nread);
+ cbp = cbuf;
+ }
+ r.ptr = cbp - 1;
+ r.limit = cb_end - 1;
+ w.ptr = data - 1;
+ w.limit = w.ptr + bytes;
+ switch ( compress )
+ {
+ case cmd_compress_rle:
+ { stream_RLD_state sstate;
+ clist_rld_init(&sstate);
+ (*s_RLD_template.process)
+ ((stream_state *)&sstate, &r, &w, true);
+ } break;
+ case cmd_compress_cfe:
+ { stream_CFD_state sstate;
+ clist_cfd_init(&sstate,
+ width_bits,
+ bits.height);
+ (*s_CFD_template.process)
+ ((stream_state *)&sstate, &r, &w, true);
+ (*s_CFD_template.release)
+ ((stream_state *)&sstate);
+ } break;
+ default:
+ goto bad_op;
+ }
+ cbp = r.ptr + 1;
+ }
+ else if ( width_bytes <= cmd_max_short_width_bytes ||
+ bits.width > rep_width
+ )
+ { cmd_read_short_bits(data, width_bytes,
+ rep_height, bits.cb_raster, cbp);
+ }
+ else
+ { cmd_read(data, bytes, cbp);
+ }
+ if ( bits.width > rep_width )
+ bits_replicate_horizontally(data,
+ rep_width * bits.cb_depth, rep_height,
+ bits.cb_raster,
+ bits.width * bits.cb_depth,
+ bits.cb_raster);
+ if ( bits.height > rep_height )
+ bits_replicate_vertically(data,
+ rep_height, bits.cb_raster,
+ bits.height);
+#ifdef DEBUG
+if ( gs_debug_c('L') )
+ { dprintf4("[L]index=%u, offset=%lu, data=[%lu..%lu)\n",
+ state.tile_index, offset,
+ (ulong)(data - cdev->chunk.data),
+ (ulong)(data - cdev->chunk.data) +
+ bits.cb_raster * bits.height);
+ cmd_print_bits(data, bits.width, bits.height,
+ bits.cb_raster);
+ }
+#endif
+ }
+ continue;
+ case cmd_opv_set_bits:
+ compress = *cbp & 3;
+ bits.cb_depth = *cbp++ >> 2;
+ cmd_getw(bits.width, cbp);
+ cmd_getw(bits.height, cbp);
+ rep_width = bits.width;
+ rep_height = bits.height;
+ bits.cb_raster =
+ bitmap_raster(bits.width * bits.cb_depth);
+ goto stb;
+ case cmd_opv_set_tile_color:
+ set_colors = state.tile_colors;
+ continue;
+ case cmd_opv_set_lop:
+ cmd_getw(state.lop, cbp);
+ continue;
+ case cmd_opv_enable_lop:
+ state.lop_enabled = 1;
+ continue;
+ case cmd_opv_disable_lop:
+ state.lop_enabled = 0;
+ continue;
+ case cmd_opv_set_ht_size:
+ { ulong offset;
+ tile_slot *slot;
+ cmd_getw(offset, cbp);
+ slot = (tile_slot *)(cdev->chunk.data + offset);
+ order.bits = (gx_ht_bit *)(slot + 1);
+ cmd_getw(order.width, cbp);
+ cmd_getw(order.height, cbp);
+ cmd_getw(order.raster, cbp);
+ cmd_getw(order.shift, cbp);
+ cmd_getw(order.num_levels, cbp);
+ cmd_getw(order.num_bits, cbp);
+ order.full_height =
+ ht_order_full_height(&order);
+ order.levels =
+ (uint *)(order.bits + order.num_bits);
+ }
+ ht_data_index = 0;
+ continue;
+ case cmd_opv_set_ht_data:
+ { int n = *cbp++;
+ if ( ht_data_index < order.num_levels )
+ { /* Setting levels */
+ byte *lptr = (byte *)
+ (order.levels + ht_data_index);
+ cmd_read(lptr, n * sizeof(*order.levels),
+ cbp);
+ }
+ else
+ { /* Setting bits */
+ byte *bptr = (byte *)
+ (order.bits +
+ (ht_data_index - order.num_levels));
+ cmd_read(bptr, n * sizeof(*order.bits),
+ cbp);
+ }
+ ht_data_index += n;
+ }
+ continue;
+ case cmd_opv_next_data_x:
+ data_x = *cbp++;
+ continue;
+ case cmd_opv_delta2_color0:
+ pcolor = &set_colors[0];
+ goto delta2_c;
+ case cmd_opv_delta2_color1:
+ pcolor = &set_colors[1];
+delta2_c: set_colors = state.colors;
+ { gx_color_index b = ((uint)*cbp << 8) + cbp[1];
+ cbp += 2;
+ if ( dev_depth > 24 )
+ *pcolor +=
+ ((b & 0xf000) << 12) + ((b & 0x0f00) << 8) +
+ ((b & 0x00f0) << 4) + (b & 0x000f) -
+ cmd_delta2_32_bias;
+ else
+ *pcolor +=
+ ((b & 0xf800) << 5) + ((b & 0x07e0) << 3) +
+ (b & 0x001f) - cmd_delta2_24_bias;
+ }
+ continue;
+ case cmd_opv_set_copy_color:
+ state.color_is_alpha = 0;
+ continue;
+ case cmd_opv_set_copy_alpha:
+ state.color_is_alpha = 1;
+ continue;
+ default:
+ goto bad_op;
+ }
+ tile_phase.x =
+ (state.tile_phase.x + x0) % state_tile.size.x;
+ tile_phase.y =
+ (state.tile_phase.y + y0) % state_tile.size.y;
+ continue;
+ case cmd_op_set_color0 >> 4:
+ pcolor = &set_colors[0];
+ goto set_color;
+ case cmd_op_set_color1 >> 4:
+ pcolor = &set_colors[1];
+set_color: set_colors = state.colors;
+ switch ( op & 0xf )
+ {
+ case 0:
+ break;
+ case 15: /* special handling because this may */
+ /* require more bits than depth */
+ *pcolor = gx_no_color_index;
+ continue;
+ default:
+ switch ( dev_depth_bytes )
+ {
+ case 4:
+ { gx_color_index b =
+ ((gx_color_index)(op & 0xf) << 8) + *cbp++;
+ *pcolor +=
+ ((b & 07000) << 15) + ((b & 0700) << 10) +
+ ((b & 070) << 5) + (b & 7) -
+ cmd_delta1_32_bias;
+ continue;
+ }
+ case 3:
+ { gx_color_index b = *cbp++;
+ *pcolor +=
+ ((gx_color_index)(op & 0xf) << 16) +
+ ((b & 0xf0) << 4) + (b & 0x0f) -
+ cmd_delta1_24_bias;
+ continue;
+ }
+ case 2:
+ break;
+ case 1:
+ *pcolor += (gx_color_index)(op & 0xf) - 8;
+ continue;
+ }
+ }
+ { gx_color_index color = 0;
+ switch ( dev_depth_bytes )
+ {
+ case 4: color |= (gx_color_index)*cbp++ << 24;
+ case 3: color |= (gx_color_index)*cbp++ << 16;
+ case 2: color |= (gx_color_index)*cbp++ << 8;
+ case 1: color |= (gx_color_index)*cbp++;
+ }
+ *pcolor = color;
+ }
+ continue;
+ case cmd_op_fill_rect >> 4:
+ case cmd_op_tile_rect >> 4:
+ cbp = cmd_read_rect(op, &state.rect, cbp);
+ break;
+ case cmd_op_fill_rect_short >> 4:
+ case cmd_op_tile_rect_short >> 4:
+ state.rect.x += *cbp + cmd_min_short;
+ state.rect.width += cbp[1] + cmd_min_short;
+ if ( op & 0xf )
+ { state.rect.height += (op & 0xf) + cmd_min_dxy_tiny;
+ cbp += 2;
+ }
+ else
+ { state.rect.y += cbp[2] + cmd_min_short;
+ state.rect.height += cbp[3] + cmd_min_short;
+ cbp += 4;
+ }
+ break;
+ case cmd_op_fill_rect_tiny >> 4:
+ case cmd_op_tile_rect_tiny >> 4:
+ if ( op & 8 )
+ state.rect.x += state.rect.width;
+ else
+ { int txy = *cbp++;
+ state.rect.x += (txy >> 4) + cmd_min_dxy_tiny;
+ state.rect.y += (txy & 0xf) + cmd_min_dxy_tiny;
+ }
+ state.rect.width += (op & 7) + cmd_min_dw_tiny;
+ break;
+ case cmd_op_copy_mono >> 4:
+ depth = 1;
+ goto copy;
+ case cmd_op_copy_color_alpha >> 4:
+ if ( state.color_is_alpha )
+ { if ( !(op & 8) )
+ depth = *cbp++;
+ }
+ else
+ depth = dev_depth;
+copy: cmd_getw(state.rect.x, cbp);
+ cmd_getw(state.rect.y, cbp);
+ if ( op & 8 )
+ { /* Use the current "tile". */
+#ifdef DEBUG
+ if ( state_slot->index != state.tile_index )
+ { lprintf2("state_slot->index = %d, state.tile_index = %d!\n",
+ state_slot->index,
+ state.tile_index);
+ code = gs_note_error(gs_error_ioerror);
+ goto out;
+ }
+#endif
+ depth = state_slot->cb_depth;
+ state.rect.width = state_slot->width;
+ state.rect.height = state_slot->height;
+ raster = state_slot->cb_raster;
+ source = (byte *)(state_slot + 1);
+ }
+ else
+ { /* Read width, height, bits. */
+ /* depth was set already. */
+ uint bytes;
+ int width_bytes;
+ cmd_getw(state.rect.width, cbp);
+ cmd_getw(state.rect.height, cbp);
+ raster =
+ bitmap_raster(state.rect.width * depth);
+ width_bytes =
+ (state.rect.width * depth + 7) >> 3;
+ bytes =
+ (state.rect.height == 0 ? 0 :
+ state.rect.height == 1 ? width_bytes :
+ op & 3 ? raster * state.rect.height :
+ raster * (state.rect.height - 1) +
+ width_bytes);
+ /* copy_mono and copy_color/alpha */
+ /* ensure that the bits will fit in a single buffer, */
+ /* even after decompression if compressed. */
+#ifdef DEBUG
+ if ( bytes > cbuf_size )
+ { lprintf6("bitmap size exceeds buffer! width=%d raster=%d height=%d\n file pos %ld buf pos %d/%d\n",
+ state.rect.width, raster,
+ state.rect.height,
+ stell(s), (int)(cbp - cbuf),
+ (int)(cb_end - cbuf));
+ code = gs_note_error(gs_error_ioerror);
+ goto out;
+ }
+#endif
+ if ( op & 3 )
+ { /* Decompress the image data. */
+ stream_cursor_read r;
+ stream_cursor_write w;
+ /* We don't know the data length a priori, */
+ /* so to be conservative, we read */
+ /* the uncompressed size. */
+ uint cleft = cb_end - cbp;
+ if ( cleft < bytes )
+ { uint nread = cbuf_size - cleft;
+ memmove(cbuf, cbp, cleft);
+ end_status = sgets(s, cbuf + cleft, nread, &nread);
+ set_cb_end(cbuf + cleft + nread);
+ cbp = cbuf;
+ }
+ r.ptr = cbp - 1;
+ r.limit = cb_end - 1;
+ w.ptr = data_bits - 1;
+ w.limit = w.ptr + data_bits_size;
+ switch ( op & 3 )
+ {
+ case cmd_compress_rle:
+ { stream_RLD_state sstate;
+ clist_rld_init(&sstate);
+ /* The process procedure can't fail. */
+ (*s_RLD_template.process)
+ ((stream_state *)&sstate, &r, &w, true);
+ } break;
+ case cmd_compress_cfe:
+ { stream_CFD_state sstate;
+ clist_cfd_init(&sstate,
+ state.rect.width,
+ state.rect.height);
+ /* The process procedure can't fail. */
+ (*s_CFD_template.process)
+ ((stream_state *)&sstate, &r, &w, true);
+ (*s_CFD_template.release)
+ ((stream_state *)&sstate);
+ } break;
+ default:
+ goto bad_op;
+ }
+ cbp = r.ptr + 1;
+ source = data_bits;
+ }
+ else if ( width_bytes <= cmd_max_short_width_bytes )
+ { source = data_bits;
+ cmd_read_short_bits(source, width_bytes,
+ state.rect.height,
+ raster, cbp);
+ }
+ else
+ { cmd_read(cbuf, bytes, cbp);
+ source = cbuf;
+ }
+#ifdef DEBUG
+if ( gs_debug_c('L') )
+ { dprintf2("[L] depth=%d, data_x=%d\n",
+ depth, data_x);
+ cmd_print_bits(source, state.rect.width,
+ state.rect.height, raster);
+ }
+#endif
+ }
+ break;
+ case cmd_op_delta_tile_index >> 4:
+ state.tile_index += (int)(op & 0xf) - 8;
+ goto sti;
+ case cmd_op_set_tile_index >> 4:
+ state.tile_index =
+ ((op & 0xf) << 8) + *cbp++;
+sti: state_slot =
+ (tile_slot *)(cdev->chunk.data +
+ cdev->tile_table[state.tile_index].offset);
+ if_debug2('L', "[L]index=%u, offset=%lu\n",
+ state.tile_index,
+ cdev->tile_table[state.tile_index].offset);
+ state_tile.data = (byte *)(state_slot + 1);
+ continue;
+ case cmd_op_misc2 >> 4:
+ switch ( op )
+ {
+ case cmd_opv_set_flatness:
+ cmd_get_value(imager_state.flatness, cbp);
+ continue;
+ case cmd_opv_set_fill_adjust:
+ cmd_get_value(imager_state.fill_adjust.x, cbp);
+ cmd_get_value(imager_state.fill_adjust.y, cbp);
+ continue;
+ case cmd_opv_set_ctm:
+ cbp = cmd_read_matrix(
+ (gs_matrix *)&imager_state.ctm, cbp);
+ continue;
+ case cmd_opv_set_line_width:
+ { float width;
+ cmd_get_value(width, cbp);
+ gx_set_line_width(&imager_state.line_params, width);
+ }
+ continue;
+ case cmd_opv_set_misc:
+ imager_state.overprint =
+ (*cbp & 0x80) != 0;
+ imager_state.stroke_adjust =
+ (*cbp & 0x40) != 0;
+ imager_state.line_params.cap =
+ (gs_line_cap)((*cbp >> 3) & 7);
+ imager_state.line_params.join =
+ (gs_line_join)(*cbp & 7);
+ cbp++;
+ continue;
+ case cmd_opv_set_miter_limit:
+ { float limit;
+ cmd_get_value(limit, cbp);
+ gx_set_miter_limit(&imager_state.line_params, limit);
+ }
+ continue;
+ case cmd_opv_set_dash:
+ { int n = *cbp++;
+ float offset;
+ cmd_get_value(offset, cbp);
+ memcpy(dash_pattern, cbp, n * sizeof(float));
+ gx_set_dash(&imager_state.line_params.dash,
+ dash_pattern, n, offset,
+ NULL);
+ cbp += n * sizeof(float);
+ }
+ break;
+ case cmd_opv_enable_clip:
+ pcpath = &clip_path;
+ break;
+ case cmd_opv_disable_clip:
+ pcpath = NULL;
+ break;
+ case cmd_opv_begin_clip:
+ pcpath = NULL;
+ gx_cpath_release(&clip_path);
+ gx_cpath_accum_begin(&clip_accum, mem);
+ tdev = (gx_device *)&clip_accum;
+ clip_save.lop_enabled = state.lop_enabled;
+ clip_save.fill_adjust =
+ imager_state.fill_adjust;
+ state.lop_enabled = false;
+ imager_state.fill_adjust.x =
+ imager_state.fill_adjust.y = fixed_half;
+ break;
+ case cmd_opv_end_clip:
+ gx_cpath_accum_end(&clip_accum, &clip_path);
+ gx_cpath_set_outside(&clip_path, *cbp++);
+ pcpath = &clip_path;
+ tdev = target;
+ state.lop_enabled = clip_save.lop_enabled;
+ imager_state.fill_adjust =
+ clip_save.fill_adjust;
+ break;
+ case cmd_opv_set_color_space:
+ { byte b = *cbp++;
+ /****** TO BE COMPLETED ******/
+ if ( b & 8 )
+ cmd_getw(color_space.params.indexed.hival,
+ cbp);
+ }
+ break;
+ case cmd_opv_set_color_mapping:
+ goto bad_op; /****** NYI ******/
+ case cmd_opv_begin_image:
+ { byte b = *cbp++;
+ int bpci = b >> 5;
+ static byte bpc[6] = {1, 1, 2, 4, 8, 12};
+ byte shape = *cbp++;
+ gs_image_t image;
+ gx_drawing_color devc;
+ int num_components;
+
+ cmd_getw(image.Width, cbp);
+ cmd_getw(image.Height, cbp);
+ if ( b & (1 << 3) )
+ { /* Non-standard ImageMatrix */
+ cbp = cmd_read_matrix(
+ (gs_matrix *)&imager_state.ctm, cbp);
+ }
+ else
+ { image.ImageMatrix.xx = image.Width;
+ image.ImageMatrix.xy = 0;
+ image.ImageMatrix.yx = 0;
+ image.ImageMatrix.yy = -image.Height;
+ image.ImageMatrix.tx = 0;
+ image.ImageMatrix.ty = image.Height;
+ }
+ image.BitsPerComponent = bpc[bpci];
+ if ( bpci == 0 )
+ { image.ColorSpace = 0;
+ image.ImageMask = true;
+ image.Decode[0] = 0;
+ image.Decode[1] = 1;
+ num_components = 1;
+ }
+ else
+ { image.ColorSpace = pcs;
+ image.ImageMask = false;
+ if ( gs_color_space_get_index(pcs) == gs_color_space_index_Indexed )
+ { image.Decode[0] = 0;
+ image.Decode[1] =
+ (1 << image.BitsPerComponent) - 1;
+ }
+ else
+ { static const float decode01[8] =
+ { 0, 1, 0, 1, 0, 1, 0, 1 };
+ memcpy(image.Decode, decode01,
+ sizeof(image.Decode));
+ }
+ num_components =
+ gs_color_space_num_components(pcs);
+ }
+ image.Interpolate = (b & (1 << 4)) != 0;
+ if ( b & (1 << 2) )
+ { /* Non-standard Decode */
+ byte dflags = *cbp++;
+ int i;
+
+ for ( i = 0; i < num_components * 2;
+ dflags <<= 2, i += 2
+ )
+ switch ( (dflags >> 6) & 3 )
+ {
+ case 0: /* default */
+ break;
+ case 1: /* swapped default */
+ image.Decode[i] =
+ image.Decode[i+1];
+ image.Decode[i+1] = 0;
+ break;
+ case 3:
+ cmd_get_value(image.Decode[i],
+ cbp);
+ /* falls through */
+ case 2:
+ cmd_get_value(image.Decode[i+1],
+ cbp);
+ }
+ }
+ if ( b & (1 << 1) )
+ { if ( image.ImageMask )
+ image.adjust = true;
+ else
+ image.CombineWithColor = true;
+ }
+ color_set_pure(&devc, state.colors[1]);
+ code = (*dev_proc(tdev, begin_image))
+ (tdev, &imager_state, &image,
+ gs_image_format_chunky,
+ (gs_image_shape_t)shape,
+ &devc, pcpath, mem, &image_info);
+ if ( code < 0 )
+ goto out;
+ image_xywh[0] = 0;
+ image_xywh[1] = 0;
+ image_xywh[2] = image.Width;
+ image_xywh[3] = 1;
+ }
+ break;
+ case cmd_opv_image_data:
+ { uint nbytes;
+
+ cmd_get_value(nbytes, cbp);
+ if ( nbytes == 0 )
+ { code = (*dev_proc(tdev, end_image))
+ (tdev, image_info, true);
+ }
+ else
+ { byte b = *cbp++;
+ int diff, i;
+ const byte *data;
+
+ for ( i = 0; i < 4; b <<= 2, ++i )
+ switch ( (b >> 6) & 3 )
+ {
+ case 1:
+ image_xywh[i]++;
+ case 0:
+ break;
+ case 2:
+ cmd_get_value(diff, cbp);
+ image_xywh[i] += diff;
+ break;
+ case 3:
+ cmd_get_value(diff, cbp);
+ image_xywh[i] += diff;
+ break;
+ }
+ if ( cb_end - cbp >= nbytes )
+ { data = cbp;
+ cbp += nbytes;
+ }
+ else
+ { uint cleft = cb_end - cbp;
+ uint rleft = nbytes - cleft;
+
+ memmove(cbuf, cbp, cleft);
+ sgets(s, cbuf + cleft, rleft,
+ &rleft);
+ data = cbuf;
+ cbp = cb_end; /* force refill */
+ }
+ code = (*dev_proc(tdev, image_data))
+ (tdev, image_info, &data,
+ nbytes / image_xywh[3],
+ image_xywh[0], image_xywh[1],
+ image_xywh[2], image_xywh[3]);
+ }
+ }
+ if ( code < 0 )
+ goto out;
+ continue;
+ default:
+ goto bad_op;
+ }
+ continue;
+ case cmd_op_segment >> 4:
+ { fixed vs[6];
+ int i, code;
+
+ for ( i = 0;
+ i < clist_segment_op_num_operands[op & 0xf];
+ ++i
+ )
+ { fixed v;
+ int b = *cbp;
+ switch ( b >> 5 )
+ {
+ case 0: case 1:
+ vs[i++] =
+ ((fixed)((b ^ 0x20) - 0x20) << 13) +
+ ((int)cbp[1] << 5) + (cbp[2] >> 3);
+ cbp += 2;
+ v = (int)((*cbp & 7) ^ 4) - 4;
+ break;
+ case 2: case 3:
+ v = (b ^ 0x60) - 0x20;
+ break;
+ case 4: case 5:
+ v = (((b ^ 0xa0) - 0x20) << 8) + *++cbp;
+ break;
+ case 6:
+ v = (b ^ 0xd0) - 0x10;
+ vs[i] =
+ ((v << 8) + cbp[1]) << (_fixed_shift - 2);
+ cbp += 2;
+ continue;
+ default /*case 7*/:
+ v = (int)(*++cbp ^ 0x80) - 0x80;
+ for ( b = 0; b < sizeof(fixed) - 3; ++b )
+ v = (v << 8) + *++cbp;
+ break;
+ }
+ cbp += 3;
+ /* Absent the cast in the next statement, */
+ /* the Borland C++ 4.5 compiler incorrectly */
+ /* sign-extends the result of the shift. */
+ vs[i] =
+ (v << 16) + (uint)(cbp[-2] << 8) + cbp[-1];
+ }
+ if ( !in_path )
+ { ppos.x = int2fixed(state.rect.x);
+ ppos.y = int2fixed(state.rect.y);
+ in_path = true;
+ }
+ code = clist_decode_segment(&path, op, vs, &ppos,
+ x0, y0);
+ if ( code < 0 )
+ goto out;
+ } continue;
+ case cmd_op_path >> 4:
+ { gx_device_color devc;
+ gx_ht_tile ht_tile;
+ switch ( op )
+ {
+ case cmd_opv_fill:
+ fill_params.rule = gx_rule_winding_number;
+ goto fill_pure;
+ case cmd_opv_eofill:
+ fill_params.rule = gx_rule_even_odd;
+fill_pure: color_set_pure(&devc, state.colors[1]);
+ goto fill;
+ case cmd_opv_htfill:
+ fill_params.rule = gx_rule_winding_number;
+ goto fill_ht;
+ case cmd_opv_hteofill:
+ fill_params.rule = gx_rule_even_odd;
+fill_ht: ht_tile.tiles = state_tile;
+ color_set_binary_tile(&devc,
+ state.tile_colors[0],
+ state.tile_colors[1],
+ &ht_tile);
+ devc.phase = tile_phase;
+fill: fill_params.adjust = imager_state.fill_adjust;
+ fill_params.flatness = imager_state.flatness;
+ code = gx_fill_path_only(&path, tdev,
+ &imager_state,
+ &fill_params,
+ &devc, pcpath);
+ break;
+ case cmd_opv_stroke:
+ color_set_pure(&devc, state.colors[1]);
+ goto stroke;
+ case cmd_opv_htstroke:
+ ht_tile.tiles = state_tile;
+ color_set_binary_tile(&devc,
+ state.tile_colors[0],
+ state.tile_colors[1],
+ &ht_tile);
+ devc.phase = tile_phase;
+stroke: stroke_params.flatness = imager_state.flatness;
+ code = gx_stroke_path_only(&path,
+ (gx_path *)0, tdev,
+ &imager_state, &stroke_params,
+ &devc, pcpath);
+ break;
+ default:
+ goto bad_op;
+ }
+ }
+ if ( in_path ) /* path might be empty! */
+ { state.rect.x = fixed2int_var(ppos.x);
+ state.rect.y = fixed2int_var(ppos.y);
+ in_path = false;
+ }
+ gx_path_release(&path);
+ gx_path_init(&path, mem);
+ if ( code < 0 )
+ goto out;
+ continue;
+ default:
+bad_op: lprintf5("Bad op %02x band y0 = %d file pos %ld buf pos %d/%d\n",
+ op, y0, stell(s), (int)(cbp - cbuf), (int)(cb_end - cbuf));
+ { const byte *pp;
+ for ( pp = cbuf; pp < cb_end; pp += 10 )
+ { dprintf1("%4d:", (int)(pp - cbuf));
+ dprintf10(" %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ pp[0], pp[1], pp[2], pp[3], pp[4],
+ pp[5], pp[6], pp[7], pp[8], pp[9]);
+ }
+ }
+ code = gs_note_error(gs_error_Fatal);
+ goto out;
+ }
+ if_debug4('L', "[L] x=%d y=%d w=%d h=%d\n",
+ state.rect.x, state.rect.y, state.rect.width,
+ state.rect.height);
+ switch ( op >> 4 )
+ {
+ case cmd_op_fill_rect >> 4:
+ case cmd_op_fill_rect_short >> 4:
+ case cmd_op_fill_rect_tiny >> 4:
+ if ( !state.lop_enabled )
+ { code = (*dev_proc(tdev, fill_rectangle))
+ (tdev, state.rect.x - x0, state.rect.y - y0,
+ state.rect.width, state.rect.height,
+ state.colors[1]);
+ break;
+ }
+ source = NULL;
+ data_x = 0;
+ raster = 0;
+ colors[0] = colors[1] = state.colors[1];
+ pcolor = colors;
+do_rop: code = (*dev_proc(tdev, strip_copy_rop))
+ (tdev, source, data_x, raster, gx_no_bitmap_id,
+ pcolor, &state_tile,
+ (state.tile_colors[0] == gx_no_color_index &&
+ state.tile_colors[1] == gx_no_color_index ?
+ NULL : state.tile_colors),
+ state.rect.x - x0, state.rect.y - y0,
+ state.rect.width, state.rect.height,
+ tile_phase.x, tile_phase.y, state.lop);
+ data_x = 0;
+ break;
+ case cmd_op_tile_rect >> 4:
+ case cmd_op_tile_rect_short >> 4:
+ case cmd_op_tile_rect_tiny >> 4:
+ /* Currently we don't use lop with tile_rectangle. */
+ code = (*dev_proc(tdev, strip_tile_rectangle))
+ (tdev, &state_tile,
+ state.rect.x - x0, state.rect.y - y0,
+ state.rect.width, state.rect.height,
+ state.tile_colors[0], state.tile_colors[1],
+ tile_phase.x, tile_phase.y);
+ break;
+ case cmd_op_copy_mono >> 4:
+ if ( state.lop_enabled )
+ { pcolor = state.colors;
+ goto do_rop;
+ }
+ if ( (op & cmd_copy_ht_color) || pcpath != NULL )
+ { gx_drawing_color dcolor;
+ gx_ht_tile ht_tile;
+ if ( op & cmd_copy_ht_color )
+ { /* Screwy C assignment rules don't allow: */
+ /* dcolor.colors = state.tile_colors; */
+ ht_tile.tiles = state_tile;
+ color_set_binary_tile(&dcolor,
+ state.tile_colors[0],
+ state.tile_colors[1], &ht_tile);
+ dcolor.phase = tile_phase;
+ }
+ else
+ { color_set_pure(&dcolor, state.colors[1]);
+ }
+ code = (*dev_proc(tdev, fill_mask))
+ (tdev, source, data_x, raster, gx_no_bitmap_id,
+ state.rect.x - x0, state.rect.y - y0,
+ state.rect.width, state.rect.height,
+ &dcolor, 1, state.lop, pcpath);
+ }
+ else
+ code = (*dev_proc(tdev, copy_mono))
+ (tdev, source, data_x, raster, gx_no_bitmap_id,
+ state.rect.x - x0, state.rect.y - y0,
+ state.rect.width, state.rect.height,
+ state.colors[0], state.colors[1]);
+ data_x = 0;
+ break;
+ case cmd_op_copy_color_alpha >> 4:
+ if ( state.color_is_alpha )
+ { /****** CAN'T DO ROP WITH ALPHA ******/
+ code = (*dev_proc(tdev, copy_alpha))
+ (tdev, source, data_x, raster, gx_no_bitmap_id,
+ state.rect.x - x0, state.rect.y - y0,
+ state.rect.width, state.rect.height,
+ state.colors[1], depth);
+ }
+ else
+ { if ( state.lop_enabled )
+ { pcolor = NULL;
+ goto do_rop;
+ }
+ code = (*dev_proc(tdev, copy_color))
+ (tdev, source, data_x, raster, gx_no_bitmap_id,
+ state.rect.x - x0, state.rect.y - y0,
+ state.rect.width, state.rect.height);
+ }
+ data_x = 0;
+ break;
+ default: /* can't happen */
+ goto bad_op;
+ }
+ }
+ /* Clean up before we exit. */
+out: gx_cpath_release(&clip_path);
+ gx_path_release(&path);
+ gs_free_object(mem, data_bits, "clist_render_band(data_bits)");
+ if ( code < 0 )
+ return_error(code);
+ else
+ return code;
+}
+
+/* Unpack a short bitmap */
+private void
+clist_unpack_short_bits(byte *dest, const byte *src, register int width_bytes,
+ int height, uint raster)
+{ uint bytes = width_bytes * height;
+ const byte *pdata = src + bytes;
+ byte *udata = dest + height * raster;
+ while ( --height >= 0 )
+ { udata -= raster, pdata -= width_bytes;
+ switch ( width_bytes )
+ {
+ default: memmove(udata, pdata, width_bytes); break;
+ case 6: udata[5] = pdata[5];
+ case 5: udata[4] = pdata[4];
+ case 4: udata[3] = pdata[3];
+ case 3: udata[2] = pdata[2];
+ case 2: udata[1] = pdata[1];
+ case 1: udata[0] = pdata[0];
+ case 0: ;
+ }
+ }
+}
+
+/* Read a rectangle. */
+private const byte *
+cmd_read_rect(int op, register gx_cmd_rect *prect, register const byte *cbp)
+{ cmd_getw(prect->x, cbp);
+ if ( op & 0xf )
+ prect->y += ((op >> 2) & 3) - 2;
+ else
+ { cmd_getw(prect->y, cbp);
+ }
+ cmd_getw(prect->width, cbp);
+ if ( op & 0xf )
+ prect->height += (op & 3) - 2;
+ else
+ { cmd_getw(prect->height, cbp);
+ }
+ return cbp;
+}
+
+/* Read a transformation matrix. */
+private const byte *
+cmd_read_matrix(gs_matrix *pmat, const byte *cbp)
+{ byte b = *cbp++;
+ float coeff[6];
+ int i;
+
+ for ( i = 0; i < 4; i += 2, b <<= 2 )
+ if ( !(b & 0xc0) )
+ coeff[i] = coeff[i^3] = 0.0;
+ else
+ { float value;
+ cmd_get_value(value, cbp);
+ coeff[i] = value;
+ switch ( (b >> 6) & 3 )
+ {
+ case 1:
+ coeff[i^3] = value; break;
+ case 2:
+ coeff[i^3] = -value; break;
+ case 3:
+ cmd_get_value(coeff[i^3], cbp);
+ }
+ }
+ for ( ; i < 6; ++i, b <<= 1 )
+ if ( b & 0x80 )
+ { cmd_get_value(coeff[i], cbp);
+ }
+ else
+ coeff[i] = 0.0;
+ pmat->xx = coeff[0];
+ pmat->xy = coeff[1];
+ pmat->yx = coeff[2];
+ pmat->yy = coeff[3];
+ pmat->tx = coeff[4];
+ pmat->ty = coeff[5];
+ return cbp;
+}
+
+/* ------ Path operations ------ */
+
+/* Decode a path segment. */
+private int
+clist_decode_segment(gx_path *ppath, int op, fixed vs[6],
+ gs_fixed_point *ppos, int x0, int y0)
+{ fixed px = ppos->x - int2fixed(x0);
+ fixed py = ppos->y - int2fixed(y0);
+ int code;
+#define A vs[0]
+#define B vs[1]
+#define C vs[2]
+#define D vs[3]
+#define E vs[4]
+#define F vs[5]
+
+ switch ( op )
+ {
+ case cmd_opv_rmoveto:
+ code = gx_path_add_point(ppath, px += A, py += B);
+ break;
+ case cmd_opv_rlineto:
+ code = gx_path_add_line(ppath, px += A, py += B);
+ break;
+ case cmd_opv_hlineto:
+ code = gx_path_add_line(ppath, px += A, py);
+ break;
+ case cmd_opv_vlineto:
+ code = gx_path_add_line(ppath, px, py += A);
+ break;
+ case cmd_opv_rrcurveto: /* a b c d e f => a b a+c b+d a+c+e b+d+f */
+ E += (C += A);
+ F += (D += B);
+curve: code = gx_path_add_curve(ppath, px + A, py + B,
+ px + C, py + D,
+ px + E, py + F);
+ px += E, py += F;
+ break;
+ case cmd_opv_hvcurveto: /* a b c d => a 0 a+b c a+b c+d */
+ F = C + D, D = C, E = C = A + B, B = 0;
+ goto curve;
+ case cmd_opv_vhcurveto: /* a b c d => 0 a b a+c b+d a+c */
+ E = B + D, F = D = A + C, C = B, B = A, A = 0;
+ goto curve;
+ case cmd_opv_nrcurveto: /* a b c d => 0 0 a b a+c b+d */
+ F = B + D, E = A + C, D = B, C = A, B = A = 0;
+ goto curve;
+ case cmd_opv_rncurveto: /* a b c d => a b a+c b+d a+c b+d */
+ F = D += B, E = C += A;
+ goto curve;
+ case cmd_opv_rmlineto:
+ if ( (code = gx_path_add_point(ppath, px += A, py += B)) < 0 )
+ break;
+ code = gx_path_add_line(ppath, px += C, py += D);
+ break;
+ case cmd_opv_rm2lineto:
+ if ( (code = gx_path_add_point(ppath, px += A, py += B)) < 0 ||
+ (code = gx_path_add_line(ppath, px += C, py += D)) < 0
+ )
+ break;
+ code = gx_path_add_line(ppath, px += E, py += F);
+ break;
+ case cmd_opv_rm3lineto:
+ if ( (code = gx_path_add_point(ppath, px += A, py += B)) < 0 ||
+ (code = gx_path_add_line(ppath, px += C, py += D)) < 0 ||
+ (code = gx_path_add_line(ppath, px += E, py += F)) < 0
+ )
+ break;
+ code = gx_path_add_line(ppath, px -= C, py -= D);
+ break;
+ case cmd_opv_closepath:
+ code = gx_path_close_subpath(ppath);
+ gx_path_current_point(ppath, (gs_fixed_point *)vs);
+ px = A, py = B;
+ break;
+ default:
+ return_error(gs_error_rangecheck);
+ }
+#undef A
+#undef B
+#undef C
+#undef D
+#undef E
+#undef F
+ ppos->x = px + int2fixed(x0);
+ ppos->y = py + int2fixed(y0);
+ return code;
+}
diff --git a/pstoraster/gxcmap.c b/pstoraster/gxcmap.c
new file mode 100644
index 000000000..78b8d74ca
--- /dev/null
+++ b/pstoraster/gxcmap.c
@@ -0,0 +1,626 @@
+/* Copyright (C) 1992, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxcmap.c */
+/* Color mapping for Ghostscript */
+#include "gx.h"
+#include "gserrors.h"
+#include "gsccolor.h"
+#include "gxcspace.h"
+#include "gxfarith.h"
+#include "gxfrac.h"
+#include "gxdcconv.h"
+#include "gxdevice.h"
+#include "gxcmap.h"
+#include "gxlum.h"
+#include "gzstate.h"
+#include "gxdither.h"
+
+/* Structure descriptor */
+public_st_device_color();
+#define cptr ((gx_device_color *)vptr)
+private ENUM_PTRS_BEGIN(device_color_enum_ptrs) {
+ struct_proc_enum_ptrs((*proc)) = cptr->type->enum_ptrs;
+ if ( proc == 0 )
+ return 0;
+ return (*proc)(vptr, size, index, pep);
+} } }
+private RELOC_PTRS_BEGIN(device_color_reloc_ptrs) {
+ struct_proc_reloc_ptrs((*proc)) = cptr->type->reloc_ptrs;
+ if ( proc != 0 )
+ (*proc)(vptr, size, gcst);
+} RELOC_PTRS_END
+#undef cptr
+
+/* ------ Trace device mapping procedures ------ */
+
+/* If DEBUG is defined, these procedures substitute for direct calls */
+/* on the device map_{rgb,cmyk}_color procedures. */
+
+gx_color_index
+gx_proc_map_rgb_color(gx_device *dev,
+ gx_color_value vr, gx_color_value vg, gx_color_value vb)
+{ gx_color_index cindex =
+ (*dev_proc(dev, map_rgb_color))(dev, vr, vg, vb);
+ if_debug5('C', "%s [C]RGB %u,%u,%u -> color 0x%lx\n",
+ dev->dname, (uint)vr, (uint)vg, (uint)vb, (ulong)cindex);
+ return cindex;
+}
+
+gx_color_index
+gx_proc_map_rgb_alpha_color(gx_device *dev,
+ gx_color_value vr, gx_color_value vg, gx_color_value vb, gx_color_value va)
+{ gx_color_index cindex =
+ (*dev_proc(dev, map_rgb_alpha_color))(dev, vr, vg, vb, va);
+ if_debug6('C', "[C]%s RGBA %u,%u,%u,%u -> color 0x%lx\n",
+ dev->dname, (uint)vr, (uint)vg, (uint)vb, (uint)va,
+ (ulong)cindex);
+ return cindex;
+}
+
+gx_color_index
+gx_proc_map_cmyk_color(gx_device *dev,
+ gx_color_value vc, gx_color_value vm, gx_color_value vy, gx_color_value vk)
+{ gx_color_index cindex =
+ (*dev_proc(dev, map_cmyk_color))(dev, vc, vm, vy, vk);
+ if_debug6('C', "[C]%s CMYK %u,%u,%u,%u -> color 0x%lx\n",
+ dev->dname, (uint)vc, (uint)vm, (uint)vy, (uint)vk,
+ (ulong)cindex);
+ return cindex;
+}
+
+/* ---------------- Device color rendering ---------------- */
+
+private cmap_proc_gray(cmap_gray_halftoned);
+private cmap_proc_gray(cmap_gray_direct);
+private cmap_proc_gray(cmap_gray_to_rgb_halftoned);
+private cmap_proc_gray(cmap_gray_to_rgb_direct);
+private cmap_proc_gray(cmap_gray_to_cmyk_halftoned);
+private cmap_proc_gray(cmap_gray_to_cmyk_direct);
+private cmap_proc_rgb(cmap_rgb_halftoned);
+private cmap_proc_rgb(cmap_rgb_direct);
+private cmap_proc_rgb(cmap_rgb_to_gray_halftoned);
+private cmap_proc_rgb(cmap_rgb_to_gray_direct);
+private cmap_proc_rgb(cmap_rgb_to_cmyk);
+#define cmap_cmyk_halftoned cmap_cmyk_direct
+private cmap_proc_cmyk(cmap_cmyk_direct);
+private cmap_proc_cmyk(cmap_cmyk_to_gray);
+private cmap_proc_cmyk(cmap_cmyk_to_rgb);
+
+private const gx_color_map_procs
+ cmap_gray_few =
+ { cmap_gray_halftoned, cmap_rgb_to_gray_halftoned, cmap_cmyk_to_gray },
+ cmap_gray_many =
+ { cmap_gray_direct, cmap_rgb_to_gray_direct, cmap_cmyk_to_gray },
+ cmap_rgb_few =
+ { cmap_gray_to_rgb_halftoned, cmap_rgb_halftoned, cmap_cmyk_to_rgb },
+ cmap_rgb_many =
+ { cmap_gray_to_rgb_direct, cmap_rgb_direct, cmap_cmyk_to_rgb },
+ cmap_cmyk_few =
+ { cmap_gray_to_cmyk_halftoned, cmap_rgb_to_cmyk, cmap_cmyk_halftoned },
+ cmap_cmyk_many =
+ { cmap_gray_to_cmyk_direct, cmap_rgb_to_cmyk, cmap_cmyk_direct };
+
+const gx_color_map_procs *cmap_procs_default = &cmap_gray_many;
+
+private const gx_color_map_procs _ds *cmap_few[] = {
+ 0, &cmap_gray_few, 0, &cmap_rgb_few, &cmap_cmyk_few
+};
+
+private const gx_color_map_procs _ds *cmap_many[] = {
+ 0, &cmap_gray_many, 0, &cmap_rgb_many, &cmap_cmyk_many
+};
+
+/* Determine the color mapping procedures for a device. */
+const gx_color_map_procs *
+gx_device_cmap_procs(const gx_device *dev)
+{ return ((gx_device_has_color(dev) ? dev->color_info.max_color :
+ dev->color_info.max_gray) >= 31 ? cmap_many : cmap_few)
+ [dev->color_info.num_components];
+}
+
+/* Set the color mapping procedures in the graphics state. */
+void
+gx_set_cmap_procs(gs_state *pgs)
+{ pgs->cmap_procs = gx_device_cmap_procs(pgs->device);
+}
+
+/* Remap the color in the graphics state. */
+int
+gx_remap_color(gs_state *pgs)
+{ const gs_color_space *pcs = pgs->color_space;
+ return (*pcs->type->remap_color)(pgs->ccolor, pcs, pgs->dev_color, pgs);
+}
+
+/* Indicate that a color space has no underlying concrete space. */
+const gs_color_space *
+gx_no_concrete_space(const gs_color_space *pcs, const gs_state *pgs)
+{ return NULL;
+}
+
+/* Indicate that a color space is concrete. */
+const gs_color_space *
+gx_same_concrete_space(const gs_color_space *pcs, const gs_state *pgs)
+{ return pcs;
+}
+
+/* Indicate that a color cannot be concretized. */
+int
+gx_no_concretize_color(const gs_client_color *pcc, const gs_color_space *pcs,
+ frac *pconc, const gs_state *pgs)
+{ return_error(gs_error_rangecheck);
+}
+
+/* By default, remap a color by concretizing it and then */
+/* remapping the concrete color. */
+int
+gx_default_remap_color(const gs_client_color *pcc, const gs_color_space *pcs,
+ gx_device_color *pdc, const gs_state *pgs)
+{ frac conc[4];
+ const gs_color_space *pconcs;
+ int code = (*pcs->type->concretize_color)(pcc, pcs, conc, pgs);
+ if ( code < 0 )
+ return code;
+ pconcs = cs_concrete_space(pcs, pgs);
+ return (*pconcs->type->remap_concrete_color)(conc, pdc, pgs);
+}
+
+/* Color remappers for the standard color spaces. */
+/* Note that we use D... instead of Device... in some places because */
+/* gcc under VMS only retains 23 characters of procedure names. */
+
+#define unit_frac(v, ftemp)\
+ (ftemp = (v),\
+ (is_fneg(ftemp) ? frac_0 : is_fge1(ftemp) ? frac_1 : float2frac(ftemp)))
+
+/* DeviceGray */
+int
+gx_concretize_DeviceGray(const gs_client_color *pc, const gs_color_space *pcs,
+ frac *pconc, const gs_state *pgs)
+{ float ftemp;
+ pconc[0] = unit_frac(pc->paint.values[0], ftemp);
+ return 0;
+}
+int
+gx_remap_concrete_DGray(const frac *pconc,
+ gx_device_color *pdc, const gs_state *pgs)
+{ (*pgs->cmap_procs->map_gray)
+ (pconc[0], pdc, pgs);
+ return 0;
+}
+int
+gx_remap_DeviceGray(const gs_client_color *pc, const gs_color_space *pcs,
+ gx_device_color *pdc, const gs_state *pgs)
+{ float ftemp;
+ (*pgs->cmap_procs->map_gray)
+ (unit_frac(pc->paint.values[0], ftemp),
+ pdc, pgs);
+ return 0;
+}
+
+/* DeviceRGB */
+int
+gx_concretize_DeviceRGB(const gs_client_color *pc, const gs_color_space *pcs,
+ frac *pconc, const gs_state *pgs)
+{ float ftemp;
+ pconc[0] = unit_frac(pc->paint.values[0], ftemp);
+ pconc[1] = unit_frac(pc->paint.values[1], ftemp);
+ pconc[2] = unit_frac(pc->paint.values[2], ftemp);
+ return 0;
+}
+int
+gx_remap_concrete_DRGB(const frac *pconc,
+ gx_device_color *pdc, const gs_state *pgs)
+{ gx_remap_concrete_rgb(pconc[0], pconc[1], pconc[2], pdc, pgs);
+ return 0;
+}
+int
+gx_remap_DeviceRGB(const gs_client_color *pc, const gs_color_space *pcs,
+ gx_device_color *pdc, const gs_state *pgs)
+{ float ft0, ft1, ft2;
+ gx_remap_concrete_rgb(unit_frac(pc->paint.values[0], ft0),
+ unit_frac(pc->paint.values[1], ft1),
+ unit_frac(pc->paint.values[2], ft2),
+ pdc, pgs);
+ return 0;
+}
+
+/* DeviceCMYK */
+int
+gx_concretize_DeviceCMYK(const gs_client_color *pc, const gs_color_space *pcs,
+ frac *pconc, const gs_state *pgs)
+{ float ftemp;
+ pconc[0] = unit_frac(pc->paint.values[0], ftemp);
+ pconc[1] = unit_frac(pc->paint.values[1], ftemp);
+ pconc[2] = unit_frac(pc->paint.values[2], ftemp);
+ pconc[3] = unit_frac(pc->paint.values[3], ftemp);
+ return 0;
+}
+int
+gx_remap_concrete_DCMYK(const frac *pconc,
+ gx_device_color *pdc, const gs_state *pgs)
+{ gx_remap_concrete_cmyk(pconc[0], pconc[1], pconc[2], pconc[3], pdc, pgs);
+ return 0;
+}
+int
+gx_remap_DeviceCMYK(const gs_client_color *pc, const gs_color_space *pcs,
+ gx_device_color *pdc, const gs_state *pgs)
+{ float ft0, ft1, ft2, ft3;
+ gx_remap_concrete_cmyk(unit_frac(pc->paint.values[0], ft0),
+ unit_frac(pc->paint.values[1], ft1),
+ unit_frac(pc->paint.values[2], ft2),
+ unit_frac(pc->paint.values[3], ft3),
+ pdc, pgs);
+ return 0;
+}
+
+/* ------ Render Gray color. ------ */
+
+private void
+cmap_gray_halftoned(frac gray, gx_device_color *pdc, const gs_state *pgs)
+{ if ( gx_render_gray(gx_map_color_frac(pgs, gray, effective_transfer.colored.gray), pdc, pgs) == 1 )
+ gx_color_load(pdc, pgs);
+}
+
+private void
+cmap_gray_direct(frac gray, gx_device_color *pdc, const gs_state *pgs)
+{ gx_device *dev = gs_currentdevice_inline(pgs);
+ frac mgray = gx_map_color_frac(pgs, gray, effective_transfer.colored.gray);
+ gx_color_value cv_gray = frac2cv(mgray);
+ gx_color_index color =
+ (pgs->alpha == gx_max_color_value ?
+ gx_map_rgb_color(dev, cv_gray, cv_gray, cv_gray) :
+ gx_map_rgb_alpha_color(dev, cv_gray, cv_gray, cv_gray, pgs->alpha));
+ if ( color == gx_no_color_index )
+ { if ( gx_render_gray(mgray, pdc, pgs) == 1 )
+ gx_color_load(pdc, pgs);
+ return;
+ }
+ color_set_pure(pdc, color);
+}
+
+private void
+cmap_gray_to_rgb_halftoned(frac gray, gx_device_color *pdc,
+ const gs_state *pgs)
+{ cmap_rgb_halftoned(gray, gray, gray, pdc, pgs);
+}
+
+private void
+cmap_gray_to_rgb_direct(frac gray, gx_device_color *pdc, const gs_state *pgs)
+{ cmap_rgb_direct(gray, gray, gray, pdc, pgs);
+}
+
+private void
+cmap_gray_to_cmyk_halftoned(frac gray, gx_device_color *pdc,
+ const gs_state *pgs)
+{ /*
+ * Per the last paragraph of section 6.3 (p. 309) of the
+ * PostScript Language Reference Manual, 2nd Edition,
+ * we must bypass the C, M, and Y transfer functions in this case.
+ */
+ frac mgray = gx_map_color_frac(pgs, gray, effective_transfer.colored.gray);
+ if ( gx_render_gray(mgray, pdc, pgs) == 1 )
+ gx_color_load(pdc, pgs);
+}
+
+private void
+cmap_gray_to_cmyk_direct(frac gray, gx_device_color *pdc,
+ const gs_state *pgs)
+{ /*
+ * Per the last paragraph of section 6.3 (p. 309) of the
+ * PostScript Language Reference Manual, 2nd Edition,
+ * we must bypass the C, M, and Y transfer functions in this case.
+ */
+ frac mgray = gx_map_color_frac(pgs, gray, effective_transfer.colored.gray);
+ frac mblack = frac_1 - mgray;
+ gx_device *dev = gs_currentdevice_inline(pgs);
+ gx_color_index color =
+ gx_map_cmyk_color(dev,
+ frac2cv(frac_0), frac2cv(frac_0),
+ frac2cv(frac_0), frac2cv(mblack));
+ if ( color != gx_no_color_index )
+ { color_set_pure(pdc, color);
+ return;
+ }
+ if ( gx_render_gray(mgray, pdc, pgs) == 1 )
+ gx_color_load(pdc, pgs);
+}
+
+/* ------ Render RGB color. ------ */
+
+/*
+ * This code should test r == g and g == b and then use the gray
+ * rendering procedures. The Adobe documentation allows this:
+ * conversion between color spaces occurs before the transfer function
+ * and halftoning. However, output from FrameMaker (mis)uses the
+ * transfer function to provide the equivalent of indexed color;
+ * it requires the color components to be passed through unchanged.
+ * For this reason, we have to make the check after the transfer
+ * function rather than before.
+ *
+ * Since this procedure is used so heavily, we duplicate most of its code
+ * rather than making a text for color_info.max_color >= 31.
+ */
+
+private void
+cmap_rgb_halftoned(frac r, frac g, frac b, gx_device_color *pdc,
+ const gs_state *pgs)
+{ frac mred = gx_map_color_frac(pgs, r, effective_transfer.colored.red);
+ frac mgreen = gx_map_color_frac(pgs, g, effective_transfer.colored.green);
+ frac mblue = gx_map_color_frac(pgs, b, effective_transfer.colored.blue);
+ if ( (mred == mgreen && mred == mblue ? /* gray shade */
+ gx_render_gray(mred, pdc, pgs) :
+ gx_render_rgb(mred, mgreen, mblue, pdc, pgs)) == 1 )
+ gx_color_load(pdc, pgs);
+}
+
+private void
+cmap_rgb_direct(frac r, frac g, frac b, gx_device_color *pdc,
+ const gs_state *pgs)
+{ gx_device *dev = gs_currentdevice_inline(pgs);
+ frac mred = gx_map_color_frac(pgs, r, effective_transfer.colored.red);
+ frac mgreen = gx_map_color_frac(pgs, g, effective_transfer.colored.green);
+ frac mblue = gx_map_color_frac(pgs, b, effective_transfer.colored.blue);
+ gx_color_index color =
+ (pgs->alpha == gx_max_color_value ?
+ gx_map_rgb_color(dev, frac2cv(mred), frac2cv(mgreen),
+ frac2cv(mblue)) :
+ gx_map_rgb_alpha_color(dev, frac2cv(mred), frac2cv(mgreen),
+ frac2cv(mblue), pgs->alpha));
+ if ( color != gx_no_color_index )
+ { color_set_pure(pdc, color);
+ return;
+ }
+ if ( (mred == mgreen && mred == mblue ? /* gray shade */
+ gx_render_gray(mred, pdc, pgs) :
+ gx_render_rgb(mred, mgreen, mblue, pdc, pgs)) == 1 )
+ gx_color_load(pdc, pgs);
+}
+
+private void
+cmap_rgb_to_gray_halftoned(frac r, frac g, frac b, gx_device_color *pdc,
+ const gs_state *pgs)
+{ cmap_gray_halftoned(color_rgb_to_gray(r, g, b, pgs), pdc, pgs);
+}
+
+private void
+cmap_rgb_to_gray_direct(frac r, frac g, frac b, gx_device_color *pdc,
+ const gs_state *pgs)
+{ cmap_gray_direct(color_rgb_to_gray(r, g, b, pgs), pdc, pgs);
+}
+
+private void
+cmap_rgb_to_cmyk(frac r, frac g, frac b, gx_device_color *pdc,
+ const gs_state *pgs)
+{ frac cmyk[4];
+ color_rgb_to_cmyk(r, g, b, pgs, cmyk);
+ (*pgs->cmap_procs->map_cmyk)(cmyk[0], cmyk[1], cmyk[2], cmyk[3], pdc, pgs);
+}
+
+/* ------ Render CMYK color. ------ */
+
+private void
+cmap_cmyk_to_gray(frac c, frac m, frac y, frac k, gx_device_color *pdc, const gs_state *pgs)
+{ (*pgs->cmap_procs->map_gray)(color_cmyk_to_gray(c, m, y, k, pgs), pdc, pgs);
+}
+
+private void
+cmap_cmyk_direct(frac c, frac m, frac y, frac k, gx_device_color *pdc,
+ const gs_state *pgs)
+{ gx_device *dev = gs_currentdevice_inline(pgs);
+ frac mcyan = frac_1 - gx_map_color_frac(pgs, frac_1 - c, effective_transfer.colored.red);
+ frac mmagenta = frac_1 - gx_map_color_frac(pgs, frac_1 - m, effective_transfer.colored.green);
+ frac myellow = frac_1 - gx_map_color_frac(pgs, frac_1 - y, effective_transfer.colored.blue);
+ frac mblack = frac_1 - gx_map_color_frac(pgs, frac_1 - k, effective_transfer.colored.gray);
+ /* We make a test for direct vs. halftoned, rather than */
+ /* duplicating most of the code of this procedure. */
+ if ( dev->color_info.max_color >= 31 )
+ { gx_color_index color =
+ gx_map_cmyk_color(dev,
+ frac2cv(mcyan), frac2cv(mmagenta),
+ frac2cv(myellow), frac2cv(mblack));
+ if ( color != gx_no_color_index )
+ { color_set_pure(pdc, color);
+ return;
+ }
+ }
+ /* Don't convert colors with C = M = Y to gray shades: */
+ /* on a CMYK device, this may produce quite different output. */
+ if ( gx_render_cmyk(mcyan, mmagenta, myellow, mblack, pdc, pgs) == 1 )
+ gx_color_load(pdc, pgs);
+}
+
+private void
+cmap_cmyk_to_rgb(frac c, frac m, frac y, frac k, gx_device_color *pdc, const gs_state *pgs)
+{ frac rgb[3];
+ color_cmyk_to_rgb(c, m, y, k, pgs, rgb);
+ (*pgs->cmap_procs->map_rgb)(rgb[0], rgb[1], rgb[2], pdc, pgs);
+}
+
+/* ------ Transfer function mapping ------ */
+
+#if FRAC_MAP_INTERPOLATE /* NOTA BENE */
+
+/* Map a color fraction through a transfer map. */
+/* We only use this if we are interpolating. */
+frac
+gx_color_frac_map(frac cv, const frac *values)
+{
+#define cp_frac_bits (frac_bits - log2_transfer_map_size)
+ int cmi = frac2bits_floor(cv, log2_transfer_map_size);
+ frac mv = values[cmi];
+ int rem, mdv;
+ /* Interpolate between two adjacent values if needed. */
+ rem = cv - bits2frac(cmi, log2_transfer_map_size);
+ if ( rem == 0 ) return mv;
+ mdv = values[cmi + 1] - mv;
+#if arch_ints_are_short
+ /* Only use long multiplication if necessary. */
+ if ( mdv < -1 << (16 - cp_frac_bits) ||
+ mdv > 1 << (16 - cp_frac_bits)
+ )
+ return mv + (uint)(((ulong)rem * mdv) >> cp_frac_bits);
+#endif
+ return mv + ((rem * mdv) >> cp_frac_bits);
+#undef cp_frac_bits
+}
+
+#endif /* FRAC_MAP_INTERPOLATE */
+
+/* ------ Default device color mapping ------ */
+
+/* RGB mapping for black-and-white devices */
+
+/* White-on-black */
+gx_color_index
+gx_default_w_b_map_rgb_color(gx_device *dev,
+ gx_color_value r, gx_color_value g, gx_color_value b)
+{ /* Map values >= 1/2 to 1, < 1/2 to 0. */
+ return ((r | g | b) > gx_max_color_value / 2 ?
+ (gx_color_index)1 : (gx_color_index)0);
+}
+int
+gx_default_w_b_map_color_rgb(gx_device *dev, gx_color_index color,
+ gx_color_value prgb[3])
+{ /* Map 1 to max_value, 0 to 0. */
+ prgb[0] = prgb[1] = prgb[2] = -(gx_color_value)color;
+ return 0;
+}
+/* Black-on-white */
+gx_color_index
+gx_default_b_w_map_rgb_color(gx_device *dev,
+ gx_color_value r, gx_color_value g, gx_color_value b)
+{ /* Map values >= 1/2 to 0, < 1/2 to 1. */
+ return ((r | g | b) > gx_max_color_value / 2 ?
+ (gx_color_index)0 : (gx_color_index)1);
+}
+int
+gx_default_b_w_map_color_rgb(gx_device *dev, gx_color_index color,
+ gx_color_value prgb[3])
+{ /* Map 0 to max_value, 1 to 0. */
+ prgb[0] = prgb[1] = prgb[2] = -((gx_color_value)color ^ 1);
+ return 0;
+}
+
+/* RGB mapping for gray-scale devices */
+
+gx_color_index
+gx_default_gray_map_rgb_color(gx_device *dev,
+ gx_color_value r, gx_color_value g, gx_color_value b)
+{ /* We round the value rather than truncating it. */
+ gx_color_value gray =
+ ((r * (ulong)lum_red_weight) +
+ (g * (ulong)lum_green_weight) +
+ (b * (ulong)lum_blue_weight) +
+ (lum_all_weights / 2)) / lum_all_weights
+ * dev->color_info.max_gray / gx_max_color_value;
+ return gray;
+}
+
+int
+gx_default_gray_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;
+}
+
+/* RGB mapping for 24-bit true (RGB) color devices */
+
+gx_color_index
+gx_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
+ { uint bits_per_color = dev->color_info.depth / 3;
+ ulong max_value = (1 << bits_per_color) - 1;
+
+ return ((r * max_value / gx_max_color_value) << (bits_per_color * 2)) +
+ ((g * max_value / gx_max_color_value) << (bits_per_color)) +
+ (b * max_value / gx_max_color_value);
+ }
+}
+
+/* Map a color index to a r-g-b color. */
+int
+gx_default_rgb_map_color_rgb(gx_device *dev, gx_color_index color,
+ gx_color_value prgb[3])
+{ if ( dev->color_info.depth == 24 )
+ { prgb[0] = gx_color_value_from_byte(color >> 16);
+ prgb[1] = gx_color_value_from_byte((color >> 8) & 0xff);
+ prgb[2] = gx_color_value_from_byte(color & 0xff);
+ }
+ else
+ { uint bits_per_color = dev->color_info.depth / 3;
+ uint color_mask = (1 << bits_per_color) - 1;
+
+ prgb[0] = ((color >> (bits_per_color * 2)) & color_mask) *
+ (ulong)gx_max_color_value / color_mask;
+ prgb[1] = ((color >> (bits_per_color)) & color_mask) *
+ (ulong)gx_max_color_value / color_mask;
+ prgb[2] = (color & color_mask) *
+ (ulong)gx_max_color_value / color_mask;
+ }
+ return 0;
+}
+
+/* CMYK mapping for RGB devices (should never be called!) */
+
+gx_color_index
+gx_default_map_cmyk_color(gx_device *dev,
+ gx_color_value c, gx_color_value m, gx_color_value y, gx_color_value k)
+{ /* Convert to RGB */
+ frac rgb[3];
+ color_cmyk_to_rgb(cv2frac(c), cv2frac(m), cv2frac(y), cv2frac(k),
+ NULL, rgb);
+ return gx_map_rgb_color(dev, frac2cv(rgb[0]),
+ frac2cv(rgb[1]), frac2cv(rgb[2]));
+}
+
+/* CMYK mapping for CMYK devices */
+
+gx_color_index
+gx_default_cmyk_map_cmyk_color(gx_device *dev,
+ gx_color_value c, gx_color_value m, gx_color_value y, gx_color_value k)
+{ gx_color_index color =
+ (gx_color_value_to_byte(k) +
+ ((uint)gx_color_value_to_byte(y) << 8)) +
+ ((ulong)(gx_color_value_to_byte(m) +
+ ((uint)gx_color_value_to_byte(c) << 8)) << 16);
+
+ return (color == gx_no_color_index ? color ^ 1 : color);
+}
+
+/* Default mapping from RGB-alpha to RGB. */
+
+gx_color_index
+gx_default_map_rgb_alpha_color(gx_device *dev,
+ gx_color_value r, gx_color_value g, gx_color_value b, gx_color_value alpha)
+{ return gx_map_rgb_color(dev, r, g, b);
+}
diff --git a/pstoraster/gxcmap.h b/pstoraster/gxcmap.h
new file mode 100644
index 000000000..3069ff26b
--- /dev/null
+++ b/pstoraster/gxcmap.h
@@ -0,0 +1,84 @@
+/* Copyright (C) 1989, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxcmap.h */
+/* Private definition of color mapping for Ghostscript */
+/* Requires gxdcolor.h, gxdevice.h. */
+#include "gxfmap.h"
+
+/* Procedures for rendering colors specified by fractions. */
+
+#define cmap_proc_gray(proc)\
+ void proc(P3(frac, gx_device_color *, const gs_state *))
+#define cmap_proc_rgb(proc)\
+ void proc(P5(frac, frac, frac, gx_device_color *, const gs_state *))
+#define cmap_proc_cmyk(proc)\
+ void proc(P6(frac, frac, frac, frac, gx_device_color *, const gs_state *))
+
+/* Because of a bug in the Watcom C compiler, */
+/* we have to split the struct from the typedef. */
+struct gx_color_map_procs_s {
+ cmap_proc_gray((*map_gray));
+ cmap_proc_rgb((*map_rgb));
+ cmap_proc_cmyk((*map_cmyk));
+};
+typedef struct gx_color_map_procs_s gx_color_map_procs;
+
+/* Determine the color mapping procedures for a device. */
+const gx_color_map_procs *gx_device_cmap_procs(P1(const gx_device *));
+
+/* Set the color mapping procedures in the graphics state. */
+/* This is only needed when switching devices. */
+void gx_set_cmap_procs(P1(gs_state *));
+
+/* Remap a concrete (frac) RGB or CMYK color. */
+/* These cannot fail, and do not return a value. */
+#define gx_remap_concrete_rgb(cr, cg, cb, pdc, pgs)\
+ (*pgs->cmap_procs->map_rgb)(cr, cg, cb, pdc, pgs)
+#define gx_remap_concrete_cmyk(cc, cm, cy, ck, pdc, pgs)\
+ (*pgs->cmap_procs->map_cmyk)(cc, cm, cy, ck, pdc, pgs)
+
+/* Map a color, with optional tracing if we are debugging. */
+#ifdef DEBUG
+/* Use procedures in gxcmap.c */
+#include "gxcvalue.h"
+gx_color_index gx_proc_map_rgb_color(P4(gx_device *,
+ gx_color_value, gx_color_value, gx_color_value));
+gx_color_index gx_proc_map_rgb_alpha_color(P5(gx_device *,
+ gx_color_value, gx_color_value, gx_color_value, gx_color_value));
+gx_color_index gx_proc_map_cmyk_color(P5(gx_device *,
+ gx_color_value, gx_color_value, gx_color_value, gx_color_value));
+# define gx_map_rgb_color(dev, vr, vg, vb)\
+ gx_proc_map_rgb_color(dev, vr, vg, vb)
+# define gx_map_rgb_alpha_color(dev, vr, vg, vb, va)\
+ gx_proc_map_rgb_alpha_color(dev, vr, vg, vb, va)
+# define gx_map_cmyk_color(dev, vc, vm, vy, vk)\
+ gx_proc_map_cmyk_color(dev, vc, vm, vy, vk)
+#else
+# define gx_map_rgb_color(dev, vr, vg, vb)\
+ (*dev_proc(dev, map_rgb_color))(dev, vr, vg, vb)
+# define gx_map_rgb_alpha_color(dev, vr, vg, vb, va)\
+ (*dev_proc(dev, map_rgb_alpha_color))(dev, vr, vg, vb, va)
+# define gx_map_cmyk_color(dev, vc, vm, vy, vk)\
+ (*dev_proc(dev, map_cmyk_color))(dev, vc, vm, vy, vk)
+#endif
diff --git a/pstoraster/gxcolor2.h b/pstoraster/gxcolor2.h
new file mode 100644
index 000000000..723bbcb61
--- /dev/null
+++ b/pstoraster/gxcolor2.h
@@ -0,0 +1,72 @@
+/* Copyright (C) 1993, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxcolor2.h */
+/* Internal definitions for Level 2 color routines for Ghostscript library */
+/* (requires gxfixed.h) */
+#include "gscolor2.h"
+#include "gsrefct.h"
+#include "gxbitmap.h"
+
+/* Cache for Indexed color with procedure, or Separation color. */
+struct gs_indexed_map_s {
+ rc_header rc;
+ union {
+ int (*lookup_index)(P3(const gs_indexed_params *, int, float *));
+ int (*tint_transform)(P3(const gs_separation_params *, floatp, float *));
+ } proc;
+ uint num_values; /* base_space->type->num_components * (hival + 1) */
+ float *values; /* actually [num_values] */
+};
+#define private_st_indexed_map() /* in zcsindex.c */\
+ gs_private_st_ptrs1(st_indexed_map, gs_indexed_map, "gs_indexed_map",\
+ indexed_map_enum_ptrs, indexed_map_reloc_ptrs, values)
+
+/*
+ * We define 'tiling space' as the space in which (0,0) is the origin of
+ * the key pattern cell and in which coordinate (i,j) is displaced by
+ * i * XStep + j * YStep from the origin. In this space, it is easy to
+ * compute a (rectangular) set of tile copies that cover a (rectangular)
+ * region to be tiled.
+ */
+
+/* Implementation of Pattern instances. */
+struct gs_pattern_instance_s {
+ rc_header rc;
+ gs_client_pattern template;
+ /* Following are created by makepattern */
+ gs_state *saved;
+ gs_matrix matrix; /* tiling space -> device space */
+ gs_rect bbox; /* bbox of tile in tiling space */
+ gs_point offset; /* of tile from matrix.tx/y */
+ gs_int_point size; /* in device coordinates */
+ gx_bitmap_id id; /* key for cached bitmap */
+ /* (= id of mask) */
+};
+/* The following is only public for a type test in the interpreter */
+/* (.buildpattern operator). */
+extern_st(st_pattern_instance);
+#define public_st_pattern_instance() /* in gspcolor.c */\
+ gs_public_st_ptrs_add1(st_pattern_instance, gs_pattern_instance,\
+ "pattern instance", pattern_instance_enum_ptrs,\
+ pattern_instance_reloc_ptrs, st_client_pattern, template, saved)
diff --git a/pstoraster/gxcoord.h b/pstoraster/gxcoord.h
new file mode 100644
index 000000000..b543f7710
--- /dev/null
+++ b/pstoraster/gxcoord.h
@@ -0,0 +1,38 @@
+/* Copyright (C) 1994 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxcoord.h */
+/* Internal coordinate system procedures */
+/* Requires gxmatrix.h and gzstate.h */
+#include "gscoord.h"
+
+/* Set the translation to a fixed value, and translate any existing path. */
+/* Used by gschar.c to prepare for a BuildChar or BuildGlyph procedure. */
+int gx_translate_to_fixed(P3(gs_state *, fixed, fixed));
+
+/* Scale the CTM and character matrix for oversampling. */
+int gx_scale_char_matrix(P3(gs_state *, int, int));
+
+/* Compute the coefficients for fast fixed-point distance transformations */
+/* from a transformation matrix. */
+int gx_matrix_to_fixed_coeff(P3(const gs_matrix *, fixed_coeff *, int));
diff --git a/pstoraster/gxcpath.c b/pstoraster/gxcpath.c
new file mode 100644
index 000000000..42a326006
--- /dev/null
+++ b/pstoraster/gxcpath.c
@@ -0,0 +1,802 @@
+/* Copyright (C) 1991, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxcpath.c */
+/* Implementation of clipping paths */
+#include "gx.h"
+#include "gserrors.h"
+#include "gsstruct.h"
+#include "gsutil.h"
+#include "gxdevice.h"
+#include "gxfixed.h"
+#include "gzpath.h"
+#include "gzcpath.h"
+
+/* Imported from gxacpath.c */
+extern int gx_cpath_intersect_slow(P4(gs_state *, gx_clip_path *,
+ gx_path *, int));
+
+/* Forward references */
+private void gx_clip_list_from_rectangle(P2(gx_clip_list *, gs_fixed_rect *));
+private int gx_clip_list_add_to_path(P2(gx_clip_list *, gx_path *));
+
+/* Structure types */
+public_st_clip_rect();
+private_st_clip_list();
+public_st_clip_path();
+public_st_device_clip();
+
+/* GC procedures for gx_clip_path */
+#define cptr ((gx_clip_path *)vptr)
+private ENUM_PTRS_BEGIN(clip_path_enum_ptrs) ;
+ if ( index < st_clip_list_max_ptrs )
+ { gs_ptr_type_t ret = clip_list_enum_ptrs(&cptr->list, sizeof(cptr->list), index, pep);
+ if ( ret == 0 ) /* don't stop early */
+ ret = ptr_struct_type, *pep = 0;
+ return ret;
+ }
+ return (*st_path.enum_ptrs)(&cptr->path, sizeof(cptr->path), index - st_clip_list_max_ptrs, pep);
+} }
+private RELOC_PTRS_BEGIN(clip_path_reloc_ptrs) {
+ clip_list_reloc_ptrs(&cptr->list, sizeof(gx_clip_list), gcst);
+ (*st_path.reloc_ptrs)(&cptr->path, sizeof(gx_path), gcst);
+} RELOC_PTRS_END
+#undef cptr
+
+/* GC procedures for gx_device_clip */
+#define cptr ((gx_device_clip *)vptr)
+private ENUM_PTRS_BEGIN(device_clip_enum_ptrs) {
+ if ( index < st_clip_list_max_ptrs + 1 )
+ return clip_list_enum_ptrs(&cptr->list, sizeof(gx_clip_list),
+ index - 1, pep);
+ return (*st_device_forward.enum_ptrs)(vptr, sizeof(gx_device_forward),
+ index - (st_clip_list_max_ptrs + 1), pep);
+ }
+ case 0:
+ *pep = (cptr->current == &cptr->list.single ? NULL :
+ (void *)cptr->current);
+ break;
+ENUM_PTRS_END
+private RELOC_PTRS_BEGIN(device_clip_reloc_ptrs) {
+ if ( cptr->current == &cptr->list.single )
+ cptr->current =
+ &((gx_device_clip *)gs_reloc_struct_ptr(vptr, gcst))->list.single;
+ else
+ RELOC_PTR(gx_device_clip, current);
+ clip_list_reloc_ptrs(&cptr->list, sizeof(gx_clip_list), gcst);
+ (*st_device_forward.reloc_ptrs)(vptr, sizeof(gx_device_forward), gcst);
+} RELOC_PTRS_END
+#undef cptr
+
+/* Define an empty clip list. */
+private const gx_clip_list clip_list_empty =
+{ { 0, 0, min_int, max_int, 0, 0 },
+ 0, 0, 0, 0 /*false*/
+};
+
+/* Debugging */
+
+#ifdef DEBUG
+/* Validate a clipping path. */
+bool /* only exported for gxacpath.c */
+clip_list_validate(const gx_clip_list *clp)
+{ if ( clp->count <= 1 )
+ return (clp->head == 0 && clp->tail == 0 &&
+ clp->single.next == 0 && clp->single.prev == 0);
+ else
+ { const gx_clip_rect *prev = clp->head;
+ const gx_clip_rect *ptr;
+ bool ok = true;
+ while ( (ptr = prev->next) != 0 )
+ { if ( ptr->ymin > ptr->ymax || ptr->xmin > ptr->xmax ||
+ !(ptr->ymin >= prev->ymax ||
+ (ptr->ymin == prev->ymin &&
+ ptr->ymax == prev->ymax &&
+ ptr->xmin >= prev->xmax)) ||
+ ptr->prev != prev
+ )
+ { clip_rect_print('q', "WRONG:", ptr);
+ ok = false;
+ }
+ prev = ptr;
+ }
+ return ok && prev == clp->tail;
+ }
+}
+#endif
+
+/* ------ Clipping path accessing ------ */
+
+/* Return the path of a clipping path. */
+int
+gx_cpath_path(gx_clip_path *pcpath, gx_path *ppath)
+{ if ( !pcpath->segments_valid )
+ { int code = gx_clip_list_add_to_path(&pcpath->list, &pcpath->path);
+ if ( code < 0 ) return code;
+ pcpath->segments_valid = 1;
+ }
+ *ppath = pcpath->path;
+ return 0;
+}
+
+/* Return the inner and outer check rectangles for a clipping path. */
+/* Return true iff the path is a rectangle. */
+/* Note that these must return something strange if we are using */
+/* outside clipping. */
+bool
+gx_cpath_inner_box(const gx_clip_path *pcpath, gs_fixed_rect *pbox)
+{ if ( pcpath->list.outside )
+ { pbox->p.x = pbox->p.y = pbox->q.x = pbox->q.y = 0;
+ return false;
+ }
+ else
+ { *pbox = pcpath->inner_box;
+ return clip_list_is_rectangle(&pcpath->list);
+ }
+}
+bool
+gx_cpath_outer_box(const gx_clip_path *pcpath, gs_fixed_rect *pbox)
+{ if ( pcpath->list.outside )
+ { pbox->p.x = pbox->p.y = min_fixed;
+ pbox->q.x = pbox->q.y = max_fixed;
+ return false;
+ }
+ else
+ { *pbox = pcpath->outer_box;
+ return clip_list_is_rectangle(&pcpath->list);
+ }
+}
+
+/* Test if a clipping path includes a rectangle. */
+/* The rectangle need not be oriented correctly, i.e. x0 > x1 is OK. */
+int
+gx_cpath_includes_rectangle(register const gx_clip_path *pcpath,
+ fixed x0, fixed y0, fixed x1, fixed y1)
+{ return
+ (x0 <= x1 ?
+ (pcpath->inner_box.p.x <= x0 && x1 <= pcpath->inner_box.q.x) :
+ (pcpath->inner_box.p.x <= x1 && x0 <= pcpath->inner_box.q.x)) &&
+ (y0 <= y1 ?
+ (pcpath->inner_box.p.y <= y0 && y1 <= pcpath->inner_box.q.y) :
+ (pcpath->inner_box.p.y <= y1 && y0 <= pcpath->inner_box.q.y));
+}
+
+/* Set the current outsideness of a clipping path. */
+int
+gx_cpath_set_outside(gx_clip_path *pcpath, bool outside)
+{ if ( outside != pcpath->list.outside )
+ { pcpath->id = gs_next_ids(1); /* path changed => change id */
+ pcpath->list.outside = outside;
+ }
+ return 0;
+}
+
+/* Return the current outsideness of a clipping path. */
+int
+gx_cpath_is_outside(const gx_clip_path *pcpath)
+{ return pcpath->list.outside;
+}
+
+/* Release a clipping path. */
+void
+gx_cpath_release(gx_clip_path *pcpath)
+{ if ( !pcpath->shares_list )
+ gx_clip_list_free(&pcpath->list, pcpath->path.memory);
+ gx_path_release(&pcpath->path);
+}
+
+/* Share a clipping path. */
+void
+gx_cpath_share(gx_clip_path *pcpath)
+{ gx_path_share(&pcpath->path);
+ pcpath->shares_list = 1;
+}
+
+/* Set the outer clipping box to the path bounding box, */
+/* expanded to pixel boundaries. */
+void
+gx_cpath_set_outer_box(gx_clip_path *pcpath)
+{ pcpath->outer_box.p.x = fixed_floor(pcpath->path.bbox.p.x);
+ pcpath->outer_box.p.y = fixed_floor(pcpath->path.bbox.p.y);
+ pcpath->outer_box.q.x = fixed_ceiling(pcpath->path.bbox.q.x);
+ pcpath->outer_box.q.y = fixed_ceiling(pcpath->path.bbox.q.y);
+}
+
+/* ------ Clipping path setting ------ */
+
+/* Initialize a clipping path. */
+int
+gx_cpath_init(gx_clip_path *pcpath, gs_memory_t *mem)
+{ static /*const*/ gs_fixed_rect null_rect = { { 0, 0 }, { 0, 0 } };
+ return gx_cpath_from_rectangle(pcpath, &null_rect, mem); /* does a gx_path_init */
+}
+
+/* Create a rectangular clipping path. */
+/* The supplied rectangle may not be oriented correctly, */
+/* but it will be oriented correctly upon return. */
+int
+gx_cpath_from_rectangle(gx_clip_path *pcpath, gs_fixed_rect *pbox,
+ gs_memory_t *mem)
+{ gx_clip_list_from_rectangle(&pcpath->list, pbox);
+ pcpath->inner_box = *pbox;
+ pcpath->segments_valid = 0;
+ pcpath->shares_list = 0;
+ gx_path_init(&pcpath->path, mem);
+ pcpath->path.bbox = *pbox;
+ gx_cpath_set_outer_box(pcpath);
+ pcpath->id = gs_next_ids(1); /* path changed => change id */
+ return 0;
+}
+
+/* Intersect a new clipping path with an old one. */
+/* Note that it may overwrite its path argument; return 1 in this case, */
+/* otherwise 0 for success, <0 for failure as usual. */
+int
+gx_cpath_intersect(gs_state *pgs, gx_clip_path *pcpath, gx_path *ppath,
+ int rule)
+{ gs_fixed_rect old_box, new_box;
+ int code;
+ if ( gx_cpath_inner_box(pcpath, &old_box) &&
+ gx_path_is_rectangle(ppath, &new_box)
+ )
+ { bool changed = false;
+ bool outside = pcpath->list.outside;
+
+ /* Intersect the two rectangles if necessary. */
+ if ( old_box.p.x > new_box.p.x )
+ new_box.p.x = old_box.p.x, changed = true;
+ if ( old_box.p.y > new_box.p.y )
+ new_box.p.y = old_box.p.y, changed = true;
+ if ( old_box.q.x < new_box.q.x )
+ new_box.q.x = old_box.q.x, changed = true;
+ if ( old_box.q.y < new_box.q.y )
+ new_box.q.y = old_box.q.y, changed = true;
+ if ( changed )
+ { /* Store the new rectangle back into the new path. */
+ register segment *pseg =
+ (segment *)ppath->first_subpath;
+#define set_pt(pqx,pqy)\
+ pseg->pt.x = new_box.pqx.x, pseg->pt.y = new_box.pqy.y
+ set_pt(p, p); pseg = pseg->next;
+ set_pt(q, p); pseg = pseg->next;
+ set_pt(q, q); pseg = pseg->next;
+ set_pt(p, q); pseg = pseg->next;
+ if ( pseg != 0 ) /* might be an open rectangle */
+ set_pt(p, p);
+#undef set_pt
+ }
+ ppath->bbox = new_box;
+ gx_clip_list_from_rectangle(&pcpath->list, &new_box);
+ pcpath->list.outside = outside;
+ pcpath->inner_box = new_box;
+ pcpath->path = *ppath;
+ gx_cpath_set_outer_box(pcpath);
+ pcpath->segments_valid = 1;
+ code = 1;
+ pcpath->id = gs_next_ids(1); /* path changed => change id */
+ }
+ else
+ { /* Not a rectangle. Intersect the slow way. */
+ code = gx_cpath_intersect_slow(pgs, pcpath, ppath, rule);
+ }
+ return code;
+}
+
+/* Scale a clipping path by a power of 2. */
+int
+gx_cpath_scale_exp2(gx_clip_path *pcpath, int log2_scale_x, int log2_scale_y)
+{ int code =
+ gx_path_scale_exp2(&pcpath->path, log2_scale_x, log2_scale_y);
+ gx_clip_rect *pr;
+ if ( code < 0 )
+ return code;
+ /* Scale the fixed entries. */
+ gx_rect_scale_exp2(&pcpath->inner_box, log2_scale_x, log2_scale_y);
+ gx_rect_scale_exp2(&pcpath->outer_box, log2_scale_x, log2_scale_y);
+ /* Scale the clipping list. */
+ pr = pcpath->list.head;
+ if ( pr == 0 )
+ pr = &pcpath->list.single;
+ for ( ; pr != 0; pr = pr->next )
+ if ( pr != pcpath->list.head && pr != pcpath->list.tail )
+ {
+#define scale_v(v, s)\
+ if ( pr->v != min_int && pr->v != max_int )\
+ pr->v = (s >= 0 ? pr->v << s : pr->v >> -s)
+ scale_v(xmin, log2_scale_x);
+ scale_v(xmax, log2_scale_x);
+ scale_v(ymin, log2_scale_y);
+ scale_v(ymax, log2_scale_y);
+#undef scale_v
+ }
+ pcpath->id = gs_next_ids(1); /* path changed => change id */
+ return 0;
+}
+
+/* ------ Clipping list routines ------ */
+
+/* Initialize a clip list. */
+void
+gx_clip_list_init(gx_clip_list *clp)
+{ *clp = clip_list_empty;
+}
+
+/* Initialize a clip list to a rectangle. */
+/* The supplied rectangle may not be oriented correctly, */
+/* but it will be oriented correctly upon return. */
+private void
+gx_clip_list_from_rectangle(register gx_clip_list *clp,
+ register gs_fixed_rect *rp)
+{ gx_clip_list_init(clp);
+ if ( rp->p.x > rp->q.x )
+ { fixed t = rp->p.x; rp->p.x = rp->q.x; rp->q.x = t; }
+ if ( rp->p.y > rp->q.y )
+ { fixed t = rp->p.y; rp->p.y = rp->q.y; rp->q.y = t; }
+ clp->single.xmin = fixed2int_var(rp->p.x);
+ clp->single.ymin = fixed2int_var(rp->p.y);
+ clp->single.xmax = fixed2int_var_ceiling(rp->q.x);
+ clp->single.ymax = fixed2int_var_ceiling(rp->q.y);
+ clp->count = 1;
+ clp->outside = false;
+}
+
+/* Add a clip list to a path. */
+/* The current implementation is very inefficient. */
+private int
+gx_clip_list_add_to_path(gx_clip_list *clp, gx_path *ppath)
+{ const gx_clip_rect *rp;
+ int code = -1;
+ for ( rp = (clp->count <= 1 ? &clp->single : clp->head); rp != 0;
+ rp = rp->next
+ )
+ { if ( rp->xmin < rp->xmax && rp->ymin < rp->ymax )
+ { code = gx_path_add_rectangle(ppath,
+ int2fixed(rp->xmin),
+ int2fixed(rp->ymin),
+ int2fixed(rp->xmax),
+ int2fixed(rp->ymax));
+ if ( code < 0 )
+ return code;
+ }
+ }
+ if ( code < 0 )
+ { /* We didn't have any rectangles. */
+ code = gx_path_add_point(ppath, fixed_0, fixed_0);
+ }
+ return code;
+}
+
+/* Free a clip list. */
+void
+gx_clip_list_free(gx_clip_list *clp, gs_memory_t *mem)
+{ gx_clip_rect *rp = clp->tail;
+ while ( rp != 0 )
+ { gx_clip_rect *prev = rp->prev;
+ gs_free_object(mem, rp, "gx_clip_list_free");
+ rp = prev;
+ }
+ gx_clip_list_init(clp);
+}
+
+/* ------ Rectangle list clipper ------ */
+
+/* Device for clipping with a region. */
+/* We forward non-drawing operations, but we must be sure to intercept */
+/* all drawing operations. */
+private dev_proc_open_device(clip_open);
+private dev_proc_fill_rectangle(clip_fill_rectangle);
+private dev_proc_copy_mono(clip_copy_mono);
+private dev_proc_copy_color(clip_copy_color);
+private dev_proc_get_bits(clip_get_bits);
+private dev_proc_copy_alpha(clip_copy_alpha);
+private dev_proc_fill_mask(clip_fill_mask);
+private dev_proc_strip_tile_rectangle(clip_strip_tile_rectangle);
+private dev_proc_strip_copy_rop(clip_strip_copy_rop);
+
+/* The device descriptor. */
+private const gx_device_clip gs_clip_device =
+{ std_device_std_body(gx_device_clip, 0, "clipper",
+ 0, 0, 1, 1),
+ { clip_open,
+ gx_forward_get_initial_matrix,
+ gx_default_sync_output,
+ gx_default_output_page,
+ gx_default_close_device,
+ gx_forward_map_rgb_color,
+ gx_forward_map_color_rgb,
+ clip_fill_rectangle,
+ gx_default_tile_rectangle,
+ clip_copy_mono,
+ clip_copy_color,
+ gx_default_draw_line,
+ clip_get_bits,
+ gx_forward_get_params,
+ gx_forward_put_params,
+ gx_forward_map_cmyk_color,
+ gx_forward_get_xfont_procs,
+ gx_forward_get_xfont_device,
+ gx_forward_map_rgb_alpha_color,
+ gx_forward_get_page_device,
+ gx_forward_get_alpha_bits,
+ clip_copy_alpha,
+ gx_forward_get_band,
+ gx_default_copy_rop,
+ gx_default_fill_path,
+ gx_default_stroke_path,
+ clip_fill_mask,
+ gx_default_fill_trapezoid,
+ gx_default_fill_parallelogram,
+ gx_default_fill_triangle,
+ gx_default_draw_thin_line,
+ gx_default_begin_image,
+ gx_default_image_data,
+ gx_default_end_image,
+ clip_strip_tile_rectangle,
+ clip_strip_copy_rop
+ }
+};
+#define rdev ((gx_device_clip *)dev)
+
+/* Make a clipping device. */
+void
+gx_make_clip_translate_device(gx_device_clip *dev, void *container,
+ const gx_clip_list *list, int tx, int ty)
+{ *dev = gs_clip_device;
+ dev->list = *list;
+ dev->translation.x = tx;
+ dev->translation.y = ty;
+}
+void
+gx_make_clip_path_device(gx_device_clip *dev, const gx_clip_path *pcpath)
+{ gx_make_clip_device(dev, NULL, &pcpath->list);
+}
+
+/* Declare and initialize the cursor variables. */
+#ifdef DEBUG
+private ulong clip_loops, clip_in, clip_down, clip_up, clip_x, clip_no_x;
+private uint clip_interval = 100;
+# define inc(v) v++
+# define print_clip()\
+ if ( clip_loops % clip_interval == 0 )\
+ if_debug10('q', "[q]rect=(%d,%d),(%d,%d)\n loops=%ld in=%ld down=%ld up=%ld x=%ld no_x=%ld\n",\
+ x, y, x + w, y + h,\
+ clip_loops, clip_in, clip_down, clip_up, clip_x, clip_no_x)
+#else
+# define inc(v) discard(0)
+# define print_clip() DO_NOTHING
+#endif
+#define DECLARE_CLIP\
+ register gx_clip_rect *rptr = rdev->current;\
+ gx_device *tdev = rdev->target;\
+ bool outside = rdev->list.outside;
+/* Translate the supplied coordinates. */
+#define TRANSLATE_CLIP\
+ x += rdev->translation.x;\
+ y += rdev->translation.y;
+/* Check whether the rectangle x,y,w,h falls within the current entry. */
+#define xywh_is_in_ryptr()\
+ (!outside &&\
+ y >= rptr->ymin && y + h <= rptr->ymax &&\
+ x >= rptr->xmin && x + w <= rptr->xmax)
+#ifdef DEBUG
+# define xywh_in_ryptr() (xywh_is_in_ryptr() ? (inc(clip_in), 1) : 0)
+#else
+# define xywh_in_ryptr() xywh_is_in_ryptr()
+#endif
+/*
+ * Warp the cursor forward or backward to the first rectangle row that
+ * could include a given y value. Assumes rptr is set, and updates it.
+ * Specifically, after warp_cursor, either rptr == 0 (if the y value is
+ * greater than all y values in the list), or y < rptr->ymax and either
+ * rptr->prev == 0 or y >= rptr->prev->ymax. Note that y <= rptr->ymin
+ * is possible.
+ *
+ * In the first case below, the while loop is safe because if there is
+ * more than one rectangle, there is a 'stopper' at the end of the list.
+ */
+#define warp_cursor(y)\
+ if ( (y) >= rptr->ymax )\
+ { if ( (rptr = rptr->next) != 0 )\
+ while ( inc(clip_up), (y) >= rptr->ymax ) rptr = rptr->next;\
+ }\
+ else while ( rptr->prev != 0 && (y) < rptr->prev->ymax )\
+ { inc(clip_down); rptr = rptr->prev; }
+/*
+ * Enumerate the rectangles of the x,w,y,h argument that fall within
+ * the clipping region. Usage:
+ * DO_CLIP(adjust for yc > yp if necessary,
+ * process(xc, yc, xec, yec) [must be an expression])
+ */
+#define DO_CLIP(adjust_for_y, process_rectangle)\
+ if ( w <= 0 || h <= 0 ) return 0;\
+ inc(clip_loops);\
+ { const int xe = x + w, ye = y + h;\
+ int xc, xec, yc, yec, yp, yep;\
+ int code;\
+ warp_cursor(y);\
+ if ( rptr == 0 || (yc = rptr->ymin) >= ye )\
+ return (outside ? (xc = x, xec = xe, yc = y, yec = ye,\
+ process_rectangle) : 0);\
+ rdev->current = rptr;\
+ if ( yc < y ) yc = y;\
+ yp = y;\
+ if ( outside )\
+ { for ( yep = y; ; )\
+ { const int ymax = rptr->ymax;\
+ xc = x;\
+ if ( yc > yep )\
+ { yec = yc, yc = yep;\
+ adjust_for_y;\
+ xec = xe;\
+ code = process_rectangle;\
+ if ( code < 0 ) return code;\
+ yp = yep;\
+ yc = yec;\
+ adjust_for_y;\
+ }\
+ yec = min(ymax, ye);\
+ do \
+ { xec = rptr->xmin;\
+ if ( xec > xc )\
+ { if ( xec > xe ) xec = xe;\
+ code = process_rectangle;\
+ if ( code < 0 ) return code;\
+ xc = rptr->xmax;\
+ if ( xc >= xe ) xc = max_int;\
+ }\
+ }\
+ while ( (rptr = rptr->next) != 0 && rptr->ymax == ymax );\
+ if ( xc < xe )\
+ { xec = xe;\
+ code = process_rectangle;\
+ if ( code < 0 ) return code;\
+ }\
+ yp = yc;\
+ yep = yec;\
+ if ( rptr == 0 || (yc = rptr->ymin) >= ye ) break;\
+ }\
+ if ( yep < ye )\
+ { xc = x, xec = xe, yc = yep, yec = ye;\
+ code = process_rectangle;\
+ if ( code < 0 ) return code;\
+ }\
+ }\
+ else \
+ for ( ; ; )\
+ { const int ymax = rptr->ymax;\
+ yec = min(ymax, ye);\
+ if ( yc > yp ) adjust_for_y;\
+ if_debug2('Q', "[Q]yc=%d yec=%d\n", yc, yec);\
+ do \
+ { xc = rptr->xmin;\
+ xec = rptr->xmax;\
+ if ( xc < x ) xc = x;\
+ if ( xec > xe ) xec = xe;\
+ if ( xec > xc )\
+ { clip_rect_print('Q', "match", rptr);\
+ if_debug2('Q', "[Q]xc=%d xec=%d\n", xc, xec);\
+ inc(clip_x);\
+ code = process_rectangle;\
+ if ( code < 0 ) return code;\
+ }\
+ else inc(clip_no_x);\
+ }\
+ while ( (rptr = rptr->next) != 0 && rptr->ymax == ymax );\
+ if ( rptr == 0 || (yec = rptr->ymin) >= ye ) break;\
+ yp = yc;\
+ yc = yec;\
+ }\
+ print_clip();\
+ }
+
+/* Open a clipping device */
+private int
+clip_open(register gx_device *dev)
+{ gx_device *tdev = rdev->target;
+ /* Initialize the cursor. */
+ rdev->current =
+ (rdev->list.head == 0 ? &rdev->list.single : rdev->list.head);
+ rdev->color_info = tdev->color_info;
+ return 0;
+}
+
+/* Fill a rectangle */
+private int
+clip_fill_rectangle(gx_device *dev, int x, int y, int w, int h,
+ gx_color_index color)
+{ DECLARE_CLIP
+ dev_proc_fill_rectangle((*fill)) = dev_proc(tdev, fill_rectangle);
+
+ TRANSLATE_CLIP
+ if ( xywh_in_ryptr() )
+ return (*fill)(tdev, x, y, w, h, color);
+ DO_CLIP(DO_NOTHING,
+ (*fill)(tdev, xc, yc, xec - xc, yec - yc, color))
+ return 0;
+}
+
+/* Copy a monochrome rectangle */
+private int
+clip_copy_mono(gx_device *dev,
+ const byte *data, int sourcex, int raster, gx_bitmap_id id,
+ int x, int y, int w, int h,
+ gx_color_index color0, gx_color_index color1)
+{ DECLARE_CLIP
+ dev_proc_copy_mono((*copy)) = dev_proc(tdev, copy_mono);
+
+ TRANSLATE_CLIP
+ if ( xywh_in_ryptr() )
+ return (*copy)(tdev, data, sourcex, raster, id, x, y, w, h, color0, color1);
+ DO_CLIP(data += (yc - yp) * raster,
+ (*copy)(tdev, data, sourcex + xc - x, raster, gx_no_bitmap_id,
+ xc, yc, xec - xc, yec - yc, color0, color1))
+ return 0;
+}
+
+/* Copy a color rectangle */
+private int
+clip_copy_color(gx_device *dev,
+ const byte *data, int sourcex, int raster, gx_bitmap_id id,
+ int x, int y, int w, int h)
+{ DECLARE_CLIP
+ dev_proc_copy_color((*copy)) = dev_proc(tdev, copy_color);
+
+ TRANSLATE_CLIP
+ if ( xywh_in_ryptr() )
+ return (*copy)(tdev, data, sourcex, raster, id, x, y, w, h);
+ DO_CLIP(data += (yc - yp) * raster,
+ (*copy)(tdev, data, sourcex + xc - x, raster, gx_no_bitmap_id,
+ xc, yc, xec - xc, yec - yc))
+ return 0;
+}
+
+/* Copy a rectangle with alpha */
+private int
+clip_copy_alpha(gx_device *dev,
+ const byte *data, int sourcex, int raster, gx_bitmap_id id,
+ int x, int y, int w, int h,
+ gx_color_index color, int depth)
+{ DECLARE_CLIP
+ dev_proc_copy_alpha((*copy)) = dev_proc(tdev, copy_alpha);
+
+ TRANSLATE_CLIP
+ if ( xywh_in_ryptr() )
+ return (*copy)(tdev, data, sourcex, raster, id, x, y, w, h, color, depth);
+ DO_CLIP(data += (yc - yp) * raster,
+ (*copy)(tdev, data, sourcex + xc - x, raster, gx_no_bitmap_id,
+ xc, yc, xec - xc, yec - yc, color, depth))
+ return 0;
+}
+
+/* Fill a region defined by a mask. */
+private int
+clip_fill_mask(gx_device *dev,
+ const byte *data, int sourcex, 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)
+{ DECLARE_CLIP
+ dev_proc_fill_mask((*fill)) = dev_proc(tdev, fill_mask);
+
+ if ( pcpath != 0 )
+ return gx_default_fill_mask(dev, data, sourcex, raster, id,
+ x, y, w, h, pdcolor, depth, lop,
+ pcpath);
+ TRANSLATE_CLIP
+ if ( xywh_in_ryptr() )
+ return (*fill)(tdev, data, sourcex, raster, id, x, y, w, h,
+ pdcolor, depth, lop, NULL);
+ DO_CLIP(data += (yc - yp) * raster,
+ (*fill)(tdev, data, sourcex + xc - x, raster, gx_no_bitmap_id,
+ xc, yc, xec - xc, yec - yc, pdcolor, depth, lop,
+ NULL))
+ return 0;
+}
+
+/* Get bits back from the device. */
+private int
+clip_get_bits(gx_device *dev, int y, byte *data, byte **actual_data)
+{ gx_device *tdev = rdev->target;
+ return (*dev_proc(tdev, get_bits))(tdev, y - rdev->translation.y,
+ data, actual_data);
+}
+
+/* Strip-tile a rectangle. */
+private int
+clip_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 phase_x, int phase_y)
+{ DECLARE_CLIP
+ dev_proc_strip_tile_rectangle((*fill)) =
+ dev_proc(tdev, strip_tile_rectangle);
+
+ TRANSLATE_CLIP
+ if ( xywh_in_ryptr() )
+ return (*fill)(tdev, tiles, x, y, w, h, color0, color1, phase_x, phase_y);
+ DO_CLIP(DO_NOTHING,
+ (*fill)(tdev, tiles, xc, yc, xec - xc, yec - yc,
+ color0, color1, phase_x, phase_y))
+ return 0;
+}
+
+/* Copy a rectangle with RasterOp and strip texture. */
+private int
+clip_strip_copy_rop(gx_device *dev,
+ const byte *sdata, int sourcex, uint raster, 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)
+{ DECLARE_CLIP
+ dev_proc_strip_copy_rop((*copy)) = dev_proc(tdev, strip_copy_rop);
+
+ TRANSLATE_CLIP
+ if ( xywh_in_ryptr() )
+ return (*copy)(tdev, sdata, sourcex, raster, id, scolors,
+ textures, tcolors, x, y, w, h,
+ phase_x, phase_y, lop);
+ DO_CLIP(sdata += (yc - yp) * raster,
+ (*copy)(tdev, sdata, sourcex + xc - x, raster,
+ gx_no_bitmap_id, scolors, textures, tcolors,
+ xc, yc, xec - xc, yec - yc,
+ phase_x, phase_y, lop))
+ return 0;
+}
+
+/* ------ Debugging printout ------ */
+
+#ifdef DEBUG
+
+/* Print a clipping path */
+void
+gx_cpath_print(const gx_clip_path *pcpath)
+{ const gx_clip_rect *pr;
+ if ( pcpath->segments_valid )
+ gx_path_print(&pcpath->path);
+ else
+ dputs(" (segments not valid)\n");
+ dprintf4(" inner_box=(%g,%g),(%g,%g)\n",
+ fixed2float(pcpath->inner_box.p.x),
+ fixed2float(pcpath->inner_box.p.y),
+ fixed2float(pcpath->inner_box.q.x),
+ fixed2float(pcpath->inner_box.q.y));
+ dprintf5(" outer_box=(%g,%g),(%g,%g) count=%d\n",
+ fixed2float(pcpath->outer_box.p.x),
+ fixed2float(pcpath->outer_box.p.y),
+ fixed2float(pcpath->outer_box.q.x),
+ fixed2float(pcpath->outer_box.q.y),
+ pcpath->list.count);
+ dprintf2(" rule=%d outside=%d\n",
+ pcpath->rule, pcpath->list.outside);
+ switch ( pcpath->list.count )
+ {
+ case 0: pr = 0; break;
+ case 1: pr = &pcpath->list.single; break;
+ default: pr = pcpath->list.head;
+ }
+ for ( ; pr != 0; pr = pr->next )
+ dprintf4(" rect: (%d,%d),(%d,%d)\n",
+ pr->xmin, pr->ymin, pr->xmax, pr->ymax);
+}
+
+#endif /* DEBUG */
diff --git a/pstoraster/gxcpath.h b/pstoraster/gxcpath.h
new file mode 100644
index 000000000..d734d09a0
--- /dev/null
+++ b/pstoraster/gxcpath.h
@@ -0,0 +1,123 @@
+/* Copyright (C) 1991, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxcpath.h */
+/* Interface to clipping devices */
+/* Requires gxdevice.h */
+
+/* We expose the implementation of clipping lists so that clients */
+/* can allocate clipping lists or devices on the stack. */
+
+/*
+ * For clipping, a path is represented as a list of rectangles.
+ * Normally, a path is created as a list of segments;
+ * installing it as a clipping path creates the rectangle list.
+ * However, when the clipping path originates in some other way
+ * (e.g., from initclip, or for clipping a cached character),
+ * or if it is a non-trivial intersection of two paths,
+ * the resulting clipping path exists only as a rectangle list;
+ * clippath constructs the segment representation if needed.
+ * Note that even if the path only exists as a rectangle list,
+ * its bounding box (path.bbox) is still correct.
+ */
+
+/*
+ * Rectangle list structure.
+ * Consecutive gx_clip_rect entries either have the same Y values,
+ * or ymin of this entry >= ymax of the previous entry.
+ */
+typedef struct gx_clip_rect_s gx_clip_rect;
+struct gx_clip_rect_s {
+ gx_clip_rect *next, *prev;
+ int ymin, ymax; /* ymax > ymin */
+ int xmin, xmax; /* xmax > xmin */
+};
+/* The descriptor is public only for gxacpath.c. */
+extern_st(st_clip_rect);
+#define public_st_clip_rect() /* in gxcpath.c */\
+ gs_public_st_ptrs2(st_clip_rect, gx_clip_rect, "clip_rect",\
+ clip_rect_enum_ptrs, clip_rect_reloc_ptrs, next, prev)
+#define st_clip_rect_max_ptrs 2
+
+/*
+ * A clip list may consist either of a single rectangle,
+ * with null head and tail, or a list of rectangles. In the latter case,
+ * there is a dummy head entry with p.x = q.x to cover Y values
+ * starting at min_int, and a dummy tail entry to cover Y values
+ * ending at max_int. This eliminates the need for end tests.
+ */
+#ifndef gx_clip_list_DEFINED
+# define gx_clip_list_DEFINED
+typedef struct gx_clip_list_s gx_clip_list;
+#endif
+struct gx_clip_list_s {
+ gx_clip_rect single; /* (has next = prev = 0) */
+ gx_clip_rect *head;
+ gx_clip_rect *tail;
+ int count; /* # of rectangles not counting */
+ /* head or tail */
+ bool outside; /* if true, clip to outside of list */
+ /* rather than inside */
+};
+#define private_st_clip_list() /* in gxcpath.c */\
+ gs_private_st_ptrs2(st_clip_list, gx_clip_list, "clip_list",\
+ clip_list_enum_ptrs, clip_list_reloc_ptrs, head, tail)
+#define st_clip_list_max_ptrs 2 /* head, tail */
+#define clip_list_is_rectangle(clp) ((clp)->count <= 1)
+
+/*
+ * Clipping devices provide for translation before clipping.
+ * This ability, a late addition, currently is used only in a few
+ * situations that require breaking up a transfer into pieces,
+ * but we suspect it could be used more widely.
+ */
+typedef struct gx_device_clip_s {
+ gx_device_forward_common; /* target is set by client */
+ gx_clip_list list; /* set by client */
+ gx_clip_rect *current; /* cursor in list */
+ gs_int_point translation;
+} gx_device_clip;
+extern_st(st_device_clip);
+#define public_st_device_clip() /* in gxcpath.c */\
+ gs_public_st_composite(st_device_clip, gx_device_clip,\
+ "gx_device_clip", device_clip_enum_ptrs, device_clip_reloc_ptrs)
+void gx_make_clip_translate_device(P5(gx_device_clip *dev, void *container,
+ const gx_clip_list *list, int tx, int ty));
+#define gx_make_clip_device(dev, container, list)\
+ gx_make_clip_translate_device(dev, container, list, 0, 0)
+void gx_make_clip_path_device(P2(gx_device_clip *, const gx_clip_path *));
+
+#define clip_rect_print(ch, str, ar)\
+ if_debug7(ch, "[%c]%s 0x%lx: (%d,%d),(%d,%d)\n", ch, str, (ulong)ar,\
+ (ar)->xmin, (ar)->ymin, (ar)->xmax, (ar)->ymax)
+
+/* Routines exported from gxcpath.c for gxacpath.c */
+
+/* Initialize a clip list. */
+void gx_clip_list_init(P1(gx_clip_list *));
+
+/* Free a clip list. */
+void gx_clip_list_free(P2(gx_clip_list *, gs_memory_t *));
+
+/* Set the outer box for a clipping path from its bounding box. */
+void gx_cpath_set_outer_box(P1(gx_clip_path *));
diff --git a/pstoraster/gxcspace.h b/pstoraster/gxcspace.h
new file mode 100644
index 000000000..37ee1393f
--- /dev/null
+++ b/pstoraster/gxcspace.h
@@ -0,0 +1,168 @@
+/* Copyright (C) 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxcspace.h */
+/* Implementation of color spaces */
+
+#ifndef gxcspace_INCLUDED
+# define gxcspace_INCLUDED
+
+#include "gscspace.h" /* client interface */
+#include "gsccolor.h"
+#include "gsstruct.h" /* needed for enum_ptrs & reloc_ptrs */
+#include "gxfrac.h" /* for concrete colors */
+
+/* Define an opaque type for device colors. */
+/* (The actual definition is in gxdcolor.h.) */
+#ifndef gx_device_color_DEFINED
+# define gx_device_color_DEFINED
+typedef struct gx_device_color_s gx_device_color;
+#endif
+
+/* Color space types (classes): */
+/*typedef struct gs_color_space_type_s gs_color_space_type;*/
+struct gs_color_space_type_s {
+
+ gs_color_space_index index;
+
+ /* Define the number of components in a color of this space. */
+ /* For Pattern spaces, where the number of components depends on */
+ /* the underlying space, this value is -1. */
+
+ int num_components;
+
+ /* Define whether the space can be the base space for */
+ /* an Indexed color space or the alternate space for */
+ /* a Separation color space. */
+
+ bool can_be_base_space;
+
+ /* ------ Procedures ------ */
+
+ /* Construct the initial color value for this space. */
+
+#define cs_proc_init_color(proc)\
+ void proc(P2(gs_client_color *, const gs_color_space *))
+#define cs_init_color(pcc, pcs)\
+ (*(pcs)->type->init_color)(pcc, pcs)
+#define cs_full_init_color(pcc, pcs)\
+ ((pcc)->pattern = 0, cs_init_color(pcc, pcs))
+ cs_proc_init_color((*init_color));
+
+ /* Return the concrete color space underlying this one. */
+ /* (Not defined for Pattern spaces.) */
+
+#define cs_proc_concrete_space(proc)\
+ const gs_color_space *proc(P2(const gs_color_space *, const gs_state *))
+#define cs_concrete_space(pcs, pgs)\
+ (*(pcs)->type->concrete_space)(pcs, pgs)
+ cs_proc_concrete_space((*concrete_space));
+
+ /* Reduce a color to a concrete color. */
+ /* (Not defined for Pattern spaces.) */
+
+#define cs_proc_concretize_color(proc)\
+ int proc(P4(const gs_client_color *, const gs_color_space *,\
+ frac *, const gs_state *))
+ cs_proc_concretize_color((*concretize_color));
+
+ /* Map a concrete color to a device color. */
+ /* (Only defined for concrete color spaces.) */
+
+#define cs_proc_remap_concrete_color(proc)\
+ int proc(P3(const frac *, gx_device_color *, const gs_state *))
+ cs_proc_remap_concrete_color((*remap_concrete_color));
+
+ /* Map a color directly to a device color. */
+
+#define cs_proc_remap_color(proc)\
+ int proc(P4(const gs_client_color *, const gs_color_space *,\
+ gx_device_color *, const gs_state *))
+ cs_proc_remap_color((*remap_color));
+
+ /* Install the color space in a graphics state. */
+
+#define cs_proc_install_cspace(proc)\
+ int proc(P2(gs_color_space *, gs_state *))
+ cs_proc_install_cspace((*install_cspace));
+
+ /* Adjust reference counts of indirect color space components. */
+#define cs_proc_adjust_cspace_count(proc)\
+ void proc(P3(const gs_color_space *, gs_state *, int))
+#define cs_adjust_cspace_count(pgs, delta)\
+ (*(pgs)->color_space->type->adjust_cspace_count)((pgs)->color_space, pgs, delta)
+ cs_proc_adjust_cspace_count((*adjust_cspace_count));
+
+ /* Adjust reference counts of indirect color components. */
+
+#define cs_proc_adjust_color_count(proc)\
+ void proc(P4(const gs_client_color *, const gs_color_space *, gs_state *, int))
+#define cs_adjust_color_count(pgs, delta)\
+ (*(pgs)->color_space->type->adjust_color_count)((pgs)->ccolor, (pgs)->color_space, pgs, delta)
+ cs_proc_adjust_color_count((*adjust_color_count));
+
+/* Adjust both reference counts. */
+#define cs_adjust_counts(pgs, delta)\
+ cs_adjust_color_count(pgs, delta), cs_adjust_cspace_count(pgs, delta)
+
+ /* Enumerate the pointers in a color space. */
+
+ struct_proc_enum_ptrs((*enum_ptrs));
+
+ /* Relocate the pointers in a color space. */
+
+ struct_proc_reloc_ptrs((*reloc_ptrs));
+
+};
+/* Standard color space procedures */
+cs_proc_init_color(gx_init_paint_1);
+cs_proc_init_color(gx_init_paint_3);
+cs_proc_init_color(gx_init_paint_4);
+cs_proc_concrete_space(gx_no_concrete_space);
+cs_proc_concrete_space(gx_same_concrete_space);
+cs_proc_concretize_color(gx_no_concretize_color);
+cs_proc_remap_color(gx_default_remap_color);
+cs_proc_install_cspace(gx_no_install_cspace);
+cs_proc_adjust_cspace_count(gx_no_adjust_cspace_count);
+cs_proc_adjust_color_count(gx_no_adjust_color_count);
+#define gx_no_cspace_enum_ptrs gs_no_struct_enum_ptrs
+#define gx_no_cspace_reloc_ptrs gs_no_struct_reloc_ptrs
+
+/* Macro for defining color space procedures. */
+#define cs_declare_procs(scope, concretize, install, adjust, enum_p, reloc_p)\
+ scope cs_proc_concretize_color(concretize);\
+ scope cs_proc_install_cspace(install);\
+ scope cs_proc_adjust_cspace_count(adjust);\
+ scope struct_proc_enum_ptrs(enum_p);\
+ scope struct_proc_reloc_ptrs(reloc_p)
+
+/* Standard color space types */
+extern const gs_color_space_type
+ gs_color_space_type_DeviceGray,
+ gs_color_space_type_DeviceRGB,
+ gs_color_space_type_DeviceCMYK;
+
+/* Define the allocator type for color spaces. */
+extern_st(st_color_space);
+
+#endif /* gxcspace_INCLUDED */
diff --git a/pstoraster/gxctable.c b/pstoraster/gxctable.c
new file mode 100644
index 000000000..5d49a2117
--- /dev/null
+++ b/pstoraster/gxctable.c
@@ -0,0 +1,145 @@
+/* Copyright (C) 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxctable.c */
+/* Color table lookup and interpolation */
+#include "gx.h"
+#include "gxfixed.h"
+#include "gxfrac.h"
+#include "gxctable.h"
+
+/* See gxctable.h for the API and structure definitions. */
+
+/*
+ * Define an implementation that simply picks the nearest value without
+ * any interpolation.
+ */
+void
+gx_color_interpolate_nearest(const fixed *pi,
+ const gx_color_lookup_table *pclt, frac *pv)
+{ const int *pdim = pclt->dims;
+ int m = pclt->m;
+ const gs_const_string *table = pclt->table;
+
+ if ( pclt->n > 3 )
+ { table += fixed2int_var_rounded(pi[0]) * pdim[1];
+ ++pi, ++pdim;
+ }
+ { int ic = fixed2int_var_rounded(pi[2]);
+ int ib = fixed2int_var_rounded(pi[1]);
+ int ia = fixed2int_var_rounded(pi[0]);
+ const byte *p = pclt->table[ia].data + (ib * pdim[2] + ic) * m;
+ int j;
+
+ for ( j = 0; j < m; ++j, ++p )
+ pv[j] = byte2frac(*p);
+ }
+}
+
+/*
+ * Define an implementation that uses trilinear interpolation.
+ */
+void
+gx_color_interpolate_linear(const fixed *pi,
+ const gx_color_lookup_table *pclt, frac *pv)
+{ const int *pdim = pclt->dims;
+ int m = pclt->m;
+
+ if ( pclt->n > 3 )
+ { /* Do two 3-D interpolations, */
+ /* and then interpolate between them. */
+ gx_color_lookup_table clt3;
+ frac vx[4];
+ int ix = fixed2int_var(pi[0]);
+ fixed fx = fixed_fraction(pi[0]);
+ int j;
+
+ clt3.n = 3;
+ /*clt3.dims[0] = pdim[1];*/ /* not used */
+ clt3.dims[1] = pdim[2];
+ clt3.dims[2] = pdim[3];
+ clt3.m = m;
+ clt3.table = pclt->table + ix * pdim[1];
+ gx_color_interpolate_linear(pi + 1, &clt3, pv);
+ if ( ix == pdim[0] - 1 )
+ return;
+ clt3.table += pdim[1];
+ gx_color_interpolate_linear(pi + 1, &clt3, vx);
+ for ( j = 0; j < m; ++j )
+ pv[j] += (frac)arith_rshift((long)fx * (vx[j] - pv[j]),
+ _fixed_shift);
+ }
+ else
+ { int ic = fixed2int_var(pi[2]);
+ fixed fc = fixed_fraction(pi[2]);
+ uint dc1 = (ic == pdim[2] - 1 ? 0 : m);
+ int ib = fixed2int_var(pi[1]);
+ fixed fb = fixed_fraction(pi[1]);
+ uint db1 = (ib == pdim[1] - 1 ? 0 : pdim[2] * m);
+ uint dbc = (ib * pdim[2] + ic) * m;
+ uint dbc1 = db1 + dc1;
+ int ia = fixed2int_var(pi[0]);
+ fixed fa = fixed_fraction(pi[0]);
+ const byte *pa0 = pclt->table[ia].data + dbc;
+ const byte *pa1 =
+ (ia == pdim[0] - 1 ? pa0 : pclt->table[ia+1].data + dbc);
+ int j;
+
+ /* The values to be interpolated are */
+ /* pa{0,1}[{0,db1,dc1,dbc1}]. */
+ for ( j = 0; j < m; ++j, ++pa0, ++pa1 )
+ { frac v000 = byte2frac(pa0[0]);
+ frac v001 = byte2frac(pa0[dc1]);
+ frac v010 = byte2frac(pa0[db1]);
+ frac v011 = byte2frac(pa0[dbc1]);
+ frac v100 = byte2frac(pa1[0]);
+ frac v101 = byte2frac(pa1[dc1]);
+ frac v110 = byte2frac(pa1[db1]);
+ frac v111 = byte2frac(pa1[dbc1]);
+
+ frac v00 = v000 +
+ (frac)arith_rshift((long)fc * (v001 - v000),
+ _fixed_shift);
+ frac v01 = v010 +
+ (frac)arith_rshift((long)fc * (v011 - v010),
+ _fixed_shift);
+ frac v10 = v100 +
+ (frac)arith_rshift((long)fc * (v101 - v100),
+ _fixed_shift);
+ frac v11 = v110 +
+ (frac)arith_rshift((long)fc * (v111 - v110),
+ _fixed_shift);
+
+ frac v0 = v00 +
+ (frac)arith_rshift((long)fb * (v01 - v00),
+ _fixed_shift);
+ frac v1 = v10 +
+ (frac)arith_rshift((long)fb * (v11 - v10),
+ _fixed_shift);
+
+ pv[j] = v0 +
+ (frac)arith_rshift((long)fa * (v1 - v0),
+ _fixed_shift);
+ }
+ }
+}
diff --git a/pstoraster/gxctable.h b/pstoraster/gxctable.h
new file mode 100644
index 000000000..36c27db7b
--- /dev/null
+++ b/pstoraster/gxctable.h
@@ -0,0 +1,63 @@
+/* Copyright (C) 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxctable.h */
+/* Interface to color table lookup and interpolation */
+#include "gxfixed.h"
+#include "gxfrac.h"
+
+/*
+ * Define a 3- or 4-D color lookup table.
+ * n is the number of dimensions (input indices), 3 or 4.
+ * dims[0..n-1] are the table dimensions.
+ * m is the number of output values, 3 or 4.
+ * For n = 3:
+ * table[i], 0 <= i < dims[0], point to strings of length
+ * dims[1] x dims[2] x m.
+ * For n = 4:
+ * table[i], 0 <= i < dims[0] x dims[1], points to strings of length
+ * dims[2] x dims[3] x m.
+ * It isn't really necessary to store the size of each string, since
+ * they're all the same size, but it makes things a lot easier for the GC.
+ */
+typedef struct gx_color_lookup_table_s {
+ int n;
+ int dims[4]; /* [ndims] */
+ int m;
+ const gs_const_string *table;
+} gx_color_lookup_table;
+
+/*
+ * Interpolate in a 3- or 4-D color lookup table.
+ * pi[0..n-1] are the table indices, guaranteed to be in the ranges
+ * [0..dims[n]-1] respectively.
+ * Return interpolated values in pv[0..m-1].
+ */
+
+/* Return the nearest value without interpolation. */
+void gx_color_interpolate_nearest(P3(const fixed *pi,
+ const gx_color_lookup_table *pclt, frac *pv));
+
+/* Use trilinear interpolation. */
+void gx_color_interpolate_linear(P3(const fixed *pi,
+ const gx_color_lookup_table *pclt, frac *pv));
diff --git a/pstoraster/gxcvalue.h b/pstoraster/gxcvalue.h
new file mode 100644
index 000000000..653882746
--- /dev/null
+++ b/pstoraster/gxcvalue.h
@@ -0,0 +1,46 @@
+/* Copyright (C) 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxcvalue.h */
+/* Definition of device color values */
+
+#ifndef gxcvalue_INCLUDED
+# define gxcvalue_INCLUDED
+
+/* Define the type for gray or RGB values at the driver interface. */
+typedef unsigned short gx_color_value;
+#define arch_sizeof_gx_color_value arch_sizeof_short
+/* We might use less than the full range someday. */
+/* ...bits must lie between 8 and 16. */
+#define gx_color_value_bits (sizeof(gx_color_value) * 8)
+#define gx_max_color_value ((gx_color_value)((1L << gx_color_value_bits) - 1))
+#define gx_color_value_to_byte(cv)\
+ ((cv) >> (gx_color_value_bits - 8))
+#define gx_color_value_from_byte(cb)\
+ (((cb) << (gx_color_value_bits - 8)) + ((cb) >> (16 - gx_color_value_bits)))
+
+/* Convert between gx_color_values and fracs. */
+#define frac2cv(fr) frac2ushort(fr)
+#define cv2frac(cv) ushort2frac(cv)
+
+#endif /* gxcvalue_INCLUDED */
diff --git a/pstoraster/gxdcconv.c b/pstoraster/gxdcconv.c
new file mode 100644
index 000000000..bc4880937
--- /dev/null
+++ b/pstoraster/gxdcconv.c
@@ -0,0 +1,151 @@
+/* Copyright (C) 1992, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxdcconv.c */
+/* Conversion between device color spaces for Ghostscript */
+#include "gx.h"
+#include "gsdcolor.h" /* for gxcmap.h */
+#include "gxdcconv.h" /* interface */
+#include "gxdevice.h" /* for gxcmap.h */
+#include "gxcmap.h"
+#include "gxfarith.h"
+#include "gxlum.h"
+#include "gzstate.h"
+
+/*
+ * The CMYK to RGB algorithms specified by Adobe are, e.g.,
+ * R = 1.0 - min(1.0, C + K)
+ * C = max(0.0, min(1.0, 1 - R - UCR))
+ * but we get much better results with
+ * R = (1.0 - C) * (1.0 - K)
+ * C = max(0.0, min(1.0, 1 - R / (1 - UCR)))
+ * For utmost compatibility, we offer the Adobe algorithms as an option:
+ */
+/*#define USE_ADOBE_CMYK_RGB*/
+
+/* ------ Color space conversion ------ */
+
+/* Only 4 of the 6 conversions are implemented here; */
+/* the other 2 (Gray to RGB/CMYK) are trivial. */
+
+/* Convert RGB to Gray. */
+frac
+color_rgb_to_gray(frac r, frac g, frac b, const gs_state *pgs)
+{ return (r * (unsigned long)lum_red_weight +
+ g * (unsigned long)lum_green_weight +
+ b * (unsigned long)lum_blue_weight +
+ (lum_all_weights / 2))
+ / lum_all_weights;
+}
+
+/* Convert RGB to CMYK. */
+/* Note that this involves black generation and undercolor removal. */
+void
+color_rgb_to_cmyk(frac r, frac g, frac b, const gs_state *pgs,
+ frac cmyk[4])
+{ frac c = frac_1 - r, m = frac_1 - g, y = frac_1 - b;
+ frac k = (c < m ? min(c, y) : min(m, y));
+ /* The default UCR and BG functions are pretty arbitrary.... */
+ frac bg =
+ (pgs->black_generation == NULL ? frac_0 :
+ gx_map_color_frac(pgs, k, black_generation));
+ signed_frac ucr =
+ (pgs->undercolor_removal == NULL ? frac_0 :
+ gx_map_color_frac(pgs, k, undercolor_removal));
+ if ( ucr == frac_1 )
+ cmyk[0] = cmyk[1] = cmyk[2] = 0;
+ else
+ {
+#ifdef USE_ADOBE_CMYK_RGB
+ /* C = max(0.0, min(1.0, 1 - R - UCR)), etc. */
+ signed_frac not_ucr = (ucr < 0 ? frac_1 + ucr : frac_1);
+ cmyk[0] = (c < ucr ? frac_0 : c > not_ucr ? frac_1 : c - ucr);
+ cmyk[1] = (m < ucr ? frac_0 : m > not_ucr ? frac_1 : m - ucr);
+ cmyk[2] = (y < ucr ? frac_0 : y > not_ucr ? frac_1 : y - ucr);
+#else
+ /* C = max(0.0, min(1.0, 1 - R / (1 - UCR))), etc. */
+ float denom = frac2float(frac_1 - ucr); /* unscaled */
+ float v;
+ v = (float)frac_1 - r / denom; /* unscaled */
+ cmyk[0] =
+ (is_fneg(v) ? frac_0 : v >= (float)frac_1 ? frac_1 : (frac)v);
+ v = (float)frac_1 - g / denom; /* unscaled */
+ cmyk[1] =
+ (is_fneg(v) ? frac_0 : v >= (float)frac_1 ? frac_1 : (frac)v);
+ v = (float)frac_1 - b / denom; /* unscaled */
+ cmyk[2] =
+ (is_fneg(v) ? frac_0 : v >= (float)frac_1 ? frac_1 : (frac)v);
+#endif
+ }
+ cmyk[3] = bg;
+ if_debug7('c', "[c]RGB 0x%x,0x%x,0x%x -> CMYK 0x%x,0x%x,0x%x,0x%x\n",
+ r, g, b, cmyk[0], cmyk[1], cmyk[2], cmyk[3]);
+}
+
+/* Convert CMYK to Gray. */
+frac
+color_cmyk_to_gray(frac c, frac m, frac y, frac k, const gs_state *pgs)
+{ frac not_gray = color_rgb_to_gray(c, m, y, pgs);
+ return (not_gray > frac_1 - k ? /* gray + k > 1.0 */
+ frac_0 : frac_1 - (not_gray + k));
+}
+
+/* Convert CMYK to RGB. */
+void
+color_cmyk_to_rgb(frac c, frac m, frac y, frac k, const gs_state *pgs,
+ frac rgb[3])
+{ switch ( k )
+ {
+ case frac_0:
+ rgb[0] = frac_1 - c;
+ rgb[1] = frac_1 - m;
+ rgb[2] = frac_1 - y;
+ break;
+ case frac_1:
+ rgb[0] = rgb[1] = rgb[2] = frac_0;
+ break;
+ default:
+ {
+#ifdef USE_ADOBE_CMYK_RGB
+ /* R = 1.0 - min(1.0, C + K), etc. */
+ frac not_k = frac_1 - k;
+ rgb[0] = (c > not_k ? frac_0 : not_k - c);
+ rgb[1] = (m > not_k ? frac_0 : not_k - m);
+ rgb[2] = (y > not_k ? frac_0 : not_k - y);
+#else
+ /* R = (1.0 - C) * (1.0 - K), etc. */
+ ulong not_k = frac_1 - k;
+ /* Compute not_k * (frac_1 - v) / frac_1 efficiently. */
+ ulong prod;
+#define deduct_black(v)\
+ (prod = (frac_1 - (v)) * not_k, frac_1_quo(prod))
+ rgb[0] = deduct_black(c);
+ rgb[1] = deduct_black(m);
+ rgb[2] = deduct_black(y);
+#undef deduct_black
+#endif
+ }
+ }
+ if_debug7('c', "[c]CMYK 0x%x,0x%x,0x%x,0x%x -> RGB 0x%x,0x%x,0x%x\n",
+ c, m, y, k, rgb[0], rgb[1], rgb[2]);
+}
diff --git a/pstoraster/gxdcconv.h b/pstoraster/gxdcconv.h
new file mode 100644
index 000000000..5007be3c6
--- /dev/null
+++ b/pstoraster/gxdcconv.h
@@ -0,0 +1,36 @@
+/* Copyright (C) 1992, 1993 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxdcconv.h */
+/* Internal device color conversion interfaces */
+#include "gxfrac.h"
+
+/* Color space conversion routines */
+frac color_rgb_to_gray(P4(frac r, frac g, frac b,
+ const gs_state *pgs));
+void color_rgb_to_cmyk(P5(frac r, frac g, frac b,
+ const gs_state *pgs, frac cmyk[4]));
+frac color_cmyk_to_gray(P5(frac c, frac m, frac y, frac k,
+ const gs_state *pgs));
+void color_cmyk_to_rgb(P6(frac c, frac m, frac y, frac k,
+ const gs_state *pgs, frac rgb[3]));
diff --git a/pstoraster/gxdcolor.c b/pstoraster/gxdcolor.c
new file mode 100644
index 000000000..cddf20289
--- /dev/null
+++ b/pstoraster/gxdcolor.c
@@ -0,0 +1,89 @@
+/* Copyright (C) 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxdcolor.c */
+/* Pure and null device color implementation */
+#include "gx.h"
+#include "gxdcolor.h"
+#include "gxdevice.h"
+
+/* Define the standard device color types. */
+private dev_color_proc_load(gx_dc_no_load);
+private dev_color_proc_fill_rectangle(gx_dc_no_fill_rectangle);
+private dev_color_proc_load(gx_dc_pure_load);
+private dev_color_proc_fill_rectangle(gx_dc_pure_fill_rectangle);
+const gx_device_color_procs
+ gx_dc_procs_none =
+ { gx_dc_no_load, gx_dc_no_fill_rectangle, 0, 0 },
+ gx_dc_procs_pure =
+ { gx_dc_pure_load, gx_dc_pure_fill_rectangle, 0, 0 };
+#undef gx_dc_type_none
+const gx_device_color_procs _ds *gx_dc_type_none = &gx_dc_procs_none;
+#define gx_dc_type_none (&gx_dc_procs_none)
+#undef gx_dc_type_pure
+const gx_device_color_procs _ds *gx_dc_type_pure = &gx_dc_procs_pure;
+#define gx_dc_type_pure (&gx_dc_procs_pure)
+
+/* Define a null RasterOp source. */
+const gx_rop_source_t gx_rop_no_source = { gx_rop_no_source_body };
+
+/* Null color */
+private int
+gx_dc_no_load(gx_device_color *pdevc, const gs_state *ignore_pgs)
+{ return 0;
+}
+private int
+gx_dc_no_fill_rectangle(const gx_device_color *pdevc, int x, int y,
+ int w, int h, gx_device *dev, gs_logical_operation_t lop,
+ const gx_rop_source_t *source)
+{ return 0;
+}
+
+/* Pure color */
+private int
+gx_dc_pure_load(gx_device_color *pdevc, const gs_state *ignore_pgs)
+{ return 0;
+}
+/* Fill a rectangle with a pure color. */
+/* Note that we treat this as "texture" for RasterOp. */
+private int
+gx_dc_pure_fill_rectangle(const gx_device_color *pdevc, int x, int y,
+ int w, int h, gx_device *dev, gs_logical_operation_t lop,
+ const gx_rop_source_t *source)
+{ if ( source == NULL && lop_no_S_is_T(lop) )
+ return (*dev_proc(dev, fill_rectangle))(dev, x, y, w, h,
+ pdevc->colors.pure);
+ { gx_color_index colors[2];
+
+ colors[0] = colors[1] = pdevc->colors.pure;
+ if ( source == NULL )
+ source = &gx_rop_no_source;
+ return (*dev_proc(dev, strip_copy_rop))(dev, source->sdata,
+ source->sourcex, source->sraster,
+ source->id,
+ (source->use_scolors ?
+ source->scolors : NULL),
+ NULL /*arbitrary*/, colors,
+ x, y, w, h, 0, 0, lop);
+ }
+}
diff --git a/pstoraster/gxdcolor.h b/pstoraster/gxdcolor.h
new file mode 100644
index 000000000..1ab1d6bc4
--- /dev/null
+++ b/pstoraster/gxdcolor.h
@@ -0,0 +1,144 @@
+/* Copyright (C) 1993, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxdcolor.h */
+/* Device color representation for Ghostscript */
+
+#ifndef gxdcolor_INCLUDED
+# define gxdcolor_INCLUDED
+
+#include "gsdcolor.h"
+#include "gsropt.h"
+#include "gsstruct.h" /* for extern_st, GC procs */
+
+/* Define opaque types. */
+
+#ifndef gx_device_DEFINED
+# define gx_device_DEFINED
+typedef struct gx_device_s gx_device;
+#endif
+
+/*
+ * Define a source structure for RasterOp.
+ */
+typedef struct gx_rop_source_s {
+ const byte *sdata;
+ int sourcex;
+ uint sraster;
+ gx_bitmap_id id;
+ gx_color_index scolors[2];
+ bool use_scolors;
+} gx_rop_source_t;
+#define gx_rop_no_source_body\
+ NULL, 0, 0, gx_no_bitmap_id, {0, 0}, true
+extern const gx_rop_source_t gx_rop_no_source;
+
+/*
+ * Device colors are 'objects' (with very few procedures). In order to
+ * simplify memory management, we use a union, but since different variants
+ * may have different pointer tracing procedures, we have to include those
+ * procedures in the type.
+ */
+
+struct gx_device_color_procs_s {
+
+ /*
+ * If necessary and possible, load the halftone or Pattern cache
+ * with the rendering of this color.
+ */
+
+#define dev_color_proc_load(proc)\
+ int proc(P2(gx_device_color *pdevc, const gs_state *pgs))
+ dev_color_proc_load((*load));
+
+ /*
+ * Fill a rectangle with the color.
+ * We pass the device separately so that pattern fills can
+ * substitute a tiled mask clipping device.
+ */
+
+#define dev_color_proc_fill_rectangle(proc)\
+ int proc(P8(const gx_device_color *pdevc, int x, int y, int w, int h,\
+ gx_device *dev, gs_logical_operation_t lop, const gx_rop_source_t *source))
+ dev_color_proc_fill_rectangle((*fill_rectangle));
+
+ /*
+ * Trace the pointers for the garbage collector.
+ */
+
+ struct_proc_enum_ptrs((*enum_ptrs));
+ struct_proc_reloc_ptrs((*reloc_ptrs));
+
+};
+
+extern_st(st_device_color);
+/* public_st_device_color() is defined in gsdcolor.h */
+
+/* Define the standard device color types. */
+/* See gsdcolor.h for details. */
+extern const gx_device_color_procs
+#define gx_dc_type_none (&gx_dc_procs_none)
+ gx_dc_procs_none, /* gxdcolor.c */
+#define gx_dc_type_pure (&gx_dc_procs_pure)
+ gx_dc_procs_pure, /* gxdcolor.c */
+/*#define gx_dc_type_pattern (&gx_dc_procs_pattern)*/
+ /*gx_dc_procs_pattern,*/ /* gspcolor.c */
+#define gx_dc_type_ht_binary (&gx_dc_procs_ht_binary)
+ gx_dc_procs_ht_binary, /* gxht.c */
+#define gx_dc_type_ht_colored (&gx_dc_procs_ht_colored)
+ gx_dc_procs_ht_colored; /* gxcht.c */
+
+#define gs_color_writes_pure(pgs)\
+ color_writes_pure((pgs)->dev_color, (pgs)->log_op)
+
+/* Set up device color 1 for writing into a mask cache */
+/* (e.g., the character cache). */
+void gx_set_device_color_1(P1(gs_state *pgs));
+
+/* Remap the color if necessary. */
+int gx_remap_color(P1(gs_state *));
+#define gx_set_dev_color(pgs)\
+ if ( !color_is_set((pgs)->dev_color) )\
+ { int code_dc = gx_remap_color(pgs);\
+ if ( code_dc != 0 ) return code_dc;\
+ }
+
+/* Indicate that the device color needs remapping. */
+#define gx_unset_dev_color(pgs)\
+ color_unset((pgs)->dev_color)
+
+/* Load the halftone cache in preparation for drawing. */
+#define gx_color_load(pdevc, pgs)\
+ (*(pdevc)->type->load)(pdevc, pgs)
+
+/* Fill a rectangle with a color. */
+#define gx_device_color_fill_rectangle(pdevc, x, y, w, h, dev, lop, source)\
+ (*(pdevc)->type->fill_rectangle)(pdevc, x, y, w, h, dev, lop, source)
+#define gx_fill_rectangle_device_rop(x, y, w, h, pdevc, dev, lop)\
+ gx_device_color_fill_rectangle(pdevc, x, y, w, h, dev, lop, NULL)
+#define gx_fill_rectangle_rop(x, y, w, h, pdevc, lop, pgs)\
+ gx_fill_rectangle_device_rop(x, y, w, h, pdevc, (pgs)->device, lop)
+#define gx_fill_rectangle(x, y, w, h, pdevc, pgs)\
+ gx_fill_rectangle_rop(x, y, w, h, pdevc, (pgs)->log_op, pgs)
+
+#endif /* gxdcolor_INCLUDED */
diff --git a/pstoraster/gxdda.h b/pstoraster/gxdda.h
new file mode 100644
index 000000000..a9cef7e72
--- /dev/null
+++ b/pstoraster/gxdda.h
@@ -0,0 +1,118 @@
+/* Copyright (C) 1994, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxdda.h */
+/* DDA definitions for Ghostscript line drawing */
+/* Requires gxfixed.h */
+
+/*
+ * We use the familiar Bresenham DDA algorithm for several purposes:
+ * - tracking the edges when filling trapezoids;
+ * - tracking the current pixel corner coordinates when rasterizing
+ * skewed or rotated images;
+ * - converting curves to sequences of lines (this is a 3rd-order
+ * DDA, the others are 1st-order);
+ * - perhaps someday for drawing single-pixel lines.
+ * In the case of trapezoids, lines, and curves, we need to use
+ * the DDA to find the integer X values at integer+0.5 values of Y;
+ * in the case of images, we use DDAs to compute the (fixed)
+ * X and Y values at (integer) source pixel corners.
+ *
+ * The purpose of the DDA is to compute the exact values Q(i) = floor(i*D/N)
+ * for increasing integers i, 0 <= i <= N. D is considered to be an
+ * integer, although it may actually be a fixed. For the algorithm,
+ * we maintain i*D/N as Q + R/N where Q and R are integers, 0 <= R < N,
+ * with the following auxiliary values:
+ * dQ = floor(D/N)
+ * dR = D mod N
+ * NdR = N - dR
+ * Then at each iteration we do:
+ * Q += dQ;
+ * if ( R < dR ) ++Q, R += NdR; else R -= dR;
+ * These formulas work regardless of the sign of D, and never let R go
+ * out of range.
+ */
+#define dda_state_struct(sname, dtype, ntype)\
+ struct sname { dtype Q; ntype R; }
+#define dda_step_struct(sname, dtype, ntype)\
+ struct sname { dtype dQ; ntype dR, NdR; }
+/* DDA with fixed Q and (unsigned) integer N */
+typedef dda_state_struct(_a, fixed, uint) gx_dda_state_fixed;
+typedef dda_step_struct(_e, fixed, uint) gx_dda_step_fixed;
+typedef struct gx_dda_fixed_s {
+ gx_dda_state_fixed state;
+ gx_dda_step_fixed step;
+} gx_dda_fixed;
+/*
+ * Initialize a DDA. The sign test is needed only because C doesn't
+ * provide reliable definitions of / and % for integers (!!!).
+ */
+#define dda_init_state(dstate, init, N)\
+ (dstate).Q = (init), (dstate).R = (N)
+#define dda_init_step(dstep, D, N)\
+ if ( (N) == 0 )\
+ (dstep).dQ = 0, (dstep).dR = 0;\
+ else if ( (D) < 0 )\
+ { (dstep).dQ = -(-(D) / (N));\
+ if ( ((dstep).dR = -(D) % (N)) != 0 )\
+ --(dstep).dQ, (dstep).dR = (N) - (dstep).dR;\
+ }\
+ else\
+ { (dstep).dQ = (D) / (N); (dstep).dR = (D) % (N); }\
+ (dstep).NdR = (N) - (dstep).dR
+#define dda_init(dda, init, D, N)\
+ dda_init_state((dda).state, init, N);\
+ dda_init_step((dda).step, D, N)
+/*
+ * Compute the sum of two DDA steps with the same D and N.
+ */
+#define dda_step_add(tostep, fromstep)\
+ (tostep).dQ +=\
+ ((tostep).dR < (fromstep).NdR ?\
+ ((tostep).dR += (fromstep).dR, (fromstep).dQ) :\
+ ((tostep).dR -= (fromstep).NdR, (fromstep).dQ + 1))
+/*
+ * Return the current value in a DDA.
+ */
+#define dda_state_current(dstate) (dstate).Q
+#define dda_current(dda) dda_state_current((dda).state)
+/*
+ * Increment a DDA to the next point.
+ * Returns the updated current value.
+ */
+#define dda_state_next(dstate, dstep)\
+ (dstate).Q +=\
+ ((dstate).R >= (dstep).dR ?\
+ ((dstate).R -= (dstep).dR, (dstep).dQ) :\
+ ((dstate).R += (dstep).NdR, (dstep).dQ + 1))
+#define dda_next(dda) dda_state_next((dda).state, (dda).step)
+/*
+ * Back up a DDA to the previous point.
+ * Returns the updated current value.
+ */
+#define dda_state_previous(dstate, dstep)\
+ (dstate).Q -=\
+ ((dstate).R < (dstep).NdR ?\
+ ((dstate).R += (dstep).dR, (dstep).dQ) :\
+ ((dstate).R -= (dstep).NdR, (dstep).dQ + 1))
+#define dda_previous(dda) dda_state_previous((dda).state, (dda).step)
diff --git a/pstoraster/gxdevice.h b/pstoraster/gxdevice.h
new file mode 100644
index 000000000..168868e2c
--- /dev/null
+++ b/pstoraster/gxdevice.h
@@ -0,0 +1,950 @@
+/* Copyright (C) 1989, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxdevice.h */
+/* Device description structure */
+
+#ifndef gxdevice_INCLUDED
+# define gxdevice_INCLUDED
+
+#include "gsdcolor.h"
+#include "gsmatrix.h"
+#include "gsiparam.h" /* requires gsmatrix.h */
+#include "gsropt.h"
+#include "gsstruct.h"
+#include "gsxfont.h"
+#include "gxbitmap.h"
+#include "gxcindex.h"
+#include "gxcvalue.h"
+#include "gxfixed.h"
+
+/* See drivers.doc for documentation of the driver interface. */
+
+#ifndef gx_device_DEFINED
+# define gx_device_DEFINED
+typedef struct gx_device_s gx_device;
+#endif
+
+/* We need at least an abstract type for a graphics state, */
+/* which is passed to the page device procedures. */
+#ifndef gs_state_DEFINED
+# define gs_state_DEFINED
+typedef struct gs_state_s gs_state;
+#endif
+
+/* We need abstract types for paths and fill/stroke parameters, */
+/* for the path-oriented device procedures. */
+#ifndef gx_path_DEFINED
+# define gx_path_DEFINED
+typedef struct gx_path_s gx_path;
+#endif
+#ifndef gx_clip_path_DEFINED
+# define gx_clip_path_DEFINED
+typedef struct gx_clip_path_s gx_clip_path;
+#endif
+#ifndef gx_fill_params_DEFINED
+# define gx_fill_params_DEFINED
+typedef struct gx_fill_params_s gx_fill_params;
+#endif
+#ifndef gx_stroke_params_DEFINED
+# define gx_stroke_params_DEFINED
+typedef struct gx_stroke_params_s gx_stroke_params;
+#endif
+#ifndef gs_imager_state_DEFINED
+# define gs_imager_state_DEFINED
+typedef struct gs_imager_state_s gs_imager_state;
+#endif
+
+/* Define the type for colors passed to the higher-level procedures. */
+typedef gx_device_color gx_drawing_color;
+
+/* Define a type for telling get_alpha_bits what kind of object */
+/* is being rendered. */
+typedef enum {
+ go_text,
+ go_graphics
+} graphics_object_type;
+
+/* Define an edge of a trapezoid. Requirement: end.y >= start.y. */
+typedef struct gs_fixed_edge_s {
+ gs_fixed_point start;
+ gs_fixed_point end;
+} gs_fixed_edge;
+
+/* Define the structure for device color capabilities. */
+typedef struct gx_device_color_info_s {
+ int num_components; /* 1 = gray only, 3 = RGB, */
+ /* 4 = CMYK */
+ int depth; /* # of bits per pixel */
+ gx_color_value max_gray; /* # of distinct gray levels -1 */
+ gx_color_value max_color; /* # of distinct color levels -1 */
+ /* (only relevant if num_comp. > 1) */
+ gx_color_value dither_grays; /* size of gray ramp for dithering */
+ gx_color_value dither_colors; /* size of color cube ditto */
+ /* (only relevant if num_comp. > 1) */
+} gx_device_color_info;
+#define dci_values(nc,depth,mg,mc,dg,dc) { nc, depth, mg, mc, dg, dc }
+#define dci_std_color(color_bits)\
+ dci_values(\
+ (color_bits == 32 ? 4 : color_bits > 1 ? 3 : 1),\
+ ((color_bits > 1) & (color_bits < 8) ? 8 : color_bits),\
+ (color_bits >= 8 ? 255 : 1),\
+ (color_bits >= 8 ? 255 : color_bits > 1 ? 1 : 0),\
+ (color_bits >= 8 ? 5 : 2),\
+ (color_bits >= 8 ? 5 : color_bits > 1 ? 2 : 0)\
+ )
+#define dci_black_and_white dci_std_color(1)
+#define dci_black_and_white_() dci_black_and_white
+#define dci_color(depth,maxv,dither)\
+ dci_values(3, depth, maxv, maxv, dither, dither)
+#define gx_device_has_color(dev) ((dev)->color_info.num_components > 1)
+
+/* Structure for device procedures. */
+typedef struct gx_device_procs_s gx_device_procs;
+
+/* Structure for page device procedures. */
+/* Note that these take the graphics state as a parameter. */
+typedef struct gx_page_device_procs_s {
+
+#define dev_page_proc_install(proc)\
+ int proc(P2(gx_device *dev, gs_state *pgs))
+ dev_page_proc_install((*install));
+
+#define dev_page_proc_begin_page(proc)\
+ int proc(P2(gx_device *dev, gs_state *pgs))
+ dev_page_proc_begin_page((*begin_page));
+
+#define dev_page_proc_end_page(proc)\
+ int proc(P3(gx_device *dev, int reason, gs_state *pgs))
+ dev_page_proc_end_page((*end_page));
+
+} gx_page_device_procs;
+/* Default procedures */
+dev_page_proc_install(gx_default_install);
+dev_page_proc_begin_page(gx_default_begin_page);
+dev_page_proc_end_page(gx_default_end_page);
+
+/* ---------------- Device structure ---------------- */
+
+/*
+ * Define the generic device structure. The device procedures can
+ * have two different configurations:
+ *
+ * - Statically initialized devices predating release 2.8.1
+ * set the static_procs pointer to point to a separate procedure record,
+ * and do not initialize std_procs.
+ *
+ * - Statically initialized devices starting with release 2.8.1,
+ * and all dynamically created device instances,
+ * set the static_procs pointer to 0, and initialize std_procs.
+ *
+ * The gx_device_set_procs procedure converts the first of these to
+ * the second, which is what all client code starting in 2.8.1 expects
+ * (using the std_procs record, not the static_procs pointer, to call the
+ * driver procedures).
+ *
+ * The choice of the name Margins (rather than, say, HWOffset), and the
+ * specification in terms of a default device resolution rather than
+ * 1/72" units, are due to Adobe.
+ *
+ * ****** NOTE: If you define any subclasses of gx_device, you *must* define
+ * ****** the finalization procedure as gx_device_finalize. Finalization
+ * ****** procedures are not automatically inherited.
+ */
+#define gx_device_common\
+ int params_size; /* OBSOLETE if stype != 0: */\
+ /* size of this structure */\
+ const gx_device_procs *static_procs; /* OBSOLETE */\
+ /* pointer to std_procs */\
+ const char *dname; /* the device name */\
+ gs_memory_t *memory; /* (0 iff static prototype) */\
+ gs_memory_type_ptr_t stype; /* memory manager structure type, */\
+ /* 0 iff static prototype */\
+ bool is_open; /* true if device has been opened */\
+ int max_fill_band; /* limit on band size for fill, */\
+ /* must be 0 or a power of 2 */\
+ /* (see gdevabuf.c for more info) */\
+ gx_device_color_info color_info; /* color information */\
+ int width; /* width in pixels */\
+ int height; /* height in pixels */\
+ float MediaSize[2]; /* media dimensions in points */\
+ float ImagingBBox[4]; /* imageable region in points */\
+ bool ImagingBBox_set;\
+ float HWResolution[2]; /* resolution, dots per inch */\
+ float MarginsHWResolution[2]; /* resolution for Margins */\
+ float Margins[2]; /* offset of physical page corner */\
+ /* from device coordinate (0,0), */\
+ /* in units given by MarginsHWResolution */\
+ float HWMargins[4]; /* margins around imageable area, */\
+ /* in default user units ("points") */\
+ long PageCount; /* number of pages written */\
+ long ShowpageCount; /* number of calls on showpage */\
+ bool IgnoreNumCopies; /* if true, force num_copies = 1 */\
+ gx_page_device_procs page_procs; /* must be last */\
+ /* end of std_device_body */\
+ gx_device_procs std_procs /* standard procedures */
+#define x_pixels_per_inch HWResolution[0]
+#define y_pixels_per_inch HWResolution[1]
+#define offset_margin_values(x, y, left, bot, right, top)\
+ {x, y}, {left, bot, right, top}
+#define margin_values(left, bot, right, top)\
+ offset_margin_values(0, 0, left, bot, right, top)
+#define no_margins margin_values(0, 0, 0, 0)
+#define no_margins_() no_margins
+/* Define macros that give the page offset ("Margins") in inches. */
+#define dev_x_offset(dev) ((dev)->Margins[0] / (dev)->MarginsHWResolution[0])
+#define dev_y_offset(dev) ((dev)->Margins[1] / (dev)->MarginsHWResolution[1])
+#define dev_y_offset_points(dev) (dev_y_offset(dev) * 72.0)
+/* Note that left/right/top/bottom are defined relative to */
+/* the physical paper, not the coordinate system. */
+/* For backward compatibility, we define macros that give */
+/* the margins in inches. */
+#define dev_l_margin(dev) ((dev)->HWMargins[0] / 72.0)
+#define dev_b_margin(dev) ((dev)->HWMargins[1] / 72.0)
+#define dev_b_margin_points(dev) ((dev)->HWMargins[1])
+#define dev_r_margin(dev) ((dev)->HWMargins[2] / 72.0)
+#define dev_t_margin(dev) ((dev)->HWMargins[3] / 72.0)
+#define dev_t_margin_points(dev) ((dev)->HWMargins[3])
+/* The extra () are to prevent premature expansion. */
+#define open_init_closed() 0 /*false*/, 0 /* max_fill_band */
+#define open_init_open() 1 /*true*/, 0 /* max_fill_band */
+/* Accessors for device procedures */
+#define dev_proc(dev, p) ((dev)->std_procs.p)
+#define set_dev_proc(dev, p, proc) ((dev)->std_procs.p = (proc))
+#define fill_dev_proc(dev, p, dproc)\
+ if ( dev_proc(dev, p) == 0 ) set_dev_proc(dev, p, dproc)
+
+/*
+ * To insulate statically defined device templates from the
+ * consequences of changes in the device structure, the following macros
+ * must be used for generating initialized device structures.
+ *
+ * The computations of page width and height in pixels should really be
+ * ((int)(page_width_inches*x_dpi))
+ * but some compilers (the Ultrix 3.X pcc compiler and the HPUX compiler)
+ * can't cast a computed float to an int. That's why we specify
+ * the page width and height in inches/10 instead of inches.
+ *
+ * Note that the macro is broken up so as to be usable for devices that
+ * add further initialized state to the generic device.
+ * Note also that the macro does not initialize std_procs, which is
+ * the next element of the structure.
+ */
+#define std_device_part1_(devtype, ptr_procs, dev_name, stype, open_init)\
+ sizeof(devtype), ptr_procs, dev_name,\
+ 0 /*memory*/, stype, open_init() /*stype, is_open, cached*/
+/* color_info goes here */
+#define std_device_part2_(width, height, x_dpi, y_dpi)\
+ width, height,\
+ { (width) * 72.0 / (x_dpi), (height) * 72.0 / (y_dpi) },\
+ { 0, 0, 0, 0 }, 0/*false*/, { x_dpi, y_dpi }, { x_dpi, y_dpi }
+/* offsets and margins go here */
+#define std_device_part3_()\
+ 0, 0, 0/*false*/,\
+ { gx_default_install, gx_default_begin_page, gx_default_end_page }
+/*
+ * We need a number of different variants of the std_device_ macro simply
+ * because we can't pass the color_info or offsets/margins
+ * as macro arguments, which in turn is because of the early macro
+ * expansion issue noted in stdpre.h. The basic variants are:
+ * ...body_with_macros_, which uses 0-argument macros to supply
+ * open_init, color_info, and offsets/margins;
+ * ...full_body, which takes 12 values (6 for dci_values,
+ * 6 for offsets/margins);
+ * ...color_full_body, which takes 9 values (3 for dci_color,
+ * 6 for margins/offset).
+ * ...std_color_full_body, which takes 7 values (1 for dci_std_color,
+ * 6 for margins/offset).
+ *
+ */
+#define std_device_body_with_macros_(dtype, pprocs, dname, w, h, xdpi, ydpi, stype, open_init, dci_macro, margins_macro)\
+ std_device_part1_(dtype, pprocs, dname, stype, open_init),\
+ dci_macro(),\
+ std_device_part2_(w, h, xdpi, ydpi),\
+ margins_macro(),\
+ std_device_part3_()
+
+#define std_device_std_body(dtype, pprocs, dname, w, h, xdpi, ydpi)\
+ std_device_body_with_macros_(dtype, pprocs, dname,\
+ w, h, xdpi, ydpi,\
+ 0, open_init_closed, dci_black_and_white_, no_margins_)
+
+#define std_device_std_body_open(dtype, pprocs, dname, w, h, xdpi, ydpi)\
+ std_device_body_with_macros_(dtype, pprocs, dname,\
+ w, h, xdpi, ydpi,\
+ 0, open_init_open, dci_black_and_white_, no_margins_)
+
+#define std_device_full_body(dtype, pprocs, dname, w, h, xdpi, ydpi, ncomp, depth, mg, mc, dg, dc, xoff, yoff, lm, bm, rm, tm)\
+ std_device_part1_(dtype, pprocs, dname, 0, open_init_closed),\
+ dci_values(ncomp, depth, mg, mc, dg, dc),\
+ std_device_part2_(w, h, xdpi, ydpi),\
+ offset_margin_values(xoff, yoff, lm, bm, rm, tm),\
+ std_device_part3_()
+
+#define std_device_dci_body(dtype, pprocs, dname, w, h, xdpi, ydpi, ncomp, depth, mg, mc, dg, dc)\
+ std_device_full_body(dtype, pprocs, dname,\
+ w, h, xdpi, ydpi,\
+ ncomp, depth, mg, mc, dg, dc,\
+ 0, 0, 0, 0, 0, 0)
+
+#define std_device_color_full_body(dtype, pprocs, dname, w, h, xdpi, ydpi, depth, max_value, dither, xoff, yoff, lm, bm, rm, tm)\
+ std_device_part1_(dtype, pprocs, dname, 0, open_init_closed),\
+ dci_color(depth, max_value, dither),\
+ std_device_part2_(w, h, xdpi, ydpi),\
+ offset_margin_values(xoff, yoff, lm, bm, rm, tm),\
+ std_device_part3_()
+
+#define std_device_color_body(dtype, pprocs, dname, w, h, xdpi, ydpi, depth, max_value, dither)\
+ std_device_color_full_body(dtype, pprocs, dname,\
+ w, h, xdpi, ydpi,\
+ depth, max_value, dither,\
+ 0, 0, 0, 0, 0, 0)
+
+#define std_device_color_stype_body(dtype, pprocs, dname, stype, w, h, xdpi, ydpi, depth, max_value, dither)\
+ std_device_part1_(dtype, pprocs, dname, stype, open_init_closed),\
+ dci_color(depth, max_value, dither),\
+ std_device_part2_(w, h, xdpi, ydpi),\
+ offset_margin_values(0, 0, 0, 0, 0, 0),\
+ std_device_part3_()
+
+#define std_device_std_color_full_body(dtype, pprocs, dname, w, h, xdpi, ydpi, depth, xoff, yoff, lm, bm, rm, tm)\
+ std_device_part1_(dtype, pprocs, dname, 0, open_init_closed),\
+ dci_std_color(depth),\
+ std_device_part2_(w, h, xdpi, ydpi),\
+ offset_margin_values(xoff, yoff, lm, bm, rm, tm),\
+ std_device_part3_()
+
+/* ---------------- Device procedures ---------------- */
+
+/* Define an opaque type for parameter lists. */
+#ifndef gs_param_list_DEFINED
+# define gs_param_list_DEFINED
+typedef struct gs_param_list_s gs_param_list;
+#endif
+
+/*
+ * Definition of device procedures.
+ * Note that the gx_device * argument is not declared const,
+ * because many drivers maintain dynamic state in the device structure.
+ * Note also that the structure is defined as a template, so that
+ * we can instantiate it with device subclasses.
+ * Because C doesn't have real templates, we must do this with macros.
+ */
+
+/* Define macros for declaring device procedures. */
+
+#define dev_t_proc_open_device(proc, dev_t)\
+ int proc(P1(dev_t *dev))
+#define dev_proc_open_device(proc)\
+ dev_t_proc_open_device(proc, gx_device)
+
+#define dev_t_proc_get_initial_matrix(proc, dev_t)\
+ void proc(P2(dev_t *dev, gs_matrix *pmat))
+#define dev_proc_get_initial_matrix(proc)\
+ dev_t_proc_get_initial_matrix(proc, gx_device)
+
+#define dev_t_proc_sync_output(proc, dev_t)\
+ int proc(P1(dev_t *dev))
+#define dev_proc_sync_output(proc)\
+ dev_t_proc_sync_output(proc, gx_device)
+
+#define dev_t_proc_output_page(proc, dev_t)\
+ int proc(P3(dev_t *dev, int num_copies, int flush))
+#define dev_proc_output_page(proc)\
+ dev_t_proc_output_page(proc, gx_device)
+
+#define dev_t_proc_close_device(proc, dev_t)\
+ int proc(P1(dev_t *dev))
+#define dev_proc_close_device(proc)\
+ dev_t_proc_close_device(proc, gx_device)
+
+#define dev_t_proc_map_rgb_color(proc, dev_t)\
+ gx_color_index proc(P4(dev_t *dev,\
+ gx_color_value red, gx_color_value green, gx_color_value blue))
+#define dev_proc_map_rgb_color(proc)\
+ dev_t_proc_map_rgb_color(proc, gx_device)
+
+#define dev_t_proc_map_color_rgb(proc, dev_t)\
+ int proc(P3(dev_t *dev,\
+ gx_color_index color, gx_color_value rgb[3]))
+#define dev_proc_map_color_rgb(proc)\
+ dev_t_proc_map_color_rgb(proc, gx_device)
+
+#define dev_t_proc_fill_rectangle(proc, dev_t)\
+ int proc(P6(dev_t *dev,\
+ int x, int y, int width, int height, gx_color_index color))
+#define dev_proc_fill_rectangle(proc)\
+ dev_t_proc_fill_rectangle(proc, gx_device)
+
+#define dev_t_proc_tile_rectangle(proc, dev_t)\
+ int proc(P10(dev_t *dev,\
+ const gx_tile_bitmap *tile, int x, int y, int width, int height,\
+ gx_color_index color0, gx_color_index color1,\
+ int phase_x, int phase_y))
+#define dev_proc_tile_rectangle(proc)\
+ dev_t_proc_tile_rectangle(proc, gx_device)
+
+#define dev_t_proc_copy_mono(proc, dev_t)\
+ int proc(P11(dev_t *dev,\
+ const byte *data, int data_x, int raster, gx_bitmap_id id,\
+ int x, int y, int width, int height,\
+ gx_color_index color0, gx_color_index color1))
+#define dev_proc_copy_mono(proc)\
+ dev_t_proc_copy_mono(proc, gx_device)
+
+#define dev_t_proc_copy_color(proc, dev_t)\
+ int proc(P9(dev_t *dev,\
+ const byte *data, int data_x, int raster, gx_bitmap_id id,\
+ int x, int y, int width, int height))
+#define dev_proc_copy_color(proc)\
+ dev_t_proc_copy_color(proc, gx_device)
+
+ /* OBSOLETED in release 3.66 */
+
+#define dev_t_proc_draw_line(proc, dev_t)\
+ int proc(P6(dev_t *dev,\
+ int x0, int y0, int x1, int y1, gx_color_index color))
+#define dev_proc_draw_line(proc)\
+ dev_t_proc_draw_line(proc, gx_device)
+
+ /* Added in release 2.4 */
+
+#define dev_t_proc_get_bits(proc, dev_t)\
+ int proc(P4(dev_t *dev,\
+ int y, byte *data, byte **actual_data))
+#define dev_proc_get_bits(proc)\
+ dev_t_proc_get_bits(proc, gx_device)
+
+ /* Added in release 2.4, changed in 2.8, */
+ /* renamed in 2.9.6 */
+
+#define dev_t_proc_get_params(proc, dev_t)\
+ int proc(P2(dev_t *dev, gs_param_list *plist))
+#define dev_proc_get_params(proc)\
+ dev_t_proc_get_params(proc, gx_device)
+
+#define dev_t_proc_put_params(proc, dev_t)\
+ int proc(P2(dev_t *dev, gs_param_list *plist))
+#define dev_proc_put_params(proc)\
+ dev_t_proc_put_params(proc, gx_device)
+
+ /* Added in release 2.6 */
+
+#define dev_t_proc_map_cmyk_color(proc, dev_t)\
+ gx_color_index proc(P5(dev_t *dev,\
+ gx_color_value cyan, gx_color_value magenta, gx_color_value yellow,\
+ gx_color_value black))
+#define dev_proc_map_cmyk_color(proc)\
+ dev_t_proc_map_cmyk_color(proc, gx_device)
+
+#define dev_t_proc_get_xfont_procs(proc, dev_t)\
+ gx_xfont_procs *proc(P1(dev_t *dev))
+#define dev_proc_get_xfont_procs(proc)\
+ dev_t_proc_get_xfont_procs(proc, gx_device)
+
+ /* Added in release 2.6.1 */
+
+#define dev_t_proc_get_xfont_device(proc, dev_t)\
+ gx_device *proc(P1(dev_t *dev))
+#define dev_proc_get_xfont_device(proc)\
+ dev_t_proc_get_xfont_device(proc, gx_device)
+
+ /* Added in release 2.7.1 */
+
+#define dev_t_proc_map_rgb_alpha_color(proc, dev_t)\
+ gx_color_index proc(P5(dev_t *dev,\
+ gx_color_value red, gx_color_value green, gx_color_value blue,\
+ gx_color_value alpha))
+#define dev_proc_map_rgb_alpha_color(proc)\
+ dev_t_proc_map_rgb_alpha_color(proc, gx_device)
+
+ /* Added in release 2.8.1 */
+
+#define dev_t_proc_get_page_device(proc, dev_t)\
+ gx_device *proc(P1(dev_t *dev))
+#define dev_proc_get_page_device(proc)\
+ dev_t_proc_get_page_device(proc, gx_device)
+
+ /* Added in release 3.20 */
+
+#define dev_t_proc_get_alpha_bits(proc, dev_t)\
+ int proc(P2(dev_t *dev, graphics_object_type type))
+#define dev_proc_get_alpha_bits(proc)\
+ dev_t_proc_get_alpha_bits(proc, gx_device)
+
+#define dev_t_proc_copy_alpha(proc, dev_t)\
+ int proc(P11(dev_t *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))
+#define dev_proc_copy_alpha(proc)\
+ dev_t_proc_copy_alpha(proc, gx_device)
+
+ /* Added in release 3.38 */
+
+#define dev_t_proc_get_band(proc, dev_t)\
+ int proc(P3(dev_t *dev, int y, int *band_start))
+#define dev_proc_get_band(proc)\
+ dev_t_proc_get_band(proc, gx_device)
+
+ /* Added in release 3.44 */
+
+#define dev_t_proc_copy_rop(proc, dev_t)\
+ int proc(P15(dev_t *dev,\
+ const byte *sdata, int sourcex, uint sraster, gx_bitmap_id id,\
+ const gx_color_index *scolors,\
+ const gx_tile_bitmap *texture, const gx_color_index *tcolors,\
+ int x, int y, int width, int height,\
+ int phase_x, int phase_y, gs_logical_operation_t lop))
+#define dev_proc_copy_rop(proc)\
+ dev_t_proc_copy_rop(proc, gx_device)
+
+ /* Added in release 3.60, changed in release 3.68. */
+
+#define dev_t_proc_fill_path(proc, dev_t)\
+ int proc(P6(dev_t *dev,\
+ const gs_imager_state *pis, gx_path *ppath,\
+ const gx_fill_params *params,\
+ const gx_drawing_color *pdcolor, const gx_clip_path *pcpath))
+#define dev_proc_fill_path(proc)\
+ dev_t_proc_fill_path(proc, gx_device)
+
+#define dev_t_proc_stroke_path(proc, dev_t)\
+ int proc(P6(dev_t *dev,\
+ const gs_imager_state *pis, gx_path *ppath,\
+ const gx_stroke_params *params,\
+ const gx_drawing_color *pdcolor, const gx_clip_path *pcpath))
+#define dev_proc_stroke_path(proc)\
+ dev_t_proc_stroke_path(proc, gx_device)
+
+ /* Added in release 3.60 */
+
+#define dev_t_proc_fill_mask(proc, dev_t)\
+ int proc(P13(dev_t *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))
+#define dev_proc_fill_mask(proc)\
+ dev_t_proc_fill_mask(proc, gx_device)
+
+ /* Added in release 3.66, changed in 3.69 */
+
+#define dev_t_proc_fill_trapezoid(proc, dev_t)\
+ int proc(P8(dev_t *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))
+#define dev_proc_fill_trapezoid(proc)\
+ dev_t_proc_fill_trapezoid(proc, gx_device)
+
+#define dev_t_proc_fill_parallelogram(proc, dev_t)\
+ int proc(P9(dev_t *dev,\
+ fixed px, fixed py, fixed ax, fixed ay, fixed bx, fixed by,\
+ const gx_drawing_color *pdcolor, gs_logical_operation_t lop))
+#define dev_proc_fill_parallelogram(proc)\
+ dev_t_proc_fill_parallelogram(proc, gx_device)
+
+#define dev_t_proc_fill_triangle(proc, dev_t)\
+ int proc(P9(dev_t *dev,\
+ fixed px, fixed py, fixed ax, fixed ay, fixed bx, fixed by,\
+ const gx_drawing_color *pdcolor, gs_logical_operation_t lop))
+#define dev_proc_fill_triangle(proc)\
+ dev_t_proc_fill_triangle(proc, gx_device)
+
+#define dev_t_proc_draw_thin_line(proc, dev_t)\
+ int proc(P7(dev_t *dev,\
+ fixed fx0, fixed fy0, fixed fx1, fixed fy1,\
+ const gx_drawing_color *pdcolor, gs_logical_operation_t lop))
+#define dev_proc_draw_thin_line(proc)\
+ dev_t_proc_draw_thin_line(proc, gx_device)
+
+ /* Added in release 3.66 (as stubs), */
+ /* changed in 3.68 */
+
+#define dev_t_proc_begin_image(proc, dev_t)\
+ int proc(P9(dev_t *dev,\
+ const gs_imager_state *pis, const gs_image_t *pim,\
+ gs_image_format_t format, gs_image_shape_t shape,\
+ const gx_drawing_color *pdcolor, const gx_clip_path *pcpath,\
+ gs_memory_t *memory, void **pinfo))
+#define dev_proc_begin_image(proc)\
+ dev_t_proc_begin_image(proc, gx_device)
+
+#define dev_t_proc_image_data(proc, dev_t)\
+ int proc(P8(dev_t *dev,\
+ void *info, const byte **planes, uint raster,\
+ int x, int y, int width, int height))
+#define dev_proc_image_data(proc)\
+ dev_t_proc_image_data(proc, gx_device)
+
+#define dev_t_proc_end_image(proc, dev_t)\
+ int proc(P3(dev_t *dev,\
+ void *info, bool draw_last))
+#define dev_proc_end_image(proc)\
+ dev_t_proc_end_image(proc, gx_device)
+
+ /* Added in release 3.68 */
+
+#define dev_t_proc_strip_tile_rectangle(proc, dev_t)\
+ int proc(P10(dev_t *dev,\
+ const gx_strip_bitmap *tiles, int x, int y, int width, int height,\
+ gx_color_index color0, gx_color_index color1,\
+ int phase_x, int phase_y))
+#define dev_proc_strip_tile_rectangle(proc)\
+ dev_t_proc_strip_tile_rectangle(proc, gx_device)
+
+#define dev_t_proc_strip_copy_rop(proc, dev_t)\
+ int proc(P15(dev_t *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))
+#define dev_proc_strip_copy_rop(proc)\
+ dev_t_proc_strip_copy_rop(proc, gx_device)
+
+/* Define the device procedure vector template proper. */
+
+#define gx_device_proc_struct(dev_t)\
+{ dev_t_proc_open_device((*open_device), dev_t);\
+ dev_t_proc_get_initial_matrix((*get_initial_matrix), dev_t);\
+ dev_t_proc_sync_output((*sync_output), dev_t);\
+ dev_t_proc_output_page((*output_page), dev_t);\
+ dev_t_proc_close_device((*close_device), dev_t);\
+ dev_t_proc_map_rgb_color((*map_rgb_color), dev_t);\
+ dev_t_proc_map_color_rgb((*map_color_rgb), dev_t);\
+ dev_t_proc_fill_rectangle((*fill_rectangle), dev_t);\
+ dev_t_proc_tile_rectangle((*tile_rectangle), dev_t);\
+ dev_t_proc_copy_mono((*copy_mono), dev_t);\
+ dev_t_proc_copy_color((*copy_color), dev_t);\
+ dev_t_proc_draw_line((*draw_line), dev_t);\
+ dev_t_proc_get_bits((*get_bits), dev_t);\
+ dev_t_proc_get_params((*get_params), dev_t);\
+ dev_t_proc_put_params((*put_params), dev_t);\
+ dev_t_proc_map_cmyk_color((*map_cmyk_color), dev_t);\
+ dev_t_proc_get_xfont_procs((*get_xfont_procs), dev_t);\
+ dev_t_proc_get_xfont_device((*get_xfont_device), dev_t);\
+ dev_t_proc_map_rgb_alpha_color((*map_rgb_alpha_color), dev_t);\
+ dev_t_proc_get_page_device((*get_page_device), dev_t);\
+ dev_t_proc_get_alpha_bits((*get_alpha_bits), dev_t);\
+ dev_t_proc_copy_alpha((*copy_alpha), dev_t);\
+ dev_t_proc_get_band((*get_band), dev_t);\
+ dev_t_proc_copy_rop((*copy_rop), dev_t);\
+ dev_t_proc_fill_path((*fill_path), dev_t);\
+ dev_t_proc_stroke_path((*stroke_path), dev_t);\
+ dev_t_proc_fill_mask((*fill_mask), dev_t);\
+ dev_t_proc_fill_trapezoid((*fill_trapezoid), dev_t);\
+ dev_t_proc_fill_parallelogram((*fill_parallelogram), dev_t);\
+ dev_t_proc_fill_triangle((*fill_triangle), dev_t);\
+ dev_t_proc_draw_thin_line((*draw_thin_line), dev_t);\
+ dev_t_proc_begin_image((*begin_image), dev_t);\
+ dev_t_proc_image_data((*image_data), dev_t);\
+ dev_t_proc_end_image((*end_image), dev_t);\
+ dev_t_proc_strip_tile_rectangle((*strip_tile_rectangle), dev_t);\
+ dev_t_proc_strip_copy_rop((*strip_copy_rop), dev_t);\
+}
+
+/* A generic device procedure record. */
+struct gx_device_procs_s gx_device_proc_struct(gx_device);
+
+/*
+ * Define a procedure for setting up a memory device for buffering output
+ * for a given device. This is only used by band devices, but we define it
+ * here for convenience. The default implementation just calls
+ * gs_make_mem_device. Possibly this should be a generic device
+ * procedure....
+ */
+#ifndef gx_device_memory_DEFINED
+# define gx_device_memory_DEFINED
+typedef struct gx_device_memory_s gx_device_memory;
+#endif
+#define dev_proc_make_buffer_device(proc)\
+ int proc(P4(gx_device_memory *, gx_device *, gs_memory_t *, bool))
+dev_proc_make_buffer_device(gx_default_make_buffer_device);
+
+/*
+ * Define unaligned analogues of the copy_xxx procedures.
+ * These are slower than the standard procedures, which require
+ * aligned bitmaps, and also are not portable to non-byte-addressed machines.
+ *
+ * We allow both unaligned data and unaligned scan line widths;
+ * however, we do require that both of these be aligned modulo the largest
+ * power of 2 bytes that divides the data depth, i.e.:
+ * depth alignment
+ * <= 8 1
+ * 16 2
+ * 24 1
+ * 32 4
+ */
+dev_proc_copy_mono(gx_copy_mono_unaligned);
+dev_proc_copy_color(gx_copy_color_unaligned);
+dev_proc_copy_alpha(gx_copy_alpha_unaligned);
+
+/* A generic device */
+struct gx_device_s {
+ gx_device_common;
+};
+extern_st(st_device);
+struct_proc_finalize(gx_device_finalize); /* public for subclasses */
+#define public_st_device() /* in gsdevice.c */\
+ gs_public_st_complex_only(st_device, gx_device, "gx_device",\
+ 0, 0, 0, gx_device_finalize)
+#define st_device_max_ptrs 0
+
+/* Enumerate or relocate a pointer to a device. */
+/* These take the containing space into account properly. */
+gx_device *gx_device_enum_ptr(P1(gx_device *));
+gx_device *gx_device_reloc_ptr(P2(gx_device *, gc_state_t *));
+
+/* Define typedefs for some of the device procedures, because */
+/* ansi2knr can't handle dev_proc_xxx((*xxx)) in a formal argument list. */
+typedef dev_proc_map_rgb_color((*dev_proc_map_rgb_color_t));
+typedef dev_proc_map_color_rgb((*dev_proc_map_color_rgb_t));
+
+/* A device that forwards non-display operations to another device */
+/* called the "target". This is used for clipping, banding, image, */
+/* and null devices. */
+#define gx_device_forward_common\
+ gx_device_common;\
+ gx_device *target
+/* A generic forwarding device. */
+typedef struct gx_device_forward_s {
+ gx_device_forward_common;
+} gx_device_forward;
+extern_st(st_device_forward);
+#define public_st_device_forward() /* in gsdevice.c */\
+ gs_public_st_complex_only(st_device_forward, gx_device_forward,\
+ "gx_device_forward", 0, device_forward_enum_ptrs,\
+ device_forward_reloc_ptrs, gx_device_finalize)
+#define st_device_forward_max_ptrs (st_device_max_ptrs + 1)
+
+/* A null device. This is used to temporarily disable output. */
+#ifndef gx_device_null_DEFINED
+# define gx_device_null_DEFINED
+typedef struct gx_device_null_s gx_device_null;
+#endif
+struct gx_device_null_s {
+ gx_device_forward_common;
+};
+extern gx_device_null far_data gs_null_device; /* (should be const) */
+extern_st(st_device_null);
+#define public_st_device_null() /* in gsdevice.c */\
+ gs_public_st_complex_only(st_device_null, gx_device_null,\
+ "gx_device_null", 0, device_forward_enum_ptrs,\
+ device_forward_reloc_ptrs, gx_device_finalize)
+#define st_device_null_max_ptrs st_device_forward_max_ptrs
+/* Make a null device. */
+/* The gs_memory_t argument is 0 if the device is temporary and local, */
+/* or the allocator that was used to allocate it if it is a real object. */
+void gs_make_null_device(P2(gx_device_null *, gs_memory_t *));
+
+/* Calculate the raster (number of bytes in a scan line), */
+/* with byte or word padding. */
+uint gx_device_raster(P2(const gx_device *dev, bool pad_to_word));
+
+/* Adjust the resolution for devices that only have a fixed set of */
+/* geometries, so that the apparent size in inches remains constant. */
+/* If fit=1, the resolution is adjusted so that the entire image fits; */
+/* if fit=0, one dimension fits, but the other one is clipped. */
+int gx_device_adjust_resolution(P4(gx_device *dev, int actual_width, int actual_height, int fit));
+
+/* Set the HWMargins to values defined in inches. */
+/* If move_origin is true, also reset the Margins. */
+void gx_device_set_margins(P3(gx_device *dev, const float *margins /*[4]*/,
+ bool move_origin));
+
+/* Set the width and height (in pixels), updating MediaSize. */
+void gx_device_set_width_height(P3(gx_device *dev, int width, int height));
+/* Set the resolution (in pixels per inch), updating width and height. */
+void gx_device_set_resolution(P3(gx_device *dev, floatp x_dpi, floatp y_dpi));
+/* Set the MediaSize (in 1/72" units), updating width and height. */
+void gx_device_set_media_size(P3(gx_device *dev, floatp media_width, floatp media_height));
+/****** BACKWARD COMPATIBILITY ******/
+#define gx_device_set_page_size(dev, w, h)\
+ gx_device_set_media_size(dev, w, h)
+
+/*
+ * Macros to help the drawing procedures clip coordinates to
+ * fit into the drawing region. Note that these may modify
+ * x, y, w, h, data, data_x, and id.
+ */
+
+/* Macros for fill_rectangle and [strip_]tile_rectangle. */
+#define fit_fill_xyw(dev, x, y, w, h)\
+ if ( (x | y) < 0 )\
+ { if ( x < 0 ) w += x, x = 0;\
+ if ( y < 0 ) h += y, y = 0;\
+ }\
+ if ( x > dev->width - w ) w = dev->width - x
+#define fit_fill_y(dev, y, h)\
+ if ( y < 0 ) h += y, y = 0
+#define fit_fill_h(dev, y, h)\
+ if ( y > dev->height - h ) h = dev->height - y
+#define fit_fill_yh(dev, y, h)\
+ fit_fill_y(dev, y, h);\
+ fit_fill_h(dev, y, h)
+#define fit_fill_xywh(dev, x, y, w, h)\
+ fit_fill_xyw(dev, x, y, w, h);\
+ fit_fill_h(dev, y, h)
+#define fit_fill(dev, x, y, w, h)\
+ fit_fill_xywh(dev, x, y, w, h);\
+ if ( w <= 0 || h <= 0 ) return 0
+
+/* Macro for copy_mono and copy_color. */
+#define fit_copy_xw(dev, data, data_x, raster, id, x, y, w, h)\
+ if ( (x | y) < 0 )\
+ { if ( x < 0 ) w += x, data_x -= x, x = 0;\
+ if ( y < 0 ) h += y, data -= y * raster, id = gx_no_bitmap_id, y = 0;\
+ }\
+ if ( x > dev->width - w ) w = dev->width - x
+#define fit_copy_xwh(dev, data, data_x, raster, id, x, y, w, h)\
+ fit_copy_xw(dev, data, data_x, raster, id, x, y, w, h);\
+ if ( w <= 0 || h <= 0 ) return 0
+#define fit_copy(dev, data, data_x, raster, id, x, y, w, h)\
+ fit_copy_xw(dev, data, data_x, raster, id, x, y, w, h);\
+ if ( y > dev->height - h ) h = dev->height - y;\
+ if ( w <= 0 || h <= 0 ) return 0
+
+/* Default implementations of optional procedures. */
+/* Note that the default map_xxx_color routines assume white_on_black. */
+dev_proc_open_device(gx_default_open_device);
+dev_proc_get_initial_matrix(gx_default_get_initial_matrix);
+dev_proc_get_initial_matrix(gx_upright_get_initial_matrix);
+dev_proc_sync_output(gx_default_sync_output);
+dev_proc_output_page(gx_default_output_page);
+dev_proc_close_device(gx_default_close_device);
+dev_proc_map_rgb_color(gx_default_w_b_map_rgb_color);
+dev_proc_map_color_rgb(gx_default_w_b_map_color_rgb);
+#define gx_default_map_rgb_color gx_default_w_b_map_rgb_color
+#define gx_default_map_color_rgb gx_default_w_b_map_color_rgb
+dev_proc_tile_rectangle(gx_default_tile_rectangle);
+dev_proc_copy_mono(gx_default_copy_mono);
+dev_proc_copy_color(gx_default_copy_color);
+dev_proc_draw_line(gx_default_draw_line);
+dev_proc_get_bits(gx_default_get_bits);
+dev_proc_get_params(gx_default_get_params);
+dev_proc_put_params(gx_default_put_params);
+dev_proc_map_cmyk_color(gx_default_map_cmyk_color);
+dev_proc_get_xfont_procs(gx_default_get_xfont_procs);
+dev_proc_get_xfont_device(gx_default_get_xfont_device);
+dev_proc_map_rgb_alpha_color(gx_default_map_rgb_alpha_color);
+dev_proc_get_page_device(gx_default_get_page_device); /* returns NULL */
+dev_proc_get_page_device(gx_page_device_get_page_device); /* returns dev */
+dev_proc_get_alpha_bits(gx_default_get_alpha_bits);
+dev_proc_copy_alpha(gx_no_copy_alpha); /* gives error */
+dev_proc_copy_alpha(gx_default_copy_alpha);
+dev_proc_get_band(gx_default_get_band);
+dev_proc_copy_rop(gx_no_copy_rop); /* gives error */
+extern dev_proc_copy_rop((*gx_default_copy_rop_proc));
+dev_proc_copy_rop(gx_default_copy_rop); /* calls ...proc */
+dev_proc_fill_path(gx_default_fill_path);
+dev_proc_stroke_path(gx_default_stroke_path);
+dev_proc_fill_mask(gx_default_fill_mask);
+dev_proc_fill_trapezoid(gx_default_fill_trapezoid);
+dev_proc_fill_parallelogram(gx_default_fill_parallelogram);
+dev_proc_fill_triangle(gx_default_fill_triangle);
+dev_proc_draw_thin_line(gx_default_draw_thin_line);
+dev_proc_begin_image(gx_default_begin_image);
+dev_proc_image_data(gx_default_image_data);
+dev_proc_end_image(gx_default_end_image);
+dev_proc_strip_tile_rectangle(gx_default_strip_tile_rectangle);
+dev_proc_strip_copy_rop(gx_no_strip_copy_rop); /* gives error */
+extern dev_proc_strip_copy_rop((*gx_default_strip_copy_rop_proc));
+dev_proc_strip_copy_rop(gx_default_strip_copy_rop); /* calls ...proc */
+
+/* Color mapping routines for black-on-white, gray scale, true RGB, */
+/* and true CMYK color. */
+dev_proc_map_rgb_color(gx_default_b_w_map_rgb_color);
+dev_proc_map_color_rgb(gx_default_b_w_map_color_rgb);
+dev_proc_map_rgb_color(gx_default_gray_map_rgb_color);
+dev_proc_map_color_rgb(gx_default_gray_map_color_rgb);
+dev_proc_map_rgb_color(gx_default_rgb_map_rgb_color);
+dev_proc_map_color_rgb(gx_default_rgb_map_color_rgb);
+dev_proc_map_cmyk_color(gx_default_cmyk_map_cmyk_color);
+
+/* Default implementations for forwarding devices */
+dev_proc_get_initial_matrix(gx_forward_get_initial_matrix);
+dev_proc_sync_output(gx_forward_sync_output);
+dev_proc_output_page(gx_forward_output_page);
+dev_proc_map_rgb_color(gx_forward_map_rgb_color);
+dev_proc_map_color_rgb(gx_forward_map_color_rgb);
+dev_proc_tile_rectangle(gx_forward_tile_rectangle);
+dev_proc_get_bits(gx_forward_get_bits);
+dev_proc_get_params(gx_forward_get_params);
+dev_proc_put_params(gx_forward_put_params);
+dev_proc_map_cmyk_color(gx_forward_map_cmyk_color);
+dev_proc_get_xfont_procs(gx_forward_get_xfont_procs);
+dev_proc_get_xfont_device(gx_forward_get_xfont_device);
+dev_proc_map_rgb_alpha_color(gx_forward_map_rgb_alpha_color);
+dev_proc_get_page_device(gx_forward_get_page_device);
+dev_proc_get_alpha_bits(gx_forward_get_alpha_bits);
+dev_proc_get_band(gx_forward_get_band);
+extern dev_proc_copy_rop((*gx_forward_copy_rop_proc));
+dev_proc_fill_path(gx_forward_fill_path);
+dev_proc_stroke_path(gx_forward_stroke_path);
+dev_proc_fill_mask(gx_forward_fill_mask);
+dev_proc_fill_trapezoid(gx_forward_fill_trapezoid);
+dev_proc_fill_parallelogram(gx_forward_fill_parallelogram);
+dev_proc_fill_triangle(gx_forward_fill_triangle);
+dev_proc_draw_thin_line(gx_forward_draw_thin_line);
+dev_proc_begin_image(gx_forward_begin_image);
+dev_proc_image_data(gx_forward_image_data);
+dev_proc_end_image(gx_forward_end_image);
+dev_proc_strip_tile_rectangle(gx_forward_strip_tile_rectangle);
+extern dev_proc_strip_copy_rop((*gx_forward_strip_copy_rop_proc));
+
+/* Convert the device procedures to the proper form (see above). */
+void gx_device_set_procs(P1(gx_device *));
+
+/* Fill in defaulted procedures in a device procedure record. */
+void gx_device_fill_in_procs(P1(gx_device *));
+void gx_device_forward_fill_in_procs(P1(gx_device_forward *));
+
+/* Forward the color mapping procedures from a device to its target. */
+void gx_device_forward_color_procs(P1(gx_device_forward *));
+
+/* Temporarily install a null device, or a special device such as */
+/* a clipping device. */
+void gx_device_no_output(P1(gs_state *));
+void gx_set_device_only(P2(gs_state *, gx_device *));
+
+/* Close a device. */
+int gs_closedevice(P1(gx_device *));
+
+/* ------ Device types ------ */
+
+#define dev_type_proc_initialize(proc)\
+ int proc(P1(gx_device *))
+
+typedef struct gx_device_type_s {
+ gs_memory_type_ptr_t stype;
+ dev_type_proc_initialize((*initialize));
+} gx_device_type;
+
+#define device_type(dtname, stype, initproc)\
+private dev_type_proc_initialize(initproc);\
+const gx_device_type dtname = { &stype, initproc }
+
+dev_type_proc_initialize(gdev_initialize);
+
+#endif /* gxdevice_INCLUDED */
diff --git a/pstoraster/gxdevmem.h b/pstoraster/gxdevmem.h
new file mode 100644
index 000000000..325387c91
--- /dev/null
+++ b/pstoraster/gxdevmem.h
@@ -0,0 +1,148 @@
+/* Copyright (C) 1989, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxdevmem.h */
+/* "Memory" device structure for Ghostscript library */
+/* Requires gxdevice.h */
+
+/*
+ * A 'memory' device is essentially a stored bitmap.
+ * There are several different kinds: 1-bit black and white,
+ * 2-, 4-, and 8-bit mapped color, 16- and 24-bit RGB color,
+ * and 32-bit CMYK color. (16-bit uses 5/6/5 bits per color.)
+ * All use the same structure, since it's so awkward to get the effect of
+ * subclasses in C.
+ *
+ * Memory devices come in two flavors: standard, which always stores bytes
+ * big-endian, and word-oriented, which stores bytes in the machine order
+ * within 32-bit "words". The source data for copy_mono and
+ * copy_color must be in big-endian order, and since memory devices
+ * also are guaranteed to allocate the bitmap consecutively,
+ * the bitmap of a standard memory device can serve directly as input
+ * to copy_mono or copy_color operations. This is not true of word-oriented
+ * memory devices, which are provided only in response to a request by
+ * a customer with their own image processing library that uses this format.
+ */
+#ifndef gx_device_memory_DEFINED
+# define gx_device_memory_DEFINED
+typedef struct gx_device_memory_s gx_device_memory;
+#endif
+
+struct gx_device_memory_s {
+ gx_device_forward_common; /* (see gxdevice.h) */
+ gs_matrix initial_matrix; /* the initial transformation */
+ uint raster; /* bytes per scan line, */
+ /* filled in by 'open' */
+ bool foreign_bits; /* if true, bits are not in */
+ /* GC-able space */
+ byte *base;
+ byte **line_ptrs; /* scan line pointers */
+#define scan_line_base(dev,y) ((dev)->line_ptrs[y])
+ /* If the bitmap_memory pointer is non-zero, it is used for */
+ /* allocating the bitmap when the device is opened, */
+ /* and freeing it when the device is closed. */
+ gs_memory_t *bitmap_memory;
+ /* Following is used for mapped color, */
+ /* including 1-bit devices (to specify polarity). */
+ gs_const_string palette; /* RGB triples */
+ /* Following is only used for 24-bit color. */
+ struct _c24 {
+ gx_color_index rgb; /* cache key */
+ bits32 rgbr, gbrg, brgb; /* cache value */
+ } color24;
+ /* Following are only used for alpha buffers. */
+ /* The client initializes those marked with $; */
+ /* they don't change after initialization. */
+ gs_log2_scale_point log2_scale; /* $ oversampling scale factors */
+ int log2_alpha_bits; /* $ log2 of # of alpha bits being produced */
+ int mapped_x; /* $ X value mapped to buffer X=0 */
+ int mapped_y; /* lowest Y value mapped to buffer */
+ int mapped_height; /* # of Y values mapped to buffer */
+ int mapped_start; /* local Y value corresponding to mapped_y */
+ gx_color_index save_color; /* last (only) color displayed */
+};
+extern_st(st_device_memory);
+#define public_st_device_memory() /* in gdevmem.c */\
+ gs_public_st_composite(st_device_memory, gx_device_memory,\
+ "gx_device_memory", device_memory_enum_ptrs, device_memory_reloc_ptrs)
+#define st_device_memory_max_ptrs (st_device_forward_max_ptrs + 2)
+#define mem_device_init_private\
+ { identity_matrix_body }, /* initial matrix (filled in) */\
+ 0, /* raster (filled in) */\
+ true, /* foreign_bits (default) */\
+ (byte *)0, /* base (filled in) */\
+ (byte **)0, /* line_ptrs (filled in by mem_open) */\
+ 0, /* bitmap_memory */\
+ { (byte *)0, 0 }, /* palette (filled in for color) */\
+ { gx_no_color_index }, /* color24 */\
+ { 0, 0 }, 0, /* scale, log2_alpha_bits */\
+ 0, 0, 0, 0, /* mapped_* */\
+ gx_no_color_index /* save_color */
+
+/*
+ * Memory devices may have special setup requirements.
+ * In particular, it may not be obvious how much space to allocate
+ * for the bitmap. Here is the routine that computes this
+ * from the width and height in the device structure.
+ */
+ulong gdev_mem_bitmap_size(P1(const gx_device_memory *));
+/*
+ * Compute the raster (bytes per line) similarly.
+ */
+#define gdev_mem_raster(mdev)\
+ gx_device_raster((const gx_device *)(mdev), true)
+
+/* Determine the appropriate memory device for a given */
+/* number of bits per pixel (0 if none suitable). */
+const gx_device_memory *gdev_mem_device_for_bits(P1(int));
+
+/* Determine the word-oriented memory device for a given depth. */
+const gx_device_memory *gdev_mem_word_device_for_bits(P1(int));
+
+/* Make a memory device. */
+/* mem is 0 if the device is temporary and local, */
+/* or the allocator that was used to allocate it if it is a real object. */
+/* page_device is 1 if the device should be a page device, */
+/* 0 if it should propagate this property from its target, or */
+/* -1 if it should not be a page device. */
+void gs_make_mem_mono_device(P3(gx_device_memory *mdev, gs_memory_t *mem,
+ gx_device *target));
+void gs_make_mem_device(P5(gx_device_memory *mdev,
+ const gx_device_memory *mdproto,
+ gs_memory_t *mem, int page_device,
+ gx_device *target));
+void gs_make_mem_abuf_device(P6(gx_device_memory *adev, gs_memory_t *mem,
+ gx_device *target,
+ const gs_log2_scale_point *pscale,
+ int alpha_bits, int mapped_x));
+void gs_make_mem_alpha_device(P4(gx_device_memory *adev, gs_memory_t *mem,
+ gx_device *target, int alpha_bits));
+
+/* Define whether a monobit memory device is inverted (black=1). */
+void gdev_mem_mono_set_inverted(P2(gx_device_memory *mdev, bool black_is_1));
+
+/* Test whether a device is a memory device. */
+bool gs_device_is_memory(P1(const gx_device *));
+
+/* Test whether a device is an alpha-buffering device. */
+bool gs_device_is_abuf(P1(const gx_device *));
diff --git a/pstoraster/gxdevrop.h b/pstoraster/gxdevrop.h
new file mode 100644
index 000000000..a6c127069
--- /dev/null
+++ b/pstoraster/gxdevrop.h
@@ -0,0 +1,36 @@
+/* Copyright (C) 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxdevrop.h */
+/* Extension of gxdevice.h for RasterOp */
+
+/* Define an unaligned implementation of copy_rop. */
+dev_proc_copy_rop(gx_copy_rop_unaligned);
+dev_proc_strip_copy_rop(gx_strip_copy_rop_unaligned);
+
+/* Define the default and forwarding implementations of [strip_]copy_rop. */
+/* (Normally these are never referenced directly.) */
+dev_proc_copy_rop(gx_real_default_copy_rop);
+dev_proc_copy_rop(gx_forward_copy_rop);
+dev_proc_strip_copy_rop(gx_real_default_strip_copy_rop);
+dev_proc_strip_copy_rop(gx_forward_strip_copy_rop);
diff --git a/pstoraster/gxdht.h b/pstoraster/gxdht.h
new file mode 100644
index 000000000..1478dc189
--- /dev/null
+++ b/pstoraster/gxdht.h
@@ -0,0 +1,172 @@
+/* Copyright (C) 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxdht.h */
+/* Definition of device halftones */
+
+#ifndef gxdht_INCLUDED
+# define gxdht_INCLUDED
+
+#include "gxarith.h" /* for igcd */
+
+/*
+ * The whitening order is represented by a pair of arrays.
+ * The levels array contains an integer (an index into the bits array)
+ * for each distinct halftone level, indicating how many pixels should be
+ * whitened for that level; levels[0] = 0, levels[i] <= levels[i+1], and
+ * levels[num_levels-1] <= num_bits.
+ * The bits array contains an (offset,mask) pair for each pixel in the tile.
+ * bits[i].offset is the (properly aligned) byte index of a pixel
+ * in the tile; bits[i].mask is the mask to be or'ed into this byte and
+ * following ones. This is arranged so it will work properly on
+ * either big- or little-endian machines, and with different mask widths.
+ */
+/* The mask width must be at least as wide as uint, */
+/* and must not be wider than the width implied by align_bitmap_mod. */
+typedef uint ht_mask_t;
+#define ht_mask_bits (sizeof(ht_mask_t) * 8)
+typedef struct gx_ht_bit_s {
+ uint offset;
+ ht_mask_t mask;
+} gx_ht_bit;
+/* During sampling, bits[i].mask is used to hold a normalized sample value. */
+typedef ht_mask_t ht_sample_t;
+/* The following awkward expression avoids integer overflow. */
+#define max_ht_sample (ht_sample_t)(((1 << (ht_mask_bits - 2)) - 1) * 2 + 1)
+
+/*
+ * Define the internal representation of a halftone order.
+ * Note that it may include a cached transfer function.
+ *
+ * Halftone orders exist in two slightly different configurations, strip and
+ * complete. In a complete order, shift = 0 and full_height = height; in a
+ * strip order, shift != 0 and full_height is the height of a fully expanded
+ * halftone made up of enough shifted strip copies to get back to a zero
+ * shift. In other words, full_height is a cached value, but it is an
+ * important one, since it is the modulus used for computing the
+ * tile-relative phase. Requirements:
+ * width > 0, height > 0, multiple > 0
+ * raster >= bitmap_raster(width)
+ * 0 <= shift < width
+ * num_bits = width * height
+ * For complete orders:
+ * full_height = height
+ * For strip orders:
+ * full_height = height * width / gcd(width, shift)
+ * Note that during the sampling of a complete spot halftone, these
+ * invariants may be violated; in particular, it is possible that shift != 0
+ * and height < full_height, even though num_bits and num_levels reflect the
+ * full height. In this case, the invariant is restored (by resetting
+ * shift and height) when sampling is finished. However, we must save the
+ * original height and shift values used for sampling, since sometimes we
+ * run the "finishing" routines more than once. (This is ugly, but it's
+ * too hard to fix.)
+ *
+ * See gxbitmap.h for more details about strip halftones.
+ */
+typedef struct gx_ht_cache_s gx_ht_cache;
+typedef struct gx_ht_order_s {
+ ushort width;
+ ushort height;
+ ushort raster;
+ ushort shift;
+ ushort orig_height;
+ ushort orig_shift;
+ ushort full_height;
+ ushort multiple; /* square root of number of basic cells */
+ /* per multi-cell, see gshtscr.c */
+ uint num_levels; /* = levels size */
+ uint num_bits; /* = bits size = width * height */
+ uint *levels;
+ gx_ht_bit *bits;
+ gx_ht_cache *cache; /* cache to use */
+ gx_transfer_map *transfer; /* TransferFunction or 0 */
+} gx_ht_order;
+#define ht_order_is_complete(porder)\
+ ((porder)->shift == 0)
+#define ht_order_full_height(porder)\
+ ((porder)->shift == 0 ? (porder)->height :\
+ (porder)->width / igcd((porder)->width, (porder)->shift) *\
+ (porder)->height)
+
+/* We only export st_ht_order for use in st_screen_enum. */
+extern_st(st_ht_order);
+#define public_st_ht_order() /* in gsht.c */\
+ gs_public_st_ptrs4(st_ht_order, gx_ht_order, "gx_ht_order",\
+ ht_order_enum_ptrs, ht_order_reloc_ptrs, levels, bits, cache, transfer)
+#define st_ht_order_max_ptrs 4
+
+/*
+ * Define a device halftone. This consists of one or more orders.
+ * If components = 0, then order is the only current halftone screen
+ * (set by setscreen, Type 1 sethalftone, Type 3 sethalftone, or
+ * Type 5 sethalftone with only a Default). Otherwise, order is the
+ * gray or black screen (for gray/RGB or CMYK devices respectively),
+ * and components is an array of gx_ht_order_components parallel to
+ * the components of the client halftone (set by setcolorscreen or
+ * Type 5 sethalftone).
+ */
+typedef struct gx_ht_order_component_s {
+ gx_ht_order corder;
+ gs_ht_separation_name cname;
+} gx_ht_order_component;
+#define private_st_ht_order_component() /* in gsht1.c */\
+ gs_private_st_ptrs_add0(st_ht_order_component, gx_ht_order_component,\
+ "gx_ht_order_component", ht_order_component_enum_ptrs,\
+ ht_order_component_reloc_ptrs, st_ht_order, corder)
+#define st_ht_order_component_max_ptrs st_ht_order_max_ptrs
+#define private_st_ht_order_comp_element() /* in gsht1.c */\
+ gs_private_st_element(st_ht_order_component_element, gx_ht_order_component,\
+ "gx_ht_order_component[]", ht_order_element_enum_ptrs,\
+ ht_order_element_reloc_ptrs, st_ht_order_component)
+
+#ifndef gx_device_halftone_DEFINED
+# define gx_device_halftone_DEFINED
+typedef struct gx_device_halftone_s gx_device_halftone;
+#endif
+
+/*
+ * color_indices is a cache that gives the indices in components of
+ * the screens for the 1, 3, or 4 primary color(s). These indices are
+ * always in the same order, namely:
+ * -,-,-,W(gray)
+ * R,G,B,-
+ * C,M,Y,K
+ */
+struct gx_device_halftone_s {
+ gx_ht_order order;
+ uint color_indices[4];
+ gx_ht_order_component *components;
+ uint num_comp;
+ /* The following are computed from the above. */
+ int lcm_width, lcm_height; /* LCM of primary color tile sizes, */
+ /* max_int if overflowed */
+};
+extern_st(st_device_halftone);
+#define public_st_device_halftone() /* in gsht.c */\
+ gs_public_st_ptrs_add1(st_device_halftone, gx_device_halftone,\
+ "gx_device_halftone", device_halftone_enum_ptrs,\
+ device_halftone_reloc_ptrs, st_ht_order, order, components)
+#define st_device_halftone_max_ptrs (st_ht_order_max_ptrs + 1)
+
+#endif /* gxdht_INCLUDED */
diff --git a/pstoraster/gxdither.c b/pstoraster/gxdither.c
new file mode 100644
index 000000000..ce43983c0
--- /dev/null
+++ b/pstoraster/gxdither.c
@@ -0,0 +1,483 @@
+/* Copyright (C) 1989, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxdither.c */
+#include "gx.h"
+#include "gsstruct.h"
+#include "gsdcolor.h"
+#include "gxdevice.h"
+#include "gxlum.h"
+#include "gxcmap.h"
+#include "gxdither.h"
+#include "gzht.h"
+
+/*
+ * The procedures in this file use halftoning (if necessary)
+ * to implement a given device color that has already gone through
+ * the transfer function. There are two major cases: gray and color.
+ * Gray halftoning always uses a binary screen. Color halftoning
+ * uses either a fast algorithm with a binary screen that produces
+ * relatively poor approximations, or a very slow algorithm with a
+ * general colored screen (or screens) that faithfully implements
+ * the Adobe specifications.
+ */
+
+/* Tables for fast computation of fractional color levels. */
+/* We have to put the table before any uses of it because of a bug */
+/* in the VAX C compiler. */
+/* We have to split up the definition of the table itself because of a bug */
+/* in the IBM AIX 3.2 C compiler. */
+private const gx_color_value
+ q0[] = { 0 };
+private const gx_color_value
+ q1[] = { 0, frac_color_(1,1) };
+private const gx_color_value
+ q2[] = { 0, frac_color_(1,2), frac_color_(2,2) };
+private const gx_color_value
+ q3[] = { 0, frac_color_(1,3), frac_color_(2,3), frac_color_(3,3) };
+private const gx_color_value
+ q4[] = { 0, frac_color_(1,4), frac_color_(2,4), frac_color_(3,4), frac_color_(4,4) };
+private const gx_color_value
+ q5[] = { 0, frac_color_(1,5), frac_color_(2,5), frac_color_(3,5), frac_color_(4,5), frac_color_(5,5) };
+private const gx_color_value
+ q6[] = { 0, frac_color_(1,6), frac_color_(2,6), frac_color_(3,6), frac_color_(4,6), frac_color_(5,6), frac_color_(6,6) };
+private const gx_color_value
+ q7[] = { 0, frac_color_(1,7), frac_color_(2,7), frac_color_(3,7), frac_color_(4,7), frac_color_(5,7), frac_color_(6,7), frac_color_(7,7) };
+/* We export fc_color_quo for the fractional_color macro in gzht.h. */
+const gx_color_value _ds *fc_color_quo[8] =
+ { q0, q1, q2, q3, q4, q5, q6, q7 };
+
+/* Render a gray, possibly by halftoning. */
+int
+gx_render_device_gray(frac gray, gx_color_value alpha, gx_device_color *pdevc,
+ gx_device *dev, const gx_device_halftone *pdht,
+ const gs_int_point *ht_phase)
+{ bool cmyk = dev->color_info.num_components == 4;
+
+/* Make a special check for black and white. */
+ if ( alpha == gx_max_color_value )
+ { gx_color_value lum;
+ switch ( gray )
+ {
+ case frac_0:
+ lum = 0;
+ goto bw;
+ case frac_1:
+ lum = gx_max_color_value;
+bw: color_set_pure(pdevc,
+ (cmyk ?
+ gx_map_cmyk_color(dev, 0, 0, 0,
+ gx_max_color_value - lum) :
+ gx_map_rgb_color(dev, lum, lum, lum)));
+ return 0;
+ default:
+ ;
+ }
+ }
+
+/* get a few handy values */
+ { uint max_value = dev->color_info.dither_grays - 1;
+ unsigned long hsize = (unsigned)pdht->order.num_levels;
+ unsigned long nshades = hsize * max_value + 1;
+ unsigned long lx = (nshades * gray) / (frac_1_long + 1);
+ uint v = lx / hsize;
+ gx_color_value lum = fractional_color(v, max_value);
+ gx_color_index color1;
+ int level = lx % hsize;
+ /* The following should be a conditional expression, */
+ /* but the DECStation 3100 Ultrix 4.3 compiler */
+ /* generates bad code for it. */
+#define set_color_lum(col, lum)\
+ if ( cmyk )\
+ col = gx_map_cmyk_color(dev, 0, 0, 0,\
+ gx_max_color_value - lum);\
+ else if ( alpha == gx_max_color_value )\
+ col = gx_map_rgb_color(dev, lum, lum, lum);\
+ else\
+ col = gx_map_rgb_alpha_color(dev, lum, lum, lum, alpha)
+ set_color_lum(color1, lum);
+ if_debug5('c', "[c]gray=0x%x --> (%d+%d/%lu)/%d\n",
+ (unsigned)gray, v, level, hsize, max_value + 1);
+ if ( level == 0 )
+ { /* Close enough to a pure color, */
+ /* no dithering needed. */
+ color_set_pure(pdevc, color1);
+ return 0;
+ }
+ else
+ { gx_color_index color2;
+ v++;
+ lum = fractional_color(v, max_value);
+ set_color_lum(color2, lum);
+ color_set_binary_halftone(pdevc, pdht,
+ color1, color2, level);
+ color_set_phase_mod(pdevc, ht_phase->x, ht_phase->y,
+ pdht->order.width,
+ pdht->order.full_height);
+ return 1;
+ }
+ }
+}
+
+/*
+ * Color dithering for Ghostscript. The underlying device imaging model
+ * supports dithering between two colors to generate intermediate shades.
+ *
+ * If the device has high quality colors (at least 32 values
+ * per axis), we ask it to map the color directly.
+ *
+ * Otherwise, things are a bit more complicated. If the device
+ * supports N shades of each R, G and B independently, there are a total
+ * of N*N*N colors. These colors form a 3-D grid in a cubical color
+ * space. The following dithering technique works by locating the
+ * color we want in this 3-D color grid and finding the eight colors
+ * that surround it. In the case of dithering into 8 colors with 1
+ * bit for each red, green and blue, these eight colors will always
+ * be the same.
+ *
+ * Now we consider all possible diagonal paths between the eight colors
+ * and chose the path that runs closest to our desired color in 3-D
+ * color space. There are 28 such paths. Then we find the position
+ * on the path that is closest to our color.
+ *
+ * The search is made faster by always reflecting our color into
+ * the bottom octant of the cube and comparing it to 7 paths.
+ * After the best path and the best position on that path are found,
+ * the results are reflected back into the original color space.
+ *
+ * NOTE: This code has been tested for B/W and Color imaging with
+ * 1, 2, 3 and 8 bits per component.
+ *
+ * --- original code by Paul Haeberli @ Silicon Graphics - 1990
+ * --- extensively revised by L. Peter Deutsch, Aladdin Enterprises
+ *
+ * lpd 3/14/94: added support for CMYK.
+ */
+
+/*
+ * The weights are arbitrary, as long as their ratios are correct
+ * and they will fit into the difference between a ulong and a frac
+ * with room to spare. By making WEIGHT1 and WEIGHT4 powers of 2,
+ * we can turn some multiplies into shifts.
+ */
+#define WNUM 128000
+#define WEIGHT1 (ulong)(WNUM/1000) /* 1.0 */
+#define WEIGHT2 (ulong)(WNUM/1414) /* 1/sqrt(2.0) */
+#define WEIGHT3 (ulong)(WNUM/1732) /* 1/sqrt(3.0) */
+#define WEIGHT4 (ulong)(WNUM/2000) /* 1/sqrt(4.0) */
+
+#define DIAG_R (0x1)
+#define DIAG_G (0x2)
+#define DIAG_B (0x4)
+#define DIAG_W (0x8)
+#define DIAG_RG (0x3)
+#define DIAG_GB (0x6)
+#define DIAG_BR (0x5)
+#define DIAG_RGB (0x7)
+#define DIAG_RGBW (0xf)
+
+/* What should we do about the W/K component? For the moment, */
+/* we ignore it in the luminance computation. */
+#define lum_white_weight 0
+private const unsigned short lum_w[16] = {
+ (0*lum_blue_weight+0*lum_green_weight+0*lum_red_weight+0*lum_white_weight),
+ (0*lum_blue_weight+0*lum_green_weight+1*lum_red_weight+0*lum_white_weight),
+ (0*lum_blue_weight+1*lum_green_weight+0*lum_red_weight+0*lum_white_weight),
+ (0*lum_blue_weight+1*lum_green_weight+1*lum_red_weight+0*lum_white_weight),
+ (1*lum_blue_weight+0*lum_green_weight+0*lum_red_weight+0*lum_white_weight),
+ (1*lum_blue_weight+0*lum_green_weight+1*lum_red_weight+0*lum_white_weight),
+ (1*lum_blue_weight+1*lum_green_weight+0*lum_red_weight+0*lum_white_weight),
+ (1*lum_blue_weight+1*lum_green_weight+1*lum_red_weight+0*lum_white_weight),
+ (0*lum_blue_weight+0*lum_green_weight+0*lum_red_weight+1*lum_white_weight),
+ (0*lum_blue_weight+0*lum_green_weight+1*lum_red_weight+1*lum_white_weight),
+ (0*lum_blue_weight+1*lum_green_weight+0*lum_red_weight+1*lum_white_weight),
+ (0*lum_blue_weight+1*lum_green_weight+1*lum_red_weight+1*lum_white_weight),
+ (1*lum_blue_weight+0*lum_green_weight+0*lum_red_weight+1*lum_white_weight),
+ (1*lum_blue_weight+0*lum_green_weight+1*lum_red_weight+1*lum_white_weight),
+ (1*lum_blue_weight+1*lum_green_weight+0*lum_red_weight+1*lum_white_weight),
+ (1*lum_blue_weight+1*lum_green_weight+1*lum_red_weight+1*lum_white_weight)
+};
+
+/* Render RGB or CMYK, possibly by halftoning. */
+/* If we are rendering RGB, white is ignored. */
+/* If we are rendering CMYK, red/green/blue/white are actually */
+/* cyan/magenta/yellow/black. */
+int
+gx_render_device_color(frac red, frac green, frac blue, frac white, bool cmyk,
+ gx_color_value alpha, gx_device_color *pdevc, gx_device *dev,
+ const gx_device_halftone *pdht, const gs_int_point *ht_phase)
+{ uint max_value = dev->color_info.dither_colors - 1;
+ uint num_levels = pdht->order.num_levels;
+ frac rem_r, rem_g, rem_b, rem_w;
+ uint r, g, b, w;
+ gx_color_value vr, vg, vb, vw;
+#define map_color_rgb()\
+ (alpha == gx_max_color_value ?\
+ gx_map_rgb_color(dev, vr, vg, vb) :\
+ gx_map_rgb_alpha_color(dev, vr, vg, vb, alpha))
+#define map_color_cmyk()\
+ gx_map_cmyk_color(dev, vr, vg, vb, vw)
+#define map_color()\
+ (cmyk ? map_color_cmyk() : map_color_rgb())
+
+ /* Compute the quotient and remainder of each color component */
+ /* with the actual number of available colors. */
+ switch ( max_value )
+ {
+ case 1: /* 8 or 16 colors */
+ if ( red == frac_1 ) rem_r = 0, r = 1;
+ else rem_r = red, r = 0;
+ if ( green == frac_1 ) rem_g = 0, g = 1;
+ else rem_g = green, g = 0;
+ if ( blue == frac_1 ) rem_b = 0, b = 1;
+ else rem_b = blue, b = 0;
+ if ( white == frac_1 ) rem_w = 0, w = 1;
+ else rem_w = white, w = 0;
+ break;
+ default:
+ { ulong want_r, want_g, want_b, want_w;
+ want_r = (ulong)max_value * red;
+ r = frac_1_quo(want_r);
+ rem_r = frac_1_rem(want_r, r);
+ want_g = (ulong)max_value * green;
+ g = frac_1_quo(want_g);
+ rem_g = frac_1_rem(want_g, g);
+ want_b = (ulong)max_value * blue;
+ b = frac_1_quo(want_b);
+ rem_b = frac_1_rem(want_b, b);
+ want_w = (ulong)max_value * white;
+ w = frac_1_quo(want_w);
+ rem_w = frac_1_rem(want_w, w);
+ }
+ }
+
+ /* Check for no dithering required */
+ if ( !(rem_r | rem_g | rem_b | rem_w) )
+ { vr = fractional_color(r, max_value);
+ vg = fractional_color(g, max_value);
+ vb = fractional_color(b, max_value);
+ vw = fractional_color(w, max_value);
+ color_set_pure(pdevc, map_color());
+ return 0;
+ }
+
+ if_debug12('c', "[c]rgbw=0x%x,0x%x,0x%x,0x%x -->\n %x+0x%x,%x+0x%x,%x+0x%x,%x+0x%x -->\n",
+ (unsigned)red, (unsigned)green, (unsigned)blue, (unsigned)white,
+ (unsigned)r, (unsigned)rem_r, (unsigned)g, (unsigned)rem_g,
+ (unsigned)b, (unsigned)rem_b, (unsigned)w, (unsigned)rem_w);
+
+ /* Dithering is required. Choose between two algorithms. */
+
+ if ( pdht->components != 0 && dev->color_info.depth >= 4 )
+ { /* Someone went to the trouble of setting different */
+ /* screens for the different components. */
+ /* Use the slow, general colored halftone algorithm. */
+#define rgb_rem(rem_v, i)\
+ (rem_v * (ulong)(pdht->components[pdht->color_indices[i]].corder.num_levels) / frac_1)
+ uint lr = rgb_rem(rem_r, 0), lg = rgb_rem(rem_g, 1),
+ lb = rgb_rem(rem_b, 2);
+ if ( cmyk )
+ color_set_cmyk_halftone(pdevc, pdht, r, lr, g, lg, b, lb,
+ w, rgb_rem(rem_w, 3));
+ else
+ color_set_rgb_halftone(pdevc, pdht, r, lr, g, lg, b, lb, alpha);
+ color_set_phase_mod(pdevc, ht_phase->x, ht_phase->y,
+ pdht->lcm_width, pdht->lcm_height);
+#undef rgb_rem
+ return 1;
+ }
+
+ /* Fast, approximate binary halftone algorithm. */
+
+ { ulong hsize = num_levels;
+ int adjust_r, adjust_b, adjust_g, adjust_w;
+ gx_color_index color1;
+ frac amax, amin;
+ ulong fmax, cmax;
+ int axisc, facec, cubec, diagc;
+ unsigned short lum_invert;
+ ulong dot1, dot2, dot3, dot4;
+ int level;
+ int code;
+
+/* Flip the remainder color into the 0, 0, 0 octant. */
+ lum_invert = 0;
+#define half (frac_1/2)
+ if ( rem_r > half )
+ rem_r = frac_1 - rem_r,
+ adjust_r = -1, r++, lum_invert += lum_red_weight * 2;
+ else
+ adjust_r = 1;
+ if ( rem_g > half )
+ rem_g = frac_1 - rem_g,
+ adjust_g = -1, g++, lum_invert += lum_green_weight * 2;
+ else
+ adjust_g = 1;
+ if ( rem_b > half )
+ rem_b = frac_1 - rem_b,
+ adjust_b = -1, b++, lum_invert += lum_blue_weight * 2;
+ else
+ adjust_b = 1;
+ vr = fractional_color(r, max_value);
+ vg = fractional_color(g, max_value);
+ vb = fractional_color(b, max_value);
+ if ( cmyk )
+ { if ( rem_w > half )
+ rem_w = frac_1 - rem_w,
+ adjust_w = -1, b++, lum_invert += lum_white_weight * 2;
+ else
+ adjust_w = 1;
+ vw = fractional_color(w, max_value);
+ color1 = map_color_cmyk();
+ }
+ else
+ color1 = map_color_rgb();
+
+/*
+ * Dot the color with each axis to find the best one of 15;
+ * find the color at the end of the axis chosen.
+ */
+ cmax = (ulong)rem_r+rem_g+rem_b;
+ dot4 = cmax + rem_w;
+ if ( rem_g > rem_r )
+ { if ( rem_b > rem_g )
+ amax = rem_b, axisc = DIAG_B;
+ else
+ amax = rem_g, axisc = DIAG_G;
+ if ( rem_b > rem_r )
+ amin = rem_r, fmax = (ulong)rem_g+rem_b, facec = DIAG_GB;
+ else
+ amin = rem_b, fmax = (ulong)rem_r+rem_g, facec = DIAG_RG;
+ }
+ else
+ { if ( rem_b > rem_r )
+ amax = rem_b, axisc = DIAG_B;
+ else
+ amax = rem_r, axisc = DIAG_R;
+ if ( rem_b > rem_g )
+ amin = rem_g, fmax = (ulong)rem_b+rem_r, facec = DIAG_BR;
+ else
+ amin = rem_b, fmax = (ulong)rem_r+rem_g, facec = DIAG_RG;
+ }
+ if ( rem_w > amin )
+ { cmax = fmax + rem_w, cubec = facec + DIAG_W;
+ if ( rem_w > amax )
+ fmax = (ulong)amax + rem_w, facec = axisc + DIAG_W,
+ amax = rem_w, axisc = DIAG_W;
+ else if ( rem_w > fmax - amax )
+ fmax = (ulong)amax + rem_w, facec = axisc + DIAG_W;
+ }
+ else
+ cubec = DIAG_RGB;
+
+ dot1 = amax*WEIGHT1;
+ dot2 = fmax*WEIGHT2;
+ dot3 = cmax*WEIGHT3;
+ /*dot4 see above*/
+#define use_axis()\
+ diagc = axisc, level = (hsize * amax + (frac_1_long / 2)) / frac_1_long
+#define use_face()\
+ diagc = facec, level = (hsize * fmax + frac_1_long) / (2 * frac_1_long)
+#define use_cube()\
+ diagc = cubec, level = (hsize * cmax + (3 * frac_1_long / 2)) / (3 * frac_1_long)
+#define use_tesseract()\
+ diagc = DIAG_RGBW, level = (hsize * dot4 + (2 * frac_1_long)) / (4 * frac_1_long)
+ if ( dot1 > dot2 )
+ { if ( dot3 > dot1 )
+ { if ( dot4*WEIGHT4 > dot3 )
+ use_tesseract();
+ else
+ use_cube();
+ }
+ else
+ { if ( dot4*WEIGHT4 > dot1 )
+ use_tesseract();
+ else
+ use_axis();
+ }
+ }
+ else
+ { if ( dot3 > dot2 )
+ { if ( dot4*WEIGHT4 > dot3 )
+ use_tesseract();
+ else
+ use_cube();
+ }
+ else
+ { if ( dot4*WEIGHT4 > dot2 )
+ use_tesseract();
+ else
+ use_face();
+ }
+ };
+
+ if_debug12('c', " %x+0x%x,%x+0x%x,%x+0x%x,%x+0x%x; adjust=%d,%d,%d,%d\n",
+ (unsigned)r, (unsigned)rem_r, (unsigned)g, (unsigned)rem_g,
+ (unsigned)b, (unsigned)rem_b, (unsigned)w, (unsigned)rem_w,
+ adjust_r, adjust_g, adjust_b, adjust_w);
+
+ if ( level == 0 )
+ { color_set_pure(pdevc, color1);
+ code = 0;
+ }
+ else
+ { gx_color_index color2;
+/* construct the second color, inverting back to original space if needed */
+ if (diagc & DIAG_R) r += adjust_r;
+ if (diagc & DIAG_G) g += adjust_g;
+ if (diagc & DIAG_B) b += adjust_b;
+/* get the second device color, sorting by luminance */
+ vr = fractional_color(r, max_value);
+ vg = fractional_color(g, max_value);
+ vb = fractional_color(b, max_value);
+ if ( cmyk )
+ { if (diagc & DIAG_W) w += adjust_w;
+ vw = fractional_color(w, max_value);
+ color2 = map_color_cmyk();
+ }
+ else
+ color2 = map_color_rgb();
+ if ( level == num_levels )
+ { /* This can only happen through rounding.... */
+ color_set_pure(pdevc, color2);
+ code = 0;
+ }
+ else
+ { if ( lum_w[diagc] < lum_invert )
+ color_set_binary_halftone(pdevc, pdht, color2, color1, hsize - level);
+ else
+ color_set_binary_halftone(pdevc, pdht, color1, color2, level);
+ color_set_phase_mod(pdevc, ht_phase->x, ht_phase->y,
+ pdht->order.width,
+ pdht->order.full_height);
+ code = 1;
+ }
+ }
+
+ if_debug7('c', "[c]diagc=%d; colors=0x%lx,0x%lx; level=%d/%d; lum=%d,diag=%d\n",
+ diagc, (ulong)pdevc->colors.binary.color[0],
+ (ulong)pdevc->colors.binary.color[1],
+ level, (unsigned)hsize, lum_invert, lum_w[diagc]);
+ return code;
+ }
+}
diff --git a/pstoraster/gxdither.h b/pstoraster/gxdither.h
new file mode 100644
index 000000000..6e2bf540e
--- /dev/null
+++ b/pstoraster/gxdither.h
@@ -0,0 +1,55 @@
+/* Copyright (C) 1994, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxdither.h */
+/* Interface to gxdither.c */
+
+#ifndef gx_device_halftone_DEFINED
+# define gx_device_halftone_DEFINED
+typedef struct gx_device_halftone_s gx_device_halftone;
+#endif
+
+/* Render a gray, possibly by halftoning. */
+/* Return 0 if complete, 1 if caller must do gx_color_load, <0 on error. */
+int gx_render_device_gray(P6(frac gray, gx_color_value alpha,
+ gx_device_color *pdevc, gx_device *dev,
+ const gx_device_halftone *dev_ht,
+ const gs_int_point *ht_phase));
+#define gx_render_gray(gray, pdevc, pgs)\
+ gx_render_device_gray(gray, pgs->alpha, pdevc, pgs->device, pgs->dev_ht,\
+ &pgs->ht_phase)
+
+/* Render a color, possibly by halftoning. */
+/* Return as for gx_render_[device_]gray. */
+int gx_render_device_color(P10(frac red, frac green, frac blue, frac white,
+ bool cmyk, gx_color_value alpha,
+ gx_device_color *pdevc, gx_device *dev,
+ const gx_device_halftone *pdht,
+ const gs_int_point *ht_phase));
+#define gx_render_color(r, g, b, w, cmyk, pdevc, pgs)\
+ gx_render_device_color(r, g, b, w, cmyk, pgs->alpha,\
+ pdevc, pgs->device, pgs->dev_ht, &pgs->ht_phase)
+#define gx_render_rgb(r, g, b, pdevc, pgs)\
+ gx_render_color(r, g, b, frac_0, false, pdevc, pgs)
+#define gx_render_cmyk(c, m, y, k, pdevc, pgs)\
+ gx_render_color(c, m, y, k, true, pdevc, pgs)
diff --git a/pstoraster/gxfarith.h b/pstoraster/gxfarith.h
new file mode 100644
index 000000000..34111d25c
--- /dev/null
+++ b/pstoraster/gxfarith.h
@@ -0,0 +1,134 @@
+/* Copyright (C) 1993, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxfarith.h */
+/* Floating point arithmetic macros for Ghostscript library */
+#include "gconfigv.h" /* for USE_FPU */
+#include "gxarith.h"
+
+/*
+ * The following macros replace the ones in gxarith.h on machines
+ * that are likely to have very slow floating point.
+ *
+ * None of these macros would be necessary if compilers had a clue
+ * about generating good floating point comparisons on machines with
+ * slow (or no) floating point hardware.
+ */
+
+# if USE_FPU <= 0 && arch_floats_are_IEEE && (arch_sizeof_float == arch_sizeof_int || arch_sizeof_float == arch_sizeof_long)
+
+# if arch_sizeof_float == arch_sizeof_int
+typedef int _f_int_t;
+typedef uint _f_uint_t;
+# else /* arch_sizeof_float == arch_sizeof_long */
+typedef long _f_int_t;
+typedef ulong _f_uint_t;
+# endif
+# define _f_as_int(f) *(_f_int_t *)(&(f))
+# define _f_as_uint(f) *(_f_uint_t *)(&(f))
+
+# if arch_sizeof_double == arch_sizeof_int
+# define _d_int_t int
+# else
+# if arch_sizeof_double == arch_sizeof_long
+# define _d_int_t long
+# endif
+# endif
+# define _d_uint_t unsigned _d_int_t
+# define _d_as_int(f) *(_d_int_t *)(&(d))
+# define _d_as_uint(f) *(_d_uint_t *)(&(d))
+
+# define _ftest(v,f,n)\
+ (sizeof(v)==sizeof(float)?(f):(n))
+# ifdef _d_int_t
+# define _fdtest(v,f,d,n)\
+ (sizeof(v)==sizeof(float)?(f):sizeof(v)==sizeof(double)?(d):(n))
+# else
+# define _fdtest(v,f,d,n)\
+ _ftest(v,f,n)
+# endif
+
+# undef is_fzero
+# define is_fzero(f) /* must handle both +0 and -0 */\
+ _fdtest(f, (_f_as_int(f) << 1) == 0, (_d_as_int(f) << 1) == 0,\
+ (f) == 0.0)
+
+# undef is_fzero2
+# define is_fzero2(f1,f2)\
+ (sizeof(f1) == sizeof(float) && sizeof(f2) == sizeof(float) ?\
+ ((_f_as_int(f1) | _f_as_int(f2)) << 1) == 0 :\
+ (f1) == 0.0 && (f2) == 0.0)
+
+# undef is_fneg
+# if arch_is_big_endian
+# define _is_fnegb(f) (*(byte *)&(f) >= 0x80)
+# else
+# define _is_fnegb(f) (((byte *)&(f))[sizeof(f) - 1] >= 0x80)
+# endif
+# if arch_sizeof_float == arch_sizeof_int
+# define is_fneg(f)\
+ (sizeof(f) == sizeof(float) ? _f_as_int(f) < 0 :\
+ _is_fnegb(f))
+# else
+# define is_fneg(f) _is_fnegb(f)
+# endif
+
+# define IEEE_expt 0x7f800000 /* IEEE exponent mask */
+# define IEEE_f1 0x3f800000 /* IEEE 1.0 */
+
+# undef is_fge1
+# if arch_sizeof_float == arch_sizeof_int
+# define is_fge1(f)\
+ (sizeof(f) == sizeof(float) ?\
+ (_f_as_int(f)) >= IEEE_f1 :\
+ (f) >= 1.0)
+# else /* arch_sizeof_float == arch_sizeof_long */
+# define is_fge1(f)\
+ (sizeof(f) == sizeof(float) ?\
+ (int)(_f_as_int(f) >> 16) >= (IEEE_f1 >> 16) :\
+ (f) >= 1.0)
+# endif
+
+# undef f_fits_in_ubits
+# undef f_fits_in_bits
+# define _f_bits(n) (4.0 * (1L << ((n) - 2)))
+# define f_fits_in_ubits(f, n)\
+ _ftest(f, _f_as_uint(f) < (_f_uint_t)IEEE_f1 + ((_f_uint_t)(n) << 23),\
+ (f) >= 0 && (f) < _f_bits(n))
+# define f_fits_in_bits(f, n)\
+ _ftest(f, (_f_as_uint(f) & IEEE_expt) < IEEE_f1 + ((_f_uint_t)((n)-1) << 23),\
+ (f) >= -_f_bits((n)-1) && (f) < _f_bits((n)-1))
+
+# endif /* USE_FPU <= 0 & ... */
+
+/*
+ * Define sine and cosine functions that take angles in degrees rather than
+ * radians, and that are implemented efficiently on machines with slow
+ * (or no) floating point.
+ */
+double gs_sin_degrees(P1(double angle));
+double gs_cos_degrees(P1(double angle));
+typedef struct gs_sincos_s {
+ double sin, cos;
+} gs_sincos_t;
+void gs_sincos_degrees(P2(double angle, gs_sincos_t *psincos));
diff --git a/pstoraster/gxfcache.h b/pstoraster/gxfcache.h
new file mode 100644
index 000000000..fe506ca0f
--- /dev/null
+++ b/pstoraster/gxfcache.h
@@ -0,0 +1,248 @@
+/* Copyright (C) 1992, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxfcache.h */
+/* Definitions for Ghostscript font and character caches */
+/* Requires gsfont.h */
+#include "gsuid.h"
+#include "gsxfont.h"
+#include "gxbcache.h"
+
+/* ------ Font/matrix pair cache entry ------ */
+
+#ifndef cached_fm_pair_DEFINED
+# define cached_fm_pair_DEFINED
+typedef struct cached_fm_pair_s cached_fm_pair;
+#endif
+
+/*
+ * Define the entry for a cached (font,matrix) pair. If the UID
+ * is valid, the font pointer may be 0, since we keep entries even for
+ * fonts unloaded by a restore if they have valid UIDs.
+ * We can't use the address of the pair for the hash value,
+ * since the GC may move pairs in storage, so we create a hash
+ * when we allocate the pair initially.
+ */
+struct cached_fm_pair_s {
+ gs_font *font; /* base font */
+ gs_uid UID; /* font UniqueID or XUID */
+ uint hash; /* hash for this pair */
+ float mxx, mxy, myx, myy; /* transformation */
+ int num_chars; /* # of cached chars with this */
+ /* f/m pair */
+ bool xfont_tried; /* true if we looked up an xfont */
+ gx_xfont *xfont; /* the xfont (if any) */
+ gs_memory_t *memory; /* the allocator for the xfont */
+ uint index; /* index of this pair in mdata */
+};
+#define private_st_cached_fm_pair() /* in gxccman.c */\
+ gs_private_st_ptrs3(st_cached_fm_pair, cached_fm_pair,\
+ "cached_fm_pair", fm_pair_enum_ptrs, fm_pair_reloc_ptrs,\
+ font, UID.xvalues, xfont)
+#define private_st_cached_fm_pair_elt() /* in gxccman.c */\
+ gs_private_st_element(st_cached_fm_pair_element, cached_fm_pair,\
+ "cached_fm_pair[]", fm_pair_element_enum_ptrs, fm_pair_element_reloc_ptrs,\
+ st_cached_fm_pair)
+/* If font == 0 and UID is invalid, this is a free entry. */
+#define fm_pair_is_free(pair)\
+ ((pair)->font == 0 && !uid_is_valid(&(pair)->UID))
+#define fm_pair_set_free(pair)\
+ ((pair)->font = 0, uid_set_invalid(&(pair)->UID))
+#define fm_pair_init(pair)\
+ (fm_pair_set_free(pair), (pair)->xfont_tried = false, (pair)->xfont = 0)
+
+/* The font/matrix pair cache itself. */
+typedef struct fm_pair_cache_s {
+ uint msize, mmax; /* # of cached font/matrix pairs */
+ cached_fm_pair *mdata;
+ uint mnext; /* rover for allocating font/matrix pairs */
+} fm_pair_cache;
+
+/* ------ Character cache entry ------- */
+
+/* Define the allocation chunk type. */
+typedef gx_bits_cache_chunk char_cache_chunk;
+
+/*
+ * This is a subclass of the entry in a general bitmap cache.
+ * The character cache contains both used and free blocks.
+ * All blocks have a common header; free blocks have ONLY the header.
+ */
+typedef gx_cached_bits_head cached_char_head;
+#define cc_head_is_free(cch) cb_head_is_free(cch)
+#define cc_head_set_free(cch) cb_head_set_free(cch)
+/*
+ * Define the cache entry for an individual character.
+ * The bits, if any, immediately follow the structure;
+ * characters with only xfont definitions may not have bits.
+ * An entry is 'real' if it is not free and if pair != 0.
+ * We maintain the invariant that at least one of the following must be true
+ * for all real entries:
+ * - cc_has_bits(cc);
+ * - cc->xglyph != gx_no_xglyph && cc_pair(cc)->xfont != 0.
+ */
+#ifndef cached_char_DEFINED
+# define cached_char_DEFINED
+typedef struct cached_char_s cached_char;
+#endif
+struct cached_char_s {
+
+ /* The code, font/matrix pair, wmode, and depth */
+ /* are the 'key' in the cache. */
+ /* gx_cached_bits_common includes depth. */
+
+ gx_cached_bits_common; /* (must be first) */
+#define cc_depth(cc) ((cc)->cb_depth)
+#define cc_set_depth(cc, d) ((cc)->cb_depth = (d))
+ cached_fm_pair *pair;
+#define cc_pair(cc) ((cc)->pair)
+#define cc_set_pair_only(cc, p) ((cc)->pair = (p))
+ gs_glyph code; /* glyph code */
+ byte wmode; /* writing mode (0 or 1) */
+
+ /* The following are neither 'key' nor 'value'. */
+
+ char_cache_chunk *chunk; /* chunk where this char */
+ /* is allocated */
+ uint loc; /* relative location in chunk */
+ uint pair_index; /* index of pair in mdata */
+
+ /* The rest of the structure is the 'value'. */
+ /* gx_cached_bits_common has width, height, raster, */
+ /* shift (not used here), id. */
+
+#define cc_raster(cc) ((cc)->raster)
+#define cc_set_raster(cc, r) ((cc)->raster = (r))
+ gx_xglyph xglyph; /* the xglyph for the xfont, if any */
+ gs_fixed_point wxy; /* width in device coords */
+ gs_fixed_point offset; /* (-llx, -lly) in device coords */
+};
+#define cc_is_free(cc) cc_head_is_free(&(cc)->head)
+#define cc_set_free(cc) cc_head_set_free(&(cc)->head)
+#define cc_set_pair(cc, p)\
+ ((cc)->pair_index = ((cc)->pair = (p))->index)
+#define cc_has_bits(cc) ((cc)->id != gx_no_bitmap_id)
+/*
+ * Memory management for cached_chars is a little unusual.
+ * cached_chars are never instantiated on their own; a pointer to
+ * a cached_char points into the middle of a cache chunk.
+ * Consequently, such pointers can't be traced or relocated
+ * in the usual way. What we do instead is allocate the cache
+ * outside garbage-collectable space; we do all the tracing and relocating
+ * of pointers *from* the cache (currently only the head.pair pointer)
+ * when we trace or relocate the font "directory" that owns the cache.
+ *
+ * Since cached_chars are (currently) never instantiated on their own,
+ * they only have a descriptor so that cached_char_ptr can trace them.
+ */
+#define private_st_cached_char() /* in gxccman.c */\
+ gs_private_st_composite(st_cached_char, cached_char, "cached_char",\
+ cached_char_enum_ptrs, cached_char_reloc_ptrs)
+#define private_st_cached_char_ptr() /* in gxccman.c */\
+ gs_private_st_composite(st_cached_char_ptr, cached_char *,\
+ "cached_char *", cc_ptr_enum_ptrs, cc_ptr_reloc_ptrs)
+#define private_st_cached_char_ptr_elt() /* in gxccman.c */\
+ gs_private_st_element(st_cached_char_ptr_element, cached_char *,\
+ "cached_char *[]", cc_ptr_element_enum_ptrs, cc_ptr_element_reloc_ptrs,\
+ st_cached_char_ptr)
+
+/*
+ * Define the alignment and size of the cache structures.
+ */
+#define align_cached_char_mod align_cached_bits_mod
+#define sizeof_cached_char\
+ round_up(sizeof(cached_char), align_cached_char_mod)
+#define cc_bits(cc) ((byte *)(cc) + sizeof_cached_char)
+
+/* Define the hash index for a (glyph, fm_pair) key. */
+#define chars_head_index(glyph, pair)\
+ ((uint)(glyph) * 59 + (pair)->hash * 73) /* scramble it a bit */
+
+/* Define the glyph marking procedure for the GC. */
+typedef bool (*cc_mark_glyph_proc_t)(P1(gs_glyph));
+
+/* ------ Character cache ------ */
+
+/*
+ * So that we can find all the entries in the cache without
+ * following chains of pointers, we use open hashing rather than
+ * chained hashing for the lookup table.
+ */
+typedef struct char_cache_s {
+ /* gx_bits_cache_common provides chunks, cnext, */
+ /* bsize, csize. */
+ gx_bits_cache_common;
+ gs_memory_t *memory;
+ cached_char **table; /* hash table */
+ uint table_mask; /* (a power of 2 -1) */
+ uint bmax; /* max bsize */
+ uint cmax; /* max csize */
+ uint bspace; /* space allocated for chunks */
+ uint lower; /* min size at which cached chars */
+ /* should be stored compressed */
+ uint upper; /* max size of a single cached char */
+ cc_mark_glyph_proc_t mark_glyph;
+} char_cache;
+
+/* ------ Font/character cache ------ */
+
+/* A font "directory" (font/character cache manager). */
+struct gs_font_dir_s {
+
+ /* Original (unscaled) fonts */
+
+ gs_font *orig_fonts;
+
+ /* Scaled font cache */
+
+ gs_font *scaled_fonts; /* list of recently scaled fonts */
+ uint ssize, smax;
+
+ /* Font/matrix pair cache */
+
+ fm_pair_cache fmcache;
+
+ /* Character cache */
+
+ char_cache ccache;
+};
+#define private_st_font_dir() /* in gsfont.c */\
+ gs_private_st_composite(st_font_dir, gs_font_dir, "gs_font_dir",\
+ font_dir_enum_ptrs, font_dir_reloc_ptrs)
+
+/* Enumerate the pointers in a font directory, except for orig_fonts. */
+#define font_dir_do_ptrs(m)\
+ /*m(-,orig_fonts)*/ m(0,scaled_fonts) m(1,fmcache.mdata)\
+ m(2,ccache.table)
+#define st_font_dir_max_ptrs 3
+
+/* Character cache procedures (in gxccache.c and gxccman.c) */
+int gx_char_cache_alloc(P6(gs_memory_t *, gs_font_dir *, uint, uint, uint, uint));
+void gx_char_cache_init(P1(gs_font_dir *));
+void gx_purge_selected_cached_chars(P3(gs_font_dir *, bool (*)(P2(cached_char *, void *)), void *));
+cached_fm_pair *
+ gx_lookup_fm_pair(P2(gs_font *, const gs_state *));
+cached_fm_pair *
+ gx_add_fm_pair(P4(gs_font_dir *, gs_font *, const gs_uid *, const gs_state *));
+void gx_lookup_xfont(P3(const gs_state *, cached_fm_pair *, int));
+void gs_purge_fm_pair(P3(gs_font_dir *, cached_fm_pair *, int));
diff --git a/pstoraster/gxfill.c b/pstoraster/gxfill.c
new file mode 100644
index 000000000..dae955497
--- /dev/null
+++ b/pstoraster/gxfill.c
@@ -0,0 +1,1495 @@
+/* Copyright (C) 1989, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxfill.c */
+/* Lower-level path filling procedures */
+#include "math_.h" /* for floor in fixed_mult_quo */
+#include "gx.h"
+#include "gserrors.h"
+#include "gsstruct.h"
+#include "gxfixed.h"
+#include "gxdevice.h"
+#include "gzpath.h"
+#include "gzcpath.h"
+#include "gxdcolor.h"
+#include "gxhttile.h"
+#include "gxistate.h"
+#include "gxpaint.h" /* for prototypes */
+
+/* Define which fill algorithm(s) to use. */
+#define FILL_SCAN_LINES
+#define FILL_CURVES
+#define FILL_TRAPEZOIDS
+
+/* Define the structure for keeping track of active lines. */
+typedef struct active_line_s active_line;
+struct active_line_s {
+ gs_fixed_point start; /* x,y where line starts */
+ gs_fixed_point end; /* x,y where line ends */
+ gs_fixed_point diff; /* end - start */
+#define al_dx(alp) ((alp)->diff.x)
+#define al_dy(alp) ((alp)->diff.y)
+ fixed y_fast_max; /* can do x_at_y in fixed point */
+ /* if y <= y_fast_max */
+#define set_al_points(alp, startp, endp)\
+ (alp)->diff.x = (endp).x - (startp).x,\
+ (alp)->y_fast_max = max_fixed /\
+ (((alp)->diff.x >= 0 ? (alp)->diff.x : -(alp)->diff.x) | 1) + (startp).y,\
+ (alp)->diff.y = (endp).y - (startp).y,\
+ (alp)->start = startp, (alp)->end = endp
+#define al_x_at_y(alp, yv)\
+ ((yv) == (alp)->end.y ? (alp)->end.x :\
+ ((yv) <= (alp)->y_fast_max ?\
+ ((yv) - (alp)->start.y) * al_dx(alp) / al_dy(alp) :\
+ (n_add1_expr(n_slow_x),\
+ fixed_mult_quo(al_dx(alp), (yv) - (alp)->start.y, al_dy(alp)))) +\
+ (alp)->start.x)
+ fixed x_current; /* current x position */
+ fixed x_next; /* x position at end of band */
+ const segment *pseg; /* endpoint of this line */
+ int direction; /* direction of line segment */
+#define dir_up 1
+#define dir_horizontal 0 /* (these are handled specially) */
+#define dir_down (-1)
+ int curve_k; /* # of subdivisions for curves, */
+ /* -1 for lines */
+ curve_cursor cursor; /* cursor for curves, */
+ /* unused for lines */
+/* "Pending" lines (not reached in the Y ordering yet) use next and prev */
+/* to order lines by increasing starting Y. "Active" lines (being scanned) */
+/* use next and prev to order lines by increasing current X, or if the */
+/* current Xs are equal, by increasing final X. */
+ active_line *prev, *next;
+/* Link together active_lines allocated individually */
+ active_line *alloc_next;
+};
+/*
+ * The active_line structure isn't really simple, but since its instances
+ * only exist temporarily during a fill operation, we don't have to
+ * worry about a garbage collection occurring.
+ */
+gs_private_st_simple(st_active_line, active_line, "active_line");
+
+/* Define the ordering criterion for active lines. */
+/* The xc argument is a copy of lp2->x_current. */
+#define x_precedes(lp1, lp2, xc)\
+ (lp1->x_current < xc || (lp1->x_current == xc &&\
+ (lp1->start.x > lp2->start.x || lp1->end.x < lp2->end.x)))
+
+#ifdef DEBUG
+/* Internal procedures for printing and checking active lines. */
+private void
+print_active_line(const char *label, const active_line *alp)
+{ dprintf5("[f]%s 0x%lx(%d): x_current=%f x_next=%f\n",
+ label, (ulong)alp, alp->direction,
+ fixed2float(alp->x_current), fixed2float(alp->x_next));
+ dprintf5(" start=(%f,%f) pt_end=0x%lx(%f,%f)\n",
+ fixed2float(alp->start.x), fixed2float(alp->start.y),
+ (ulong)alp->pseg,
+ fixed2float(alp->end.x), fixed2float(alp->end.y));
+ dprintf2(" prev=0x%lx next=0x%lx\n",
+ (ulong)alp->prev, (ulong)alp->next);
+}
+private void
+print_line_list(const active_line *flp)
+{ const active_line *lp;
+ for ( lp = flp; lp != 0; lp = lp->next )
+ { fixed xc = lp->x_current, xn = lp->x_next;
+ dprintf3("[f]0x%lx(%d): x_current/next=%g",
+ (ulong)lp, lp->direction,
+ fixed2float(xc));
+ if ( xn != xc ) dprintf1("/%g", fixed2float(xn));
+ dputc('\n');
+ }
+}
+#define print_al(label,alp)\
+ if ( gs_debug_c('F') ) print_active_line(label, alp)
+private int
+check_line_list(const active_line *flp)
+{ const active_line *alp;
+
+ if ( flp != 0 )
+ for ( alp = flp->prev->next; alp != 0; alp = alp->next )
+ if ( alp->next != 0 && alp->next->x_current < alp->x_current )
+ { lprintf("[f]Lines out of order!\n");
+ print_active_line(" 1:", alp);
+ print_active_line(" 2:", alp->next);
+ return_error(gs_error_Fatal);
+ }
+ return 0;
+}
+#else
+#define print_al(label,alp) DO_NOTHING
+#endif
+
+/* Line list structure */
+struct line_list_s {
+ gs_memory_t *memory;
+ active_line *active_area; /* allocated active_line list */
+ active_line *next_active; /* next allocation slot */
+ active_line *limit; /* limit of local allocation */
+ int close_count; /* # of added closing lines */
+ active_line *y_list; /* Y-sorted list of pending lines */
+ active_line *y_line; /* most recently inserted line */
+ active_line x_head; /* X-sorted list of active lines */
+#define x_list x_head.next
+ /* Put the arrays last so the scalars will have */
+ /* small displacements. */
+ /* Allocate a few active_lines locally */
+ /* to avoid round trips through the allocator. */
+#if arch_small_memory
+# define max_local_active 5 /* don't overburden the stack */
+#else
+# define max_local_active 20
+#endif
+ active_line local_active[max_local_active];
+};
+typedef struct line_list_s line_list;
+typedef line_list _ss *ll_ptr;
+
+/* Forward declarations */
+private void init_line_list(P2(ll_ptr, gs_memory_t *));
+private void unclose_path(P2(gx_path *, int));
+private void free_line_list(P1(ll_ptr));
+private int add_y_list(P5(gx_path *, ll_ptr, fixed, fixed,
+ const gs_fixed_rect *));
+private int add_y_line(P4(const segment *, const segment *, int, ll_ptr));
+private void near insert_x_new(P2(active_line *, ll_ptr));
+private bool near end_x_line(P1(active_line *));
+private int fill_loop_by_scan_lines(P11(ll_ptr, gx_device *,
+ const gx_fill_params *, const gx_device_color *, gs_logical_operation_t,
+ const gs_fixed_rect *, fixed, fixed, fixed, fixed, fixed));
+private int fill_loop_by_trapezoids(P11(ll_ptr, gx_device *,
+ const gx_fill_params *, const gx_device_color *, gs_logical_operation_t,
+ const gs_fixed_rect *, fixed, fixed, fixed, fixed, fixed));
+
+/* Statistics */
+#ifdef DEBUG
+# define n_add1(x) (x++)
+# define n_add1_expr(x) n_add1(x)
+# define n_add(x,n) (x += (n))
+private long n_fill;
+private long n_fill_alloc;
+private long n_y_up;
+private long n_y_down;
+private long n_horiz;
+private long n_x_step;
+private long n_slow_x;
+private long n_iter;
+private long n_find_y;
+private long n_band;
+private long n_band_step;
+private long n_band_fill;
+private long n_afill;
+private long n_slant;
+private long n_slant_shallow;
+private long n_sfill;
+#else
+# define n_add1(x) DO_NOTHING
+# define n_add1_expr(x) discard(0)
+# define n_add(x,n) DO_NOTHING
+#endif
+
+/*
+ * This is the general path filling algorithm.
+ * It uses the center-of-pixel rule for filling.
+ * We can implement Microsoft's upper-left-corner-of-pixel rule
+ * by subtracting (0.5, 0.5) from all the coordinates in the path.
+ *
+ * The adjust parameters are a hack for keeping regions
+ * from coming out too faint: they specify an amount by which to expand
+ * the sides of every filled region.
+ * Setting adjust = fixed_half is supposed to produce the effect of Adobe's
+ * any-part-of-pixel rule, but it doesn't quite, because of the
+ * closed/open interval rule for regions. We detect this as a special case
+ * and do the slightly ugly things necessary to make it work.
+ */
+
+/*
+ * Tweak the fill adjustment if necessary so that (nearly) empty
+ * rectangles are guaranteed to produce some output. This is a hack
+ * to work around a bug in the Microsoft Windows PostScript driver,
+ * which draws thin lines by filling zero-width rectangles, and in
+ * some other drivers that try to fill epsilon-width rectangles.
+ */
+void
+gx_adjust_if_empty(const gs_fixed_rect *pbox, gs_fixed_point *adjust)
+{ const fixed
+ dx = pbox->q.x - pbox->p.x,
+ dy = pbox->q.y - pbox->p.y;
+ if ( dx < fixed_half && dy >= int2fixed(2) )
+ { adjust->x = arith_rshift_1(fixed_1 + fixed_epsilon - dx);
+ if_debug1('f', "[f]thin adjust_x=%g\n",
+ fixed2float(adjust->x));
+ }
+ else
+ if ( dy < fixed_half && dx >= int2fixed(2) )
+ { adjust->y = arith_rshift_1(fixed_1 + fixed_epsilon - dy);
+ if_debug1('f', "[f]thin adjust_y=%g\n",
+ fixed2float(adjust->y));
+ }
+}
+
+/*
+ * Fill a path. This is the default implementation of the driver
+ * fill_path procedure.
+ */
+int
+gx_default_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)
+{ gs_fixed_point adjust;
+#define adjust_x adjust.x
+#define adjust_y adjust.y
+ gs_logical_operation_t lop = pis->log_op;
+ gs_fixed_rect ibox, cbox;
+ gx_device_clip cdev;
+ gx_device *dev = pdev;
+ gx_device *save_dev = dev;
+ gx_path ffpath;
+ gx_path *pfpath;
+ int code;
+ fixed adjust_left, adjust_right, adjust_below, adjust_above;
+ int max_fill_band = dev->max_fill_band;
+#define no_band_mask ((fixed)(-1) << (sizeof(fixed) * 8 - 1))
+ bool fill_by_trapezoids;
+ line_list lst;
+
+ adjust = params->adjust;
+ /*
+ * Compute the bounding box before we flatten the path.
+ * This can save a lot of time if the path has curves.
+ * If the path is neither fully within nor fully outside
+ * the quick-check boxes, we could recompute the bounding box
+ * and make the checks again after flattening the path,
+ * but right now we don't bother.
+ */
+ gx_path_bbox(ppath, &ibox);
+ if ( params->fill_zero_width )
+ gx_adjust_if_empty(&ibox, &adjust);
+ /* Check the bounding boxes. */
+ if_debug6('f', "[f]adjust=%g,%g bbox=(%g,%g),(%g,%g)\n",
+ fixed2float(adjust_x), fixed2float(adjust_y),
+ fixed2float(ibox.p.x), fixed2float(ibox.p.y),
+ fixed2float(ibox.q.x), fixed2float(ibox.q.y));
+ if ( pcpath != NULL &&
+ (gx_cpath_inner_box(pcpath, &cbox), !rect_within(ibox, cbox))
+ )
+ { /* Intersect the path box and the clip bounding box. */
+ /* If the intersection is empty, this fill is a no-op. */
+ gs_fixed_rect bbox;
+
+ gx_cpath_outer_box(pcpath, &bbox);
+ if_debug4('f', " outer_box=(%g,%g),(%g,%g)\n",
+ fixed2float(bbox.p.x), fixed2float(bbox.p.y),
+ fixed2float(bbox.q.x), fixed2float(bbox.q.y));
+ rect_intersect(ibox, bbox);
+ if ( ibox.p.x - adjust_x >= ibox.q.x + adjust_x ||
+ ibox.p.y - adjust_y >= ibox.q.y + adjust_y
+ )
+ { /* Intersection of boxes is empty! */
+ return 0;
+ }
+#undef adjust_x
+#undef adjust_y
+ /*
+ * The path is neither entirely inside the inner clip box
+ * nor entirely outside the outer clip box.
+ * If we had to flatten the path, this is where we would
+ * recompute its bbox and make the tests again,
+ * but we don't bother right now.
+ *
+ * Set up a clipping device.
+ */
+ dev = (gx_device *)&cdev;
+ gx_make_clip_device(&cdev, &cdev, &pcpath->list);
+ cdev.target = save_dev;
+ cdev.max_fill_band = save_dev->max_fill_band;
+ (*dev_proc(dev, open_device))(dev);
+ }
+ /*
+ * Compute the proper adjustment values.
+ * To get the effect of the any-part-of-pixel rule,
+ * we may have to tweak them slightly.
+ */
+ if ( adjust.x == fixed_half )
+ adjust_left = fixed_half - fixed_epsilon,
+ adjust_right = fixed_half + fixed_epsilon;
+ else
+ adjust_left = adjust_right = adjust.x;
+ if ( adjust.y == fixed_half )
+ adjust_below = fixed_half - fixed_epsilon,
+ adjust_above = fixed_half + fixed_epsilon;
+ else
+ adjust_below = adjust_above = adjust.y;
+ /* Initialize the active line list. */
+ init_line_list(&lst, ppath->memory);
+ /*
+ * We have a choice of two different filling algorithms:
+ * scan-line-based and trapezoid-based. They compare as follows:
+ *
+ * Scan Trap
+ * ---- ----
+ * no +yes perfectly accurate Y adjustment
+ * skip +draw 0-height horizontal lines
+ * slow +fast rectangles
+ * +fast slow curves
+ * +yes no write pixels at most once
+ *
+ * Normally we use the scan line algorithm for characters, where
+ * curve speed is important and no Y adjustment is involved, and for
+ * non-idempotent RasterOps, where double pixel writing must be
+ * avoided, and the trapezoid algorithm otherwise.
+ */
+#define double_write_ok lop_is_idempotent(lop)
+#ifdef FILL_SCAN_LINES
+# ifdef FILL_TRAPEZOIDS
+ fill_by_trapezoids =
+ ((adjust_below | adjust_above) != 0 || ppath->curve_count == 0) &&
+ double_write_ok;
+# else
+ fill_by_trapezoids = false;
+# endif
+#else
+ fill_by_trapezoids = double_write_ok;
+#endif
+#undef double_write_ok
+ /*
+ * Pre-process curves. When filling by trapezoids, we need to
+ * flatten the path completely; when filling by scan lines, we only
+ * need to monotonize it, unless FILL_CURVES is undefined.
+ */
+ if ( !ppath->curve_count ) /* don't need to flatten */
+ pfpath = ppath;
+ else
+#ifdef FILL_CURVES
+ if ( fill_by_trapezoids )
+ { code = gx_path_flatten(ppath, &ffpath, params->flatness);
+ if ( code < 0 )
+ return code;
+ pfpath = &ffpath;
+ }
+ else if ( gx_path_is_monotonic(ppath) )
+ pfpath = ppath;
+ else
+ { code = gx_path_monotonize(ppath, &ffpath);
+ if ( code < 0 )
+ return code;
+ pfpath = &ffpath;
+ }
+#else
+ { code = gx_path_flatten(ppath, &ffpath, params->flatness);
+ if ( code < 0 )
+ return code;
+ pfpath = &ffpath;
+ }
+#endif
+ if ( (code = add_y_list(pfpath, &lst, adjust_below, adjust_above, &ibox)) < 0 )
+ goto nope;
+ code =
+ (fill_by_trapezoids ? fill_loop_by_trapezoids :
+ fill_loop_by_scan_lines)
+ (&lst, dev, params, pdevc, lop, &ibox,
+ adjust_left, adjust_right, adjust_below, adjust_above,
+ (max_fill_band == 0 ? no_band_mask : int2fixed(-max_fill_band)));
+nope: if ( lst.close_count != 0 )
+ unclose_path(pfpath, lst.close_count);
+ free_line_list(&lst);
+ if ( pfpath != ppath ) /* had to flatten */
+ gx_path_release(pfpath);
+#ifdef DEBUG
+ if ( gs_debug_c('f') )
+ { dputs("[f] # alloc up down horiz step slowx iter find band bstep bfill\n");
+ dprintf5(" %5ld %5ld %5ld %5ld %5ld",
+ n_fill, n_fill_alloc, n_y_up, n_y_down, n_horiz);
+ dprintf4(" %5ld %5ld %5ld %5ld",
+ n_x_step, n_slow_x, n_iter, n_find_y);
+ dprintf3(" %5ld %5ld %5ld\n",
+ n_band, n_band_step, n_band_fill);
+ dputs("[f] afill slant shall sfill\n");
+ dprintf4(" %5ld %5ld %5ld %5ld\n",
+ n_afill, n_slant, n_slant_shallow, n_sfill);
+ }
+#endif
+ return code;
+}
+
+/* Initialize the line list for a path. */
+private void
+init_line_list(ll_ptr ll, gs_memory_t *mem)
+{ ll->memory = mem;
+ ll->active_area = 0;
+ ll->next_active = ll->local_active;
+ ll->limit = ll->next_active + max_local_active;
+ ll->close_count = 0;
+ ll->y_list = 0;
+ ll->y_line = 0;
+ n_add1(n_fill);
+}
+
+/* Unlink any line_close segments added temporarily. */
+private void
+unclose_path(gx_path *ppath, int count)
+{ subpath *psub;
+ for ( psub = ppath->first_subpath; count != 0;
+ psub = (subpath *)psub->last->next
+ )
+ if ( psub->last == (segment *)&psub->closer )
+ { segment *prev = psub->closer.prev, *next = psub->closer.next;
+ prev->next = next;
+ if ( next ) next->prev = prev;
+ psub->last = prev;
+ count--;
+ }
+}
+
+/* Free the line list. */
+private void
+free_line_list(ll_ptr ll)
+{ gs_memory_t *mem = ll->memory;
+ active_line *alp;
+
+ /* Free any individually allocated active_lines. */
+ while ( (alp = ll->active_area) != 0 )
+ { active_line *next = alp->alloc_next;
+ gs_free_object(mem, alp, "active line");
+ ll->active_area = next;
+ }
+}
+
+/*
+ * Construct a Y-sorted list of segments for rasterizing a path. We assume
+ * the path is non-empty. Only include non-horizontal lines or (monotonic)
+ * curve segments where one endpoint is locally Y-minimal, and horizontal
+ * lines that might color some additional pixels.
+ */
+private int
+add_y_list(gx_path *ppath, ll_ptr ll, fixed adjust_below, fixed adjust_above,
+ const gs_fixed_rect *pbox)
+{ register segment *pseg = (segment *)ppath->first_subpath;
+ int close_count = 0;
+ /* fixed xmin = pbox->p.x; */ /* not currently used */
+ fixed ymin = pbox->p.y;
+ /* fixed xmax = pbox->q.x; */ /* not currently used */
+ fixed ymax = pbox->q.y;
+ int code;
+
+ while ( pseg )
+ { /* We know that pseg points to a subpath head (s_start). */
+ subpath *psub = (subpath *)pseg;
+ segment *plast = psub->last;
+ int dir = 2; /* hack to skip first segment */
+ int first_dir, prev_dir;
+ segment *prev;
+
+ if ( plast->type != s_line_close )
+ { /* Create a fake s_line_close */
+ line_close_segment *lp = &psub->closer;
+ segment *next = plast->next;
+ lp->next = next;
+ lp->prev = plast;
+ plast->next = (segment *)lp;
+ if ( next ) next->prev = (segment *)lp;
+ lp->type = s_line_close;
+ lp->pt = psub->pt;
+ lp->sub = psub;
+ psub->last = plast = (segment *)lp;
+ ll->close_count++;
+ }
+ while ( (prev_dir = dir, prev = pseg,
+ (pseg = pseg->next) != 0 && pseg->type != s_start)
+ )
+ { /*
+ * This element is either a line or a monotonic
+ * curve segment.
+ */
+ fixed iy = pseg->pt.y;
+ fixed py = prev->pt.y;
+ /*
+ * Segments falling entirely outside the ibox in Y
+ * are treated as though they were horizontal, *
+ * i.e., they are never put on the list.
+ */
+#define compute_dir(xo, xe, yo, ye)\
+ (ye > yo ? (ye <= ymin || yo >= ymax ? 0 : dir_up) :\
+ ye < yo ? (yo <= ymin || ye >= ymax ? 0 : dir_down) :\
+ 2)
+#define add_dir_lines(prev2, prev, this, pdir, dir)\
+ if ( pdir )\
+ { if ( (code = add_y_line(prev2, prev, pdir, ll)) < 0 ) return code; }\
+ if ( dir )\
+ { if ( (code = add_y_line(prev, this, dir, ll)) < 0 ) return code; }
+ dir = compute_dir(prev->pt.x, pseg->pt.x, py, iy);
+ if ( dir == 2 )
+ { /* Put horizontal lines on the list */
+ /* if they would color any pixels. */
+ if ( fixed2int_pixround(iy - adjust_below) <
+ fixed2int_pixround(iy + adjust_above)
+ )
+ { n_add1(n_horiz);
+ if ( (code = add_y_line(prev, pseg,
+ dir_horizontal, ll)) < 0
+ )
+ return code;
+ }
+ dir = 0;
+ }
+ if ( dir > prev_dir )
+ { add_dir_lines(prev->prev, prev, pseg, prev_dir, dir);
+ }
+ else if ( prev_dir == 2 ) /* first segment */
+ first_dir = dir;
+ if ( pseg == plast )
+ { /*
+ * We skipped the first segment of the
+ * subpath, so the last segment must receive
+ * special consideration. Note that we have
+ * `closed' all subpaths.
+ */
+ if ( first_dir > dir )
+ { add_dir_lines(prev, pseg, psub->next,
+ dir, first_dir);
+ }
+ }
+ }
+#undef compute_dir
+#undef add_dir_lines
+ }
+ return close_count;
+}
+/*
+ * Internal routine to test a segment and add it to the pending list if
+ * appropriate.
+ */
+private int
+add_y_line(const segment *prev_lp, const segment *lp, int dir, ll_ptr ll)
+{ gs_fixed_point this, prev;
+ register active_line *alp = ll->next_active;
+ fixed y_start;
+ if ( alp == ll->limit )
+ { /* Allocate separately */
+ alp = gs_alloc_struct(ll->memory, active_line,
+ &st_active_line, "active line");
+ if ( alp == 0 ) return_error(gs_error_VMerror);
+ alp->alloc_next = ll->active_area;
+ ll->active_area = alp;
+ n_add1(n_fill_alloc);
+ }
+ else
+ ll->next_active++;
+ this.x = lp->pt.x;
+ this.y = lp->pt.y;
+ prev.x = prev_lp->pt.x;
+ prev.y = prev_lp->pt.y;
+ switch ( (alp->direction = dir) )
+ {
+ case dir_up:
+ y_start = prev.y;
+ set_al_points(alp, prev, this);
+ alp->pseg = lp;
+ break;
+ case dir_down:
+ y_start = this.y;
+ set_al_points(alp, this, prev);
+ alp->pseg = prev_lp;
+ break;
+ case dir_horizontal:
+ y_start = this.y; /* = prev.y */
+ alp->start = prev;
+ alp->end = this;
+ /* Don't need to set dx or y_fast_max */
+ alp->pseg = prev_lp; /* may not need this either */
+ break;
+ }
+ /* Insert the new line in the Y ordering */
+ { register active_line *yp = ll->y_line;
+ register active_line *nyp;
+ if ( yp == 0 )
+ { alp->next = alp->prev = 0;
+ ll->y_list = alp;
+ }
+ else if ( y_start >= yp->start.y )
+ { /* Insert the new line after y_line */
+ while ( n_add1_expr(n_y_up),
+ ((nyp = yp->next) != NULL &&
+ y_start > nyp->start.y)
+ )
+ yp = nyp;
+ alp->next = nyp;
+ alp->prev = yp;
+ yp->next = alp;
+ if ( nyp ) nyp->prev = alp;
+ }
+ else
+ { /* Insert the new line before y_line */
+ while ( n_add1_expr(n_y_down),
+ ((nyp = yp->prev) != NULL &&
+ y_start < nyp->start.y)
+ )
+ yp = nyp;
+ alp->prev = nyp;
+ alp->next = yp;
+ yp->prev = alp;
+ if ( nyp ) nyp->next = alp;
+ else ll->y_list = alp;
+ }
+ }
+ ll->y_line = alp;
+ print_al("add ", alp);
+ return 0;
+}
+
+/* ---------------- Filling loop utilities ---------------- */
+
+/* Insert a newly active line in the X ordering. */
+private void near
+insert_x_new(active_line *alp, ll_ptr ll)
+{ register active_line *next;
+ register active_line *prev = &ll->x_head;
+ register fixed x = alp->start.x;
+ alp->x_current = x;
+ while ( n_add1_expr(n_x_step),
+ (next = prev->next) != 0 && x_precedes(next, alp, x)
+ )
+ prev = next;
+ alp->next = next;
+ alp->prev = prev;
+ if ( next != 0 )
+ next->prev = alp;
+ prev->next = alp;
+}
+
+/* Handle a line segment that just ended. Return true iff this was */
+/* the end of a line sequence. */
+private bool near
+end_x_line(active_line *alp)
+{ const segment *pseg = alp->pseg;
+ /*
+ * The computation of next relies on the fact that
+ * all subpaths have been closed. When we cycle
+ * around to the other end of a subpath, we must be
+ * sure not to process the start/end point twice.
+ */
+ const segment *next =
+ (alp->direction == dir_up ?
+ (/* Upward line, go forward along path. */
+ pseg->type == s_line_close ? /* end of subpath */
+ ((const line_close_segment *)pseg)->sub->next :
+ pseg->next) :
+ (/* Downward line, go backward along path. */
+ pseg->type == s_start ? /* start of subpath */
+ ((const subpath *)pseg)->last->prev :
+ pseg->prev)
+ );
+ gs_fixed_point npt;
+
+ npt.y = next->pt.y;
+ if_debug5('F', "[F]ended 0x%lx: pseg=0x%lx y=%f next=0x%lx npt.y=%f\n",
+ (ulong)alp, (ulong)pseg, fixed2float(pseg->pt.y),
+ (ulong)next, fixed2float(npt.y));
+ if ( npt.y <= pseg->pt.y )
+ { /* End of a line sequence */
+ active_line *nlp = alp->next;
+ alp->prev->next = nlp;
+ if ( nlp )
+ nlp->prev = alp->prev;
+ if_debug1('F', "[F]drop 0x%lx\n", (ulong)alp);
+ return true;
+ }
+ alp->pseg = next;
+ npt.x = next->pt.x;
+ set_al_points(alp, alp->end, npt);
+ print_al("repl", alp);
+ return false;
+}
+
+#define loop_fill_rectangle(x, y, w, h)\
+ gx_fill_rectangle_device_rop(x, y, w, h, pdevc, dev, lop)
+#define loop_fill_rectangle_direct(x, y, w, h)\
+ (fill_direct ?\
+ (*fill_rect)(dev, x, y, w, h, cindex) :\
+ gx_fill_rectangle_device_rop(x, y, w, h, pdevc, dev, lop))
+
+/* ---------------- Scan line filling loop ---------------- */
+
+/* Forward references */
+private void set_scan_line_points(P2(active_line *, fixed));
+
+/* Main filling loop. */
+private int
+fill_loop_by_scan_lines(ll_ptr ll, gx_device *dev,
+ const gx_fill_params *params, const gx_device_color *pdevc,
+ gs_logical_operation_t lop, const gs_fixed_rect *pbox,
+ fixed adjust_left, fixed adjust_right,
+ fixed adjust_below, fixed adjust_above, fixed band_mask)
+{ int rule = params->rule;
+ fixed fixed_flat = float2fixed(params->flatness);
+ bool fill_direct = color_writes_pure(pdevc, lop);
+ gx_color_index cindex;
+ dev_proc_fill_rectangle((*fill_rect));
+ active_line *yll = ll->y_list;
+ fixed y_limit = pbox->q.y;
+ fixed y;
+ /*
+ * The meaning of adjust_below (B) and adjust_above (A) is that
+ * the pixels that would normally be painted at coordinate Y get
+ * "smeared" to coordinates Y-B through Y+A-epsilon, inclusive.
+ * This is equivalent to saying that the pixels actually painted
+ * at coordinate Y are those contributed by scan lines Y-A+epsilon
+ * through Y+B, inclusive, or up to Y+B+epsilon, half-open.
+ * (A = B = 0 is a special case, equivalent to B = 0, A = epsilon.)
+ */
+ fixed look_below =
+ (adjust_above == fixed_0 ? fixed_0 : adjust_above - fixed_epsilon);
+ fixed look_above =
+ adjust_below + fixed_epsilon;
+ fixed look_height = look_above + look_below;
+ bool do_adjust = look_height > fixed_epsilon;
+
+ if ( yll == 0 ) /* empty list */
+ return 0;
+ if ( fill_direct )
+ cindex = pdevc->colors.pure,
+ fill_rect = dev_proc(dev, fill_rectangle);
+#define next_pixel_center(y)\
+ (fixed_pixround(y) + fixed_half)
+ y = next_pixel_center(yll->start.y) - look_below; /* first Y sample point */
+ ll->x_list = 0;
+ ll->x_head.x_current = min_fixed; /* stop backward scan */
+ while ( 1 )
+ { active_line *alp, *nlp;
+ fixed x;
+ fixed ya = y + look_height;
+
+ n_add1(n_iter);
+ /* Move newly active lines from y to x list. */
+ while ( yll != 0 && yll->start.y < ya )
+ { active_line *ynext = yll->next; /* insert smashes next/prev links */
+ if ( yll->direction == dir_horizontal )
+ { /* Ignore for now. */
+ }
+ else
+ { insert_x_new(yll, ll);
+ set_scan_line_points(yll, fixed_flat);
+ }
+ yll = ynext;
+ }
+ /* Check whether we've reached the maximum y. */
+ if ( y >= y_limit )
+ break;
+ if ( ll->x_list == 0 )
+ { /* No active lines, skip to next start */
+ if ( yll == 0 ) break; /* no lines left */
+ y = next_pixel_center(yll->start.y) - look_below;
+ continue;
+ }
+
+ /* Update active lines to y. */
+ x = min_fixed;
+ for ( alp = ll->x_list; alp != 0; alp = nlp )
+ { fixed nx;
+
+ nlp = alp->next;
+e: if ( alp->end.y <= y )
+ { if ( end_x_line(alp) )
+ continue;
+ set_scan_line_points(alp, fixed_flat);
+ goto e;
+ }
+ /* Note that if Y adjustment is in effect, */
+ /* alp->start.y might be greater than y. */
+ nx = alp->x_current =
+ (alp->start.y >= y ? alp->start.x :
+ alp->curve_k < 0 ?
+ al_x_at_y(alp, y) :
+ gx_curve_x_at_y(&alp->cursor, y));
+ if ( nx < x )
+ { /* Move this line backward in the list. */
+ active_line *ilp = alp;
+ while ( nx < (ilp = ilp->prev)->x_current )
+ ;
+ /* Now ilp->x_current <= nx < ilp->next->x_cur. */
+ alp->prev->next = alp->next;
+ if ( alp->next )
+ alp->next->prev = alp->prev;
+ if ( ilp->next )
+ ilp->next->prev = alp;
+ alp->next = ilp->next;
+ ilp->next = alp;
+ alp->prev = ilp;
+ continue;
+ }
+ x = nx;
+ }
+
+ /* Fill inside regions at y. */
+ { int inside = 0;
+ int x1_prev = min_int;
+
+ /* rule = -1 for winding number rule, i.e. */
+ /* we are inside if the winding number is non-zero; */
+ /* rule = 1 for even-odd rule, i.e. */
+ /* we are inside if the winding number is odd. */
+#define inside_path_p() ((inside & rule) != 0)
+ n_add1(n_band);
+ for ( alp = ll->x_list; alp != 0; alp = alp->next )
+ { /* We're outside a filled region. */
+ int x0 = fixed2int_pixround(alp->x_current -
+ adjust_left);
+
+ /*
+ * This doesn't handle lines that cross
+ * within the adjustment region, but it's a
+ * good start.
+ */
+ if ( do_adjust && alp->end.x < alp->start.x )
+ { fixed xa = (alp->end.y < ya ? alp->end.x :
+ alp->curve_k < 0 ?
+ al_x_at_y(alp, ya) :
+ gx_curve_x_at_y(&alp->cursor,
+ ya));
+ int x0a = fixed2int_pixround(xa -
+ adjust_left);
+
+ if ( x0a < x0 )
+ x0 = x0a;
+ }
+ for ( ; ; )
+ { /* We're inside a filled region. */
+ print_al("step", alp);
+ n_add1(n_band_step);
+ inside += alp->direction;
+ if ( !inside_path_p() )
+ break;
+ /*
+ * Since we're dealing with closed
+ * paths, the test for alp == 0
+ * shouldn't be needed, but we may have
+ * omitted lines that are to the right
+ * of the clipping region. */
+ if ( (alp = alp->next) == 0 )
+ goto out;
+ }
+#undef inside_path_p
+ /*
+ * We just went from inside to outside, so
+ * fill the region. Avoid writing pixels
+ * twice.
+ */
+
+ if ( x0 < x1_prev )
+ x0 = x1_prev;
+ { int x1 = fixed2int_rounded(alp->x_current +
+ adjust_right);
+
+ if ( do_adjust && alp->end.x > alp->start.x )
+ { fixed xa = (alp->end.y < ya ?
+ alp->end.x :
+ alp->curve_k < 0 ?
+ al_x_at_y(alp, ya) :
+ gx_curve_x_at_y(&alp->cursor,
+ ya));
+ int x1a = fixed2int_rounded(xa +
+ adjust_right);
+ if ( x1a > x1 )
+ x1 = x1a;
+ }
+ if ( x1 > x0 )
+ { int code =
+ loop_fill_rectangle_direct(x0,
+ fixed2int_var(y),
+ x1 - x0, 1);
+ if_debug3('F', "[F]drawing [%d:%d),%d\n",
+ x0, x1, fixed2int_var(y));
+ if ( code < 0 )
+ return code;
+ x1_prev = x1;
+ }
+ }
+ }
+out: ;
+ }
+ y += fixed_1;
+ }
+ return 0;
+}
+
+private void
+set_scan_line_points(active_line *alp, fixed fixed_flat)
+{ const segment *pseg = alp->pseg;
+ const gs_fixed_point *pp0;
+
+ if ( alp->direction < 0 )
+ { pseg =
+ (pseg->type == s_line_close ?
+ ((const line_close_segment *)pseg)->sub->next :
+ pseg->next);
+ if ( pseg->type != s_curve )
+ { alp->curve_k = -1;
+ return;
+ }
+ pp0 = &alp->end;
+ }
+ else
+ { if ( pseg->type != s_curve )
+ { alp->curve_k = -1;
+ return;
+ }
+ pp0 = &alp->start;
+ }
+#define pcseg ((const curve_segment *)pseg)
+ alp->curve_k =
+ gx_curve_log2_samples(pp0->x, pp0->y, pcseg, fixed_flat);
+ gx_curve_cursor_init(&alp->cursor, pp0->x, pp0->y, pcseg,
+ alp->curve_k);
+#undef pcseg
+}
+
+/* ---------------- Trapezoid filling loop ---------------- */
+
+/* Forward references */
+private int near fill_slant_adjust(P11(fixed, fixed, fixed, fixed, fixed,
+ fixed, fixed, fixed, const gx_device_color *, gx_device *,
+ gs_logical_operation_t));
+private void near resort_x_line(P1(active_line *));
+
+/****** PATCH ******/
+#define loop_fill_trapezoid_fixed(fx0, fw0, fy0, fx1, fw1, fh, swap_axes)\
+ loop_fill_trap(dev, fx0, fw0, fy0, fx1, fw1, fh, swap_axes, pdevc, lop)
+private int
+loop_fill_trap(gx_device *dev, fixed fx0, fixed fw0, fixed fy0,
+ fixed fx1, fixed fw1, fixed fh, bool swap_axes,
+ const gx_device_color *pdevc, gs_logical_operation_t lop)
+{ fixed fy1 = fy0 + fh;
+ gs_fixed_edge left, right;
+
+ left.start.y = right.start.y = fy0;
+ left.end.y = right.end.y = fy1;
+ right.start.x = (left.start.x = fx0) + fw0;
+ right.end.x = (left.end.x = fx1) + fw1;
+ return (*dev_proc(dev, fill_trapezoid))
+ (dev, &left, &right, fy0, fy1, swap_axes, pdevc, lop);
+}
+/****** END PATCH ******/
+
+/* Main filling loop. Takes lines off of y_list and adds them to */
+/* x_list as needed. band_mask limits the size of each band, */
+/* by requiring that ((y1 - 1) & band_mask) == (y0 & band_mask). */
+private int
+fill_loop_by_trapezoids(ll_ptr ll, gx_device *dev,
+ const gx_fill_params *params, const gx_device_color *pdevc,
+ gs_logical_operation_t lop, const gs_fixed_rect *pbox,
+ fixed adjust_left, fixed adjust_right,
+ fixed adjust_below, fixed adjust_above, fixed band_mask)
+{ int rule = params->rule;
+ fixed y_limit = pbox->q.y;
+ active_line *yll = ll->y_list;
+ fixed y;
+ int code;
+ bool fill_direct = color_writes_pure(pdevc, lop);
+ gx_color_index cindex;
+ dev_proc_fill_rectangle((*fill_rect));
+ dev_proc_fill_trapezoid((*fill_trap));
+/*
+ * Define a faster test for
+ * fixed2int_pixround(y - below) != fixed2int_pixround(y + above)
+ * where we know
+ * 0 <= below <= _fixed_pixround_v,
+ * 0 <= above <= fixed_1 - below.
+ * Subtracting out the integer parts, this is equivalent to
+ * fixed2int_pixround(fixed_fraction(y) - below) !=
+ * fixed2int_pixround(fixed_fraction(y) + above)
+ * or to
+ * fixed2int(fixed_fraction(y) + _fixed_pixround_v - below) !=
+ * fixed2int(fixed_fraction(y) + _fixed_pixround_v + above)
+ * Because of the range constraints given above, this is true precisely when
+ * fixed_fraction(y) + _fixed_pixround_v - below < fixed_1 &&
+ * fixed_fraction(y) + _fixed_pixround_v + above >= fixed_1
+ * or equivalently when
+ * fixed_fraction(y - (fixed_1 - _fixed_pixround_v - below)) <
+ * below + above
+ */
+ fixed half_minus_adjust_y = fixed_1 - _fixed_pixround_v - adjust_below;
+ fixed adjust_y2 = adjust_below + adjust_above;
+#define adjusted_y_spans_pixel(y)\
+ fixed_fraction((y) - half_minus_adjust_y) < adjust_y2
+
+ if ( yll == 0 ) return 0; /* empty list */
+ if ( fill_direct )
+ cindex = pdevc->colors.pure,
+ fill_rect = dev_proc(dev, fill_rectangle);
+ fill_trap = dev_proc(dev, fill_trapezoid);
+ y = yll->start.y; /* first Y value */
+ ll->x_list = 0;
+ ll->x_head.x_current = min_fixed; /* stop backward scan */
+ while ( 1 )
+ { fixed y1;
+ active_line *endp, *alp, *stopx;
+ fixed x;
+ int draw;
+
+ n_add1(n_iter);
+ /* Move newly active lines from y to x list. */
+ while ( yll != 0 && yll->start.y == y )
+ { active_line *ynext = yll->next; /* insert smashes next/prev links */
+ if ( yll->direction == dir_horizontal )
+ { /* This is a hack to make sure that */
+ /* isolated horizontal lines get stroked. */
+ int yi = fixed2int_pixround(y - adjust_below);
+ int xi, wi;
+ if ( yll->start.x <= yll->end.x )
+ xi = fixed2int_pixround(yll->start.x -
+ adjust_left),
+ wi = fixed2int_pixround(yll->end.x +
+ adjust_right) - xi;
+ else
+ xi = fixed2int_pixround(yll->end.x -
+ adjust_left),
+ wi = fixed2int_pixround(yll->start.x +
+ adjust_right) - xi;
+ code = loop_fill_rectangle_direct(xi, yi, wi, 1);
+ if ( code < 0 )
+ return code;
+ }
+ else
+ insert_x_new(yll, ll);
+ yll = ynext;
+ }
+ /* Check whether we've reached the maximum y. */
+ if ( y >= y_limit ) break;
+ if ( ll->x_list == 0 )
+ { /* No active lines, skip to next start */
+ if ( yll == 0 ) break; /* no lines left */
+ y = yll->start.y;
+ continue;
+ }
+
+ /* Find the next evaluation point. */
+ /* Start by finding the smallest y value */
+ /* at which any currently active line ends */
+ /* (or the next to-be-active line begins). */
+ y1 = (yll != 0 ? yll->start.y : y_limit);
+ /* Make sure we don't exceed the maximum band height. */
+ { fixed y_band = y | ~band_mask;
+ if ( y1 > y_band ) y1 = y_band + 1;
+ }
+ for ( alp = ll->x_list; alp != 0; alp = alp->next )
+ if ( alp->end.y < y1 ) y1 = alp->end.y;
+#ifdef DEBUG
+ if ( gs_debug_c('F') )
+ { dprintf2("[F]before loop: y=%f y1=%f:\n",
+ fixed2float(y), fixed2float(y1));
+ print_line_list(ll->x_list);
+ }
+#endif
+ /* Now look for line intersections before y1. */
+ x = min_fixed;
+#define have_pixels()\
+ (fixed_pixround(y - adjust_below) < fixed_pixround(y1 + adjust_above))
+ draw = (have_pixels() ? 1 : -1);
+ /*
+ * Loop invariants:
+ * alp = endp->next;
+ * for all lines lp from stopx up to alp,
+ * lp->x_next = al_x_at_y(lp, y1).
+ */
+ for ( alp = stopx = ll->x_list;
+ n_add1_expr(n_find_y), alp != 0;
+ endp = alp, alp = alp->next
+ )
+ { fixed nx = al_x_at_y(alp, y1);
+ fixed dx_old, dx_den;
+ /* Check for intersecting lines. */
+ if ( nx >= x )
+ x = nx;
+ else if
+ ( draw >= 0 && /* don't bother if no pixels */
+ (dx_old = alp->x_current - endp->x_current) >= 0 &&
+ (dx_den = dx_old + endp->x_next - nx) > dx_old
+ )
+ { /* Make a good guess at the intersection */
+ /* Y value using only local information. */
+ fixed dy = y1 - y, y_new;
+ if_debug3('f', "[f]cross: dy=%g, dx_old=%g, dx_new=%g\n",
+ fixed2float(dy), fixed2float(dx_old),
+ fixed2float(dx_den - dx_old));
+ /* Do the computation in single precision */
+ /* if the values are small enough. */
+ y_new =
+ ((dy | dx_old) < 1L << (size_of(fixed)*4-1) ?
+ dy * dx_old / dx_den :
+ fixed_mult_quo(dy, dx_old, dx_den))
+ + y;
+ /* The crossing value doesn't have to be */
+ /* very accurate, but it does have to be */
+ /* greater than y and less than y1. */
+ if_debug3('f', "[f]cross y=%g, y_new=%g, y1=%g\n",
+ fixed2float(y), fixed2float(y_new),
+ fixed2float(y1));
+ stopx = alp;
+ if ( y_new <= y ) y_new = y + 1;
+ if ( y_new < y1 )
+ { y1 = y_new;
+ nx = al_x_at_y(alp, y1);
+ draw = 0;
+ }
+ if ( nx > x ) x = nx;
+ }
+ alp->x_next = nx;
+ }
+ /* Recompute next_x for lines before the intersection. */
+ for ( alp = ll->x_list; alp != stopx; alp = alp->next )
+ alp->x_next = al_x_at_y(alp, y1);
+#ifdef DEBUG
+ if ( gs_debug_c('F') )
+ { dprintf1("[F]after loop: y1=%f\n", fixed2float(y1));
+ print_line_list(ll->x_list);
+ }
+#endif
+ /* Fill a multi-trapezoid band for the active lines. */
+ /* Don't bother if no pixel centers lie within the band. */
+ if ( draw > 0 || (draw == 0 && have_pixels()) )
+ {
+
+/*******************************************************************/
+/* For readability, we start indenting from the left margin again. */
+/*******************************************************************/
+
+ fixed height = y1 - y;
+ fixed xlbot, xltop; /* as of last "outside" line */
+ int inside = 0;
+ active_line *nlp;
+
+ n_add1(n_band);
+ for ( x = min_fixed, alp = ll->x_list; alp != 0; alp = nlp )
+ { fixed xbot = alp->x_current;
+ fixed xtop = alp->x_current = alp->x_next;
+#define nx xtop
+ fixed wtop;
+ int xi, xli;
+ int code;
+
+ print_al("step", alp);
+ n_add1(n_band_step);
+ nlp = alp->next;
+ /* Handle ended or out-of-order lines. After this, */
+ /* the only member of alp we use is alp->direction. */
+ if ( alp->end.y != y1 || !end_x_line(alp) )
+ { if ( nx <= x )
+ resort_x_line(alp);
+ else
+ x = nx;
+ }
+#undef nx
+ /* rule = -1 for winding number rule, i.e. */
+ /* we are inside if the winding number is non-zero; */
+ /* rule = 1 for even-odd rule, i.e. */
+ /* we are inside if the winding number is odd. */
+#define inside_path_p() ((inside & rule) != 0)
+ if ( !inside_path_p() ) /* i.e., outside */
+ { inside += alp->direction;
+ if ( inside_path_p() ) /* about to go in */
+ xlbot = xbot, xltop = xtop;
+ continue;
+ }
+ /* We're inside a region being filled. */
+ inside += alp->direction;
+ if ( inside_path_p() ) /* not about to go out */
+ continue;
+#undef inside_path_p
+ /* We just went from inside to outside, so fill the region. */
+ wtop = xtop - xltop;
+ n_add1(n_band_fill);
+ /* If lines are temporarily out of */
+ /* order, wtop might be negative. */
+ /* Patch this up now. */
+ if ( wtop < 0 )
+ { if_debug2('f', "[f]patch %g,%g\n",
+ fixed2float(xltop), fixed2float(xtop));
+ xtop = xltop += arith_rshift(wtop, 1);
+ wtop = 0;
+ }
+ if ( (adjust_left | adjust_right) != 0 )
+ { xlbot -= adjust_left; xbot += adjust_right;
+ xltop -= adjust_left; xtop += adjust_right;
+ wtop = xtop - xltop;
+ }
+ if ( (xli = fixed2int_var_pixround(xltop)) ==
+ fixed2int_var_pixround(xlbot) &&
+ (xi = fixed2int_var_pixround(xtop)) ==
+ fixed2int_var_pixround(xbot)
+ )
+ { /* Rectangle. */
+ int yi = fixed2int_pixround(y - adjust_below);
+ int wi = fixed2int_pixround(y1 + adjust_above) - yi;
+ code = loop_fill_rectangle_direct(xli, yi,
+ xi - xli, wi);
+ }
+ else if ( (adjust_below | adjust_above) != 0 )
+ { /*
+ * We want to get the effect of filling an area whose
+ * outline is formed by dragging a square of side adj2
+ * along the border of the trapezoid. This is *not*
+ * equivalent to simply expanding the corners by
+ * adjust: There are 3 cases needing different
+ * algorithms, plus rectangles as a fast special case.
+ */
+ fixed wbot = xbot - xlbot;
+ if ( xltop <= xlbot )
+ { if ( xtop >= xbot )
+ { /* Top wider than bottom. */
+ code = loop_fill_trapezoid_fixed(
+ xlbot, wbot, y - adjust_below,
+ xltop, wtop, height, false);
+ if ( adjusted_y_spans_pixel(y1) )
+ { if ( code < 0 ) return code;
+ n_add1(n_afill);
+ code = loop_fill_rectangle_direct(
+ xli, fixed2int_pixround(y1 - adjust_below),
+ fixed2int_var_pixround(xtop) - xli, 1);
+ }
+ }
+ else
+ { /* Slanted trapezoid. */
+ code = fill_slant_adjust(xlbot, xbot, y,
+ xltop, xtop, height, adjust_below,
+ adjust_above, pdevc, dev, lop);
+ }
+ }
+ else
+ { if ( xtop <= xbot )
+ { /* Bottom wider than top. */
+ if ( adjusted_y_spans_pixel(y) )
+ { n_add1(n_afill);
+ xli = fixed2int_var_pixround(xlbot);
+ code = loop_fill_rectangle_direct(
+ xli, fixed2int_pixround(y - adjust_below),
+ fixed2int_var_pixround(xbot) - xli, 1);
+ if ( code < 0 ) return code;
+ }
+ code = loop_fill_trapezoid_fixed(
+ xlbot, wbot, y + adjust_above,
+ xltop, wtop, height, false);
+ }
+ else
+ { /* Slanted trapezoid. */
+ code = fill_slant_adjust(xlbot, xbot, y,
+ xltop, xtop, height, adjust_below,
+ adjust_above, pdevc, dev, lop);
+ }
+ }
+ }
+ else /* No Y adjustment. */
+ code = loop_fill_trapezoid_fixed(xlbot, xbot - xlbot,
+ y, xltop, wtop, height,
+ false);
+ if ( code < 0 ) return code;
+ }
+
+/**************************************************************/
+/* End of section requiring less indentation for readability. */
+/**************************************************************/
+
+ }
+ else
+ { /* Just scan for ended or out-of-order lines. */
+ active_line *nlp;
+ for ( x = min_fixed, alp = ll->x_list; alp != 0;
+ alp = nlp
+ )
+ { fixed nx = alp->x_current = alp->x_next;
+ nlp = alp->next;
+ if_debug4('F',
+ "[F]check 0x%lx,x=%g 0x%lx,x=%g\n",
+ (ulong)alp->prev, fixed2float(x),
+ (ulong)alp, fixed2float(nx));
+ if ( alp->end.y == y1 )
+ { if ( end_x_line(alp) )
+ continue;
+ }
+ if ( nx <= x )
+ resort_x_line(alp);
+ else
+ x = nx;
+ }
+ }
+#ifdef DEBUG
+ if ( gs_debug_c('f') )
+ { int code = check_line_list(ll->x_list);
+ if ( code < 0 )
+ return code;
+ }
+#endif
+ y = y1;
+ }
+ return 0;
+}
+
+/* Handle the case of a slanted trapezoid with adjustment. */
+/* To do this exactly right requires filling a central trapezoid */
+/* plus two narrow vertical triangles or two horizontal almost-rectangles. */
+/* Yuck! */
+private int near
+fill_slant_adjust(fixed xlbot, fixed xbot, fixed y,
+ fixed xltop, fixed xtop, fixed height, fixed adjust_below,
+ fixed adjust_above, const gx_device_color *pdevc, gx_device *dev,
+ gs_logical_operation_t lop)
+{ fixed adjust_y2 = adjust_below + adjust_above;
+ fixed y1 = y + height;
+ dev_proc_fill_trapezoid((*fill_trap)) =
+ dev_proc(dev, fill_trapezoid);
+ int code;
+
+ n_add1(n_slant);
+ if ( height < adjust_y2 )
+ { /*
+ * The upper and lower adjustment bands overlap.
+ * Use a different algorithm.
+ * Since the entire entity is less than 2 pixels high
+ * in this case, we could handle it very efficiently
+ * with no more than 2 rectangle fills, but for right now
+ * we won't attempt to do this.
+ */
+ fixed xl, wx;
+ int yi, hi;
+
+ n_add1(n_slant_shallow);
+ if ( xltop >= xlbot ) /* && xtop >= xbot */
+ wx = xtop - (xl = xlbot);
+ else
+ wx = xbot - (xl = xltop);
+ code = loop_fill_trapezoid_fixed(xlbot, xbot - xlbot,
+ y - adjust_below,
+ xl, wx, height, false);
+ if ( code < 0 )
+ return code;
+ yi = fixed2int_pixround(y1 - adjust_below);
+ hi = fixed2int_pixround(y + adjust_above) - yi;
+ if ( hi > 0 )
+ { int xi = fixed2int_var_pixround(xl);
+ int wi = fixed2int_pixround(xl + wx) - xi;
+ code = loop_fill_rectangle(xi, yi, wi, hi);
+ if ( code < 0 )
+ return code;
+ }
+ code = loop_fill_trapezoid_fixed(xl, wx, y + adjust_above,
+ xltop, xtop - xltop, height,
+ false);
+ }
+ else
+ { fixed dx_left = xltop - xlbot, dx_right = xtop - xbot;
+ fixed xlb, xrb, xlt, xrt;
+ int xli, xri;
+ fixed half_minus_adjust_y =
+ fixed_1 - _fixed_pixround_v - adjust_below;
+
+ if ( dx_left <= 0 ) /* && dx_right <= 0 */
+ { xlb = xlbot -
+ fixed_mult_quo(-dx_left, adjust_y2, height);
+ xrb = xbot;
+ xlt = xltop;
+ xrt = xtop +
+ fixed_mult_quo(-dx_right, adjust_y2, height);
+ }
+ else /* dx_left >= 0, dx_right >= 0 */
+ { xlb = xlbot;
+ xrb = xbot +
+ fixed_mult_quo(dx_right, adjust_y2, height);
+ xlt = xltop -
+ fixed_mult_quo(dx_left, adjust_y2, height);
+ xrt = xtop;
+ }
+ /* Do the bottom adjustment band, if any. */
+ if ( adjusted_y_spans_pixel(y) )
+ { /* We can always do this with a rectangle, */
+ /* but the computation may be too much trouble. */
+ n_add1(n_sfill);
+ if ( (xli = fixed2int_var_pixround(xlbot)) ==
+ fixed2int_var_pixround(xlb) &&
+ (xri = fixed2int_var_pixround(xbot)) ==
+ fixed2int_var_pixround(xrb)
+ )
+ code =
+ loop_fill_rectangle(xli, fixed2int_pixround(y - adjust_below),
+ xri - xli, 1);
+ else
+ code =
+ loop_fill_trapezoid_fixed(xlbot, xbot - xlbot,
+ y - adjust_below,
+ xlb, xrb - xlb,
+ adjust_y2, false);
+ if ( code < 0 )
+ return code;
+ }
+ /* Do the central trapezoid. */
+ code = loop_fill_trapezoid_fixed(xlb, xrb - xlb,
+ y + adjust_above,
+ xlt, xrt - xlt,
+ height - adjust_y2, false);
+ /* Do the top adjustment band. */
+ if ( adjusted_y_spans_pixel(y1) )
+ { if ( code < 0 )
+ return code;
+ n_add1(n_sfill);
+ if ( (xli = fixed2int_var_pixround(xltop)) ==
+ fixed2int_var_pixround(xlt) &&
+ (xri = fixed2int_var_pixround(xtop)) ==
+ fixed2int_var_pixround(xrt)
+ )
+ code =
+ loop_fill_rectangle(xli, fixed2int_pixround(y1 - adjust_below),
+ xri - xli, 1);
+ else
+ code =
+ loop_fill_trapezoid_fixed(xlt, xrt - xlt,
+ y1 - adjust_below,
+ xltop, xtop - xltop,
+ adjust_y2, false);
+ }
+ }
+ return code;
+}
+
+/* Re-sort the x list by moving alp backward to its proper spot. */
+private void near
+resort_x_line(active_line *alp)
+{ active_line *prev = alp->prev;
+ active_line *next = alp->next;
+ fixed nx = alp->x_current;
+
+ prev->next = next;
+ if ( next )
+ next->prev = prev;
+ while ( !x_precedes(prev, alp, nx) )
+ { if_debug2('f', "[f]swap 0x%lx,0x%lx\n",
+ (ulong)alp, (ulong)prev);
+ next = prev, prev = prev->prev;
+ }
+ alp->next = next;
+ alp->prev = prev;
+ /* next might be null, if alp was in */
+ /* the correct spot already. */
+ if ( next )
+ next->prev = alp;
+ prev->next = alp;
+}
diff --git a/pstoraster/gxfixed.h b/pstoraster/gxfixed.h
new file mode 100644
index 000000000..77982d4e4
--- /dev/null
+++ b/pstoraster/gxfixed.h
@@ -0,0 +1,218 @@
+/* Copyright (C) 1989, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxfixed.h */
+/* Fixed-point arithmetic for Ghostscript */
+
+#ifndef gxfixed_INCLUDED
+# define gxfixed_INCLUDED
+
+/*
+ * Coordinates are generally represented internally by fixed-point
+ * quantities: integers lose accuracy in crucial places,
+ * and floating point arithmetic is slow.
+ */
+typedef long fixed;
+#define max_fixed max_long
+#define min_fixed min_long
+#define fixed_0 0L
+#define fixed_epsilon 1L
+/*
+ * 12 bits of fraction provides both the necessary accuracy and
+ * a sufficiently large range of coordinates.
+ */
+#define _fixed_shift 12
+#define fixed_fraction_bits _fixed_shift
+#define fixed_int_bits (sizeof(fixed) * 8 - _fixed_shift)
+#define fixed_scale (1<<_fixed_shift)
+#define _fixed_rshift(x) arith_rshift(x,_fixed_shift)
+#define _fixed_round_v (fixed_scale>>1)
+#define _fixed_fraction_v (fixed_scale-1)
+/*
+ * We use a center-of-pixel filling rule; Adobe specifies that coordinates
+ * designate half-open regions. Because of this, we need special rounding
+ * to go from a coordinate to the pixel it falls in. We use the term
+ * "pixel rounding" for this kind of rounding.
+ */
+#define _fixed_pixround_v (_fixed_round_v - fixed_epsilon)
+
+/*
+ * Most operations can be done directly on fixed-point quantities:
+ * addition, subtraction, shifting, multiplication or division by
+ * (integer) constants; assignment, assignment with zero;
+ * comparison, comparison against zero.
+ * Multiplication and division by floats is OK if the result is
+ * explicitly cast back to fixed.
+ * Conversion to and from int and float types must be done explicitly.
+ * Note that if we are casting a fixed to a float in a context where
+ * only ratios and not actual values are involved, we don't need to take
+ * the scale factor into account: we can simply cast to float directly.
+ */
+#define int2fixed(i) ((fixed)(i)<<_fixed_shift)
+/* Define some useful constants. */
+/* Avoid casts, so strict ANSI compilers will accept them in #ifs. */
+#define fixed_1 (fixed_epsilon << _fixed_shift)
+#define fixed_half (fixed_1 >> 1)
+/*
+ * On 16-bit systems, we can convert fixed variables to ints more efficiently
+ * than general fixed quantities. For this reason, we define two separate
+ * sets of conversion macros.
+ */
+#define fixed2int(x) ((int)_fixed_rshift(x))
+#define fixed2int_rounded(x) ((int)_fixed_rshift((x)+_fixed_round_v))
+#define fixed2int_ceiling(x) ((int)_fixed_rshift((x)+_fixed_fraction_v))
+#define fixed2int_pixround(x) ((int)_fixed_rshift((x)+_fixed_pixround_v))
+#if arch_ints_are_short & !arch_is_big_endian
+/* Do some of the shifting and extraction ourselves. */
+# define _fixed_hi(x) *((uint *)&(x)+1)
+# define _fixed_lo(x) *((uint *)&(x))
+# define fixed2int_var(x)\
+ ((int)((_fixed_hi(x) << (16-_fixed_shift)) +\
+ (_fixed_lo(x) >> _fixed_shift)))
+# define fixed2int_var_rounded(x)\
+ ((int)((_fixed_hi(x) << (16-_fixed_shift)) +\
+ (((_fixed_lo(x) >> (_fixed_shift-1))+1)>>1)))
+# define fixed2int_var_ceiling(x)\
+ (fixed2int_var(x) -\
+ arith_rshift((int)-(_fixed_lo(x) & _fixed_fraction_v), _fixed_shift))
+#else
+/* Use reasonable definitions. */
+# define fixed2int_var(x) fixed2int(x)
+# define fixed2int_var_rounded(x) fixed2int_rounded(x)
+# define fixed2int_var_ceiling(x) fixed2int_ceiling(x)
+#endif
+#define fixed2int_var_pixround(x) fixed2int_pixround(x)
+#define fixed2long(x) ((long)_fixed_rshift(x))
+#define fixed2long_rounded(x) ((long)_fixed_rshift((x)+_fixed_round_v))
+#define fixed2long_ceiling(x) ((long)_fixed_rshift((x)+_fixed_fraction_v))
+#define fixed2long_pixround(x) ((long)_fixed_rshift((x)+_fixed_pixround_v))
+#define float2fixed(f) ((fixed)((f)*(float)fixed_scale))
+/* Note that fixed2float actually produces a double result. */
+#define fixed2float(x) ((x)*(1.0/fixed_scale))
+
+/* Rounding and truncation on fixeds */
+#define fixed_floor(x) ((x)&(-1L<<_fixed_shift))
+#define fixed_rounded(x) (((x)+_fixed_round_v)&(-1L<<_fixed_shift))
+#define fixed_ceiling(x) (((x)+_fixed_fraction_v)&(-1L<<_fixed_shift))
+#define fixed_pixround(x) (((x)+_fixed_pixround_v)&(-1L<<_fixed_shift))
+#define fixed_fraction(x) ((int)(x)&_fixed_fraction_v)
+/* I don't see how to do truncation towards 0 so easily.... */
+#define fixed_truncated(x) ((x) < 0 ? fixed_ceiling(x) : fixed_floor(x))
+
+/*
+ * Define a procedure for computing a * b / c when b, and c are non-negative,
+ * b < c, and a * b exceeds (or might exceed) the capacity of a long.
+ * It's really annoying that C doesn't provide any way to get at
+ * the double-length multiply/divide instructions that almost all hardware
+ * provides....
+ */
+
+#define fixed_mult_quo(fixed_a, fixed_b, fixed_c)\
+ ((fixed)floor((double)(fixed_a) * (fixed_b) / (fixed_c)))
+
+/*
+ * Transforming coordinates involves multiplying two floats, or a float
+ * and a double, and then converting the result to a fixed. Since this
+ * operation is so common, we provide an alternative implementation of it
+ * on machines that use IEEE floating point representation but don't have
+ * floating point hardware. The implementation may be in either C or
+ * assembler.
+ */
+
+#ifdef USE_FPU
+# define USE_FPU_FIXED USE_FPU
+#else
+# define USE_FPU_FIXED 0
+#endif
+/*
+ * set_fmul2fixed_vars(R, FA, FB, dtemp) computes R = FA * FB:
+ * R is a fixed, FA and FB are floats (not doubles), and
+ * dtemp is a temporary double.
+ * set_dfmul2fixed_vars(R, DA, FB, dtemp) computes R = DA * FB:
+ * R is a fixed, DA is a double, FB is a float, and
+ * dtemp is a temporary double.
+ * R, FA, FB, and DA must be variables, not expressions.
+ */
+#if USE_FPU_FIXED < 0 && arch_sizeof_short == 2 && arch_sizeof_long == 4
+int set_fmul2fixed_(P3(fixed *, long, long));
+#define set_fmul2fixed_vars(vr,vfa,vfb,dtemp)\
+ set_fmul2fixed_(&vr, *(long *)&vfa, *(long *)&vfb)
+int set_dfmul2fixed_(P4(fixed *, ulong, long, long));
+# if arch_is_big_endian
+# define set_dfmul2fixed_vars(vr,vda,vfb,dtemp)\
+ set_dfmul2fixed_(&vr, ((ulong *)&vda)[1], *(long *)&vfb, *(long *)&vda)
+# else
+# define set_dfmul2fixed_vars(vr,vda,vfb,dtemp)\
+ set_dfmul2fixed_(&vr, *(ulong *)&vda, *(long *)&vfb, ((long *)&vda)[1])
+# endif
+#else /* don't bother */
+# define set_fmul2fixed_vars(vr,vfa,vfb,dtemp)\
+ (dtemp = (vfa) * (vfb),\
+ (f_fits_in_bits(dtemp, fixed_int_bits) ? (vr = float2fixed(dtemp), 0) :\
+ gs_note_error(gs_error_limitcheck)))
+# define set_dfmul2fixed_vars(vr,vda,vfb,dtemp)\
+ (dtemp = (vda) * (vfb),\
+ (f_fits_in_bits(dtemp, fixed_int_bits) ? (vr = float2fixed(dtemp), 0) :\
+ gs_note_error(gs_error_limitcheck)))
+#endif
+/*
+ * set_float2fixed_vars(R, F) does the equivalent of R = float2fixed(F):
+ * R is a fixed, F is a float or a double.
+ * set_fixed2float_var(R, V) does the equivalent of R = fixed2float(V):
+ * R is a float or a double, V is a fixed.
+ * R and F must be variables, not expressions; V may be an expression.
+ */
+#if USE_FPU_FIXED < 0 && arch_sizeof_long == 4
+int set_float2fixed_(P3(fixed *, long, int));
+int set_double2fixed_(P4(fixed *, ulong, long, int));
+# define set_float2fixed_vars(vr,vf)\
+ (sizeof(vf) == sizeof(float) ?\
+ set_float2fixed_(&vr, *(long *)&vf, fixed_fraction_bits) :\
+ set_double2fixed_(&vr, ((ulong *)&vf)[arch_is_big_endian],\
+ ((long *)&vf)[1 - arch_is_big_endian],\
+ fixed_fraction_bits))
+long fixed2float_(P2(fixed, int));
+void set_fixed2double_(P3(double *, fixed, int));
+# define set_fixed2float_var(vf,x)\
+ (sizeof(vf) == sizeof(float) ?\
+ (*(long *)&vf = fixed2float_(x, fixed_fraction_bits), 0) :\
+ (set_fixed2double_((long *)&vf, x, fixed_fraction_bits), 0))
+#else
+# define set_float2fixed_vars(vr,vf)\
+ (f_fits_in_bits(vf, fixed_int_bits) ? (vr = float2fixed(vf), 0) :\
+ gs_note_error(gs_error_limitcheck))
+# define set_fixed2float_var(vf,x)\
+ (vf = fixed2float(x), 0)
+#endif
+
+/* A point with fixed coordinates */
+typedef struct gs_fixed_point_s {
+ fixed x, y;
+} gs_fixed_point;
+
+/* A rectangle with fixed coordinates */
+typedef struct gs_fixed_rect_s {
+ gs_fixed_point p, q;
+} gs_fixed_rect;
+
+#endif /* gxfixed_INCLUDED */
diff --git a/pstoraster/gxfmap.h b/pstoraster/gxfmap.h
new file mode 100644
index 000000000..0c49fda7c
--- /dev/null
+++ b/pstoraster/gxfmap.h
@@ -0,0 +1,93 @@
+/* Copyright (C) 1992, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxfmap.h */
+/* Fraction map representation for Ghostscript */
+
+#ifndef gxfmap_INCLUDED
+# define gxfmap_INCLUDED
+
+#include "gsrefct.h"
+#include "gxfrac.h"
+#include "gxtmap.h"
+
+/*
+ * Define a cached map from fracs to fracs. Level 1 uses this only
+ * for the transfer function; level 2 also uses it for black generation
+ * and undercolor removal. Note that reference counting macros must
+ * be used to allocate, free, and assign references to gx_transfer_maps.
+ */
+/* log2... must not be greater than frac_bits, and must be least 8. */
+#define log2_transfer_map_size 8
+#define transfer_map_size (1 << log2_transfer_map_size)
+/*typedef struct gx_transfer_map_s gx_transfer_map;*/ /* in gxtmap.h */
+struct gx_transfer_map_s {
+ rc_header rc;
+ gs_mapping_proc proc; /* typedef is in gxtmap.h */
+ /* The id changes whenever the map or function changes. */
+ gs_id id;
+ frac values[transfer_map_size];
+};
+/* We export st_transfer_map for gscolor.c. */
+extern_st(st_transfer_map);
+#define public_st_transfer_map() /* in gsstate.c */\
+ gs_public_st_simple(st_transfer_map, gx_transfer_map, "gx_transfer_map")
+
+/*
+ * Map a color fraction through a transfer map. If the map is small,
+ * we interpolate; if it is large, we don't, and save a lot of time.
+ */
+#define FRAC_MAP_INTERPOLATE (log2_transfer_map_size <= 8)
+#if FRAC_MAP_INTERPOLATE
+
+frac gx_color_frac_map(P2(frac, const frac *)); /* in gxcmap.c */
+# define gx_map_color_frac(pgs,cf,m)\
+ gx_color_frac_map(cf, &pgs->m->values[0])
+
+#else /* !FRAC_MAP_INTERPOLATE */
+
+/* Do the lookup in-line. */
+# define gx_map_color_frac(pgs,cf,m)\
+ (pgs->m->values[frac2bits(cf, log2_transfer_map_size)])
+
+#endif /* (!)FRAC_MAP_INTERPOLATE */
+
+/* Map a color fraction expressed as a byte through a transfer map. */
+/* (We don't use this anywhere right now.) */
+/****************
+#if log2_transfer_map_size <= 8
+# define byte_to_tmx(b) ((b) >> (8 - log2_transfer_map_size))
+#else
+# define byte_to_tmx(b)\
+ (((b) << (log2_transfer_map_size - 8)) +\
+ ((b) >> (16 - log2_transfer_map_size)))
+#endif
+#define gx_map_color_frac_byte(pgs,b,m)\
+ (pgs->m->values[byte_to_tmx(b)])
+ ****************/
+
+/* Map a floating point value through a transfer map. */
+#define gx_map_color_float(pmap,v)\
+ ((pmap)->values[(int)((v) * transfer_map_size + 0.5)] / frac_1_float)
+
+#endif /* gxfmap_INCLUDED */
diff --git a/pstoraster/gxfont.h b/pstoraster/gxfont.h
new file mode 100644
index 000000000..d010da52c
--- /dev/null
+++ b/pstoraster/gxfont.h
@@ -0,0 +1,216 @@
+/* Copyright (C) 1989, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxfont.h */
+/* Internal font definition for Ghostscript library */
+/* Requires gsccode.h, gsmatrix.h, gxdevice.h */
+#include "gsfont.h"
+#include "gsuid.h"
+#include "gsstruct.h" /* for extern_st */
+
+/* A font object as seen by clients. */
+/* See the PostScript Language Reference Manual for details. */
+
+#ifndef gs_show_enum_DEFINED
+# define gs_show_enum_DEFINED
+typedef struct gs_show_enum_s gs_show_enum;
+#endif
+
+/*
+ * Fonts are "objects" to a limited extent, in that some of their
+ * behavior is provided by a record of procedures in the font.
+ * However, adding new types of fonts (subclasses) is not supported well.
+ */
+
+typedef struct gs_font_procs_s {
+
+ /*
+ * Define any needed procedure for initializing the composite
+ * font stack in a show enumerator. This is a no-op for
+ * all but composite fonts.
+ */
+
+#define font_proc_init_fstack(proc)\
+ int proc(P2(gs_show_enum *, gs_font *))
+ font_proc_init_fstack((*init_fstack));
+
+ /*
+ * Define the font's algorithm for getting the next character from
+ * a string being shown. This is trivial, except for composite fonts.
+ * Returns 0 if the current (base) font didn't change,
+ * 1 if it did change, 2 if there are no more characters,
+ * or an error code.
+ */
+
+#define font_proc_next_char(proc)\
+ int proc(P2(gs_show_enum *, gs_char *))
+ font_proc_next_char((*next_char));
+
+ /* A client-supplied character encoding procedure. */
+
+#define font_proc_encode_char(proc)\
+ gs_glyph proc(P3(gs_show_enum *, gs_font *, gs_char *))
+ font_proc_encode_char((*encode_char));
+
+ /*
+ * A client-supplied BuildChar/BuildGlyph procedure.
+ * The gs_char may be gs_no_char (for BuildGlyph), or the gs_glyph
+ * may be gs_no_glyph (for BuildChar), but not both.
+ */
+
+#define font_proc_build_char(proc)\
+ int proc(P5(gs_show_enum *, gs_state *, gs_font *, gs_char, gs_glyph))
+ font_proc_build_char((*build_char));
+
+ /* Callback procedures for external font rasterizers */
+ /* (see gsccode.h for details.) */
+
+ gx_xfont_callbacks callbacks;
+ /*gs_proc_glyph_name((*glyph_name));*/
+
+ /*
+ * Define any special handling of gs_definefont.
+ * We break this out so it can be different for composite fonts.
+ */
+
+#define font_proc_define_font(proc)\
+ int proc(P2(gs_font_dir *, gs_font *))
+ font_proc_define_font((*define_font));
+
+ /*
+ * Define any special handling of gs_makefont.
+ * We break this out so it can be different for composite fonts.
+ */
+
+#define font_proc_make_font(proc)\
+ int proc(P4(gs_font_dir *, const gs_font *, const gs_matrix *,\
+ gs_font **))
+ font_proc_make_font((*make_font));
+
+} gs_font_procs;
+/* Default font procedures */
+font_proc_init_fstack(gs_default_init_fstack);
+font_proc_next_char(gs_default_next_char);
+font_proc_encode_char(gs_no_encode_char);
+font_proc_build_char(gs_no_build_char);
+font_proc_define_font(gs_no_define_font);
+font_proc_make_font(gs_no_make_font);
+font_proc_make_font(gs_base_make_font);
+
+/* Define the known font types. */
+/* These numbers must be the same as the values of FontType */
+/* in font dictionaries. */
+typedef enum {
+ ft_composite = 0,
+ ft_encrypted = 1,
+ ft_user_defined = 3,
+ ft_disk_based = 4,
+ ft_TrueType = 42
+} font_type;
+
+/* Define the bitmap font behaviors. */
+/* These numbers must be the same as the values of the ExactSize, */
+/* InBetweenSize, and TransformedChar entries in font dictionaries. */
+typedef enum {
+ fbit_use_outlines = 0,
+ fbit_use_bitmaps = 1,
+ fbit_transform_bitmaps = 2
+} fbit_type;
+
+/* The font names are only needed for xfont lookup. */
+typedef struct gs_font_name_s {
+#define gs_font_name_max 47 /* must be >= 40 */
+ /* The +1 is so we can null-terminate for debugging printout. */
+ byte chars[gs_font_name_max+1];
+ uint size;
+} gs_font_name;
+
+/*
+ * Define a generic font. We include PaintType and StrokeWidth here because
+ * they affect rendering algorithms outside the Type 1 font machinery.
+ *
+ * ****** NOTE: If you define any subclasses of gs_font, you *must* define
+ * ****** the finalization procedure as gs_font_finalize. Finalization
+ * ****** procedures are not automatically inherited.
+ */
+#define gs_font_common\
+ gs_font *next, *prev; /* chain for original font list or */\
+ /* scaled font cache */\
+ gs_memory_t *memory; /* allocator for this font */\
+ gs_font_dir *dir; /* directory where registered */\
+ gs_font *base; /* original (unscaled) base font */\
+ void *client_data; /* additional client data */\
+ gs_matrix FontMatrix;\
+ font_type FontType;\
+ bool BitmapWidths;\
+ fbit_type ExactSize, InBetweenSize, TransformedChar;\
+ int WMode; /* 0 or 1 */\
+ int PaintType; /* PaintType for Type 1/4/42 fonts, */\
+ /* 0 for others */\
+ float StrokeWidth; /* StrokeWidth for Type 1/4/42 */\
+ /* fonts (if present), 0 for others */\
+ gs_font_procs procs;\
+ /* We store both the FontDirectory key (key_name) and, */\
+ /* if present, the FontName (font_name). */\
+ gs_font_name key_name, font_name
+/*typedef struct gs_font_s gs_font;*/ /* in gsfont.h and other places */
+struct gs_font_s {
+ gs_font_common;
+};
+extern_st(st_gs_font); /* (abstract) */
+struct_proc_finalize(gs_font_finalize); /* public for concrete subclasses */
+#define public_st_gs_font() /* in gsfont.c */\
+ gs_public_st_complex_only(st_gs_font, gs_font, "gs_font",\
+ 0, font_enum_ptrs, font_reloc_ptrs, gs_font_finalize)
+#define st_gs_font_max_ptrs 5
+#define private_st_gs_font_ptr() /* in gsfont.c */\
+ gs_private_st_ptr(st_gs_font_ptr, gs_font *, "gs_font *",\
+ font_ptr_enum_ptrs, font_ptr_reloc_ptrs)
+#define st_gs_font_ptr_max_ptrs 1
+extern_st(st_gs_font_ptr_element);
+#define public_st_gs_font_ptr_element() /* in gsfont.c */\
+ gs_public_st_element(st_gs_font_ptr_element, gs_font *, "gs_font *[]",\
+ font_ptr_element_enum_ptrs, font_ptr_element_reloc_ptrs, st_gs_font_ptr)
+
+/* Define a base (not composite) font. */
+#define gs_font_base_common\
+ gs_font_common;\
+ gs_rect FontBBox;\
+ gs_uid UID;\
+ int encoding_index; /* 0=Std, 1=ISOLatin1, 2=Symbol, */\
+ /* 3=Dingbats, -1=other */\
+ int nearest_encoding_index /* (may be >= 0 even if */\
+ /* encoding_index = -1) */
+#ifndef gs_font_base_DEFINED
+# define gs_font_base_DEFINED
+typedef struct gs_font_base_s gs_font_base;
+#endif
+struct gs_font_base_s {
+ gs_font_base_common;
+};
+extern_st(st_gs_font_base);
+#define public_st_gs_font_base() /* in gsfont.c */\
+ gs_public_st_suffix_add1_final(st_gs_font_base, gs_font_base,\
+ "gs_font_base", font_base_enum_ptrs, font_base_reloc_ptrs,\
+ gs_font_finalize, st_gs_font, UID.xvalues)
+#define st_gs_font_base_max_ptrs (st_gs_font_max_ptrs + 1)
diff --git a/pstoraster/gxfont0.h b/pstoraster/gxfont0.h
new file mode 100644
index 000000000..d96e96a86
--- /dev/null
+++ b/pstoraster/gxfont0.h
@@ -0,0 +1,65 @@
+/* Copyright (C) 1994, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxfont0.h */
+/* Type 0 (composite) font data definition */
+
+/* Define the composite font mapping types. */
+/* These numbers must be the same as the values of FMapType */
+/* in type 0 font dictionaries. */
+typedef enum {
+ fmap_8_8 = 2,
+ fmap_escape = 3,
+ fmap_1_7 = 4,
+ fmap_9_7 = 5,
+ fmap_SubsVector = 6,
+ fmap_double_escape = 7,
+ fmap_shift = 8
+} fmap_type;
+#define fmap_type_min 2
+#define fmap_type_max 8
+#define fmap_type_is_modal(fmt)\
+ ((fmt) == fmap_escape || (fmt) == fmap_double_escape || (fmt) == fmap_shift)
+
+/* This is the type-specific information for a type 0 (composite) gs_font. */
+typedef struct gs_type0_data_s {
+ fmap_type FMapType;
+ byte EscChar, ShiftIn, ShiftOut;
+ gs_const_string SubsVector;
+ uint subs_size; /* bytes per entry */
+ uint subs_width; /* # of entries */
+ uint *Encoding;
+ uint encoding_size;
+ gs_font **FDepVector;
+ uint fdep_size;
+} gs_type0_data;
+#define gs_type0_data_max_ptrs 3
+
+typedef struct gs_font_type0_s {
+ gs_font_common;
+ gs_type0_data data;
+} gs_font_type0;
+extern_st(st_gs_font_type0);
+#define public_st_gs_font_type0() /* in gsfont0.c */\
+ gs_public_st_complex_only(st_gs_font_type0, gs_font_type0, "gs_font_type0",\
+ 0, font_type0_enum_ptrs, font_type0_reloc_ptrs, gs_font_finalize)
diff --git a/pstoraster/gxfont1.h b/pstoraster/gxfont1.h
new file mode 100644
index 000000000..b0e9139cc
--- /dev/null
+++ b/pstoraster/gxfont1.h
@@ -0,0 +1,90 @@
+/* Copyright (C) 1994 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxfont1.h */
+/* Type 1 font data definition */
+
+/* This is the type-specific information for a type 1 (encrypted) gs_font. */
+/* The zone_table values should be ints, according to the Adobe */
+/* specification, but some fonts have arbitrary floats here. */
+#define zone_table(size)\
+ struct {\
+ int count;\
+ float values[(size)*2];\
+ }
+#define stem_table(size)\
+ struct {\
+ int count;\
+ float values[size];\
+ }
+typedef struct gs_type1_data_s gs_type1_data;
+/* The garbage collector really doesn't want the client data pointer */
+/* from a gs_type1_state to point to the gs_type1_data in the middle of */
+/* a gs_font_type1, so we make the client data pointer (which is passed */
+/* to the callback procedures) point to the gs_font_type1 itself. */
+typedef struct gs_font_type1_s gs_font_type1;
+struct gs_type1_data_s {
+ /*int PaintType;*/ /* in gs_font_common */
+ int (*subr_proc)(P3(gs_font_type1 *, int, gs_const_string *));
+ int (*seac_proc)(P3(gs_font_type1 *, int, gs_const_string *));
+ int (*push_proc)(P3(gs_font_type1 *, const fixed *, int));
+ int (*pop_proc)(P2(gs_font_type1 *, fixed *));
+ void *proc_data; /* data for subr_proc & seac_proc */
+ int lenIV;
+ /* For a description of the following hint information, */
+ /* see chapter 5 of the "Adobe Type 1 Font Format" book. */
+ int BlueFuzz;
+ float BlueScale;
+ float BlueShift;
+#define max_BlueValues 7
+ zone_table(max_BlueValues) BlueValues;
+ float ExpansionFactor;
+ bool ForceBold;
+#define max_FamilyBlues 7
+ zone_table(max_FamilyBlues) FamilyBlues;
+#define max_FamilyOtherBlues 5
+ zone_table(max_FamilyOtherBlues) FamilyOtherBlues;
+ int LanguageGroup;
+#define max_OtherBlues 5
+ zone_table(max_OtherBlues) OtherBlues;
+ bool RndStemUp;
+ stem_table(1) StdHW;
+ stem_table(1) StdVW;
+#define max_StemSnap 12
+ stem_table(max_StemSnap) StemSnapH;
+ stem_table(max_StemSnap) StemSnapV;
+ /* Additional information for Multiple Master fonts */
+#define max_WeightVector 16
+ float WeightVector[max_WeightVector];
+};
+#define gs_type1_data_s_DEFINED
+
+struct gs_font_type1_s {
+ gs_font_base_common;
+ gs_type1_data data;
+};
+extern_st(st_gs_font_type1);
+#define public_st_gs_font_type1() /* in gstype1.c */\
+ gs_public_st_suffix_add1_final(st_gs_font_type1, gs_font_type1,\
+ "gs_font_type1", font_type1_enum_ptrs, font_type1_reloc_ptrs,\
+ gs_font_finalize, st_gs_font_base, data.proc_data)
diff --git a/pstoraster/gxfont42.h b/pstoraster/gxfont42.h
new file mode 100644
index 000000000..9c726128a
--- /dev/null
+++ b/pstoraster/gxfont42.h
@@ -0,0 +1,59 @@
+/* Copyright (C) 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxfont42.h */
+/* Type 42 font data definition */
+
+/* This is the type-specific information for a Type 42 (TrueType) font. */
+
+typedef struct gs_type42_data_s gs_type42_data;
+#ifndef gs_font_type42_DEFINED
+# define gs_font_type42_DEFINED
+typedef struct gs_font_type42_s gs_font_type42;
+#endif
+struct gs_type42_data_s {
+ /* The following are set by the client. */
+ int (*string_proc)(P4(gs_font_type42 *, ulong, uint, const byte **));
+ void *proc_data; /* data for string_proc */
+ /* The following are cached values. */
+ ulong glyf; /* offset to glyf table */
+ uint unitsPerEm; /* from head */
+ uint indexToLocFormat; /* from head */
+ uint numLongMetrics; /* from hhea */
+ ulong hmtx; /* offset to hmtx table */
+ uint hmtx_length; /* length of hmtx table */
+ ulong loca; /* offset to loca table */
+};
+struct gs_font_type42_s {
+ gs_font_base_common;
+ gs_type42_data data;
+};
+extern_st(st_gs_font_type42);
+#define public_st_gs_font_type42() /* in gstype42.c */\
+ gs_public_st_suffix_add1_final(st_gs_font_type42, gs_font_type42,\
+ "gs_font_type42", font_type42_enum_ptrs, font_type42_reloc_ptrs,\
+ gs_font_finalize, st_gs_font_base, data.proc_data)
+
+/* Because a Type 42 font contains so many cached values, */
+/* we provide a procedure to initialize them from the font data. */
+int gs_type42_font_init(P1(gs_font_type42 *));
diff --git a/pstoraster/gxfrac.h b/pstoraster/gxfrac.h
new file mode 100644
index 000000000..a2b2796ca
--- /dev/null
+++ b/pstoraster/gxfrac.h
@@ -0,0 +1,96 @@
+/* Copyright (C) 1992, 1993 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxfrac.h */
+/* Fraction representation for Ghostscript */
+
+#ifndef gxfrac_INCLUDED
+# define gxfrac_INCLUDED
+
+/*
+ * Represent a fraction in [0.0..1.0].
+ * Note that the 1.0 endpoint is included.
+ * Since undercolor removal requires a signed frac,
+ * we limit fracs to 15 bits rather than 16.
+ */
+typedef short frac;
+typedef short signed_frac;
+#define arch_log2_sizeof_frac arch_log2_sizeof_short
+#define arch_sizeof_frac arch_sizeof_short
+#define frac_bits 15
+#define frac_0 ((frac)0)
+/* The following definition of frac_1 allows exact representation of */
+/* almost all common fractions (e.g., N/360 for 0<=N<=360). */
+#define frac_1_0bits 3
+#define frac_1 ((frac)0x7ff8)
+#define frac_1_long ((long)frac_1)
+#define frac_1_float ((float)frac_1)
+/* Conversion between fracs and floats. */
+#define frac2float(fr) ((fr) / frac_1_float)
+#define float2frac(fl) ((frac)(((fl) + 0.5 / frac_1_float) * frac_1_float))
+
+/*
+ * Conversion between unsigned fracs and bytes (or, in general,
+ * shorter integers) representing fractions. This is highly dependent
+ * on the definition of frac_1 above.
+ */
+#define _frac2s(fr)\
+ (((fr) >> (frac_bits - frac_1_0bits)) + (fr))
+#define frac2bits(fr, nb)\
+ ((uint)(_frac2s(fr) >> (frac_bits - (nb))))
+#define frac2byte(fr) ((byte)frac2bits(fr, 8))
+/* bits2frac requires frac_bits / 2 <= nb <= frac_bits. */
+#define bits2frac(v, nb) ((frac)(\
+ ((frac)(v) << (frac_bits - (nb))) +\
+ ((v) >> ((nb) * 2 - frac_bits)) -\
+ ((v) >> ((nb) - frac_1_0bits)) ))
+#define byte2frac(b) bits2frac(b, 8)
+/* Produce a result that is guaranteed to convert back to a frac */
+/* not exceeding the original value fr. */
+#define frac2bits_floor(fr, nb)\
+ ((uint)((_frac2s(fr) - (_frac2s(fr) >> (nb))) >> (frac_bits - (nb))))
+/*
+ * Conversion between fracs and unsigned shorts.
+ */
+#define ushort_bits (arch_sizeof_short * 8)
+#define frac2ushort(fr) ((ushort)(\
+ ((fr) << (ushort_bits - frac_bits)) +\
+ ((fr) >> (frac_bits * 2 - ushort_bits - frac_1_0bits)) ))
+#define ushort2frac(us) ((frac)(\
+ ((us) >> (ushort_bits - frac_bits)) -\
+ ((us) >> (ushort_bits - frac_1_0bits)) ))
+/*
+ * Compute the quotient Q = floor(P / frac_1),
+ * where P is the (ulong) product of a uint or ushort V and a frac F.
+ * See gxarith.h for the underlying algorithm.
+ */
+#define frac_1_quo(p)\
+ ( (((p) >> frac_1_0bits) + ((p) >> frac_bits) + 1) >> (frac_bits - frac_1_0bits) )
+/*
+ * Compute the remainder similarly, having already computed the quotient.
+ * This is, of course, P - Q * frac_1.
+ */
+#define frac_1_rem(p, q)\
+ ((frac)( (uint)(p) - ((q) << frac_bits) + ((q) << frac_1_0bits) ))
+
+#endif /* gxfrac_INCLUDED */
diff --git a/pstoraster/gxhint1.c b/pstoraster/gxhint1.c
new file mode 100644
index 000000000..ead9c87f0
--- /dev/null
+++ b/pstoraster/gxhint1.c
@@ -0,0 +1,253 @@
+/* Copyright (C) 1990, 1992, 1993 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxhint1.c */
+/* Font level hints for Type 1 fonts */
+#include "gx.h"
+#include "gserrors.h"
+#include "gxarith.h"
+#include "gxfixed.h"
+#include "gxmatrix.h"
+#include "gxchar.h"
+#include "gxfont.h"
+#include "gxfont1.h"
+#include "gxtype1.h"
+
+/* ------ Initialization ------ */
+
+typedef zone_table(1) a_zone_table;
+typedef stem_table(1) a_stem_table;
+private void near
+ compute_snaps(P6(const gs_matrix_fixed *, const a_stem_table *,
+ stem_snap_table *, int, int, const char *));
+private alignment_zone *near
+ compute_zones(P6(const gs_matrix_fixed *, const font_hints *,
+ const a_zone_table *, const a_zone_table *, alignment_zone *, int));
+private int near
+ transform_zone(P4(const gs_matrix_fixed *, const font_hints *,
+ const float *, alignment_zone *));
+
+/* Reset the font-level hints. */
+void
+reset_font_hints(font_hints *pfh, const gs_log2_scale_point *plog2_scale)
+{ set_pixel_scale(&pfh->scale.x, plog2_scale->x);
+ set_pixel_scale(&pfh->scale.y, plog2_scale->y);
+ pfh->axes_swapped = pfh->x_inverted = pfh->y_inverted = false;
+ pfh->use_x_hints = pfh->use_y_hints = false;
+ pfh->snap_h.count = pfh->snap_v.count = 0;
+ pfh->a_zone_count = 0;
+}
+
+/* Compute the font-level hints from the font and the matrix. */
+/* We should cache this with the font/matrix pair.... */
+void
+compute_font_hints(font_hints *pfh, const gs_matrix_fixed *pmat,
+ const gs_log2_scale_point *plog2_scale, const gs_type1_data *pdata)
+{ alignment_zone *zp = &pfh->a_zones[0];
+ reset_font_hints(pfh, plog2_scale);
+ /* Figure out which hints, if any, to use, */
+ /* and the orientation of the axes. */
+ if ( is_fzero(pmat->xy) )
+ pfh->y_inverted = is_fneg(pmat->yy),
+ pfh->use_y_hints = true;
+ else if ( is_fzero(pmat->xx) )
+ pfh->y_inverted = is_fneg(pmat->xy),
+ pfh->axes_swapped = true,
+ pfh->use_y_hints = true;
+ if ( is_fzero(pmat->yx) )
+ pfh->x_inverted = is_fneg(pmat->xx),
+ pfh->use_x_hints = true;
+ else if ( is_fzero(pmat->yy) )
+ pfh->x_inverted = is_fneg(pmat->yx),
+ pfh->axes_swapped = true,
+ pfh->use_x_hints = true;
+ if_debug6('y', "[y]ctm=[%g %g %g %g %g %g]\n",
+ pmat->xx, pmat->xy, pmat->yx, pmat->yy,
+ pmat->tx, pmat->ty);
+ if_debug7('y', "[y]scale=%d/%d, swapped=%d, x/y_hints=%d,%d, x/y_inverted=%d,%d\n",
+ 1 << plog2_scale->x, 1 << plog2_scale->y,
+ pfh->axes_swapped, pfh->use_x_hints, pfh->use_y_hints,
+ pfh->x_inverted, pfh->y_inverted);
+ /* Transform the actual hints. */
+ if ( pfh->use_x_hints )
+ { compute_snaps(pmat, (const a_stem_table *)&pdata->StdHW,
+ &pfh->snap_h, 0, pfh->axes_swapped, "h");
+ compute_snaps(pmat, (const a_stem_table *)&pdata->StemSnapH,
+ &pfh->snap_h, 0, pfh->axes_swapped, "h");
+ }
+ if ( pfh->use_y_hints )
+ { gs_fixed_point vw;
+ fixed *vp = (pfh->axes_swapped ? &vw.x : &vw.y);
+ pixel_scale *psp =
+ (pfh->axes_swapped ? &pfh->scale.x : &pfh->scale.y);
+ /* Convert BlueFuzz to device pixels. */
+ if ( gs_distance_transform2fixed(pmat, 0.0,
+ (float)pdata->BlueFuzz,
+ &vw) < 0
+ )
+ vw.x = vw.y = fixed_0;
+ pfh->blue_fuzz = any_abs(*vp);
+ /*
+ * Decide whether to suppress overshoots. The formula in
+ * section 5.6 of the "Adobe Type 1 Font Format" says that
+ * at 300 dpi, if BlueScale = (P - 0.49) / 240, overshoot
+ * suppression turns off at point sizes at least P, i.e.:
+ * P >= BlueScale * 240 + 0.49.
+ * At 300 dpi, P = |CTM.yy| / (300/72), so the condition is
+ * equivalent to
+ * |CTM.yy| >= BlueScale * 1000 + 2.0417,
+ * or
+ * BlueScale >= (|CTM.yy| - 2.0417) / 1000.
+ * Since *pmat is the concatenation of the FontMatrix and
+ * CTM, if we assume a 1000-unit scale, this is equivalent to
+ * BlueScale >= |pmat->yy| - 0.00020417.
+ * Since the constant term is slightly smaller than
+ * fixed_epsilon, we just disregard it.
+ *
+ * According to the same section of the Adobe documentation,
+ * there is a requirement that BlueScale times the maximum
+ * alignment zone height must be less than 1. We enforced
+ * this when the font was constructed (in zfont1.c).
+ */
+ if ( gs_distance_transform2fixed(pmat, 0.0, 1.0, &vw) < 0 )
+ vw.x = vw.y = fixed_0;
+ pfh->suppress_overshoot =
+ fixed2float(any_abs(*vp) >> psp->log2_unit) <
+ pdata->BlueScale;
+ if ( gs_distance_transform2fixed(pmat, 0.0, pdata->BlueShift,
+ &vw) < 0
+ )
+ vw.x = vw.y = fixed_0;
+ pfh->blue_shift = any_abs(*vp);
+ /* Tweak up blue_shift if it is less than half a pixel. */
+ /* See the discussion of BlueShift in section 5.7 of */
+ /* "Adobe Type 1 Font Format." */
+ if ( pfh->blue_shift < psp->half )
+ pfh->blue_shift = psp->half;
+ if_debug6('y', "[y]blue_fuzz=%d->%g, blue_scale=%g, blue_shift=%g->%g, sup_ov=%d\n",
+ pdata->BlueFuzz, fixed2float(pfh->blue_fuzz),
+ pdata->BlueScale,
+ pdata->BlueShift, fixed2float(pfh->blue_shift),
+ pfh->suppress_overshoot);
+ zp = compute_zones(pmat, pfh,
+ (const a_zone_table *)&pdata->BlueValues,
+ (const a_zone_table *)&pdata->FamilyBlues,
+ zp, 1);
+ zp = compute_zones(pmat, pfh,
+ (const a_zone_table *)&pdata->OtherBlues,
+ (const a_zone_table *)&pdata->FamilyOtherBlues,
+ zp, max_OtherBlues);
+ compute_snaps(pmat, (const a_stem_table *)&pdata->StdVW,
+ &pfh->snap_v, 1, !pfh->axes_swapped, "v");
+ compute_snaps(pmat, (const a_stem_table *)&pdata->StemSnapV,
+ &pfh->snap_v, 1, !pfh->axes_swapped, "v");
+ }
+ pfh->a_zone_count = zp - &pfh->a_zones[0];
+}
+
+/* Transform one set of stem snap widths. */
+private void near
+compute_snaps(const gs_matrix_fixed *pmat, const a_stem_table *pst,
+ stem_snap_table *psst, int from_y, int to_y, const char *tname)
+{ gs_fixed_point wxy;
+ fixed *wp = (to_y ? &wxy.y : &wxy.x);
+ int i;
+ int j = psst->count;
+ for ( i = 0; i < pst->count; i++ )
+ { float w = pst->values[i];
+ int code =
+ (from_y ?
+ gs_distance_transform2fixed(pmat, 0.0, w, &wxy) :
+ gs_distance_transform2fixed(pmat, w, 0.0, &wxy)
+ );
+ if ( code < 0 )
+ continue;
+ psst->data[j] = any_abs(*wp);
+ if_debug3('y', "[y]snap_%s[%d]=%g\n", tname, j,
+ fixed2float(psst->data[j]));
+ j++;
+ }
+ psst->count = j;
+}
+
+/* Compute the alignment zones for one set of 'blue' values. */
+private alignment_zone *near
+compute_zones(const gs_matrix_fixed *pmat, const font_hints *pfh,
+ const a_zone_table *blues, const a_zone_table *family_blues,
+ alignment_zone *zp, int bottom_count)
+{ int i;
+ fixed fuzz = pfh->blue_fuzz;
+ int inverted =
+ (pfh->axes_swapped ? pfh->x_inverted : pfh->y_inverted);
+ for ( i = 0; i < blues->count; i += 2 )
+ { const float *vp = &blues->values[i];
+ zp->is_top_zone = i >> 1 >= bottom_count;
+ if ( transform_zone(pmat, pfh, vp, zp) < 0 )
+ continue;
+ if_debug5('y', "[y]blues[%d]=%g,%g -> %g,%g\n",
+ i >> 1, vp[0], vp[1],
+ fixed2float(zp->v0), fixed2float(zp->v1));
+ if ( i < family_blues->count )
+ { /* Check whether family blues should supersede. */
+ alignment_zone fz;
+ const float *fvp = &family_blues->values[i];
+ fixed unit = (pfh->axes_swapped ?
+ pfh->scale.x.unit : pfh->scale.y.unit);
+ fixed diff;
+ if ( transform_zone(pmat, pfh, fvp, &fz) < 0 )
+ continue;
+ if_debug5('y', "[y]f_blues[%d]=%g,%g -> %g,%g\n",
+ i >> 1, fvp[0], fvp[1],
+ fixed2float(fz.v0), fixed2float(fz.v1));
+ diff = (zp->v1 - zp->v0) - (fz.v1 - fz.v0);
+ if ( diff > -unit && diff < unit )
+ zp->v0 = fz.v0, zp->v1 = fz.v1;
+ }
+ /* Compute the flat position, and add the fuzz. */
+ if ( (inverted ? zp->is_top_zone : !zp->is_top_zone) )
+ zp->flat = zp->v1, zp->v0 -= fuzz;
+ else
+ zp->flat = zp->v0, zp->v1 += fuzz;
+ zp++;
+ }
+ return zp;
+}
+
+/* Transform a single alignment zone to device coordinates, */
+/* taking axis swapping into account. */
+private int near
+transform_zone(const gs_matrix_fixed *pmat, const font_hints *pfh,
+ const float *vp, alignment_zone *zp)
+{ gs_fixed_point p0, p1;
+ fixed v0, v1;
+ int code;
+ if ( (code = gs_point_transform2fixed(pmat, 0.0, vp[0], &p0)) < 0 ||
+ (code = gs_point_transform2fixed(pmat, 0.0, vp[1], &p1)) < 0
+ )
+ return code;
+ if ( pfh->axes_swapped ) v0 = p0.x, v1 = p1.x;
+ else v0 = p0.y, v1 = p1.y;
+ if ( v0 <= v1 ) zp->v0 = v0, zp->v1 = v1;
+ else zp->v0 = v1, zp->v1 = v0;
+ return 0;
+}
diff --git a/pstoraster/gxhint2.c b/pstoraster/gxhint2.c
new file mode 100644
index 000000000..0d5bfaeac
--- /dev/null
+++ b/pstoraster/gxhint2.c
@@ -0,0 +1,308 @@
+/* Copyright (C) 1990, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxhint2.c */
+/* Character level hints for Type 1 fonts. */
+#include "gx.h"
+#include "gserrors.h"
+#include "gxarith.h"
+#include "gxfixed.h"
+#include "gxmatrix.h"
+#include "gxchar.h"
+#include "gxfont.h"
+#include "gxfont1.h"
+#include "gxtype1.h"
+#include "gxop1.h"
+
+/* Define the tolerance for testing whether a point is in a zone, */
+/* in device pixels. (Maybe this should be variable?) */
+#define stem_tolerance float2fixed(0.05)
+
+/* Forward references */
+
+private stem_hint *near type1_stem(P3(stem_hint_table *, fixed, fixed));
+private fixed near find_snap(P3(fixed, const stem_snap_table *, const pixel_scale *));
+private alignment_zone *near find_zone(P3(gs_type1_state *, fixed, fixed));
+
+/* Reset the stem hints. */
+void
+reset_stem_hints(register gs_type1_state *pcis)
+{ pcis->hstem_hints.count = 0;
+ pcis->vstem_hints.count = 0;
+ update_stem_hints(pcis);
+}
+
+/* Update the internal stem hint pointers after moving or copying the state. */
+void
+update_stem_hints(register gs_type1_state *pcis)
+{ pcis->hstem_hints.current = 0;
+ pcis->vstem_hints.current = 0;
+}
+
+/* ------ Add hints ------ */
+
+#define c_fixed(d, c) m_fixed(d, c, pcis->fc, max_coeff_bits)
+
+/* Add a horizontal stem hint. */
+void
+type1_do_hstem(register gs_type1_state *pcis, fixed y, fixed dy,
+ const gs_matrix_fixed *pmat)
+{ stem_hint *psh;
+ alignment_zone *pz;
+ const pixel_scale *psp;
+ fixed v, dv, adj_dv;
+ fixed vtop, vbot;
+
+ if ( !pcis->fh.use_y_hints || !pmat->txy_fixed_valid )
+ return;
+ y += pcis->lsb.y + pcis->adxy.y;
+ if ( pcis->fh.axes_swapped )
+ psp = &pcis->scale.x,
+ v = pcis->vs_offset.x + c_fixed(y, yx) + pmat->tx_fixed,
+ dv = c_fixed(dy, yx);
+ else
+ psp = &pcis->scale.y,
+ v = pcis->vs_offset.y + c_fixed(y, yy) + pmat->ty_fixed,
+ dv = c_fixed(dy, yy);
+ if ( dy < 0 )
+ vbot = v + dv, vtop = v;
+ else
+ vbot = v, vtop = v + dv;
+ if ( dv < 0 ) v += dv, dv = -dv;
+ psh = type1_stem(&pcis->hstem_hints, v, dv);
+ if ( psh == 0 ) return;
+ adj_dv = find_snap(dv, &pcis->fh.snap_h, psp);
+ pz = find_zone(pcis, vbot, vtop);
+ if ( pz != 0 )
+ { /* Use the alignment zone to align the outer stem edge. */
+ int inverted =
+ (pcis->fh.axes_swapped ? pcis->fh.x_inverted : pcis->fh.y_inverted);
+ int adjust_v1 =
+ (inverted ? !pz->is_top_zone : pz->is_top_zone);
+ fixed flat_v = pz->flat;
+ fixed overshoot =
+ (pz->is_top_zone ? vtop - flat_v : flat_v - vbot);
+ fixed pos_over =
+ (inverted ? -overshoot : overshoot);
+ fixed ddv = adj_dv - dv;
+ fixed shift = scaled_rounded(flat_v, psp) - flat_v;
+ if ( pos_over > 0 )
+ {
+ if ( pos_over < pcis->fh.blue_shift || pcis->fh.suppress_overshoot )
+ { /* Character is small, suppress overshoot. */
+ if_debug0('y', "[y]suppress overshoot\n");
+ if ( pz->is_top_zone )
+ shift -= overshoot;
+ else
+ shift += overshoot;
+ }
+ else
+ if ( pos_over < psp->unit )
+ { /* Enforce overshoot. */
+ if_debug0('y', "[y]enforce overshoot\n");
+ if ( overshoot < 0 )
+ overshoot = -psp->unit - overshoot;
+ else
+ overshoot = psp->unit - overshoot;
+ if ( pz->is_top_zone )
+ shift += overshoot;
+ else
+ shift -= overshoot;
+ }
+ }
+ if ( adjust_v1 )
+ psh->dv1 = shift, psh->dv0 = shift - ddv;
+ else
+ psh->dv0 = shift, psh->dv1 = shift + ddv;
+ if_debug2('y', "[y]flat_v = %g, overshoot = %g for:\n",
+ fixed2float(flat_v), fixed2float(overshoot));
+ }
+ else
+ { /* Align the stem so its edges fall on pixel boundaries. */
+ fixed diff2_dv = arith_rshift_1(adj_dv - dv);
+ fixed edge = v - diff2_dv;
+ fixed diff_v = scaled_rounded(edge, psp) - edge;
+ psh->dv0 = diff_v - diff2_dv;
+ psh->dv1 = diff_v + diff2_dv;
+ }
+ if_debug9('y', "[y]hstem %d/%d: %g,%g -> %g(%g)%g ; d = %g,%g\n",
+ (int)(psh - &pcis->hstem_hints.data[0]),
+ pcis->hstem_hints.count,
+ fixed2float(y), fixed2float(dy),
+ fixed2float(v), fixed2float(dv), fixed2float(v + dv),
+ fixed2float(psh->dv0), fixed2float(psh->dv1));
+}
+
+/* Add a vertical stem hint. */
+void
+type1_do_vstem(register gs_type1_state *pcis, fixed x, fixed dx,
+ const gs_matrix_fixed *pmat)
+{ stem_hint *psh;
+ const pixel_scale *psp;
+ fixed v, dv, adj_dv;
+ fixed edge, diff_v, diff2_dv;
+ if ( !pcis->fh.use_x_hints ) return;
+ x += pcis->lsb.x + pcis->adxy.x;
+ if ( pcis->fh.axes_swapped )
+ psp = &pcis->scale.y,
+ v = pcis->vs_offset.y + c_fixed(x, xy) + pmat->ty_fixed,
+ dv = c_fixed(dx, xy);
+ else
+ psp = &pcis->scale.x,
+ v = pcis->vs_offset.x + c_fixed(x, xx) + pmat->tx_fixed,
+ dv = c_fixed(dx, xx);
+ if ( dv < 0 ) v += dv, dv = -dv;
+ psh = type1_stem(&pcis->vstem_hints, v, dv);
+ if ( psh == 0 ) return;
+ adj_dv = find_snap(dv, &pcis->fh.snap_v, psp);
+ if ( pcis->pfont->data.ForceBold && adj_dv < psp->unit )
+ adj_dv = psp->unit;
+ /* Align the stem so its edges fall on pixel boundaries. */
+ diff2_dv = arith_rshift_1(adj_dv - dv);
+ edge = v - diff2_dv;
+ diff_v = scaled_rounded(edge, psp) - edge;
+ psh->dv0 = diff_v - diff2_dv;
+ psh->dv1 = diff_v + diff2_dv;
+ if_debug9('y', "[y]vstem %d/%d: %g,%g -> %g(%g)%g ; d = %g,%g\n",
+ (int)(psh - &pcis->vstem_hints.data[0]),
+ pcis->vstem_hints.count,
+ fixed2float(x), fixed2float(dx),
+ fixed2float(v), fixed2float(dv), fixed2float(v + dv),
+ fixed2float(psh->dv0), fixed2float(psh->dv1));
+}
+
+/* Adjust the character center for a vstem3. */
+/****** NEEDS UPDATING FOR SCALE ******/
+void
+type1_do_center_vstem(gs_type1_state *pcis, fixed x0, fixed dx,
+ const gs_matrix_fixed *pmat)
+{ fixed x1 = x0 + dx;
+ gs_fixed_point pt0, pt1, width;
+ fixed center, int_width;
+ fixed *psxy;
+ if ( gs_point_transform2fixed(pmat, fixed2float(x0), 0.0, &pt0) < 0 ||
+ gs_point_transform2fixed(pmat, fixed2float(x1), 0.0, &pt1) < 0
+ )
+ { /* Punt. */
+ return;
+ }
+ width.x = pt0.x - pt1.x;
+ if ( width.x < 0 )
+ width.x = - width.x;
+ width.y = pt0.y - pt1.y;
+ if ( width.y < 0 )
+ width.y = - width.y;
+ if ( width.y < float2fixed(0.05) )
+ { /* Vertical on device */
+ center = arith_rshift_1(pt0.x + pt1.x);
+ int_width = fixed_rounded(width.x);
+ psxy = &pcis->vs_offset.x;
+ }
+ else
+ { /* Horizontal on device */
+ center = arith_rshift_1(pt0.y + pt1.y);
+ int_width = fixed_rounded(width.y);
+ psxy = &pcis->vs_offset.y;
+ }
+ if ( int_width == fixed_0 || (int_width & fixed_1) == 0 )
+ { /* Odd width, center stem over pixel. */
+ *psxy = fixed_floor(center) + fixed_half - center;
+ }
+ else
+ { /* Even width, center stem between pixels. */
+ *psxy = fixed_rounded(center) - center;
+ }
+ /* We can't fix up the current point here, */
+ /* but we can fix up everything else. */
+ /****** TO BE COMPLETED ******/
+}
+
+/* Add a stem hint, keeping the table sorted. */
+/* We know that d >= 0. */
+/* Return the stem hint pointer, or 0 if the table is full. */
+private stem_hint *near
+type1_stem(stem_hint_table *psht, fixed v0, fixed d)
+{ stem_hint *bot = &psht->data[0];
+ stem_hint *top = bot + psht->count;
+ if ( psht->count >= max_stems )
+ return 0;
+ while ( top > bot && v0 < top[-1].v0 )
+ { *top = top[-1];
+ top--;
+ }
+ /* Add a little fuzz for insideness testing. */
+ top->v0 = v0 - stem_tolerance;
+ top->v1 = v0 + d + stem_tolerance;
+ psht->count++;
+ return top;
+}
+
+/* Compute the adjusted width of a stem. */
+/* The value returned is always a multiple of scale.unit. */
+private fixed near
+find_snap(fixed dv, const stem_snap_table *psst, const pixel_scale *pps)
+{ fixed best = pps->unit;
+ fixed adj_dv;
+ int i;
+ for ( i = 0; i < psst->count; i++ )
+ { fixed diff = psst->data[i] - dv;
+ if ( any_abs(diff) < any_abs(best) )
+ { if_debug3('Y', "[Y]possibly snap %g to [%d]%g\n",
+ fixed2float(dv), i,
+ fixed2float(psst->data[i]));
+ best = diff;
+ }
+ }
+ adj_dv = scaled_rounded((any_abs(best) < pps->unit ? dv + best : dv),
+ pps);
+ if ( adj_dv == 0 )
+ adj_dv = pps->unit;
+#ifdef DEBUG
+ if ( adj_dv == dv )
+ if_debug1('Y', "[Y]no snap %g\n", fixed2float(dv));
+ else
+ if_debug2('Y', "[Y]snap %g to %g\n",
+ fixed2float(dv), fixed2float(adj_dv));
+#endif
+ return adj_dv;
+}
+
+/* Find the applicable alignment zone for a stem, if any. */
+/* vbot and vtop are the bottom and top of the stem, */
+/* but without interchanging if the y axis is inverted. */
+private alignment_zone *near
+find_zone(gs_type1_state *pcis, fixed vbot, fixed vtop)
+{ alignment_zone *pz;
+ for ( pz = &pcis->fh.a_zones[pcis->fh.a_zone_count];
+ --pz >= &pcis->fh.a_zones[0];
+ )
+ { fixed v = (pz->is_top_zone ? vtop : vbot);
+ if ( v >= pz->v0 && v <= pz->v1 )
+ { if_debug2('Y', "[Y]stem crosses %s-zone %d\n",
+ (pz->is_top_zone ? "top" : "bottom"),
+ (int)(pz - &pcis->fh.a_zones[0]));
+ return pz;
+ }
+ }
+ return 0;
+}
diff --git a/pstoraster/gxhint3.c b/pstoraster/gxhint3.c
new file mode 100644
index 000000000..d4dd3e64b
--- /dev/null
+++ b/pstoraster/gxhint3.c
@@ -0,0 +1,459 @@
+/* Copyright (C) 1994, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxhint3.c */
+/* Apply hints for Type 1 fonts. */
+#include "gx.h"
+#include "gserrors.h"
+#include "gxarith.h"
+#include "gxfixed.h"
+#include "gxmatrix.h"
+#include "gxchar.h"
+#include "gxfont.h"
+#include "gxfont1.h"
+#include "gxtype1.h"
+#include "gxop1.h"
+#include "gzpath.h"
+
+/* Define whether we are using new algorithms here. */
+#define NEW
+
+/* ------ Path hints ------ */
+
+/* Forward references */
+private void
+ apply_hstem_hints(P3(gs_type1_state *, int, gs_fixed_point *)),
+ apply_vstem_hints(P3(gs_type1_state *, int, gs_fixed_point *));
+
+
+/*
+ * Apply hints along a newly added tail of a subpath.
+ * Path segments require hints as follows:
+ * Nearly vertical line: vstem hints at both ends.
+ * Nearly horizontal line: hstem hints at both ends.
+ * Curve with nearly vertical/horizontal start/end:
+ * vstem/hstem hints at start/end.
+ * We also must take care to handle the implicit closing line for
+ * subpaths that aren't explicitly closed.
+ */
+#define hint_vert 001
+#define hint_vert_lower 003
+#define hint_vert_upper 005
+#define hint_horz 010
+#define hint_horz_lower 030
+#define hint_horz_upper 050
+#define nearly_axial(dmajor, dminor)\
+ ((dminor) <= (dmajor) >> 4 /* float2fixed(0.25) */)
+
+/* Determine which types of hints, if any, are applicable to a given */
+/* line segment. */
+private int near
+line_hints(const gs_type1_state *pcis, const gs_fixed_point *p0,
+ const gs_fixed_point *p1)
+{ fixed dx = p1->x - p0->x, adx;
+ fixed dy = p1->y - p0->y, ady;
+ int hints;
+
+ /* Map the deltas back into character space. */
+ if ( pcis->fh.axes_swapped )
+ { fixed t = dx; dx = dy; dy = t;
+ }
+ if ( pcis->fh.x_inverted )
+ dx = -dx;
+ if ( pcis->fh.y_inverted )
+ dy = -dy;
+ adx = any_abs(dx);
+ ady = any_abs(dy);
+ if ( dy != 0 && nearly_axial(ady, adx) )
+ hints = (dy > 0 ? hint_vert_upper : hint_vert_lower);
+ else if ( dx != 0 && nearly_axial(adx, ady) )
+ hints = (dx < 0 ? hint_horz_upper : hint_horz_lower);
+ else
+ hints = 0;
+ if_debug7('y', "[y]hint from 0x%lx(%g,%g) to 0x%lx(%g,%g) = %d\n",
+ (ulong)p0, fixed2float(p0->x), fixed2float(p0->y),
+ (ulong)p1, fixed2float(p1->x), fixed2float(p1->y),
+ hints);
+ return hints;
+}
+
+/* Apply hints at a point. Optionally return the amount of adjustment. */
+private void near
+apply_hints_at(gs_type1_state *pcis, int hints, gs_fixed_point *ppt,
+ gs_fixed_point *pdiff)
+{ fixed ptx = ppt->x, pty = ppt->y;
+ if_debug4('y', "[y]applying hints %d to 0x%lx(%g,%g) ...\n",
+ hints, (ulong)ppt, fixed2float(ptx), fixed2float(pty));
+ if ( (hints & hint_vert) != 0 &&
+ (pcis->vstem_hints.count & pcis->dotsection_flag) != 0
+ )
+ { /* "Upper" vertical edges move in +Y. */
+ apply_vstem_hints(pcis, (hints & hint_vert_upper) -
+ (hints & hint_vert_lower), ppt);
+ }
+ if ( (hints & hint_horz) != 0 &&
+ (pcis->hstem_hints.count & pcis->dotsection_flag) != 0
+ )
+ { /* "Upper" horizontal edges move in -X. */
+ apply_hstem_hints(pcis, (hints & hint_horz_lower) -
+ (hints & hint_horz_upper), ppt);
+ }
+ if ( pdiff != NULL )
+ pdiff->x = ppt->x - ptx,
+ pdiff->y = ppt->y - pty;
+ /* Here is where we would round *ppt to the nearest quarter-pixel */
+ /* if we wanted to. */
+ if_debug2('y', "[y] ... => (%g,%g)\n",
+ fixed2float(ppt->x), fixed2float(ppt->y));
+}
+
+#ifdef DEBUG
+private void near
+add_hint_diff_proc(gs_fixed_point *ppt, fixed dx, fixed dy)
+{ if_debug7('y', "[y]adding diff (%g,%g) to 0x%lx(%g,%g) => (%g,%g)\n",
+ fixed2float(dx), fixed2float(dy), (ulong)ppt,
+ fixed2float(ppt->x), fixed2float(ppt->y),
+ fixed2float(ppt->x + dx),
+ fixed2float(ppt->y + dy));
+ ppt->x += dx;
+ ppt->y += dy;
+}
+#define add_hint_dxdy(pt, dx,dy)\
+ add_hint_diff_proc(&(pt), dx, dy)
+#else
+#define add_hint_dxdy(pt, dx, dy)\
+ (pt).x += (dx), (pt).y += (dy)
+#endif
+#define add_hint_diff(pt, diff)\
+ add_hint_dxdy(pt, (diff).x, (diff).y)
+
+/* Test whether a line is null. */
+#define line_is_null(p0, p1)\
+ (any_abs((p1).x - (p0).x) + any_abs((p1).y - (p0).y) < fixed_epsilon * 4)
+
+/* Adjust the control points of a curve when moving one end. */
+private void
+adjust_curve_start(curve_segment *pcseg, const gs_fixed_point *pdiff)
+{ fixed dx = pdiff->x, dy = pdiff->y;
+ fixed dx2 = arith_rshift(dx, 2), dy2 = arith_rshift(dy, 2);
+ add_hint_dxdy(pcseg->p1, dx, dy);
+ add_hint_dxdy(pcseg->p2, dx2, dy2);
+}
+private void
+adjust_curve_end(curve_segment *pcseg, const gs_fixed_point *pdiff)
+{ fixed dx = pdiff->x, dy = pdiff->y;
+ fixed dx2 = arith_rshift(dx, 2), dy2 = arith_rshift(dy, 2);
+ add_hint_dxdy(pcseg->p1, dx2, dy2);
+ add_hint_dxdy(pcseg->p2, dx, dy);
+}
+
+/*
+ * Propagate a final wraparound hint back through any null line segments
+ * to a possible curve. pseg_last.pt has already been adjusted.
+ */
+private void
+apply_final_hint(segment *pseg_last, const gs_fixed_point *pdiff)
+{ segment *pseg;
+ for ( pseg = pseg_last; ; pseg = pseg->prev )
+ { segment *prev = pseg->prev;
+ switch ( pseg->type )
+ {
+ case s_curve:
+ adjust_curve_end(((curve_segment *)pseg), pdiff);
+ return;
+ case s_line:
+ case s_line_close:
+ if ( !line_is_null(prev->pt, pseg->pt) )
+ return;
+ add_hint_diff(prev->pt, *pdiff);
+ break;
+ default: /* s_start */
+ return;
+ }
+ }
+}
+
+/*
+ * Apply hints along a subpath. If closing is true, consider the subpath
+ * closed; if not, we may add more to the subpath later. In the latter case,
+ * don't do anything if the subpath is closed, because we already applied
+ * the hints.
+ */
+void
+type1_apply_path_hints(gs_type1_state *pcis, bool closing, gx_path *ppath)
+{ segment *pseg = pcis->hint_next;
+#define pseg_curve ((curve_segment *)pseg)
+ segment *pnext;
+#define pnext_curve ((curve_segment *)pnext)
+ subpath *psub = ppath->current_subpath;
+ /*
+ * hints holds the set of hints that have already been applied (if
+ * applicable) to pseg->pt, and hence should not be applied again.
+ */
+ int hints = pcis->hints_pending;
+ gs_fixed_point diff;
+
+ if ( pseg == 0 )
+ { /* Start at the beginning of the subpath. */
+ if ( psub == 0 )
+ return;
+ if ( psub->is_closed && !closing )
+ return;
+ pseg = (segment *)psub;
+ if ( pseg->next == 0 )
+ return;
+ hints = 0;
+ pcis->unmoved_start = psub->pt;
+ pcis->unmoved_end = psub->pt;
+ }
+ else
+ hints = pcis->hints_pending;
+ diff.x = diff.y = 0;
+ for ( ; (pnext = pseg->next) != 0; pseg = pnext )
+ { int hints_next;
+ /* Apply hints to the end of the previous segment (pseg) */
+ /* and the beginning of this one (pnext). */
+ gs_fixed_point dseg;
+ switch ( pnext->type )
+ {
+ case s_curve:
+ { int hints_first =
+ line_hints(pcis, &pcis->unmoved_end,
+ &pnext_curve->p1) & ~hints;
+ gs_fixed_point diff2;
+ if ( pseg == (segment *)psub )
+ pcis->hints_initial = hints_first;
+ apply_hints_at(pcis, hints_first, &pseg->pt, &dseg);
+ diff2.x = pseg->pt.x - pcis->unmoved_end.x;
+ diff2.y = pseg->pt.y - pcis->unmoved_end.y;
+ hints_next = line_hints(pcis, &pnext_curve->p2,
+ &pnext->pt);
+ adjust_curve_start(pnext_curve, &diff2);
+ apply_hints_at(pcis, hints_next, &pnext_curve->p2,
+ &diff);
+ pcis->unmoved_end = pnext->pt;
+ add_hint_diff(pnext->pt, diff);
+ } break;
+#ifdef NEW
+ case s_line_close:
+ /* Undo any initial hints propagated to the end. */
+ pnext->pt = pcis->unmoved_start;
+#endif
+ default: /* s_line, s_line_close */
+ if ( line_is_null(pnext->pt, pcis->unmoved_end) )
+ { /* This is a null line, just move it. */
+ hints_next = hints;
+ dseg.x = dseg.y = 0; /* don't move p2 again */
+ }
+ else
+ { hints_next =
+ line_hints(pcis, &pcis->unmoved_end, &pnext->pt);
+ apply_hints_at(pcis, hints_next & ~hints,
+ &pseg->pt, &dseg);
+ }
+ if ( pseg == (segment *)psub )
+ pcis->hints_initial = hints_next;
+ pcis->unmoved_end = pnext->pt;
+ apply_hints_at(pcis, hints_next, &pnext->pt, NULL);
+ }
+ if ( pseg->type == s_curve )
+ adjust_curve_end(pseg_curve, &dseg);
+ hints = hints_next;
+ }
+ if ( closing )
+ { /* Handle the end of the subpath wrapping around to the start. */
+ /* This is ugly, messy code that we can surely improve. */
+ fixed ctemp;
+ /* Some fonts don't use closepath when they should.... */
+ bool closed =
+ (pseg->type == s_line_close ||
+ (ctemp = pseg->pt.x - psub->pt.x,
+ any_abs(ctemp) < float2fixed(0.1)) ||
+ (ctemp = pseg->pt.y - psub->pt.y,
+ any_abs(ctemp) < float2fixed(0.1)));
+ segment *pfirst = psub->next;
+#define pfirst_curve ((curve_segment *)pfirst)
+ int hints_first = pcis->hints_initial;
+ if ( closed )
+ { /*
+ * Apply the union of the hints at both the end
+ * (pseg) and the start (psub) of the subpath. Note
+ * that we have already applied hints at the end,
+ * and hints_first at the start.
+ */
+#ifdef NEW
+ int do_x, do_y;
+ gs_fixed_point diff2;
+
+ if ( pcis->fh.axes_swapped )
+ do_x = hint_horz, do_y = hint_vert;
+ else
+ do_x = hint_vert, do_y = hint_horz;
+
+ { /* Apply hints_first - hints to the end. */
+ int hints_end = hints_first & ~hints;
+ diff2.x =
+ (hints_end & do_x ?
+ psub->pt.x - pcis->unmoved_start.x : 0);
+ diff2.y =
+ (hints_end & do_y ?
+ psub->pt.y - pcis->unmoved_start.y : 0);
+ }
+
+ { /* Apply hints - hints_first to the start. */
+ int hints_start = hints & ~hints_first;
+ diff.x =
+ (hints_start & do_x ?
+ pseg->pt.x - pcis->unmoved_end.x : 0);
+ diff.y =
+ (hints_start & do_y ?
+ pseg->pt.y - pcis->unmoved_end.y : 0);
+ }
+
+ add_hint_diff(pseg->pt, diff2);
+ apply_final_hint(pseg, &diff2);
+ add_hint_diff(psub->pt, diff);
+#else
+ hints &= ~hints_first;
+ { gs_fixed_point end, diff2;
+ end = pseg->pt;
+ apply_hints_at(pcis, hints, &end, &diff);
+ add_hint_diff(psub->pt, diff);
+ diff2.x = psub->pt.x - pcis->unmoved_start.x -
+ (pseg->pt.x - pcis->unmoved_end.x);
+ diff2.y = psub->pt.y - pcis->unmoved_start.y -
+ (pseg->pt.y - pcis->unmoved_end.y);
+ /* If the last segment closes the path, */
+ /* make sure we don't apply the last hint twice. */
+ /* (Thanks to Hans-Gerd Straeter for this fix.) */
+ if ( !line_is_null(psub->pt, pseg->pt) )
+ add_hint_diff(pseg->pt, diff2);
+ apply_final_hint(pseg, &diff2);
+ }
+#endif
+ }
+ else
+ { int hints_close =
+ line_hints(pcis, &pcis->unmoved_end,
+ &pcis->unmoved_start);
+ hints_close &= ~(hints | hints_first);
+ apply_hints_at(pcis, hints_close, &pseg->pt, &diff);
+ apply_final_hint(pseg, &diff);
+ apply_hints_at(pcis, hints_close, &psub->pt, &diff);
+ }
+ if ( pfirst->type == s_curve )
+ adjust_curve_start(pfirst_curve, &diff);
+ pcis->hint_next = 0;
+ pcis->hints_pending = 0;
+#undef pfirst_curve
+ }
+ else
+ { pcis->hint_next = pseg;
+ pcis->hints_pending = hints;
+ }
+#undef pseg_curve
+#undef pnext_curve
+}
+
+/* ------ Individual hints ------ */
+
+private stem_hint *near search_hints(P2(stem_hint_table *, fixed));
+
+/*
+ * Adjust a point according to the relevant hints.
+ * x and y are the current point in device space after moving;
+ * dx or dy is the delta component in character space.
+ * The caller is responsible for checking use_hstem_hints or use_vstem_hints
+ * and not calling the find_xxx_hints routine if this is false.
+ * Note that if use_x/y_hints is false, no entries ever get made
+ * in the stem hint tables, so these routines will not get called.
+ */
+
+/*
+ * To figure out which side of the stem we are on, we assume that
+ * the inside of the filled area is always to the left of the edge, i.e.,
+ * edges moving in -X or +Y in character space are on the "upper" side of
+ * the stem, while edges moving by +X or -Y are on the "lower" side.
+ * (See section 3.5 of the Adobe Type 1 Font Format book.)
+ * However, since dv0 and dv1 correspond to the lesser and greater
+ * values in *device* space, we have to take the inversion of the other axis
+ * into account when selecting between them.
+ */
+
+private void
+apply_vstem_hints(gs_type1_state *pcis, int dy, gs_fixed_point *ppt)
+{ fixed *pv = (pcis->fh.axes_swapped ? &ppt->y : &ppt->x);
+ stem_hint *ph = search_hints(&pcis->vstem_hints, *pv);
+ if ( ph != 0 )
+ {
+#define vstem_upper(pcis, dy)\
+ (pcis->fh.x_inverted ? dy < 0 : dy > 0)
+ if_debug3('Y', "[Y]use vstem %d: %g (%s)",
+ (int)(ph - &pcis->vstem_hints.data[0]),
+ fixed2float(*pv),
+ (dy == 0 ? "middle" :
+ vstem_upper(pcis, dy) ? "upper" : "lower"));
+ *pv += (dy == 0 ? arith_rshift_1(ph->dv0 + ph->dv1) :
+ vstem_upper(pcis, dy) ? ph->dv1 : ph->dv0);
+ if_debug1('Y', " -> %g\n", fixed2float(*pv));
+#undef vstem_upper
+ }
+}
+
+private void
+apply_hstem_hints(gs_type1_state *pcis, int dx, gs_fixed_point *ppt)
+{ fixed *pv = (pcis->fh.axes_swapped ? &ppt->x : &ppt->y);
+ stem_hint *ph = search_hints(&pcis->hstem_hints, *pv);
+ if ( ph != 0 )
+ {
+#define hstem_upper(pcis, dx)\
+ (pcis->fh.y_inverted ? dx > 0 : dx < 0)
+ if_debug3('Y', "[Y]use hstem %d: %g (%s)",
+ (int)(ph - &pcis->hstem_hints.data[0]),
+ fixed2float(*pv),
+ (dx == 0 ? "middle" :
+ hstem_upper(pcis, dx) ? "upper" : "lower"));
+ *pv += (dx == 0 ? arith_rshift_1(ph->dv0 + ph->dv1) :
+ hstem_upper(pcis, dx) ? ph->dv1 : ph->dv0);
+ if_debug1('Y', " -> %g\n", fixed2float(*pv));
+#undef hstem_upper
+ }
+}
+
+/* Search one hint table for an adjustment. */
+private stem_hint *near
+search_hints(stem_hint_table *psht, fixed v)
+{ stem_hint *table = &psht->data[0];
+ stem_hint *ph = table + psht->current;
+ if ( v >= ph->v0 && v <= ph->v1 )
+ return ph;
+ /* We don't bother with binary or even up/down search, */
+ /* because there won't be very many hints. */
+ for ( ph = &table[psht->count]; --ph >= table; )
+ if ( v >= ph->v0 && v <= ph->v1 )
+ { psht->current = ph - table;
+ return ph;
+ }
+ return 0;
+}
+
diff --git a/pstoraster/gxht.c b/pstoraster/gxht.c
new file mode 100644
index 000000000..fb5c539d0
--- /dev/null
+++ b/pstoraster/gxht.c
@@ -0,0 +1,439 @@
+/* Copyright (C) 1989, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxht.c */
+/* Halftone rendering routines for Ghostscript imaging library */
+#include "memory_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gsstruct.h"
+#include "gsbitops.h"
+#include "gsutil.h" /* for gs_next_ids */
+#include "gxfixed.h"
+#include "gzstate.h"
+#include "gxdevice.h" /* for gzht.h */
+#include "gzht.h"
+
+/* Define the sizes of the halftone cache. */
+#define max_cached_tiles_HUGE 5000 /* not used */
+#define max_ht_bits_HUGE 1000000 /* not used */
+#define max_cached_tiles_LARGE 577
+#define max_ht_bits_LARGE 100000
+#define max_cached_tiles_SMALL 25
+#define max_ht_bits_SMALL 1000
+
+/* Define the binary halftone device color type. */
+private dev_color_proc_load(gx_dc_ht_binary_load);
+private dev_color_proc_fill_rectangle(gx_dc_ht_binary_fill_rectangle);
+private struct_proc_enum_ptrs(dc_ht_binary_enum_ptrs);
+private struct_proc_reloc_ptrs(dc_ht_binary_reloc_ptrs);
+const gx_device_color_procs
+ gx_dc_procs_ht_binary =
+ { gx_dc_ht_binary_load, gx_dc_ht_binary_fill_rectangle,
+ dc_ht_binary_enum_ptrs, dc_ht_binary_reloc_ptrs
+ };
+#undef gx_dc_type_ht_binary
+const gx_device_color_procs _ds *gx_dc_type_ht_binary = &gx_dc_procs_ht_binary;
+#define gx_dc_type_ht_binary (&gx_dc_procs_ht_binary)
+/* GC procedures */
+#define cptr ((gx_device_color *)vptr)
+private ENUM_PTRS_BEGIN(dc_ht_binary_enum_ptrs) return 0;
+ ENUM_PTR(0, gx_device_color, colors.binary.b_ht);
+ case 1:
+ { gx_ht_tile *tile = cptr->colors.binary.b_tile;
+ ENUM_RETURN(tile - tile->index);
+ }
+ENUM_PTRS_END
+private RELOC_PTRS_BEGIN(dc_ht_binary_reloc_ptrs) {
+ uint index = cptr->colors.binary.b_tile->index;
+ RELOC_PTR(gx_device_color, colors.binary.b_ht);
+ RELOC_TYPED_OFFSET_PTR(gx_device_color, colors.binary.b_tile, index);
+} RELOC_PTRS_END
+#undef cptr
+
+/* Other GC procedures */
+private_st_ht_tiles();
+private ENUM_PTRS_BEGIN_PROC(ht_tiles_enum_ptrs) {
+ return 0;
+} ENUM_PTRS_END_PROC
+private RELOC_PTRS_BEGIN(ht_tiles_reloc_ptrs) {
+ /* Reset the bitmap pointers in the tiles. */
+ /* We know the first tile points to the base of the bits. */
+ gx_ht_tile *ht_tiles = vptr;
+ byte *bits = ht_tiles->tiles.data;
+ uint diff;
+
+ if ( bits == 0 )
+ return;
+ bits = gs_reloc_struct_ptr(bits, gcst);
+ if ( size == size_of(gx_ht_tile) ) /* only 1 tile */
+ { ht_tiles->tiles.data = bits;
+ return;
+ }
+ diff = ht_tiles[1].tiles.data - ht_tiles[0].tiles.data;
+ for ( ; size; ht_tiles++, size -= size_of(gx_ht_tile), bits += diff )
+ { ht_tiles->tiles.data = bits;
+ }
+} RELOC_PTRS_END
+private_st_ht_cache();
+
+/* Return the default sizes of the halftone cache. */
+uint
+gx_ht_cache_default_tiles(void)
+{
+#if arch_small_memory
+ return max_cached_tiles_SMALL;
+#else
+ return (gs_if_debug_c('.') ? max_cached_tiles_SMALL :
+ max_cached_tiles_LARGE);
+#endif
+}
+uint
+gx_ht_cache_default_bits(void)
+{
+#if arch_small_memory
+ return max_ht_bits_SMALL;
+#else
+ return (gs_if_debug_c('.') ? max_ht_bits_SMALL :
+ max_ht_bits_LARGE);
+#endif
+}
+
+/* Allocate a halftone cache. */
+gx_ht_cache *
+gx_ht_alloc_cache(gs_memory_t *mem, uint max_tiles, uint max_bits)
+{ gx_ht_cache *pcache =
+ gs_alloc_struct(mem, gx_ht_cache, &st_ht_cache,
+ "alloc_ht_cache(struct)");
+ byte *tbits =
+ gs_alloc_bytes(mem, max_bits, "alloc_ht_cache(bits)");
+ gx_ht_tile *ht_tiles =
+ gs_alloc_struct_array(mem, max_tiles, gx_ht_tile, &st_ht_tiles,
+ "alloc_ht_cache(ht_tiles)");
+
+ if ( pcache == 0 || tbits == 0 || ht_tiles == 0 )
+ { gs_free_object(mem, ht_tiles, "alloc_ht_cache(ht_tiles)");
+ gs_free_object(mem, tbits, "alloc_ht_cache(bits)");
+ gs_free_object(mem, pcache, "alloc_ht_cache(struct)");
+ return 0;
+ }
+ pcache->bits = tbits;
+ pcache->bits_size = max_bits;
+ pcache->ht_tiles = ht_tiles;
+ pcache->num_tiles = max_tiles;
+ pcache->order.cache = pcache;
+ pcache->order.transfer = 0;
+ gx_ht_clear_cache(pcache);
+ return pcache;
+}
+
+/* Make the cache order current, and return whether */
+/* there is room for all possible tiles in the cache. */
+bool
+gx_check_tile_cache(gs_state *pgs)
+{ const gx_ht_order *porder = &pgs->dev_ht->order;
+ gx_ht_cache *pcache = pgs->ht_cache;
+
+ if ( pcache->order.bits != porder->bits )
+ gx_ht_init_cache(pcache, porder);
+ return pcache->levels_per_tile == 1;
+}
+
+/*
+ * Determine whether a given (width, y, height) might fit into a single
+ * (non-strip) tile. If so, return the byte offset of the appropriate row
+ * from the beginning of the tile, and set *ppx to the x phase offset
+ * within the tile; if not, return -1.
+ */
+int
+gx_check_tile_size(gs_state *pgs, int w, int y, int h, int *ppx)
+{ int tsy;
+ const gx_strip_bitmap *ptile0 =
+ &pgs->ht_cache->ht_tiles[0].tiles; /* a typical tile */
+#define tile0 (*ptile0)
+
+ if ( h > tile0.rep_height || w > tile0.rep_width ||
+ tile0.shift != 0
+ )
+ return -1;
+ tsy = (y + imod(-pgs->ht_phase.y, tile0.rep_height)) %
+ tile0.rep_height;
+ if ( tsy + h > tile0.size.y )
+ return -1;
+ /* Tile fits in Y, might fit in X. */
+ *ppx = imod(-pgs->ht_phase.x, tile0.rep_width);
+ return tsy * tile0.raster;
+#undef tile0
+}
+
+/* Render a given level into a halftone cache. */
+private int render_ht(P4(gx_ht_tile *, int, const gx_ht_order *,
+ gx_bitmap_id));
+gx_ht_tile *
+gx_render_ht(gx_ht_cache *pcache, int b_level)
+{ const gx_ht_order *porder = &pcache->order;
+ int level = porder->levels[b_level];
+ gx_ht_tile *bt = &pcache->ht_tiles[level / pcache->levels_per_tile];
+
+ if ( bt->level != level )
+ { int code = render_ht(bt, level, porder, pcache->base_id + b_level);
+ if ( code < 0 )
+ return 0;
+ }
+ return bt;
+}
+
+/* Load the device color into the halftone cache if needed. */
+private int
+gx_dc_ht_binary_load(gx_device_color *pdevc, const gs_state *pgs)
+{ const gx_ht_order *porder = &pgs->dev_ht->order;
+ gx_ht_cache *pcache = pgs->ht_cache;
+
+ if ( pcache->order.bits != porder->bits )
+ gx_ht_init_cache(pcache, porder);
+ /* Expand gx_render_ht inline for speed. */
+ { int b_level = pdevc->colors.binary.b_level;
+ int level = porder->levels[b_level];
+ gx_ht_tile *bt = &pcache->ht_tiles[level / pcache->levels_per_tile];
+
+ if ( bt->level != level )
+ { int code = render_ht(bt, level, porder,
+ pcache->base_id + b_level);
+ if ( code < 0 )
+ return_error(gs_error_Fatal);
+ }
+ pdevc->colors.binary.b_tile = bt;
+ }
+ return 0;
+}
+
+/* Fill a rectangle with a binary halftone. */
+/* Note that we treat this as "texture" for RasterOp. */
+private int
+gx_dc_ht_binary_fill_rectangle(const gx_device_color *pdevc, int x, int y,
+ int w, int h, gx_device *dev, gs_logical_operation_t lop,
+ const gx_rop_source_t *source)
+{ if ( source == NULL && lop_no_S_is_T(lop) )
+ return (*dev_proc(dev, strip_tile_rectangle))(dev,
+ &pdevc->colors.binary.b_tile->tiles,
+ x, y, w, h, pdevc->colors.binary.color[0],
+ pdevc->colors.binary.color[1],
+ pdevc->phase.x, pdevc->phase.y);
+ /* Adjust the logical operation per transparent colors. */
+ if ( pdevc->colors.binary.color[0] == gx_no_color_index )
+ lop = rop3_use_D_when_T_0(lop);
+ if ( pdevc->colors.binary.color[1] == gx_no_color_index )
+ lop = rop3_use_D_when_T_1(lop);
+ if ( source == NULL )
+ source = &gx_rop_no_source;
+ return (*dev_proc(dev, strip_copy_rop))(dev, source->sdata,
+ source->sourcex, source->sraster, source->id,
+ (source->use_scolors ? source->scolors : NULL),
+ &pdevc->colors.binary.b_tile->tiles,
+ pdevc->colors.binary.color,
+ x, y, w, h, pdevc->phase.x, pdevc->phase.y,
+ lop);
+}
+
+/* Initialize the tile cache for a given screen. */
+/* Cache as many different levels as will fit. */
+void
+gx_ht_init_cache(gx_ht_cache *pcache, const gx_ht_order *porder)
+{ uint width = porder->width;
+ uint height = porder->height;
+ uint size = width * height + 1;
+ int width_unit =
+ (width <= ht_mask_bits / 2 ? ht_mask_bits / width * width :
+ width);
+ int height_unit = height;
+ uint raster = porder->raster;
+ uint tile_bytes = raster * height;
+ uint shift = porder->shift;
+ int num_cached;
+ int i;
+ byte *tbits = pcache->bits;
+
+ /* Make sure num_cached is within bounds */
+ num_cached = pcache->bits_size / tile_bytes;
+ if ( num_cached > size )
+ num_cached = size;
+ if ( num_cached > pcache->num_tiles )
+ num_cached = pcache->num_tiles;
+ if ( num_cached == size &&
+ tile_bytes * num_cached <= pcache->bits_size / 2
+ )
+ { /*
+ * We can afford to replicate every tile in the cache,
+ * which will reduce breakage when tiling. Since
+ * horizontal breakage is more expensive than vertical,
+ * and since wide shallow fills are more common than
+ * narrow deep fills, we replicate the tile horizontally.
+ * We do have to be careful not to replicate the tile
+ * to an absurdly large size, however.
+ */
+ uint rep_raster =
+ ((pcache->bits_size / num_cached) / height) &
+ ~(align_bitmap_mod - 1);
+ uint rep_count = rep_raster * 8 / width;
+ /*
+ * There's no real value in replicating the tile
+ * beyond the point where the byte width of the replicated
+ * tile is a multiple of a long.
+ */
+ if ( rep_count > sizeof(ulong) * 8 )
+ rep_count = sizeof(ulong) * 8;
+ width_unit = width * rep_count;
+ raster = bitmap_raster(width_unit);
+ tile_bytes = raster * height;
+ }
+ pcache->base_id = gs_next_ids(porder->num_levels + 1);
+ pcache->order = *porder;
+ pcache->num_cached = num_cached;
+ pcache->levels_per_tile = (size + num_cached - 1) / num_cached;
+ memset(tbits, 0, pcache->bits_size);
+ for ( i = 0; i < num_cached; i++, tbits += tile_bytes )
+ { register gx_ht_tile *bt = &pcache->ht_tiles[i];
+ bt->level = 0;
+ bt->index = i;
+ bt->tiles.data = tbits;
+ bt->tiles.raster = raster;
+ bt->tiles.size.x = width_unit;
+ bt->tiles.size.y = height_unit;
+ bt->tiles.rep_width = width;
+ bt->tiles.rep_height = height;
+ bt->tiles.shift = bt->tiles.rep_shift = shift;
+ }
+}
+
+/*
+ * Compute and save the rendering of a given gray level
+ * with the current halftone. The cache holds multiple tiles,
+ * where each tile covers a range of possible levels.
+ * We adjust the tile whose range includes the desired level incrementally;
+ * this saves a lot of time for the average image, where gray levels
+ * don't change abruptly. Note that the "level" is the number of bits,
+ * not the index in the levels vector.
+ */
+private int
+render_ht(gx_ht_tile *pbt, int level /* [1..num_bits-1] */,
+ const gx_ht_order *porder, gx_bitmap_id new_id)
+{ int old_level = pbt->level;
+ register gx_ht_bit *p = &porder->bits[old_level];
+ register byte *data = pbt->tiles.data;
+
+ if_debug7('H', "[H]Halftone cache slot 0x%lx: old=%d, new=%d, w=%d(%d), h=%d(%d):\n",
+ (ulong)data, old_level, level,
+ pbt->tiles.size.x, porder->width,
+ pbt->tiles.size.y, porder->num_bits / porder->width);
+#ifdef DEBUG
+ if ( level < 0 || level > porder->num_bits )
+ { lprintf3("Error in render_ht: level=%d, old_level=%d, num_bits=%d\n", level, old_level, porder->num_bits);
+ return_error(gs_error_Fatal);
+ }
+#endif
+ /* Invert bits between the two pointers. */
+ /* Note that we can use the same loop to turn bits either */
+ /* on or off, using xor. */
+ /* The Borland compiler generates truly dreadful code */
+ /* if we don't assign the offset to a temporary. */
+#if arch_ints_are_short
+# define invert_data(i)\
+ { uint off = p[i].offset; *(ht_mask_t *)&data[off] ^= p[i].mask; }
+#else
+# define invert_data(i) *(ht_mask_t *)&data[p[i].offset] ^= p[i].mask
+#endif
+#ifdef DEBUG
+# define invert(i)\
+ { if_debug3('H', "[H]invert level=%d offset=%u mask=0x%x\n",\
+ (int)(p + i - porder->bits), p[i].offset, p[i].mask);\
+ invert_data(i);\
+ }
+#else
+# define invert(i) invert_data(i)
+#endif
+sw: switch ( level - old_level )
+ {
+ default:
+ if ( level > old_level )
+ { invert(0); invert(1); invert(2); invert(3);
+ p += 4; old_level += 4;
+ }
+ else
+ { invert(-1); invert(-2); invert(-3); invert(-4);
+ p -= 4; old_level -= 4;
+ }
+ goto sw;
+ case 7: invert(6);
+ case 6: invert(5);
+ case 5: invert(4);
+ case 4: invert(3);
+ case 3: invert(2);
+ case 2: invert(1);
+ case 1: invert(0);
+ case 0: break; /* Shouldn't happen! */
+ case -7: invert(-7);
+ case -6: invert(-6);
+ case -5: invert(-5);
+ case -4: invert(-4);
+ case -3: invert(-3);
+ case -2: invert(-2);
+ case -1: invert(-1);
+ }
+#undef invert
+ pbt->level = level;
+ pbt->tiles.id = new_id;
+ /*
+ * Check whether we want to replicate the tile in the cache.
+ * Since we only do this when all the renderings will fit
+ * in the cache, we only do it once per level, and it doesn't
+ * have to be very efficient.
+ */
+ /****** TEST IS WRONG if width > rep_width but tile.raster ==
+ ****** order raster.
+ ******/
+ if ( pbt->tiles.raster > porder->raster )
+ bits_replicate_horizontally(data, pbt->tiles.rep_width,
+ pbt->tiles.rep_height, porder->raster,
+ pbt->tiles.size.x, pbt->tiles.raster);
+ if ( pbt->tiles.size.y > pbt->tiles.rep_height &&
+ pbt->tiles.shift == 0
+ )
+ bits_replicate_vertically(data, pbt->tiles.rep_height,
+ pbt->tiles.raster, pbt->tiles.size.y);
+#ifdef DEBUG
+if ( gs_debug_c('H') )
+ { const byte *p = pbt->tiles.data;
+ int wb = pbt->tiles.raster;
+ const byte *ptr = p + wb * pbt->tiles.size.y;
+
+ while ( p < ptr )
+ { dprintf8(" %d%d%d%d%d%d%d%d",
+ *p >> 7, (*p >> 6) & 1, (*p >> 5) & 1,
+ (*p >> 4) & 1, (*p >> 3) & 1, (*p >> 2) & 1,
+ (*p >> 1) & 1, *p & 1);
+ if ( (++p - data) % wb == 0 ) dputc('\n');
+ }
+ }
+#endif
+ return 0;
+}
diff --git a/pstoraster/gxht.h b/pstoraster/gxht.h
new file mode 100644
index 000000000..02c8de4da
--- /dev/null
+++ b/pstoraster/gxht.h
@@ -0,0 +1,141 @@
+/* Copyright (C) 1993, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxht.h */
+/* Rest of (client) halftone definitions */
+
+#ifndef gxht_INCLUDED
+# define gxht_INCLUDED
+
+#include "gsht1.h"
+#include "gxtmap.h"
+
+/* Halftone types */
+typedef enum {
+ ht_type_none, /* is this needed? */
+ ht_type_screen, /* set by setscreen */
+ ht_type_colorscreen, /* set by setcolorscreen */
+ ht_type_spot, /* Type 1 halftone dictionary */
+ ht_type_threshold, /* Type 3 halftone dictionary */
+ ht_type_multiple, /* Type 5 halftone dictionary */
+ ht_type_multiple_colorscreen /* Type 5 halftone dictionary */
+ /* created from Type 2 or Type 4 */
+ /* halftone dictionary */
+} gs_halftone_type;
+
+/* Type 1 halftone. This is just a Level 1 halftone with */
+/* a few extra members. */
+typedef struct gs_spot_halftone_s {
+ gs_screen_halftone screen;
+ bool accurate_screens;
+ gs_mapping_proc transfer;
+} gs_spot_halftone;
+#define st_spot_halftone_max_ptrs st_screen_halftone_max_ptrs
+
+/* Type 3 halftone. */
+typedef struct gs_threshold_halftone_s {
+ int width;
+ int height;
+ gs_const_string thresholds;
+ gs_mapping_proc transfer;
+} gs_threshold_halftone;
+#define st_threshold_halftone_max_ptrs 1
+
+/* Define the separation "names" for a Type 5 halftone. */
+typedef enum {
+ gs_ht_separation_Default, /* must be first */
+ gs_ht_separation_Gray,
+ gs_ht_separation_Red,
+ gs_ht_separation_Green,
+ gs_ht_separation_Blue,
+ gs_ht_separation_Cyan,
+ gs_ht_separation_Magenta,
+ gs_ht_separation_Yellow,
+ gs_ht_separation_Black
+} gs_ht_separation_name;
+#define gs_ht_separation_name_strings\
+ "Default", "Gray", "Red", "Green", "Blue",\
+ "Cyan", "Magenta", "Yellow", "Black"
+
+/* Define the elements of a Type 5 halftone. */
+typedef struct gs_halftone_component_s {
+ gs_ht_separation_name cname;
+ gs_halftone_type type;
+ union {
+ gs_spot_halftone spot; /* Type 1 */
+ gs_threshold_halftone threshold; /* Type 3 */
+ } params;
+} gs_halftone_component;
+extern_st(st_halftone_component);
+#define public_st_halftone_component() /* in gsht1.c */\
+ gs_public_st_composite(st_halftone_component, gs_halftone_component,\
+ "gs_halftone_component", halftone_component_enum_ptrs,\
+ halftone_component_reloc_ptrs)
+extern_st(st_ht_component_element);
+#define public_st_ht_component_element() /* in gsht1.c */\
+ gs_public_st_element(st_ht_component_element, gs_halftone_component,\
+ "gs_halftone_component[]", ht_comp_elt_enum_ptrs, ht_comp_elt_reloc_ptrs,\
+ st_halftone_component);
+#define st_halftone_component_max_ptrs\
+ max(st_spot_halftone_max_ptrs, st_threshold_halftone_max_ptrs)
+
+/* Define the Type 5 halftone itself. */
+typedef struct gs_multiple_halftone_s {
+ gs_halftone_component *components;
+ uint num_comp;
+} gs_multiple_halftone;
+#define st_multiple_halftone_max_ptrs 1
+
+/* The halftone stored in the graphics state is the union of */
+/* setscreen, setcolorscreen, Type 1, Type 3, and Type 5. */
+struct gs_halftone_s {
+ gs_halftone_type type;
+ union {
+ gs_screen_halftone screen; /* setscreen */
+ gs_colorscreen_halftone colorscreen; /* setcolorscreen */
+ gs_spot_halftone spot; /* Type 1 */
+ gs_threshold_halftone threshold; /* Type 3 */
+ gs_multiple_halftone multiple; /* Type 5 */
+ } params;
+};
+extern_st(st_halftone);
+#define public_st_halftone() /* in gsht.c */\
+ gs_public_st_composite(st_halftone, gs_halftone, "gs_halftone",\
+ halftone_enum_ptrs, halftone_reloc_ptrs)
+#define st_halftone_max_ptrs\
+ max(max(st_screen_halftone_max_ptrs, st_colorscreen_halftone_max_ptrs),\
+ max(max(st_spot_halftone_max_ptrs, st_threshold_halftone_max_ptrs),\
+ st_multiple_halftone_max_ptrs))
+
+/* Procedural interface for AccurateScreens */
+
+/* Set/get the default AccurateScreens value (for set[color]screen). */
+/* Note that this value is stored in a static variable. */
+void gs_setaccuratescreens(P1(bool));
+bool gs_currentaccuratescreens(P0());
+
+/* Initiate screen sampling with optional AccurateScreens. */
+int gs_screen_init_accurate(P4(gs_screen_enum *, gs_state *,
+ gs_screen_halftone *, bool));
+
+#endif /* gxht_INCLUDED */
diff --git a/pstoraster/gxhttile.h b/pstoraster/gxhttile.h
new file mode 100644
index 000000000..75b0ecef7
--- /dev/null
+++ b/pstoraster/gxhttile.h
@@ -0,0 +1,52 @@
+/* Copyright (C) 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxhttile.h */
+/* Halftone tile definition */
+/* Requires gxbitmap.h */
+
+#ifndef gxhttile_INCLUDED
+# define gxhttile_INCLUDED
+
+/*
+ * A halftone tile is just an ordinary bitmap tile, plus a couple of other
+ * items associated with managing its existence in a tile cache.
+ * (See gzht.h for details.) We define this in its own file so that
+ * clients of gx_device_color can access it.
+ */
+
+#ifndef gx_ht_tile_DEFINED
+# define gx_ht_tile_DEFINED
+typedef struct gx_ht_tile_s gx_ht_tile;
+#endif
+
+struct gx_ht_tile_s {
+ gx_strip_bitmap tiles; /* the currently rendered tile */
+ int level; /* the cached gray level, i.e. */
+ /* the number of spots whitened, */
+ /* or -1 if the cache is empty */
+ uint index; /* the index of the tile within */
+ /* the cache (for GC) */
+};
+
+#endif /* gxhttile_INCLUDED */
diff --git a/pstoraster/gximage.c b/pstoraster/gximage.c
new file mode 100644
index 000000000..bcef11bb9
--- /dev/null
+++ b/pstoraster/gximage.c
@@ -0,0 +1,820 @@
+/* Copyright (C) 1989, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gximage.c */
+/* Image setup procedures for Ghostscript library */
+#include "gx.h"
+#include "math_.h"
+#include "memory_.h"
+#include "gpcheck.h"
+#include "gserrors.h"
+#include "gsstruct.h"
+#include "gxfixed.h"
+#include "gxfrac.h"
+#include "gxarith.h"
+#include "gxmatrix.h"
+#include "gsccolor.h"
+#include "gspaint.h"
+#include "gzstate.h"
+#include "gxdevice.h"
+#include "gzpath.h"
+#include "gzcpath.h"
+#include "gxdevmem.h"
+#include "gximage.h"
+#include "gdevmrop.h"
+
+/****************************************************************
+ * NOTE: This file assumes that the gx_imager_state passed to
+ * gx_default_begin_image is actually a gs_state *.
+ * This is clearly incorrect and will be fixed in the future.
+ * (It is, however, correct for direct calls from the PostScript
+ * interpreter.) Meanwhile, use at your own risk.
+ ****************************************************************/
+
+/* Structure descriptor */
+private_st_gx_image_enum();
+
+/* Define the procedures for initializing gs_image_ts to default values. */
+private void
+image_t_init(gs_image_t *pim, bool mask)
+{ pim->Width = pim->Height = 0;
+ gs_make_identity(&pim->ImageMatrix);
+ pim->BitsPerComponent = 1;
+ /* Doesn't fill in ColorSpace. */
+ /* Doesn't fill in Decode. */
+ pim->Interpolate = false;
+ pim->ImageMask = pim->adjust = mask;
+ pim->CombineWithColor = false;
+}
+void
+gs_image_t_init_mask(gs_image_t *pim, bool write_1s)
+{ image_t_init(pim, true);
+ pim->ColorSpace = NULL;
+ if ( write_1s )
+ pim->Decode[0] = 1, pim->Decode[1] = 0;
+ else
+ pim->Decode[0] = 0, pim->Decode[1] = 1;
+}
+void
+gs_image_t_init_gray(gs_image_t *pim)
+{ image_t_init(pim, false);
+ pim->ColorSpace = gs_color_space_DeviceGray();
+ pim->Decode[0] = 0;
+ pim->Decode[1] = 1;
+}
+void
+gs_image_t_init_color(gs_image_t *pim)
+{ gs_image_t_init_gray(pim);
+ pim->ColorSpace = gs_color_space_DeviceRGB();
+ pim->Decode[2] = pim->Decode[4] = pim->Decode[6] = 0;
+ pim->Decode[3] = pim->Decode[5] = pim->Decode[7] = 1;
+}
+
+/* Declare the 1-for-1 unpacking procedure so we can test for it */
+/* in the GC procedures. */
+extern iunpack_proc(image_unpack_copy);
+
+/* GC procedures */
+#define eptr ((gx_image_enum *)vptr)
+private ENUM_PTRS_BEGIN(image_enum_enum_ptrs) {
+ int bps;
+ gs_ptr_type_t ret;
+ /* Enumerate the used members of clues.dev_color. */
+ index -= gx_image_enum_num_ptrs;
+ bps = eptr->unpack_bps;
+ if ( eptr->spp != 1 )
+ bps = 8;
+ else if ( bps > 8 || eptr->unpack == image_unpack_copy )
+ bps = 1;
+ if ( index >= (1 << bps) * st_device_color_max_ptrs ) /* done */
+ return 0;
+ ret = (*st_device_color.enum_ptrs)
+ (&eptr->clues[(index / st_device_color_max_ptrs) *
+ (255 / ((1 << bps) - 1))].dev_color,
+ sizeof(eptr->clues[0].dev_color),
+ index % st_device_color_max_ptrs, pep);
+ if ( ret == 0 ) /* don't stop early */
+ { *pep = 0;
+ break;
+ }
+ return ret;
+ }
+#define e1(i,elt) ENUM_PTR(i,gx_image_enum,elt);
+ gx_image_enum_do_ptrs(e1)
+#undef e1
+ENUM_PTRS_END
+private RELOC_PTRS_BEGIN(image_enum_reloc_ptrs) {
+ int i;
+#define r1(i,elt) RELOC_PTR(gx_image_enum,elt);
+ gx_image_enum_do_ptrs(r1)
+#undef r1
+ { int bps = eptr->unpack_bps;
+ if ( eptr->spp != 1 )
+ bps = 8;
+ else if ( bps > 8 || eptr->unpack == image_unpack_copy )
+ bps = 1;
+ for ( i = 0; i <= 255; i += 255 / ((1 << bps) - 1) )
+ (*st_device_color.reloc_ptrs)
+ (&eptr->clues[i].dev_color, sizeof(gx_device_color), gcst);
+ }
+} RELOC_PTRS_END
+#undef eptr
+
+/* Forward declarations */
+private void image_init_map(P3(byte *map, int map_size, const float *decode));
+private void image_init_colors(P7(gx_image_enum *penum, const gs_image_t *pim,
+ bool multi, gs_state *pgs, int spp,
+ const gs_color_space *pcs, bool *pdcb));
+
+/* Procedures for unpacking the input data into bytes or fracs. */
+/*extern iunpack_proc(image_unpack_copy);*/ /* declared above */
+extern iunpack_proc(image_unpack_1);
+extern iunpack_proc(image_unpack_1_spread);
+extern iunpack_proc(image_unpack_2);
+extern iunpack_proc(image_unpack_2_spread);
+extern iunpack_proc(image_unpack_4);
+extern iunpack_proc(image_unpack_8);
+extern iunpack_proc(image_unpack_8_spread);
+extern iunpack_proc(image_unpack_12);
+
+/* The image_render procedures work on fully expanded, complete rows. */
+/* These take a height argument, which is an integer >= 0; */
+/* they return a negative code, or the number of */
+/* rows actually processed (which may be less than the height). */
+/* height = 0 is a special call to indicated that the image has been */
+/* fully processed; this is necessary because the last scan lines of */
+/* the source data may not produce any output. */
+extern irender_proc(image_render_skip);
+extern irender_proc(image_render_simple);
+extern irender_proc(image_render_landscape);
+extern irender_proc(image_render_mono);
+extern irender_proc(image_render_color);
+extern irender_proc(image_render_frac);
+extern irender_proc(image_render_interpolate);
+
+/* Define 'strategy' procedures for selecting imaging methods. */
+/* Strategies are called in a known order, so each one may assume */
+/* that all the previous ones failed. */
+/* If a strategy succeeds, it may update the enumerator structure */
+/* as well as returning the rendering procedure. */
+typedef irender_proc((*irender_proc_t));
+#define image_strategy_proc(proc)\
+ irender_proc_t proc(P1(gx_image_enum *penum))
+private image_strategy_proc(image_strategy_skip);
+private image_strategy_proc(image_strategy_interpolate);
+private image_strategy_proc(image_strategy_simple);
+private image_strategy_proc(image_strategy_frac);
+private image_strategy_proc(image_strategy_mono);
+
+/* Standard mask tables for spreading input data. */
+/* Note that the mask tables depend on the end-orientation of the CPU. */
+/* We can't simply define them as byte arrays, because */
+/* they might not wind up properly long- or short-aligned. */
+#define map4tox(z,a,b,c,d)\
+ z, z^a, z^b, z^(a+b),\
+ z^c, z^(a+c), z^(b+c), z^(a+b+c),\
+ z^d, z^(a+d), z^(b+d), z^(a+b+d),\
+ z^(c+d), z^(a+c+d), z^(b+c+d), z^(a+b+c+d)
+#if arch_is_big_endian
+private const bits32 map_4x1_to_32[16] =
+ { map4tox(0L, 0xffL, 0xff00L, 0xff0000L, 0xff000000L) };
+private const bits32 map_4x1_to_32_invert[16] =
+ { map4tox(0xffffffffL, 0xffL, 0xff00L, 0xff0000L, 0xff000000L) };
+#else /* !arch_is_big_endian */
+private const bits32 map_4x1_to_32[16] =
+ { map4tox(0L, 0xff000000L, 0xff0000L, 0xff00L, 0xffL) };
+private const bits32 map_4x1_to_32_invert[16] =
+ { map4tox(0xffffffffL, 0xff000000L, 0xff0000L, 0xff00L, 0xffL) };
+#endif
+
+/* Start processing an image. */
+int
+gx_default_begin_image(gx_device *dev,
+ const gs_imager_state *pis, const gs_image_t *pim,
+ gs_image_format_t format, gs_image_shape_t shape,
+ const gx_drawing_color *pdcolor, const gx_clip_path *pcpath,
+ gs_memory_t *mem, void **pinfo)
+{ gx_image_enum *penum;
+ int width = pim->Width;
+ int height = pim->Height;
+ int bps = pim->BitsPerComponent;
+ bool multi;
+ int index_bps;
+ const gs_color_space *pcs = pim->ColorSpace;
+ gs_state *pgs = (gs_state *)pis; /****** SEE ABOVE ******/
+ int code;
+ gs_matrix mat;
+ int log2_xbytes = (bps <= 8 ? 0 : arch_log2_sizeof_frac);
+ int spp, nplanes, spread;
+ uint bsize;
+ byte *buffer;
+ fixed mtx, mty;
+ gs_fixed_point row_extent, col_extent;
+ bool device_color;
+ gs_fixed_rect obox, cbox;
+ fixed adjust;
+
+ if ( width < 0 || height < 0 ||
+ (shape & (gs_image_shape_clip_left |
+ gs_image_shape_clip_right |
+ gs_image_shape_varying_width)) != 0
+ )
+ return_error(gs_error_rangecheck);
+ switch ( format )
+ { case gs_image_format_chunky: multi = false; break;
+ case gs_image_format_component_planar: multi = true; break;
+ default: return_error(gs_error_rangecheck);
+ }
+ switch ( bps )
+ {
+ case 1: index_bps = 0; break;
+ case 2: index_bps = 1; break;
+ case 4: index_bps = 2; break;
+ case 8: index_bps = 3; break;
+ case 12: index_bps = 4; break;
+ default: return_error(gs_error_rangecheck);
+ }
+ if ( (code = gs_matrix_invert(&pim->ImageMatrix, &mat)) < 0 ||
+ (code = gs_matrix_multiply(&mat, &ctm_only(pgs), &mat)) < 0 ||
+ (code =
+ gs_distance_transform2fixed((const gs_matrix_fixed *)&mat,
+ (floatp)width, (floatp)0,
+ &row_extent)) < 0 ||
+ (code =
+ gs_distance_transform2fixed((const gs_matrix_fixed *)&mat,
+ (floatp)0, (floatp)height,
+ &col_extent)) < 0
+ )
+ return code;
+ penum = gs_alloc_struct(mem, gx_image_enum, &st_gx_image_enum,
+ "gx_default_begin_image");
+ if ( (penum->masked = pim->ImageMask) )
+ { /* This is imagemask. */
+ const float *decode = pim->Decode; /* [2] */
+
+ if ( pim->BitsPerComponent != 1 || multi || pcs != NULL ||
+ !((decode[0] == 0.0 && decode[1] == 1.0) ||
+ (decode[0] == 1.0 && decode[1] == 0.0))
+ )
+ return_error(gs_error_rangecheck);
+ /* Initialize color entries 0 and 255. */
+ color_set_pure(&penum->icolor0, gx_no_color_index);
+ penum->icolor1 = *pdcolor;
+ memcpy(&penum->map[0].table.lookup4x1to32[0],
+ (decode[0] == 0 ? map_4x1_to_32_invert :
+ map_4x1_to_32),
+ 16 * 4);
+ penum->map[0].decoding = sd_none;
+ spp = 1;
+ adjust = (pim->adjust ? float2fixed(0.25) : fixed_0);
+ }
+ else
+ { /* This is image, not imagemask. */
+ const gs_color_space_type _ds *pcst = pcs->type;
+
+ spp = pcst->num_components;
+ if ( spp < 0 ) /* Pattern not allowed */
+ return_error(gs_error_rangecheck);
+ device_color = (*pcst->concrete_space)(pcs, pgs) == pcs;
+ image_init_colors(penum, pim, multi, pgs, spp, pcs,
+ &device_color);
+ adjust = fixed_0;
+ }
+ penum->device_color = device_color;
+ bsize = (width + 8) * spp; /* round up, +1 for end-of-run byte */
+ buffer = gs_alloc_bytes(mem, bsize, "image buffer");
+ if ( buffer == 0 )
+ return_error(gs_error_VMerror);
+ penum->width = width;
+ penum->height = height;
+ penum->bps = bps;
+ penum->unpack_bps = bps;
+ penum->log2_xbytes = log2_xbytes;
+ penum->spp = spp;
+ nplanes = (multi ? spp : 1);
+ penum->num_planes = nplanes;
+ spread = nplanes << log2_xbytes;
+ penum->spread = spread;
+ penum->matrix = mat;
+ penum->row_extent = row_extent;
+ penum->posture =
+ ((row_extent.y | col_extent.x) == 0 ? image_portrait :
+ (row_extent.x | col_extent.y) == 0 ? image_landscape :
+ image_skewed);
+ mtx = float2fixed(mat.tx);
+ mty = float2fixed(mat.ty);
+ penum->pgs = pgs;
+ penum->pis = pis;
+ penum->pcs = pcs;
+ penum->memory = mem;
+ penum->dev = pgs->device;
+ penum->buffer = buffer;
+ penum->buffer_size = bsize;
+ penum->line = 0;
+ penum->line_size = 0;
+ penum->bytes_per_row =
+ (uint)(((ulong)width * (bps * spp) / nplanes + 7) >> 3);
+ penum->interpolate = pim->Interpolate;
+ penum->use_rop = pim->CombineWithColor && !pim->ImageMask;
+ penum->slow_loop = 0;
+ penum->clip_image =
+ (pcpath == 0 ?
+ (obox.p.x = obox.p.y = min_fixed, obox.q.x = obox.q.y = max_fixed,
+ cbox.p.x = cbox.p.y = cbox.q.x = cbox.q.y = 0, 0) :
+ gx_cpath_outer_box(pcpath, &obox) | /* not || */
+ gx_cpath_inner_box(pcpath, &cbox) ?
+ 0 : image_clip_region);
+ penum->clip_outer = obox;
+ penum->clip_inner = cbox;
+ penum->log_op = (penum->use_rop ? rop3_T : pis->log_op);
+ penum->clip_dev = 0; /* in case we bail out */
+ penum->rop_dev = 0; /* ditto */
+ penum->scaler = 0; /* ditto */
+ /*
+ * If all four extrema of the image fall within the clipping
+ * rectangle, clipping is never required. When making this check,
+ * we must carefully take into account the fact that we only care
+ * about pixel centers.
+ */
+ { int hwx, hwy;
+ fixed
+ epx = min(row_extent.x, 0) + min(col_extent.x, 0),
+ eqx = max(row_extent.x, 0) + max(col_extent.x, 0),
+ epy = min(row_extent.y, 0) + min(col_extent.y, 0),
+ eqy = max(row_extent.y, 0) + max(col_extent.y, 0);
+
+ switch ( penum->posture )
+ {
+ case image_portrait:
+ hwx = width, hwy = height;
+ break;
+ case image_landscape:
+ hwx = height, hwy = width;
+ break;
+ default:
+ hwx = hwy = 0;
+ }
+ { /*
+ * If the image is only 1 sample wide or high,
+ * and is less than 1 device pixel wide or high,
+ * move it slightly so that it covers pixel centers.
+ * This is a hack to work around a bug in some old
+ * versions of TeX/dvips, which use 1-bit-high images
+ * to draw horizontal and vertical lines without
+ * positioning them properly.
+ */
+ fixed diff;
+ if ( hwx == 1 && eqx - epx < fixed_1 )
+ { diff =
+ arith_rshift_1(row_extent.x + col_extent.x);
+ mtx = (((mtx + diff) | fixed_half)
+ & -fixed_half) - diff;
+ }
+ if ( hwy == 1 && eqy - epy < fixed_1 )
+ { diff =
+ arith_rshift_1(row_extent.y + col_extent.y);
+ mty = (((mty + diff) | fixed_half)
+ & -fixed_half) - diff;
+ }
+ }
+ if ( !penum->clip_image ) /* i.e., not clip region */
+ penum->clip_image =
+ (fixed_pixround(mtx + epx) < fixed_pixround(cbox.p.x) ?
+ image_clip_xmin : 0) +
+ (fixed_pixround(mtx + eqx) >= fixed_pixround(cbox.q.x) ?
+ image_clip_xmax : 0) +
+ (fixed_pixround(mty + epy) < fixed_pixround(cbox.p.y) ?
+ image_clip_ymin : 0) +
+ (fixed_pixround(mty + eqy) >= fixed_pixround(cbox.q.y) ?
+ image_clip_ymax : 0);
+ }
+ if_debug11('b',
+ "[b]Image: cbox=(%g,%g),(%g,%g), obox=(%g,%g),(%g,%g)\n mt=(%g,%g) clip_image=0x%x\n",
+ fixed2float(cbox.p.x), fixed2float(cbox.p.y),
+ fixed2float(cbox.q.x), fixed2float(cbox.q.y),
+ fixed2float(obox.p.x), fixed2float(obox.p.y),
+ fixed2float(obox.q.x), fixed2float(obox.q.y),
+ fixed2float(mtx), fixed2float(mty), penum->clip_image);
+ penum->byte_in_row = 0;
+ penum->xcur = penum->mtx = mtx;
+ dda_init(penum->next_x, mtx, col_extent.x, height);
+ penum->ycur = penum->mty = mty;
+ dda_init(penum->next_y, mty, col_extent.y, height);
+ penum->x = 0;
+ penum->y = 0;
+ penum->adjust = adjust;
+ { static iunpack_proc((*procs[5])) = {
+ image_unpack_1, image_unpack_2,
+ image_unpack_4, image_unpack_8, image_unpack_12
+ };
+ static iunpack_proc((*spread_procs[5])) = {
+ image_unpack_1_spread, image_unpack_2_spread,
+ image_unpack_4, image_unpack_8_spread,
+ image_unpack_12
+ };
+ if ( nplanes != 1 )
+ { penum->unpack = spread_procs[index_bps];
+ if_debug1('b', "[b]unpack=spread %d\n", bps);
+ }
+ else
+ { penum->unpack = procs[index_bps];
+ if_debug1('b', "[b]unpack=%d\n", bps);
+ }
+ /* Use slow loop for imagemask with a halftone, */
+ /* or for a non-default logical operation. */
+ penum->slow_loop |=
+ (penum->masked &&
+ !color_is_pure(pdcolor)) ||
+ penum->use_rop ||
+ !lop_no_T_is_S(pis->log_op);
+ if ( (penum->render = image_strategy_skip(penum)) == 0 &&
+ (penum->render = image_strategy_interpolate(penum)) == 0 &&
+ (penum->render = image_strategy_simple(penum)) == 0 &&
+ (penum->render = image_strategy_frac(penum)) == 0 &&
+ (penum->render = image_strategy_mono(penum)) == 0
+ )
+ { /* Use default logic. */
+ penum->render = image_render_color;
+ }
+ }
+ if ( penum->clip_image && pcpath )
+ { /* Set up the clipping device. */
+ gx_device_clip *cdev =
+ gs_alloc_struct(mem, gx_device_clip,
+ &st_device_clip, "image clipper");
+ if ( cdev == 0 )
+ { gx_default_end_image(dev, penum, false);
+ return_error(gs_error_VMerror);
+ }
+ gx_make_clip_device(cdev, cdev, &pcpath->list);
+ penum->clip_dev = cdev;
+ cdev->target = gs_currentdevice(pgs);
+ (*dev_proc(cdev, open_device))((gx_device *)cdev);
+ }
+ if ( penum->use_rop )
+ { /* Set up the RasterOp source device. */
+ gx_device_rop_texture *rtdev =
+ gs_alloc_struct(mem, gx_device_rop_texture,
+ &st_device_rop_texture, "image RasterOp");
+ if ( rtdev == 0 )
+ { gx_default_end_image(dev, penum, false);
+ return_error(gs_error_VMerror);
+ }
+ gx_make_rop_texture_device(rtdev,
+ (penum->clip_dev != 0 ?
+ (gx_device *)penum->clip_dev :
+ dev), pis->log_op, pdcolor);
+ penum->rop_dev = rtdev;
+ }
+ if_debug8('b', "[b]Image: w=%d h=%d [%g %g %g %g %g %g]\n",
+ width, height,
+ mat.xx, mat.xy, mat.yx, mat.yy, mat.tx, mat.ty);
+ *pinfo = penum;
+ return 0;
+}
+
+/* Initialize the color mapping tables for a non-mask image. */
+private void
+image_init_colors(gx_image_enum *penum, const gs_image_t *pim, bool multi,
+ gs_state *pgs, int spp, const gs_color_space *pcs, bool *pdcb)
+{ int bps = pim->BitsPerComponent;
+ const float *decode = pim->Decode; /* [spp*2] */
+ int ci;
+ static const float default_decode[8] =
+ { 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0 };
+
+ /* Initialize the color table */
+
+#define ictype(i)\
+ penum->clues[i].dev_color.type
+ switch ( (spp == 1 ? bps : 8) )
+ {
+ case 8: /* includes all color images */
+ { register gx_image_clue *pcht = &penum->clues[0];
+ register int n = 64;
+ do
+ { pcht[0].dev_color.type =
+ pcht[1].dev_color.type =
+ pcht[2].dev_color.type =
+ pcht[3].dev_color.type =
+ gx_dc_type_none;
+ pcht[0].key = pcht[1].key =
+ pcht[2].key = pcht[3].key = 0;
+ pcht += 4;
+ }
+ while ( --n > 0 );
+ penum->clues[0].key = 1; /* guarantee no hit */
+ break;
+ }
+ case 4:
+ ictype(17) = ictype(2*17) = ictype(3*17) =
+ ictype(4*17) = ictype(6*17) = ictype(7*17) =
+ ictype(8*17) = ictype(9*17) = ictype(11*17) =
+ ictype(12*17) = ictype(13*17) = ictype(14*17) =
+ gx_dc_type_none;
+ /* falls through */
+ case 2:
+ ictype(5*17) = ictype(10*17) = gx_dc_type_none;
+#undef ictype
+ }
+
+ /* Initialize the maps from samples to intensities. */
+
+ for ( ci = 0; ci < spp; ci++ )
+ { sample_map *pmap = &penum->map[ci];
+
+ /* If the decoding is [0 1] or [1 0], we can fold it */
+ /* into the expansion of the sample values; */
+ /* otherwise, we have to use the floating point method. */
+
+ const float *this_decode = &decode[ci * 2];
+ const float *map_decode; /* decoding used to */
+ /* construct the expansion map */
+
+ const float *real_decode; /* decoding for */
+ /* expanded samples */
+
+ bool no_decode;
+
+ map_decode = real_decode = this_decode;
+ if ( map_decode[0] == 0.0 && map_decode[1] == 1.0 )
+ no_decode = true;
+ else if ( map_decode[0] == 1.0 && map_decode[1] == 0.0 )
+ no_decode = true,
+ real_decode = default_decode;
+ else
+ no_decode = false,
+ *pdcb = false,
+ map_decode = default_decode;
+ if ( bps > 2 || multi )
+ { if ( bps <= 8 )
+ image_init_map(&pmap->table.lookup8[0], 1 << bps,
+ map_decode);
+ }
+ else
+ { /* The map index encompasses more than one pixel. */
+ byte map[4];
+ register int i;
+ image_init_map(&map[0], 1 << bps, map_decode);
+ switch ( bps )
+ {
+ case 1:
+ { register bits32 *p = &pmap->table.lookup4x1to32[0];
+ if ( map[0] == 0 && map[1] == 0xff )
+ memcpy((byte *)p, map_4x1_to_32, 16 * 4);
+ else if ( map[0] == 0xff && map[1] == 0 )
+ memcpy((byte *)p, map_4x1_to_32_invert, 16 * 4);
+ else
+ for ( i = 0; i < 16; i++, p++ )
+ ((byte *)p)[0] = map[i >> 3],
+ ((byte *)p)[1] = map[(i >> 2) & 1],
+ ((byte *)p)[2] = map[(i >> 1) & 1],
+ ((byte *)p)[3] = map[i & 1];
+ } break;
+ case 2:
+ { register bits16 *p = &pmap->table.lookup2x2to16[0];
+ for ( i = 0; i < 16; i++, p++ )
+ ((byte *)p)[0] = map[i >> 2],
+ ((byte *)p)[1] = map[i & 3];
+ } break;
+ }
+ }
+ pmap->decode_base /* = decode_lookup[0] */ = real_decode[0];
+ pmap->decode_factor =
+ (real_decode[1] - real_decode[0]) /
+ (bps <= 8 ? 255.0 : (float)frac_1);
+ pmap->decode_max /* = decode_lookup[15] */ = real_decode[1];
+ if ( no_decode )
+ pmap->decoding = sd_none;
+ else if ( bps <= 4 )
+ { int step = 15 / ((1 << bps) - 1);
+ int i;
+
+ pmap->decoding = sd_lookup;
+ for ( i = 15 - step; i > 0; i -= step )
+ pmap->decode_lookup[i] = pmap->decode_base +
+ i * (255.0 / 15) * pmap->decode_factor;
+ }
+ else
+ pmap->decoding = sd_compute;
+ if ( spp == 1 ) /* and ci == 0 */
+ { /* Pre-map entries 0 and 255. */
+ gs_client_color cc;
+ cc.paint.values[0] = real_decode[0];
+ (*pcs->type->remap_color)(&cc, pcs, &penum->icolor0, pgs);
+ cc.paint.values[0] = real_decode[1];
+ (*pcs->type->remap_color)(&cc, pcs, &penum->icolor1, pgs);
+ }
+ }
+
+}
+/* Construct a mapping table for sample values. */
+/* map_size is 2, 4, 16, or 256. Note that 255 % (map_size - 1) == 0. */
+private void
+image_init_map(byte *map, int map_size, const float *decode)
+{ float min_v = decode[0], max_v = decode[1];
+ byte *limit = map + map_size;
+ uint value = min_v * 0xffffL;
+ /* The division in the next statement is exact, */
+ /* see the comment above. */
+ uint diff = (max_v - min_v) * (0xffffL / (map_size - 1));
+ for ( ; map != limit; map++, value += diff )
+ *map = value >> 8;
+}
+
+/* Strategy procedures */
+
+/* If we're in a charpath, don't image anything. */
+private irender_proc_t
+image_strategy_skip(gx_image_enum *penum)
+{ if ( !penum->pgs->in_charpath )
+ return 0;
+ if_debug0('b', "[b]render=skip\n");
+ return image_render_skip;
+}
+
+/* If we're interpolating, use special logic. */
+private irender_proc_t
+image_strategy_interpolate(gx_image_enum *penum)
+{ gs_state *pgs = penum->pgs;
+ gs_memory_t *mem = penum->memory;
+ stream_IScale_state iss;
+ stream_IScale_state *pss;
+ byte *line;
+ const gs_color_space *pcs = penum->pcs;
+ gs_point dst_xy;
+
+ if ( !penum->interpolate )
+ return 0;
+ if ( penum->posture != image_portrait || penum->masked )
+ { /* We can't handle these cases yet. Punt. */
+ penum->interpolate = false;
+ return 0;
+ }
+ iss.memory = mem;
+ /* Non-ANSI compilers require the following casts: */
+ gs_distance_transform((float)penum->width, (float)penum->height,
+ &penum->matrix, &dst_xy);
+ if ( penum->bps <= 8 && penum->device_color )
+ iss.BitsPerComponentIn = 8,
+ iss.MaxValueIn = 0xff;
+ else
+ iss.BitsPerComponentIn = sizeof(frac) * 8,
+ iss.MaxValueIn = frac_1;
+ iss.BitsPerComponentOut = sizeof(frac) * 8;
+ iss.MaxValueOut = frac_1;
+ iss.WidthOut = (int)ceil(fabs(dst_xy.x));
+ iss.HeightOut = (int)ceil(fabs(dst_xy.y));
+ iss.WidthIn = penum->width;
+ iss.HeightIn = penum->height;
+ iss.Colors = cs_concrete_space(pcs, pgs)->type->num_components;
+ /* Allocate a buffer for one source/destination line. */
+ { uint in_size =
+ iss.WidthIn * iss.Colors * (iss.BitsPerComponentIn / 8);
+ uint out_size =
+ iss.WidthOut * iss.Colors *
+ max(iss.BitsPerComponentOut / 8, sizeof(gx_color_index));
+ line = gs_alloc_bytes(mem, max(in_size, out_size),
+ "image scale src line");
+ }
+ pss = gs_alloc_struct(mem, stream_IScale_state,
+ &st_IScale_state, "image scale state");
+ if ( line == 0 || pss == 0 ||
+ (*pss = iss,
+ (*s_IScale_template.init)((stream_state *)pss) < 0)
+ )
+ { gs_free_object(mem, pss, "image scale state");
+ gs_free_object(mem, line, "image scale src line");
+ /* Try again without interpolation. */
+ penum->interpolate = false;
+ return 0;
+ }
+ penum->line = line;
+ penum->scaler = pss;
+ penum->line_xy = 0;
+ if_debug0('b', "[b]render=interpolate\n");
+ return image_render_interpolate;
+}
+
+/* Use special fast logic for portrait or landscape black-and-white images. */
+private irender_proc_t
+image_strategy_simple(gx_image_enum *penum)
+{ irender_proc_t rproc;
+ if ( !(penum->spp == 1 && penum->bps == 1 && !penum->slow_loop &&
+ (penum->masked ||
+ (color_is_pure(&penum->icolor0) &&
+ color_is_pure(&penum->icolor1))))
+ )
+ return 0;
+ switch ( penum->posture )
+ {
+ case image_portrait:
+ { /* Use fast portrait algorithm. */
+ long dev_width =
+ fixed2long_rounded(penum->mtx + penum->row_extent.x) -
+ fixed2long_rounded(penum->mtx);
+
+ if ( dev_width != penum->width )
+ { /* Add an extra align_bitmap_mod of padding so that */
+ /* we can align scaled rows with the device. */
+ long line_size =
+ bitmap_raster(any_abs(dev_width)) + align_bitmap_mod;
+ if ( penum->adjust != 0 || line_size > max_uint )
+ return 0;
+ /* Must buffer a scan line. */
+ penum->line_width = any_abs(dev_width);
+ penum->line_size = (uint)line_size;
+ penum->line = gs_alloc_bytes(penum->memory,
+ penum->line_size, "image line");
+ if ( penum->line == 0 )
+ { gx_default_end_image(penum->dev, penum, false);
+ return 0;
+ }
+ }
+ if_debug2('b', "[b]render=simple, unpack=copy; width=%d, dev_width=%ld\n",
+ penum->width, dev_width);
+ rproc = image_render_simple;
+ break;
+ }
+ case image_landscape:
+ { /* Use fast landscape algorithm. */
+ long dev_width =
+ fixed2long_rounded(penum->mty + penum->row_extent.y) -
+ fixed2long_rounded(penum->mty);
+ long line_size =
+ (dev_width = any_abs(dev_width),
+ bitmap_raster(dev_width) * 8 +
+ round_up(dev_width, 8) * align_bitmap_mod);
+
+ if ( (dev_width != penum->width && penum->adjust != 0) ||
+ line_size > max_uint
+ )
+ return 0;
+ /* Must buffer a group of 8N scan lines. */
+ penum->line_width = dev_width;
+ penum->line_size = (uint)line_size;
+ penum->line = gs_alloc_bytes(penum->memory,
+ penum->line_size, "image line");
+ if ( penum->line == 0 )
+ { gx_default_end_image(penum->dev, penum, false);
+ return 0;
+ }
+ penum->line_xy = fixed2int_var_rounded(penum->xcur);
+ if_debug3('b', "[b]render=landscape, unpack=copy; width=%d, dev_width=%ld, line_size=%ld\n",
+ penum->width, dev_width, line_size);
+ rproc = image_render_landscape;
+ break;
+ }
+ default:
+ return 0;
+ }
+ /* We don't want to spread the samples, */
+ /* but we have to reset unpack_bps to prevent the buffer */
+ /* pointer from being incremented by 8 bytes */
+ /* per input byte. */
+ penum->unpack = image_unpack_copy;
+ penum->unpack_bps = 8;
+ return rproc;
+}
+
+/* We can bypass X clipping for portrait monochrome images. */
+private irender_proc_t
+image_strategy_mono(gx_image_enum *penum)
+{ if ( penum->spp == 1 )
+ { if ( !(penum->slow_loop || penum->posture != image_portrait) )
+ penum->clip_image &= ~(image_clip_xmin | image_clip_xmax);
+ if_debug0('b', "[b]render=mono\n");
+ return image_render_mono;
+ }
+ return 0;
+}
+
+/* Use special (slow) logic for 12-bit source values. */
+private irender_proc_t
+image_strategy_frac(gx_image_enum *penum)
+{ if ( penum->bps > 8 )
+ { if_debug0('b', "[b]render=frac\n");
+ return image_render_frac;
+ }
+ return 0;
+}
diff --git a/pstoraster/gximage.h b/pstoraster/gximage.h
new file mode 100644
index 000000000..20d9a6438
--- /dev/null
+++ b/pstoraster/gximage.h
@@ -0,0 +1,252 @@
+/* Copyright (C) 1989, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gximage.h */
+/* Internal definitions for image rendering */
+/* Requires gxcpath.h, gxdevmem.h, gxdcolor.h, gzpath.h */
+#include "gsiparam.h"
+#include "gxcspace.h"
+#include "strimpl.h" /* for siscale.h */
+#include "siscale.h"
+#include "gxdda.h"
+
+/* Interface for routine used to unpack and shuffle incoming samples. */
+/* The Unix C compiler can't handle typedefs for procedure */
+/* (as opposed to pointer-to-procedure) types, */
+/* so we have to do it with macros instead. */
+#define iunpack_proc(proc)\
+ void proc(P6(byte *bptr, const byte *data, uint dsize,\
+ const sample_map *pmap, int spread, uint inpos))
+/* Interface for routine used to render a (source) scan line. */
+#define irender_proc(proc)\
+ int proc(P5(gx_image_enum *penum, byte *buffer, uint w, int h,\
+ gx_device *dev))
+
+/*
+ * Incoming samples may go through two different transformations:
+ *
+ * - For N-bit input samples with N <= 8, N-to-8-bit expansion
+ * may involve a lookup map. Currently this map is either an
+ * identity function or a subtraction from 1 (inversion).
+ *
+ * - The 8-bit or frac expanded sample may undergo decoding (a linear
+ * transformation) before being handed off to the color mapping
+ * machinery.
+ *
+ * If the decoding function's range is [0..1], we fold it into the
+ * expansion lookup; otherwise we must compute it separately.
+ * For speed, we distinguish 3 different cases of the decoding step:
+ */
+typedef enum {
+ sd_none, /* decoded during expansion */
+ sd_lookup, /* use lookup_decode table */
+ sd_compute /* compute using base and factor */
+} sample_decoding;
+typedef struct sample_map_s {
+
+ /* The following union implements the expansion of sample */
+ /* values from N bits to 8, and a possible inversion. */
+
+ union {
+
+ bits32 lookup4x1to32[16]; /* 1 bit/sample, not spreading */
+ bits16 lookup2x2to16[16]; /* 2 bits/sample, not spreading */
+ byte lookup8[256]; /* 1 bit/sample, spreading [2] */
+ /* 2 bits/sample, spreading [4] */
+ /* 4 bits/sample [16] */
+ /* 8 bits/sample [256] */
+
+ } table;
+
+ /* If an 8-bit fraction doesn't represent the decoded value */
+ /* accurately enough, but the samples have 4 bits or fewer, */
+ /* we precompute the decoded values into a table. */
+ /* Different entries are used depending on bits/sample: */
+ /* 1,8,12 bits/sample: 0,15 */
+ /* 2 bits/sample: 0,5,10,15 */
+ /* 4 bits/sample: all */
+
+ float decode_lookup[16];
+#define decode_base decode_lookup[0]
+#define decode_max decode_lookup[15]
+
+ /* In the worst case, we have to do the decoding on the fly. */
+ /* The value is base + sample * factor, where the sample is */
+ /* an 8-bit (unsigned) integer or a frac. */
+
+ double decode_factor;
+
+ sample_decoding decoding;
+
+} sample_map;
+
+/* Decode an 8-bit sample into a floating point color component. */
+/* penum points to the gx_image_enum structure. */
+#define decode_sample(sample_value, cc, i)\
+ switch ( penum->map[i].decoding )\
+ {\
+ case sd_none:\
+ cc.paint.values[i] = (sample_value) * (1.0 / 255.0); /* faster than / */\
+ break;\
+ case sd_lookup: /* <= 4 significant bits */\
+ cc.paint.values[i] =\
+ penum->map[i].decode_lookup[(sample_value) >> 4];\
+ break;\
+ case sd_compute:\
+ cc.paint.values[i] =\
+ penum->map[i].decode_base + (sample_value) * penum->map[i].decode_factor;\
+ }
+
+/* Decode a frac value similarly. */
+#define decode_frac(frac_value, cc, i)\
+ cc.paint.values[i] =\
+ penum->map[i].decode_base + (frac_value) * penum->map[i].decode_factor
+
+/* Define the distinct postures of an image. */
+/* Each posture includes its reflected variant. */
+typedef enum {
+ image_portrait = 0, /* 0 or 180 degrees */
+ image_landscape, /* 90 or 270 degrees */
+ image_skewed /* any other transformation */
+} image_posture;
+
+/* Define an entry in the image color table. For single-source-plane */
+/* images, the table index is the sample value, and the key is not used; */
+/* for multiple-plane (color) images, the table index is a hash of the key, */
+/* which is the concatenation of the source pixel components. */
+/* "Clue" = Color LookUp Entry (by analogy with CLUT). */
+typedef struct gx_image_clue_s {
+ gx_device_color dev_color;
+ bits32 key;
+} gx_image_clue;
+
+/* Main state structure */
+
+#ifndef gx_device_rop_texture_DEFINED
+# define gx_device_rop_texture_DEFINED
+typedef struct gx_device_rop_texture_s gx_device_rop_texture;
+#endif
+
+typedef struct gx_image_enum_s gx_image_enum;
+struct gx_image_enum_s {
+ /* We really want the map structure to be long-aligned, */
+ /* so we choose shorter types for some flags. */
+ /* Following are set at structure initialization */
+ int width;
+ int height;
+ byte bps; /* bits per sample: 1, 2, 4, 8, 12 */
+ byte unpack_bps; /* bps for computing unpack proc, */
+ /* set to 8 if no unpacking */
+ byte log2_xbytes; /* log2(bytes per expanded sample): */
+ /* 0 if bps <= 8, log2(sizeof(frac)) */
+ /* if bps > 8 */
+ byte spp; /* samples per pixel: 1, 3, or 4 */
+ byte num_planes; /* spp if colors are separated, */
+ /* 1 otherwise */
+ byte spread; /* num_planes << log2_xbytes */
+ byte masked; /* 0 = [color]image, 1 = imagemask */
+ byte interpolate; /* true if Interpolate requested */
+ gs_matrix matrix; /* image space -> device space */
+ fixed mtx, mty; /* device coords of image origin */
+ gs_fixed_point row_extent; /* total change in X/Y over one row, */
+ /* for row DDA */
+ iunpack_proc((*unpack));
+ irender_proc((*render));
+ gs_state *pgs; /****** BEING PHASED OUT ******/
+ const gs_imager_state *pis;
+ const gs_color_space *pcs; /* color space of image */
+ gs_memory_t *memory;
+ gx_device *dev;
+ byte *buffer; /* for expanding samples to a */
+ /* byte or frac */
+ uint buffer_size;
+ byte *line; /* buffer for an output scan line */
+ uint line_size;
+ uint line_width; /* width of line in device pixels */
+ uint bytes_per_row; /* # of input bytes per row */
+ /* (per plane, if spp == 1 and */
+ /* num_planes > 1) */
+ image_posture posture;
+ byte use_rop; /* true if CombineWithColor requested */
+ byte clip_image; /* mask, see below */
+ /* Either we are clipping to a rectangle, in which case */
+ /* the individual x/y flags may be set, or we are clipping */
+ /* to a general region, in which case only clip_region */
+ /* is set. */
+#define image_clip_xmin 1
+#define image_clip_xmax 2
+#define image_clip_ymin 4
+#define image_clip_ymax 8
+#define image_clip_region 0x10
+ byte slow_loop; /* true if !(skewed | */
+ /* imagemask with a halftone) */
+ byte device_color; /* true if device color space and */
+ /* standard decoding */
+ gs_fixed_rect clip_outer; /* outer box of clip path */
+ gs_fixed_rect clip_inner; /* inner box of clip path */
+ gs_logical_operation_t log_op; /* logical operation */
+ fixed adjust; /* adjustment when rendering */
+ /* characters */
+ gx_device_clip *clip_dev; /* clipping device (if needed) */
+ gx_device_rop_texture *rop_dev; /* RasterOp device (if needed) */
+ stream_IScale_state *scaler; /* scale state for */
+ /* Interpolate (if needed) */
+ /* Following are updated dynamically */
+ int x, y; /* next source x & y */
+ uint byte_in_row; /* current input byte position in row */
+ fixed xcur, ycur; /* device x, y of current row */
+ gx_dda_fixed next_x; /* DDA for xcur, holds next values */
+ /* when render proc is called */
+ gx_dda_fixed next_y; /* DDA for ycur ditto */
+ int line_xy; /* x or y value at start of buffered line */
+ int yci, hci; /* integer y & h of row (portrait) */
+ int xci, wci; /* integer x & w of row (landscape) */
+ /* The maps are set at initialization. We put them here */
+ /* so that the scalars will have smaller offsets. */
+ sample_map map[4];
+ /* Entries 0 and 255 of the following are set at initialization */
+ /* for monochrome images; other entries are updated dynamically. */
+ gx_image_clue clues[256];
+#define icolor0 clues[0].dev_color
+#define icolor1 clues[255].dev_color
+};
+/* Enumerate the pointers in an image enumerator. */
+#define gx_image_enum_do_ptrs(m)\
+ m(0,pgs) m(1,pis) m(2,pcs) m(3,dev) m(4,buffer) m(5,line)\
+ m(6,clip_dev) m(7,rop_dev) m(8,scaler)
+#define gx_image_enum_num_ptrs 9
+#define private_st_gx_image_enum() /* in gsimage.c */\
+ gs_private_st_composite(st_gx_image_enum, gx_image_enum, "gx_image_enum",\
+ image_enum_enum_ptrs, image_enum_reloc_ptrs)
+
+/* Compare two device colors for equality. */
+#define dev_color_eq(devc1, devc2)\
+ (gx_dc_is_pure(&(devc1)) ?\
+ gx_dc_is_pure(&(devc2)) &&\
+ gx_dc_pure_color(&(devc1)) == gx_dc_pure_color(&(devc2)) :\
+ gx_dc_is_binary_halftone(&(devc1)) ?\
+ gx_dc_is_binary_halftone(&(devc2)) &&\
+ gx_dc_binary_color0(&(devc1)) == gx_dc_binary_color0(&(devc2)) &&\
+ gx_dc_binary_color1(&(devc1)) == gx_dc_binary_color1(&(devc2)) &&\
+ (devc1).colors.binary.b_level == (devc2).colors.binary.b_level :\
+ false)
diff --git a/pstoraster/gximage0.c b/pstoraster/gximage0.c
new file mode 100644
index 000000000..4fb7c0e31
--- /dev/null
+++ b/pstoraster/gximage0.c
@@ -0,0 +1,283 @@
+/* Copyright (C) 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gximage0.c */
+/* Generic image enumeration and cleanup */
+#include "gx.h"
+#include "memory_.h"
+#include "gserrors.h"
+#include "gxdevice.h"
+#include "gxcpath.h"
+#include "gximage.h"
+
+/* Process the next piece of an image */
+int
+gx_default_image_data(gx_device *dev,
+ void *info, const byte **planes, uint raster,
+ int x, int y, int dwidth, int dheight)
+{ gx_image_enum *penum = info;
+ int x_end = x + dwidth;
+ int y_end = y + dheight;
+ uint rsize = penum->bytes_per_row;
+ uint pos = penum->byte_in_row;
+ int width = penum->width;
+ int nplanes = penum->num_planes;
+ uint bcount = /* bytes per data row */
+ (dwidth * penum->bps * (penum->spp / penum->num_planes) + 7) >> 3;
+ uint dpos = 0;
+ fixed adjust = penum->adjust;
+ int code;
+
+ if ( dwidth == 0 || dheight == 0 )
+ return 0;
+ if ( penum->x != x || penum->y != y )
+ return_error(gs_error_rangecheck);
+ if ( x_end < width )
+ --y_end;
+ else
+ x_end = 0;
+
+ /* We've accumulated an entire set of planes. */
+ /* Set up the clipping and/or RasterOp device if needed. */
+
+ if ( penum->clip_dev )
+ { gx_device_clip *cdev = penum->clip_dev;
+ cdev->target = dev;
+ dev = (gx_device *)cdev;
+ }
+ if ( penum->rop_dev )
+ { gx_device_rop_texture *rtdev = penum->rop_dev;
+ ((gx_device_forward *)rtdev)->target = dev;
+ dev = (gx_device *)rtdev;
+ }
+
+ /* Now render complete rows. */
+
+ while ( penum->x != x_end || penum->y < y_end )
+ { /* Fill up a row, then display it. */
+ int px;
+
+ for ( px = 0; px < nplanes; px++ )
+ (*penum->unpack)(penum->buffer + (px << penum->log2_xbytes),
+ planes[px] + dpos, bcount,
+ &penum->map[px], penum->spread, pos);
+ pos += bcount;
+ dpos += bcount;
+ if ( pos == rsize ) /* filled an entire row */
+ {
+#ifdef DEBUG
+ if ( gs_debug_c('B') )
+ { int i, n = width * penum->spp;
+ dputs("[B]row:");
+ for ( i = 0; i < n; i++ )
+ dprintf1(" %02x", penum->buffer[i]);
+ dputs("\n");
+ }
+#endif
+ penum->xcur = dda_current(penum->next_x);
+ dda_next(penum->next_x);
+ penum->ycur = dda_current(penum->next_y);
+ dda_next(penum->next_y);
+ if ( !penum->interpolate )
+ switch ( penum->posture )
+ {
+ case image_portrait:
+ { /* Precompute integer y and height, */
+ /* and check for clipping. */
+ fixed yc = penum->ycur,
+ yn = dda_current(penum->next_y);
+
+ if ( yn < yc )
+ { fixed temp = yn; yn = yc; yc = temp; }
+ yc -= adjust;
+ if ( yc >= penum->clip_outer.q.y ) goto mt;
+ yn += adjust;
+ if ( yn <= penum->clip_outer.p.y ) goto mt;
+ penum->yci = fixed2int_pixround(yc);
+ penum->hci =
+ fixed2int_pixround(yn) - penum->yci;
+ if ( penum->hci == 0 ) goto mt;
+ } break;
+ case image_landscape:
+ { /* Check for no pixel centers in x. */
+ fixed xc = penum->xcur,
+ xn = dda_current(penum->next_x);
+
+ xc -= adjust;
+ if ( xn < xc )
+ { fixed temp = xn; xn = xc; xc = temp; }
+ if ( xc >= penum->clip_outer.q.x ) goto mt;
+ xn += adjust;
+ if ( xn <= penum->clip_outer.p.x ) goto mt;
+ penum->xci = fixed2int_pixround(xc);
+ penum->wci =
+ fixed2int_pixround(xn) - penum->xci;
+ if ( penum->wci == 0 ) goto mt;
+ } break;
+ case image_skewed:
+ ;
+ }
+ code = (*penum->render)(penum, penum->buffer,
+ width * penum->spp, 1, dev);
+ if ( code < 0 )
+ goto err;
+mt: penum->x = 0;
+ if ( ++(penum->y) == penum->height )
+ goto end;
+ pos = 0;
+ }
+ else
+ penum->x = x_end;
+ }
+ penum->byte_in_row = pos;
+ code = 0;
+ goto out;
+end: /* End of data. Render any left-over buffered data. */
+ switch ( penum->posture )
+ {
+ case image_portrait:
+ { fixed yc = dda_current(penum->next_y);
+ penum->yci = fixed2int_rounded(yc - adjust);
+ penum->hci = fixed2int_rounded(yc + adjust) - penum->yci;
+ } break;
+ case image_landscape:
+ { fixed xc = dda_current(penum->next_x);
+ penum->xci = fixed2int_rounded(xc - adjust);
+ penum->wci = fixed2int_rounded(xc + adjust) - penum->xci;
+ } break;
+ case image_skewed: /* pacify compilers */
+ ;
+ }
+ code = (*penum->render)(penum, NULL, width * penum->spp, 0, dev);
+ if ( code < 0 )
+ { penum->y--;
+ goto err;
+ }
+ code = 1;
+ goto out;
+err: /* Error or interrupt, restore original state. */
+ penum->x = x;
+ while ( penum->y > y )
+ { dda_previous(penum->next_x);
+ dda_previous(penum->next_y);
+ --(penum->y);
+ }
+ /* Note that caller must call end_image */
+ /* for both error and normal termination. */
+out: return code;
+}
+
+/* Clean up by releasing the buffers. */
+/* Currently we ignore draw_last. */
+int
+gx_default_end_image(gx_device *dev, void *info, bool draw_last)
+{ gx_image_enum *penum = info;
+ gs_memory_t *mem = penum->memory;
+ stream_IScale_state *scaler = penum->scaler;
+
+ gs_free_object(mem, penum->rop_dev, "image RasterOp");
+ gs_free_object(mem, penum->clip_dev, "image clipper");
+ if ( scaler != 0 )
+ { (*s_IScale_template.release)((stream_state *)scaler);
+ gs_free_object(mem, scaler, "image scaler state");
+ }
+ gs_free_object(mem, penum->line, "image line");
+ gs_free_object(mem, penum->buffer, "image buffer");
+ gs_free_object(mem, penum, "gx_default_end_image");
+ return 0;
+}
+
+/* ------ Unpacking procedures ------ */
+
+void
+image_unpack_copy(byte *bptr, const byte *data, uint dsize,
+ const sample_map *pmap, int spread, uint inpos)
+{ register byte *bufp = bptr + inpos;
+ if ( data != bufp )
+ memcpy(bufp, data, dsize);
+}
+
+void
+image_unpack_1(byte *bptr, register const byte *data, uint dsize,
+ const sample_map *pmap, int spread, uint inpos)
+{ register bits32 *bufp = (bits32 *)(bptr + (inpos << 3));
+ int left = dsize;
+ register const bits32 *map = &pmap->table.lookup4x1to32[0];
+ register uint b;
+ if ( left & 1 )
+ { b = data[0];
+ bufp[0] = map[b >> 4];
+ bufp[1] = map[b & 0xf];
+ data++, bufp += 2;
+ }
+ left >>= 1;
+ while ( left-- )
+ { b = data[0];
+ bufp[0] = map[b >> 4];
+ bufp[1] = map[b & 0xf];
+ b = data[1];
+ bufp[2] = map[b >> 4];
+ bufp[3] = map[b & 0xf];
+ data += 2, bufp += 4;
+ }
+}
+
+void
+image_unpack_2(byte *bptr, register const byte *data, uint dsize,
+ const sample_map *pmap, int spread, uint inpos)
+{ register bits16 *bufp = (bits16 *)(bptr + (inpos << 2));
+ int left = dsize;
+ register const bits16 *map = &pmap->table.lookup2x2to16[0];
+ while ( left-- )
+ { register unsigned b = *data++;
+ *bufp++ = map[b >> 4];
+ *bufp++ = map[b & 0xf];
+ }
+}
+
+void
+image_unpack_4(byte *bptr, register const byte *data, uint dsize,
+ const sample_map *pmap, register int spread, uint inpos)
+{ register byte *bufp = bptr + (inpos << 1) * spread;
+ int left = dsize;
+ register const byte *map = &pmap->table.lookup8[0];
+ while ( left-- )
+ { register unsigned b = *data++;
+ *bufp = map[b >> 4]; bufp += spread;
+ *bufp = map[b & 0xf]; bufp += spread;
+ }
+}
+
+void
+image_unpack_8(byte *bptr, const byte *data, uint dsize,
+ const sample_map *pmap, int spread, uint inpos)
+{ register byte *bufp = bptr + inpos;
+ if ( pmap->table.lookup8[0] != 0 || pmap->table.lookup8[255] != 255 )
+ { register uint left = dsize;
+ register const byte *map = &pmap->table.lookup8[0];
+ while ( left-- )
+ *bufp++ = map[*data++];
+ }
+ else if ( data != bufp )
+ memcpy(bufp, data, dsize);
+}
diff --git a/pstoraster/gximage1.c b/pstoraster/gximage1.c
new file mode 100644
index 000000000..35e3eb7b2
--- /dev/null
+++ b/pstoraster/gximage1.c
@@ -0,0 +1,437 @@
+/* Copyright (C) 1989, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gximage1.c */
+/* Fast monochrome image rendering */
+#include "gx.h"
+#include "memory_.h"
+#include "gpcheck.h"
+#include "gserrors.h"
+#include "gxfixed.h"
+#include "gxarith.h"
+#include "gxmatrix.h"
+#include "gsccolor.h"
+#include "gspaint.h"
+#include "gsutil.h"
+#include "gzstate.h"
+#include "gxcmap.h"
+#include "gzpath.h"
+#include "gxdevice.h"
+#include "gxdevmem.h"
+#include "gdevmem.h" /* for mem_mono_device */
+#include "gxcpath.h"
+#include "gximage.h"
+#include "gzht.h"
+
+/* Conditionally include statistics code. */
+#ifdef DEBUG
+# define STATS
+#endif
+
+/* ------ Rendering procedures ------ */
+
+/* Rendering procedure for ignoring an image. We still need to iterate */
+/* over the samples, because the procedure might have side effects. */
+int
+image_render_skip(gx_image_enum *penum, byte *data, uint w, int h,
+ gx_device *dev)
+{ return h;
+}
+
+/* Scale (and possibly reverse) one scan line of a monobit image. */
+/* This is used for both portrait and landscape image processing. */
+/* We pass in an x offset (0 <= line_x < align_bitmap_mod * 8) so that */
+/* we can align the result with the eventual device X. */
+#ifdef STATS
+struct ix_s {
+ long
+ calls, runs,
+ lbit0, byte00, byte01, byte02, byte03, byte04, rbit0,
+ lbit1, byte1, rbit1,
+ thin, thin2, nwide, bwide, nfill, bfill;
+} ix_;
+# define incs(stat) ++ix_.stat
+# define adds(stat, n) ix_.stat += n
+#else
+# define incs(stat) DO_NOTHING
+# define adds(stat, n) DO_NOTHING
+#endif
+private void
+image_simple_expand(byte *line, int line_x, uint raster, uint line_width,
+ byte *buffer, uint w, fixed xcur, fixed dxx, byte zero /* 0 or 0xff */)
+{ int ix = fixed2int_pixround(xcur);
+ fixed xl = xcur + fixed_half - int2fixed(ix);
+ byte sbit = 0x80;
+ const fixed dxx_4 = dxx << 2;
+ const fixed dxx_8 = dxx_4 << 1;
+ const fixed dxx_32 = dxx_8 << 2;
+ register const byte *psrc = buffer;
+ byte *endp = buffer + (w >> 3);
+ byte endbit = 1 << (~w & 7);
+ byte data;
+ byte one = ~zero;
+
+ if ( dxx < 0 )
+ { ix -= line_width;
+ xl += int2fixed(line_width);
+ }
+ xl += int2fixed(line_x);
+
+ /* Ensure that the line ends with a transition from 0 to 1. */
+ if ( endbit == 1 )
+ ++endp, endp[-1] &= ~1, *endp = endbit = 0x80;
+ else
+ endbit >>= 1, *endp = (*endp & ~(endbit << 1)) | endbit;
+
+ /* Pre-clear the line. */
+ memset(line + (line_x >> 3), zero, raster - (line_x >> 3));
+
+ /*
+ * Loop invariants:
+ * data = *psrc;
+ * sbit = 1 << n, 0<=n<=7.
+ */
+ incs(calls);
+ for ( data = *psrc; ; )
+ { int x0, n, bit;
+ byte *bp;
+ static const byte lmasks[9] =
+ { 0xff, 0x7f, 0x3f, 0x1f, 0xf, 7, 3, 1, 0 };
+ static const byte rmasks[9] =
+ { 0, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff };
+
+ incs(runs);
+
+ /* Scan a run of zeros. */
+ data ^= 0xff; /* invert */
+ while ( data & sbit )
+ { xl += dxx;
+ sbit >>= 1;
+ incs(lbit0);
+ }
+ if ( !sbit )
+ { /* Scan a run of zero bytes. */
+sw: if ( (data = psrc[1]) != 0 )
+ { psrc++;
+ incs(byte00);
+ }
+ else if ( (data = psrc[2]) != 0 )
+ { xl += dxx_8, psrc += 2;
+ incs(byte01);
+ }
+ else if ( (data =psrc[3]) != 0 )
+ { xl += dxx_8 << 1, psrc += 3;
+ incs(byte02);
+ }
+ else if ( (data = psrc[4]) != 0 )
+ { xl += dxx_32 - dxx_8, psrc += 4;
+ incs(byte03);
+ }
+ else
+ { xl += dxx_32;
+ psrc += 4;
+ incs(byte04);
+ goto sw;
+ }
+ if ( data > 0xf )
+ sbit = 0x80;
+ else
+ sbit = 0x08, xl += dxx_4;
+ data ^= 0xff; /* invert */
+ while ( data & sbit )
+ { xl += dxx;
+ sbit >>= 1;
+ incs(rbit0);
+ }
+ }
+ /* We know the data end with a transition from 0 to 1; */
+ /* check for that now. */
+ if ( psrc >= endp && sbit == endbit )
+ break;
+ x0 = fixed2int_var(xl);
+
+ /* Scan a run of ones. */
+ /* We know the current bit is a one. */
+ data ^= 0xff; /* un-invert */
+ do
+ { xl += dxx;
+ sbit >>= 1;
+ incs(lbit1);
+ }
+ while ( data & sbit );
+ if ( !sbit )
+ { /* Scan a run of 0xff bytes. */
+ while ( (data = *++psrc) == 0xff )
+ { xl += dxx_8;
+ incs(byte1);
+ }
+ if ( data < 0xf0 )
+ sbit = 0x80;
+ else
+ sbit = 0x08, xl += dxx_4;
+ while ( data & sbit )
+ { xl += dxx;
+ sbit >>= 1;
+ incs(rbit1);
+ }
+ }
+
+ /* Fill the run in the scan line. */
+ n = fixed2int_var(xl) - x0;
+ if ( n < 0 )
+ x0 += n, n = -n;
+ bp = line + (x0 >> 3);
+ bit = x0 & 7;
+ if ( (n += bit) <= 8 )
+ { *bp ^= lmasks[bit] - lmasks[n];
+ incs(thin);
+ }
+ else if ( (n -= 8) <= 8 )
+ { *bp ^= lmasks[bit];
+ bp[1] ^= rmasks[n];
+ incs(thin2);
+ }
+ else
+ { *bp++ ^= lmasks[bit];
+ if ( n >= 56 )
+ { int nb = n >> 3;
+ memset(bp, one, nb);
+ bp += nb;
+ incs(nwide);
+ adds(bwide, nb);
+ }
+ else
+ { adds(bfill, n >> 3);
+ while ( (n -= 8) >= 0 )
+ *bp++ = one;
+ incs(nfill);
+ }
+ *bp ^= rmasks[n & 7];
+ }
+
+ };
+}
+
+/* Rendering procedure for a monobit image with no */
+/* skew or rotation and pure colors. */
+int
+image_render_simple(gx_image_enum *penum, byte *buffer, uint w, int h,
+ gx_device *dev)
+{ dev_proc_copy_mono((*copy_mono)) = dev_proc(dev, copy_mono);
+ byte *line = penum->line;
+ uint line_width, line_size;
+ int line_x;
+ fixed xcur = penum->xcur;
+ int ix = fixed2int_pixround(xcur);
+ const int iy = penum->yci, ih = penum->hci;
+ fixed dxx =
+ float2fixed(penum->matrix.xx + fixed2float(fixed_epsilon) / 2);
+
+ gx_color_index
+ zero = penum->icolor0.colors.pure,
+ one = penum->icolor1.colors.pure;
+ int dy = 0;
+
+ if ( h == 0 )
+ return 0;
+ if ( penum->map[0].table.lookup4x1to32[0] != 0 )
+ zero = penum->icolor1.colors.pure,
+ one = penum->icolor0.colors.pure;
+
+ if ( line == 0 )
+ { /* A direct BitBlt is possible. */
+ line = buffer;
+ line_size = (w + 7) >> 3;
+ line_width = w;
+ line_x = 0;
+ }
+ else if ( copy_mono == mem_mono_device.std_procs.copy_mono &&
+ dxx > 0 && (zero ^ one) == 1 /* must be (0,1) or (1,0) */
+ )
+ { /* Do the operation directly into the memory device bitmap. */
+ int ixr = fixed2int_pixround(xcur + w * dxx) - 1;
+ int line_ix;
+ int ib_left = ix >> 3, ib_right = ixr >> 3;
+ byte save_left, save_right, mask;
+
+ line = scan_line_base((gx_device_memory *)dev, iy);
+ line_x = ix & (align_bitmap_mod * 8 - 1);
+ line_ix = ix - line_x;
+ line_size = (ixr >> 3) + 1 - (line_ix >> 3);
+ line_width = ixr + 1 - ix;
+ /* We must save and restore any unmodified bits in */
+ /* the two edge bytes. */
+ save_left = line[ib_left];
+ save_right = line[ib_right];
+ image_simple_expand(line + (line_ix >> 3), line_x,
+ line_size, line_width,
+ buffer, w, xcur, dxx,
+ -(byte)zero);
+ if ( ix & 7 )
+ mask = (byte)(0xff00 >> (ix & 7)),
+ line[ib_left] = (save_left & mask) + (line[ib_left] & ~mask);
+ if ( (ixr + 1) & 7 )
+ mask = (byte)(0xff00 >> ((ixr + 1) & 7)),
+ line[ib_right] = (line[ib_right] & mask) + (save_right & ~mask);
+ line += line_ix >> 3;
+ dy = 1;
+ /*
+ * If we're going to replicate the line, ensure that we don't
+ * attempt to change the polarity.
+ */
+ zero = 0;
+ one = 1;
+ }
+ else
+ { line_size = penum->line_size;
+ line_width = penum->line_width;
+ line_x = ix & (align_bitmap_mod * 8 - 1);
+ image_simple_expand(line, line_x, line_size, line_width,
+ buffer, w, xcur, dxx, 0);
+ }
+
+ /* Finally, transfer the scan line to the device. */
+ if ( dxx < 0 )
+ ix -= line_width;
+ for ( ; dy < ih; dy++ )
+ { int code = (*copy_mono)(dev, line, line_x, line_size,
+ gx_no_bitmap_id,
+ ix, iy + dy, line_width, 1,
+ zero, one);
+ if ( code < 0 )
+ return code;
+ }
+
+ return_check_interrupt(1);
+}
+
+/* Rendering procedure for a 90 degree rotated monobit image */
+/* with pure colors. We buffer and then flip 8 scan lines at a time. */
+private int copy_landscape(P5(gx_image_enum *, int, int, bool, gx_device *));
+int
+image_render_landscape(gx_image_enum *penum, byte *buffer, uint w, int h,
+ gx_device *dev)
+{ byte *line = penum->line;
+ uint raster = bitmap_raster(penum->line_width);
+ int ix = penum->xci, iw = penum->wci;
+ int xinc, xmod;
+ byte *row;
+ const byte *orig_row = 0;
+ fixed fxy =
+ float2fixed(penum->matrix.xy + fixed2float(fixed_epsilon) / 2);
+ bool y_neg = fxy < 0;
+
+ if ( is_fneg(penum->matrix.yx) )
+ ix += iw - 1, iw = -iw, xinc = -1;
+ else
+ xinc = 1;
+ if ( h != 0 )
+ { for ( ; iw != 0; iw -= xinc )
+ { xmod = ix & 7;
+ row = line + xmod * raster;
+ if ( orig_row == 0 )
+ { image_simple_expand(row, 0, raster,
+ penum->line_width,
+ buffer, w,
+ penum->ycur, fxy, 0);
+ orig_row = row;
+ }
+ else
+ memcpy(row, orig_row, raster);
+ if ( xinc > 0 )
+ { ++ix;
+ if ( xmod == 7 )
+ { int code =
+ copy_landscape(penum,
+ penum->line_xy, ix,
+ y_neg, dev);
+ if ( code < 0 )
+ return code;
+ orig_row = 0;
+ penum->line_xy = ix;
+ }
+ }
+ else
+ { if ( xmod == 0 )
+ { int code =
+ copy_landscape(penum, ix,
+ penum->line_xy,
+ y_neg, dev);
+ if ( code < 0 )
+ return code;
+ orig_row = 0;
+ penum->line_xy = ix;
+ }
+ --ix;
+ }
+ }
+ return 0;
+ }
+ else
+ { /* Put out any left-over bits. */
+ return
+ (xinc > 0 ?
+ copy_landscape(penum, penum->line_xy, ix, y_neg, dev) :
+ copy_landscape(penum, ix + 1, penum->line_xy, y_neg, dev));
+ }
+}
+
+/* Flip and copy one group of scan lines. */
+private int
+copy_landscape(gx_image_enum *penum, int x0, int x1, bool y_neg,
+ gx_device *dev)
+{ byte *line = penum->line;
+ uint line_width = penum->line_width;
+ uint raster = bitmap_raster(line_width);
+ byte *flipped = line + raster * 8;
+
+ /* Flip the buffered data from raster * 8 to align_bitmap_mod * */
+ /* line_width. */
+ if ( line_width > 0 )
+ { int i;
+ for ( i = (line_width - 1) >> 3; i >= 0; --i )
+ memflip8x8(line + i, raster,
+ flipped + (i << (log2_align_bitmap_mod + 3)),
+ align_bitmap_mod);
+ }
+
+ /* Transfer the scan lines to the device. */
+ { dev_proc_copy_mono((*copy_mono)) = dev_proc(dev, copy_mono);
+ gx_color_index
+ zero = penum->icolor0.colors.pure,
+ one = penum->icolor1.colors.pure;
+ int w = x1 - x0;
+ int y = fixed2int(penum->ycur);
+
+ if ( penum->map[0].table.lookup4x1to32[0] != 0 )
+ zero = penum->icolor1.colors.pure,
+ one = penum->icolor0.colors.pure;
+ if ( w < 0 )
+ x0 = x1, w = -w;
+ if ( y_neg )
+ y -= line_width;
+ return (*copy_mono)(dev, flipped, x0 & 7, align_bitmap_mod,
+ gx_no_bitmap_id,
+ x0, y, w, line_width, zero, one);
+ }
+}
diff --git a/pstoraster/gximage2.c b/pstoraster/gximage2.c
new file mode 100644
index 000000000..bb097a97a
--- /dev/null
+++ b/pstoraster/gximage2.c
@@ -0,0 +1,324 @@
+/* Copyright (C) 1989, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gximage2.c */
+/* General monochrome image rendering */
+#include "gx.h"
+#include "memory_.h"
+#include "gpcheck.h"
+#include "gserrors.h"
+#include "gxfixed.h"
+#include "gxarith.h"
+#include "gxmatrix.h"
+#include "gsccolor.h"
+#include "gspaint.h"
+#include "gsutil.h"
+#include "gzstate.h"
+#include "gxcmap.h"
+#include "gzpath.h"
+#include "gxdevice.h"
+#include "gxdevmem.h"
+#include "gdevmem.h" /* for mem_mono_device */
+#include "gxcpath.h"
+#include "gximage.h"
+#include "gzht.h"
+
+/* Rendering procedure for the general case of displaying a */
+/* monochrome image, dealing with multiple bit-per-sample images, */
+/* general transformations, and arbitrary single-component */
+/* color spaces (DeviceGray, CIEBasedA, Separation, Indexed). */
+/* This procedure handles a single scan line. */
+int
+image_render_mono(gx_image_enum *penum, byte *buffer, uint w, int h,
+ gx_device *dev)
+{ gs_state *pgs = penum->pgs;
+ const gs_imager_state *pis = penum->pis;
+ gs_logical_operation_t lop = pis->log_op;
+ const int masked = penum->masked;
+ fixed xt = penum->xcur;
+ const gs_color_space *pcs; /* only set for non-masks */
+ cs_proc_remap_color((*remap_color)); /* ditto */
+ gs_client_color cc;
+ const gx_color_map_procs *cmap_procs = gx_device_cmap_procs(dev);
+ cmap_proc_gray((*map_gray)) = cmap_procs->map_gray;
+ gx_device_color *pdevc = pgs->dev_color;
+ /* Make sure the cache setup matches the graphics state. */
+ /* Also determine whether all tiles fit in the cache. */
+ int tiles_fit = gx_check_tile_cache(pgs);
+#define image_set_gray(sample_value)\
+ { pdevc = &penum->clues[sample_value].dev_color;\
+ if ( !color_is_set(pdevc) )\
+ { if ( penum->device_color )\
+ (*map_gray)(byte2frac(sample_value), pdevc, pgs);\
+ else\
+ { decode_sample(sample_value, cc, 0);\
+ (*remap_color)(&cc, pcs, pdevc, pgs);\
+ }\
+ }\
+ else if ( !color_is_pure(pdevc) )\
+ { if ( !tiles_fit )\
+ { code = gx_color_load(pdevc, pgs);\
+ if ( code < 0 ) return code;\
+ }\
+ }\
+ }
+ fixed xl = xt;
+ gx_dda_fixed next_x;
+ register const byte *psrc = buffer;
+ byte *endp = buffer + w;
+ fixed xrun = xt; /* x at start of run */
+ register byte run; /* run value */
+ int htrun = /* halftone run value */
+ (masked ? 255 : -2);
+ int code;
+
+ if ( h == 0 )
+ return 0;
+ if ( !masked )
+ { pcs = penum->pcs; /* (may not be set for masks) */
+ remap_color = pcs->type->remap_color;
+ }
+ run = *psrc;
+ *endp = ~endp[-1]; /* force end of run */
+ if ( penum->slow_loop || penum->posture != image_portrait )
+ { /* Skewed, rotated, or imagemask with a halftone. */
+ gx_dda_fixed next_y;
+ fixed ytf = penum->ycur;
+ fixed yrun = ytf;
+ const fixed pdyx = dda_current(penum->next_x) - xl;
+ const fixed pdyy = dda_current(penum->next_y) - ytf;
+ dev_proc_fill_parallelogram((*fill_pgram)) =
+ dev_proc(dev, fill_parallelogram);
+
+ dda_init(next_x, xl, penum->row_extent.x, penum->width);
+#define xl dda_current(next_x)
+ dda_init(next_y, ytf, penum->row_extent.y, penum->width);
+#define ytf dda_current(next_y)
+ if ( masked )
+ { code = gx_color_load(pdevc, pgs);
+ if ( code < 0 )
+ return code;
+ for ( ; ; )
+ { /* To avoid rounding errors, we must run dda_next */
+ /* for each pixel. */
+ for ( ; !*psrc; ++psrc )
+ { dda_next(next_x);
+ dda_next(next_y);
+ }
+ if ( psrc >= endp )
+ break;
+ yrun = ytf;
+ xrun = xl;
+ for ( ; *psrc; ++psrc )
+ { dda_next(next_x);
+ dda_next(next_y);
+ }
+ code = (*fill_pgram)(dev, xrun, yrun, xl - xrun,
+ ytf - yrun, pdyx, pdyy, pdevc, lop);
+ if ( code < 0 )
+ return code;
+ if ( psrc >= endp )
+ break;
+ }
+ }
+ else /* not masked */
+ { for ( ; ; )
+ { /* We can't skip large constant regions quickly, */
+ /* because this leads to rounding errors. */
+ /* Just fill the region between xrun and xl. */
+ dda_next(next_x);
+ dda_next(next_y); /* harmless if no skew */
+ psrc++;
+ if ( run != htrun )
+ { htrun = run;
+ image_set_gray(run);
+ }
+ code = (*fill_pgram)(dev, xrun, yrun, xl - xrun,
+ ytf - yrun, pdyx, pdyy, pdevc, lop);
+ if ( code < 0 )
+ return code;
+ if ( psrc >= endp )
+ break;
+ yrun = ytf;
+ xrun = xl;
+ run = psrc[-1];
+ }
+ }
+#undef xl
+#undef ytf
+ }
+ else /* fast loop */
+ { /* No skew, and not imagemask with a halftone. */
+ const fixed adjust = penum->adjust;
+ const fixed dxx =
+ float2fixed(penum->matrix.xx + fixed2float(fixed_epsilon) / 2);
+ gx_dda_step_fixed dxx2, dxx3, dxx4;
+ fixed xa = (dxx >= 0 ? adjust : -adjust);
+ const int yt = penum->yci, iht = penum->hci;
+ dev_proc_fill_rectangle((*fill_proc)) =
+ dev_proc(dev, fill_rectangle);
+ dev_proc_strip_tile_rectangle((*tile_proc)) =
+ dev_proc(dev, strip_tile_rectangle);
+ dev_proc_copy_mono((*copy_mono_proc)) =
+ dev_proc(dev, copy_mono);
+ /*
+ * If each pixel is likely to fit in a single halftone tile,
+ * determine that now (tile_offset = offset of row within tile).
+ * Don't do this for band devices; they handle halftone fills
+ * more efficiently than copy_mono.
+ */
+ int bstart;
+ int phase_x;
+ int tile_offset =
+ ((*dev_proc(dev, get_band))(dev, yt, &bstart) == 0 ?
+ gx_check_tile_size(pgs,
+ fixed2int_ceiling(any_abs(dxx) + (xa << 1)),
+ yt, iht, &phase_x) :
+ -1);
+ int xmin = fixed2int_pixround(penum->clip_outer.p.x);
+ int xmax = fixed2int_pixround(penum->clip_outer.q.x);
+
+ /* Fold the adjustment into xrun and xl, */
+ /* including the +0.5-epsilon for rounding. */
+ xrun = xrun - xa + (fixed_half - fixed_epsilon);
+ dda_init(next_x, xl + xa + (fixed_half - fixed_epsilon),
+ penum->row_extent.x, penum->width);
+#define xl dda_current(next_x)
+ xa <<= 1;
+ /* Calculate multiples of the DDA step. */
+ dxx2 = next_x.step;
+ dda_step_add(dxx2, next_x.step);
+ dxx3 = dxx2;
+ dda_step_add(dxx3, next_x.step);
+ dxx4 = dxx3;
+ dda_step_add(dxx4, next_x.step);
+ for ( ; ; )
+ { /* Skip large constant regions quickly, */
+ /* but don't slow down transitions too much. */
+skf: if ( psrc[0] == run )
+ { if ( psrc[1] == run )
+ { if ( psrc[2] == run )
+ { if ( psrc[3] == run )
+ { psrc += 4;
+ dda_state_next(next_x.state, dxx4);
+ goto skf;
+ }
+ else
+ { psrc += 4;
+ dda_state_next(next_x.state, dxx3);
+ }
+ }
+ else
+ { psrc += 3;
+ dda_state_next(next_x.state, dxx2);
+ }
+ }
+ else
+ { psrc += 2;
+ dda_next(next_x);
+ }
+ }
+ else
+ psrc++;
+ { /* Now fill the region between xrun and xl. */
+ int xi = fixed2int_var(xrun);
+ int wi = fixed2int_var(xl) - xi;
+ int xei, tsx;
+ const gx_strip_bitmap *tile;
+
+ if ( wi <= 0 )
+ { if ( wi == 0 ) goto mt;
+ xi += wi, wi = -wi;
+ }
+ if ( (xei = xi + wi) > xmax || xi < xmin )
+ { /* Do X clipping */
+ if ( xi < xmin )
+ wi -= xmin - xi, xi = xmin;
+ if ( xei > xmax )
+ wi -= xei - xmax;
+ if ( wi <= 0 )
+ goto mt;
+ }
+ switch ( run )
+ {
+ case 0:
+ if ( masked ) goto mt;
+ if ( !color_is_pure(&penum->icolor0) ) goto ht;
+ code = (*fill_proc)(dev, xi, yt, wi, iht,
+ penum->icolor0.colors.pure);
+ break;
+ case 255: /* just for speed */
+ if ( !color_is_pure(&penum->icolor1) ) goto ht;
+ code = (*fill_proc)(dev, xi, yt, wi, iht,
+ penum->icolor1.colors.pure);
+ break;
+ default:
+ht: /* Use halftone if needed */
+ if ( run != htrun )
+ { image_set_gray(run);
+ htrun = run;
+ }
+ /* We open-code gx_fill_rectangle, */
+ /* because we've done some of the work for */
+ /* halftone tiles in advance. */
+ if ( color_is_pure(pdevc) )
+ { code = (*fill_proc)(dev, xi, yt, wi, iht,
+ pdevc->colors.pure);
+ }
+ else if ( !color_is_binary_halftone(pdevc) )
+ { code =
+ gx_fill_rectangle_device_rop(xi, yt, wi, iht,
+ pdevc, dev, lop);
+ }
+ else if ( tile_offset >= 0 &&
+ (tile = &pdevc->colors.binary.b_tile->tiles,
+ (tsx = (xi + phase_x) % tile->rep_width) + wi <= tile->size.x)
+ )
+ { /* The pixel(s) fit(s) in a single (binary) tile. */
+ byte *row = tile->data + tile_offset;
+ code = (*copy_mono_proc)
+ (dev, row, tsx, tile->raster, gx_no_bitmap_id,
+ xi, yt, wi, iht,
+ pdevc->colors.binary.color[0],
+ pdevc->colors.binary.color[1]);
+ }
+ else
+ { code = (*tile_proc)(dev,
+ &pdevc->colors.binary.b_tile->tiles,
+ xi, yt, wi, iht,
+ pdevc->colors.binary.color[0],
+ pdevc->colors.binary.color[1],
+ pdevc->phase.x, pdevc->phase.y);
+ }
+ }
+ if ( code < 0 ) return code;
+mt: if ( psrc >= endp ) break;
+ xrun = xl - xa; /* original xa << 1 */
+ run = psrc[-1];
+ }
+ dda_next(next_x);
+ }
+ }
+#undef xl
+ return 1;
+}
diff --git a/pstoraster/gximage3.c b/pstoraster/gximage3.c
new file mode 100644
index 000000000..aff9ab3f9
--- /dev/null
+++ b/pstoraster/gximage3.c
@@ -0,0 +1,302 @@
+/* Copyright (C) 1992, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gximage3.c */
+/* Color image and multiple-source unpacking procedures */
+#include "gx.h"
+#include "memory_.h"
+#include "gpcheck.h"
+#include "gserrors.h"
+#include "gxfixed.h"
+#include "gxfrac.h"
+#include "gxarith.h"
+#include "gxmatrix.h"
+#include "gsccolor.h"
+#include "gspaint.h"
+#include "gzstate.h"
+#include "gxcmap.h"
+#include "gzpath.h"
+#include "gxdevice.h"
+#include "gxdevmem.h"
+#include "gxcpath.h"
+#include "gximage.h"
+
+/* ------ Unpacking procedures ------ */
+
+void
+image_unpack_1_spread(byte *bptr, register const byte *data, uint dsize,
+ const sample_map *pmap, register int spread, uint inpos)
+{ register byte *bufp = bptr + (inpos << 3) * spread;
+ int left = dsize;
+ register const byte *map = &pmap->table.lookup8[0];
+ while ( left-- )
+ { register uint b = *data++;
+ *bufp = map[b >> 7]; bufp += spread;
+ *bufp = map[(b >> 6) & 1]; bufp += spread;
+ *bufp = map[(b >> 5) & 1]; bufp += spread;
+ *bufp = map[(b >> 4) & 1]; bufp += spread;
+ *bufp = map[(b >> 3) & 1]; bufp += spread;
+ *bufp = map[(b >> 2) & 1]; bufp += spread;
+ *bufp = map[(b >> 1) & 1]; bufp += spread;
+ *bufp = map[b & 1]; bufp += spread;
+ }
+}
+
+void
+image_unpack_2_spread(byte *bptr, register const byte *data, uint dsize,
+ const sample_map *pmap, register int spread, uint inpos)
+{ register byte *bufp = bptr + (inpos << 2) * spread;
+ int left = dsize;
+ register const byte *map = &pmap->table.lookup8[0];
+ while ( left-- )
+ { register unsigned b = *data++;
+ *bufp = map[b >> 6]; bufp += spread;
+ *bufp = map[(b >> 4) & 3]; bufp += spread;
+ *bufp = map[(b >> 2) & 3]; bufp += spread;
+ *bufp = map[b & 3]; bufp += spread;
+ }
+}
+
+void
+image_unpack_8_spread(byte *bptr, register const byte *data, uint dsize,
+ const sample_map *pmap, register int spread, uint inpos)
+{ register byte *bufp = bptr + inpos * spread;
+ register int left = dsize;
+ register const byte *map = &pmap->table.lookup8[0];
+ while ( left-- )
+ { *bufp = map[*data++]; bufp += spread;
+ }
+}
+
+/* ------ Rendering procedures ------ */
+
+/* Render a color image with 8 or fewer bits per sample. */
+typedef union {
+ byte v[4];
+ bits32 all; /* for fast comparison & clearing */
+} color_samples;
+int
+image_render_color(gx_image_enum *penum, byte *buffer, uint w, int h,
+ gx_device *dev)
+{ gs_state *pgs = penum->pgs;
+ const gs_imager_state *pis = penum->pis;
+ gs_logical_operation_t lop = pis->log_op;
+ gx_dda_fixed next_x, next_y;
+ image_posture posture = penum->posture;
+ fixed xl = penum->xcur;
+ fixed ytf = penum->ycur;
+ fixed pdyx, pdyy; /* edge of parallelogram */
+ int vci, vdi;
+ const gs_color_space *pcs = penum->pcs;
+ cs_proc_remap_color((*remap_color)) = pcs->type->remap_color;
+ gs_client_color cc;
+ bool device_color = penum->device_color;
+ const gx_color_map_procs *cmap_procs = gx_device_cmap_procs(dev);
+ cmap_proc_rgb((*map_rgb)) = cmap_procs->map_rgb;
+ cmap_proc_cmyk((*map_cmyk)) = cmap_procs->map_cmyk;
+ gx_image_clue *pic = &penum->clues[0];
+#define pdevc (&pic->dev_color)
+ gx_image_clue *pic_next = &penum->clues[1];
+#define pdevc_next (&pic_next->dev_color)
+ gx_image_clue empty_clue;
+ int spp = penum->spp;
+ const byte *psrc = buffer;
+ fixed xrun = xl; /* x at start of run */
+ fixed yrun = ytf; /* y ditto */
+ int irun; /* int x/rrun */
+ color_samples run; /* run value */
+ color_samples next; /* next sample value */
+ bool small =
+ fixed2int(any_abs(penum->row_extent.x)) < penum->width &&
+ fixed2int(any_abs(penum->row_extent.y)) < penum->width;
+ byte *bufend = buffer + w;
+ bool use_cache = spp * penum->bps <= 12;
+
+ if ( h == 0 )
+ return 0;
+ switch ( posture )
+ {
+ case image_portrait:
+ vci = penum->yci, vdi = penum->hci;
+ irun = fixed2int_var_rounded(xrun);
+ break;
+ case image_landscape:
+ vci = penum->xci, vdi = penum->wci;
+ irun = fixed2int_var_rounded(yrun);
+ break;
+ case image_skewed:
+ pdyx = dda_current(penum->next_x) - xl;
+ pdyy = dda_current(penum->next_y) - ytf;
+ }
+
+ dda_init(next_x, xl, penum->row_extent.x, penum->width);
+ dda_init(next_y, ytf, penum->row_extent.y, penum->width);
+ bufend[0] = ~bufend[-spp]; /* force end of run */
+ if_debug4('b', "[b]y=%d w=%d xt=%f yt=%f\n",
+ penum->y, w, fixed2float(xl), fixed2float(ytf));
+ run.all = 0;
+ next.all = 0;
+ /* Ensure that we don't get any false dev_color_eq hits. */
+ if ( use_cache )
+ { color_set_pure(&empty_clue.dev_color, gx_no_color_index);
+ pic = &empty_clue;
+ }
+ cc.paint.values[0] = cc.paint.values[1] =
+ cc.paint.values[2] = cc.paint.values[3] = 0;
+ cc.pattern = 0;
+ run.v[0] = ~psrc[0]; /* force remap */
+ while ( psrc <= bufend ) /* 1 extra iteration */
+ /* to handle final run */
+ { dda_next(next_x);
+#define xn dda_current(next_x)
+ dda_next(next_y);
+#define yn dda_current(next_y)
+#define includes_pixel_center(a, b)\
+ (fixed_floor(a < b ? (a - (fixed_half + fixed_epsilon)) ^ (b - fixed_half) :\
+ (b - (fixed_half + fixed_epsilon)) ^ (a - fixed_half)) != 0)
+#define paint_no_pixels()\
+ (small && !includes_pixel_center(xl, xn) &&\
+ !includes_pixel_center(ytf, yn) && psrc <= bufend)
+#define clue_hash3(next)\
+ &penum->clues[(next.v[0] + (next.v[1] << 2) + (next.v[2] << 4)) & 255];
+#define clue_hash4(next)\
+ &penum->clues[(next.v[0] + (next.v[1] << 2) + (next.v[2] << 4) +\
+ (next.v[3] << 6)) & 255]
+
+ next.v[0] = psrc[0];
+ next.v[1] = psrc[1];
+ next.v[2] = psrc[2];
+ if ( spp == 4 ) /* cmyk */
+ { next.v[3] = psrc[3];
+ psrc += 4;
+ if ( next.all == run.all || paint_no_pixels() )
+ goto inc;
+ if ( use_cache )
+ { pic_next = clue_hash4(next);
+ if ( pic_next->key == next.all )
+ goto f;
+ pic_next->key = next.all;
+ }
+ if ( device_color )
+ { (*map_cmyk)(byte2frac(next.v[0]),
+ byte2frac(next.v[1]),
+ byte2frac(next.v[2]),
+ byte2frac(next.v[3]),
+ pdevc_next, pgs);
+ goto mapped;
+ }
+ decode_sample(next.v[3], cc, 3);
+ if_debug1('B', "[B]cc[3]=%g\n", cc.paint.values[3]);
+ }
+ else /* rgb */
+ { psrc += 3;
+ if ( next.all == run.all || paint_no_pixels() )
+ goto inc;
+ if ( use_cache )
+ { pic_next = clue_hash3(next);
+ if ( pic_next->key == next.all )
+ goto f;
+ pic_next->key = next.all;
+ }
+ if ( device_color )
+ { (*map_rgb)(byte2frac(next.v[0]),
+ byte2frac(next.v[1]),
+ byte2frac(next.v[2]),
+ pdevc_next, pgs);
+ goto mapped;
+ }
+ }
+ decode_sample(next.v[0], cc, 0);
+ decode_sample(next.v[1], cc, 1);
+ decode_sample(next.v[2], cc, 2);
+ if_debug3('B', "[B]cc[0..2]=%g,%g,%g\n",
+ cc.paint.values[0], cc.paint.values[1],
+ cc.paint.values[2]);
+ (*remap_color)(&cc, pcs, pdevc_next, pgs);
+mapped: if ( pic == pic_next )
+ goto fill;
+f: if_debug7('B', "[B]0x%x,0x%x,0x%x,0x%x -> %ld,%ld,0x%lx\n",
+ next.v[0], next.v[1], next.v[2], next.v[3],
+ pdevc_next->colors.binary.color[0],
+ pdevc_next->colors.binary.color[1],
+ (ulong)pdevc_next->type);
+ /* Even though the supplied colors don't match, */
+ /* the device colors might. */
+ if ( dev_color_eq(*pdevc, *pdevc_next) &&
+ psrc <= bufend /* force end of last run */
+ )
+ goto set;
+fill: { /* Fill the region between */
+ /* xrun/irun and xl */
+ int code;
+ switch ( posture )
+ {
+ case image_portrait:
+ { /* Rectangle */
+ int xi = irun;
+ int wi =
+ (irun = fixed2int_var_rounded(xl)) - xi;
+ if ( wi < 0 )
+ xi += wi, wi = -wi;
+ code =
+ gx_fill_rectangle_device_rop(xi, vci,
+ wi, vdi, pdevc, dev, lop);
+ } break;
+ case image_landscape:
+ { /* 90 degree rotated rectangle */
+ int yi = irun;
+ int hi =
+ (irun = fixed2int_var_rounded(ytf)) - yi;
+ if ( hi < 0 )
+ yi += hi, hi = -hi;
+ code = gx_fill_rectangle_device_rop(vci, yi,
+ vdi, hi, pdevc, dev, lop);
+ } break;
+ default:
+ { /* Parallelogram */
+ code = (*dev_proc(dev, fill_parallelogram))
+ (dev, xrun, yrun,
+ xl - xrun, ytf - yrun, pdyx, pdyy,
+ pdevc, lop);
+ xrun = xl;
+ yrun = ytf;
+ }
+ }
+ if ( code < 0 )
+ return code;
+ if ( use_cache )
+ pic = pic_next;
+ else
+ { gx_image_clue *ptemp = pic;
+ pic = pic_next;
+ pic_next = ptemp;
+ }
+ }
+set: run.all = next.all;
+inc: xl = xn;
+ ytf = yn; /* harmless if no skew */
+#undef xn
+#undef yn
+ }
+ return 1;
+}
diff --git a/pstoraster/gximage4.c b/pstoraster/gximage4.c
new file mode 100644
index 000000000..609e93a16
--- /dev/null
+++ b/pstoraster/gximage4.c
@@ -0,0 +1,282 @@
+/* Copyright (C) 1994, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gximage4.c */
+/* 12-bit image procedures */
+#include "gx.h"
+#include "memory_.h"
+#include "gpcheck.h"
+#include "gserrors.h"
+#include "gxfixed.h"
+#include "gxfrac.h"
+#include "gxarith.h"
+#include "gxmatrix.h"
+#include "gsccolor.h"
+#include "gspaint.h"
+#include "gzstate.h"
+#include "gxcmap.h"
+#include "gzpath.h"
+#include "gxdevice.h"
+#include "gxdevmem.h"
+#include "gxcpath.h"
+#include "gximage.h"
+
+/* ---------------- Unpacking procedures ---------------- */
+
+void
+image_unpack_12(byte *bptr, register const byte *data, uint dsize,
+ const sample_map *pmap, register int spread, uint inpos)
+{ register frac *bufp = (frac *)(bptr + inpos * 2 / 3 * spread);
+#define inc_bufp(bp, n) bp = (frac *)((byte *)(bp) + (n))
+ register uint sample;
+ register int left = dsize;
+ static const frac bits2frac_4[16] = {
+#define frac15(n) ((frac_1 / 15) * (n))
+ frac15(0), frac15(1), frac15(2), frac15(3),
+ frac15(4), frac15(5), frac15(6), frac15(7),
+ frac15(8), frac15(9), frac15(10), frac15(11),
+ frac15(12), frac15(13), frac15(14), frac15(15)
+#undef frac15
+ };
+ /* We have to deal with the 3 cases of inpos % 3 individually. */
+ /* Let N = inpos / 3. */
+ switch ( inpos % 3 )
+ {
+ case 1:
+ /* bufp points to frac N, which was already filled */
+ /* with the leftover byte from the previous call. */
+ sample = (frac2byte(*bufp) << 4) + (*data >> 4);
+ *bufp = bits2frac(sample, 12);
+ inc_bufp(bufp, spread);
+ *bufp = bits2frac_4[*data++ & 0xf];
+ if ( !--left ) return;
+ case 2:
+ /* bufp points to frac N+1, which was half-filled */
+ /* with the second leftover byte from the previous call. */
+ sample = (frac2bits(*bufp, 4) << 8) + *data++;
+ *bufp = bits2frac(sample, 12);
+ inc_bufp(bufp, spread);
+ --left;
+ case 0:
+ /* Nothing special to do. */
+ ;
+ }
+ while ( left >= 3 )
+ { sample = ((uint)*data << 4) + (data[1] >> 4);
+ *bufp = bits2frac(sample, 12);
+ inc_bufp(bufp, spread);
+ sample = ((uint)(data[1] & 0xf) << 8) + data[2];
+ *bufp = bits2frac(sample, 12);
+ inc_bufp(bufp, spread);
+ data += 3;
+ left -= 3;
+ }
+ /* Handle trailing bytes. */
+ switch ( left )
+ {
+ case 2: /* dddddddd ddddxxxx */
+ sample = ((uint)*data << 4) + (data[1] >> 4);
+ *bufp = bits2frac(sample, 12);
+ inc_bufp(bufp, spread);
+ *bufp = bits2frac_4[data[1] & 0xf];
+ break;
+ case 1: /* dddddddd */
+ sample = (uint)*data << 4;
+ *bufp = bits2frac(sample, 12);
+ break;
+ case 0: /* Nothing more to do. */
+ ;
+ }
+}
+
+/* ---------------- Rendering procedures ---------------- */
+
+/* ------ Rendering for 12-bit samples ------ */
+
+/* Render an image with more than 8 bits per sample. */
+/* The samples have been expanded into fracs. */
+#define longs_per_4_fracs (arch_sizeof_frac * 4 / arch_sizeof_long)
+typedef union {
+ frac v[4];
+ long all[longs_per_4_fracs]; /* for fast comparison */
+} color_fracs;
+#if longs_per_4_fracs == 1
+# define color_frac_eq(f1, f2)\
+ ((f1).all[0] == (f2).all[0])
+#else
+#if longs_per_4_fracs == 2
+# define color_frac_eq(f1, f2)\
+ ((f1).all[0] == (f2).all[0] && (f1).all[1] == (f2).all[1])
+#endif
+#endif
+int
+image_render_frac(gx_image_enum *penum, byte *buffer, uint w, int h,
+ gx_device *dev)
+{ gs_state *pgs = penum->pgs;
+ const gs_imager_state *pis = penum->pis;
+ gs_logical_operation_t lop = pis->log_op;
+ gx_dda_fixed next_x, next_y;
+ image_posture posture = penum->posture;
+ fixed xl = penum->xcur;
+ fixed ytf = penum->ycur;
+ fixed pdyx, pdyy; /* edge of parallelogram */
+ int yt = penum->yci, iht = penum->hci;
+ const gs_color_space *pcs = penum->pcs;
+ cs_proc_remap_color((*remap_color)) = pcs->type->remap_color;
+ gs_client_color cc;
+ int device_color = penum->device_color;
+ const gx_color_map_procs *cmap_procs = gx_device_cmap_procs(dev);
+ cmap_proc_rgb((*map_rgb)) = cmap_procs->map_rgb;
+ cmap_proc_cmyk((*map_cmyk)) = cmap_procs->map_cmyk;
+ gx_device_color devc1, devc2;
+ gx_device_color _ss *spdevc = &devc1;
+ gx_device_color _ss *spdevc_next = &devc2;
+#define pdevc ((gx_device_color *)spdevc)
+#define pdevc_next ((gx_device_color *)spdevc_next)
+ int spp = penum->spp;
+ const frac *psrc = (frac *)buffer;
+ fixed xrun = xl; /* x at start of run */
+ int irun = fixed2int_var_rounded(xrun); /* int xrun */
+ fixed yrun = ytf; /* y ditto */
+ color_fracs run; /* run value */
+ color_fracs next; /* next sample value */
+ frac *bufend = (frac *)buffer + w;
+
+ if ( h == 0 )
+ return 0;
+ dda_init(next_x, xl, penum->row_extent.x, penum->width);
+ dda_init(next_y, ytf, penum->row_extent.y, penum->width);
+ pdyx = dda_current(penum->next_x) - xl;
+ pdyy = dda_current(penum->next_y) - ytf;
+ bufend[0] = ~bufend[-spp]; /* force end of run */
+ if_debug4('b', "[b]y=%d w=%d xt=%f yt=%f\n",
+ penum->y, w, fixed2float(xl), fixed2float(ytf));
+ run.v[0] = run.v[1] = run.v[2] = run.v[3] = 0;
+ next.v[0] = next.v[1] = next.v[2] = next.v[3] = 0;
+ cc.paint.values[0] = cc.paint.values[1] =
+ cc.paint.values[2] = cc.paint.values[3] = 0;
+ cc.pattern = 0;
+ (*remap_color)(&cc, pcs, pdevc, pgs);
+ run.v[0] = ~psrc[0]; /* force remap */
+
+ while ( psrc <= bufend ) /* 1 extra iteration */
+ /* to handle final run */
+ { next.v[0] = psrc[0];
+ switch ( spp )
+ {
+ case 4: /* cmyk */
+ next.v[1] = psrc[1];
+ next.v[2] = psrc[2];
+ next.v[3] = psrc[3];
+ psrc += 4;
+ if ( color_frac_eq(next, run) ) goto inc;
+ if ( device_color )
+ { (*map_cmyk)(next.v[0], next.v[1],
+ next.v[2], next.v[3],
+ pdevc_next, pgs);
+ goto f;
+ }
+ decode_frac(next.v[0], cc, 0);
+ decode_frac(next.v[1], cc, 1);
+ decode_frac(next.v[2], cc, 2);
+ decode_frac(next.v[3], cc, 3);
+ if_debug4('B', "[B]cc[0..3]=%g,%g,%g,%g\n",
+ cc.paint.values[0], cc.paint.values[1],
+ cc.paint.values[2], cc.paint.values[3]);
+ if_debug1('B', "[B]cc[3]=%g\n",
+ cc.paint.values[3]);
+ break;
+ case 3: /* rgb */
+ next.v[1] = psrc[1];
+ next.v[2] = psrc[2];
+ psrc += 3;
+ if ( color_frac_eq(next, run) ) goto inc;
+ if ( device_color )
+ { (*map_rgb)(next.v[0], next.v[1],
+ next.v[2], pdevc_next, pgs);
+ goto f;
+ }
+ decode_frac(next.v[0], cc, 0);
+ decode_frac(next.v[1], cc, 1);
+ decode_frac(next.v[2], cc, 2);
+ if_debug3('B', "[B]cc[0..2]=%g,%g,%g\n",
+ cc.paint.values[0], cc.paint.values[1],
+ cc.paint.values[2]);
+ break;
+ case 1: /* gray */
+ psrc++;
+ if ( next.v[0] == run.v[0] ) goto inc;
+ if ( device_color )
+ { (*map_rgb)(next.v[0], next.v[0],
+ next.v[0], pdevc_next, pgs);
+ goto f;
+ }
+ decode_frac(next.v[0], cc, 0);
+ if_debug1('B', "[B]cc[0]=%g\n",
+ cc.paint.values[0]);
+ break;
+ }
+ (*remap_color)(&cc, pcs, pdevc_next, pgs);
+f: if_debug7('B', "[B]0x%x,0x%x,0x%x,0x%x -> %ld,%ld,0x%lx\n",
+ next.v[0], next.v[1], next.v[2], next.v[3],
+ pdevc_next->colors.binary.color[0],
+ pdevc_next->colors.binary.color[1],
+ (ulong)pdevc_next->type);
+ /* Even though the supplied colors don't match, */
+ /* the device colors might. */
+ if ( !dev_color_eq(devc1, devc2) ||
+ psrc > bufend /* force end of last run */
+ )
+ { /* Fill the region between */
+ /* xrun/irun and xl */
+ gx_device_color _ss *sptemp;
+ int code;
+ if ( posture != image_portrait )
+ { /* Parallelogram */
+ code = (*dev_proc(dev, fill_parallelogram))
+ (dev, xrun, yrun,
+ xl - xrun, ytf - yrun, pdyx, pdyy,
+ pdevc, lop);
+ xrun = xl;
+ yrun = ytf;
+ }
+ else
+ { /* Rectangle */
+ int xi = irun;
+ int wi = (irun = fixed2int_var_rounded(xl)) - xi;
+ if ( wi < 0 ) xi += wi, wi = -wi;
+ code = gx_fill_rectangle_device_rop(xi, yt,
+ wi, iht, pdevc, dev, lop);
+ }
+ if ( code < 0 )
+ return code;
+ sptemp = spdevc;
+ spdevc = spdevc_next;
+ spdevc_next = sptemp;
+ }
+ run = next;
+inc: xl = dda_next(next_x);
+ ytf = dda_next(next_y);
+ }
+ return 1;
+}
diff --git a/pstoraster/gximage5.c b/pstoraster/gximage5.c
new file mode 100644
index 000000000..6771e39c5
--- /dev/null
+++ b/pstoraster/gximage5.c
@@ -0,0 +1,168 @@
+/* Copyright (C) 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gximage5.c */
+/* Interpolated image procedures */
+#include "gx.h"
+#include "memory_.h"
+#include "gpcheck.h"
+#include "gserrors.h"
+#include "gxfixed.h"
+#include "gxfrac.h"
+#include "gxarith.h"
+#include "gxmatrix.h"
+#include "gsccolor.h"
+#include "gspaint.h"
+#include "gzstate.h"
+#include "gxcmap.h"
+#include "gzpath.h"
+#include "gxdevice.h"
+#include "gxdevmem.h"
+#include "gxcpath.h"
+#include "gximage.h"
+
+/* ------ Rendering for interpolated images ------ */
+
+int
+image_render_interpolate(gx_image_enum *penum, byte *buffer,
+ uint iw, int h, gx_device *dev)
+{ stream_IScale_state *pss = penum->scaler;
+ const gs_state *pgs = penum->pgs;
+ const gs_imager_state *pis = penum->pis;
+ const gs_color_space *pcs = penum->pcs;
+ int c = pss->Colors;
+ stream_cursor_read r;
+ stream_cursor_write w;
+
+ if ( h != 0 )
+ { /* Convert the unpacked data to concrete values in */
+ /* the source buffer. */
+ uint row_size = pss->WidthIn * c * pss->sizeofPixelIn;
+
+ if ( pss->sizeofPixelIn == 1 )
+ { /* Easy case: 8-bit device color values. */
+ r.ptr = buffer - 1;
+ }
+ else
+ { /* Messy case: concretize each sample. */
+ int bps = penum->bps;
+ int dc = penum->spp;
+ byte *pdata = buffer;
+ frac *psrc = (frac *)penum->line;
+ gs_client_color cc;
+ int i;
+
+ r.ptr = (byte *)psrc - 1;
+ for ( i = 0; i < pss->WidthIn; i++, psrc += c )
+ { int j;
+ if ( bps <= 8 )
+ for ( j = 0; j < dc; ++pdata, ++j )
+ { decode_sample(*pdata, cc, j);
+ }
+ else /* bps == 12 */
+ for ( j = 0; j < dc; pdata += sizeof(frac), ++j )
+ { decode_frac(*(frac *)pdata, cc, j);
+ }
+ (*pcs->type->concretize_color)(&cc, pcs, psrc,
+ pgs);
+ }
+ }
+ r.limit = r.ptr + row_size;
+ }
+ else /* h == 0 */
+ r.ptr = 0, r.limit = 0;
+
+ /*
+ * Process input and/or collect output.
+ * By construction, the pixels are 1-for-1 with the device,
+ * but the Y coordinate might be inverted.
+ */
+
+ { int xo = fixed2int_pixround(penum->mtx);
+ int yo = fixed2int_pixround(penum->mty);
+ int width = pss->WidthOut;
+ int dy;
+ const gs_color_space *pconcs = cs_concrete_space(pcs, pgs);
+ gs_logical_operation_t lop = pis->log_op;
+ int bpp = dev->color_info.depth;
+ uint raster = bitmap_raster(width * bpp);
+
+ if ( dda_current(penum->next_y) > penum->mty )
+ dy = 1;
+ else
+ dy = -1, yo--;
+ for ( ; ; )
+ { int ry = yo + penum->line_xy * dy;
+ int x;
+ const frac *psrc;
+ gx_device_color devc;
+ int code;
+ declare_line_accum(penum->line, bpp, xo);
+
+ w.limit = penum->line + width * c *
+ sizeof(gx_color_index) - 1;
+ w.ptr = w.limit - width * c *
+ (sizeof(gx_color_index) - pss->sizeofPixelOut);
+ psrc = (const frac *)(w.ptr + 1);
+ code = (*s_IScale_template.process)
+ ((stream_state *)pss, &r, &w, false);
+ if ( code < 0 && code != EOFC )
+ return_error(gs_error_ioerror);
+ if ( w.ptr == w.limit )
+ { for ( x = xo; x < xo + width; x++, psrc += c )
+ { (*pconcs->type->remap_concrete_color)
+ (psrc, &devc, pgs);
+ if ( color_is_pure(&devc) )
+ { /* Just pack colors into a scan line. */
+ gx_color_index color = devc.colors.pure;
+ line_accum(color, bpp);
+ }
+ else
+ { if ( bpp < 8 )
+ { if ( (l_shift -= bpp) < 0 )
+ *l_dst++ = (byte)l_bits, l_bits = 0,
+ l_shift += 8;
+ }
+ else
+ l_dst += bpp >> 3;
+ line_accum_copy(dev, penum->line, bpp,
+ xo, x, raster, ry);
+ code = gx_fill_rectangle_device_rop(x, ry,
+ 1, 1, &devc, dev, lop);
+ if ( code < 0 )
+ return code;
+ l_xprev = x + 1;
+ }
+ }
+ line_accum_copy(dev, penum->line, bpp,
+ xo, x, raster, ry);
+ penum->line_xy++;
+ continue;
+ }
+ if ( r.ptr == r.limit || code == EOFC )
+ break;
+ }
+ }
+
+ return (h == 0 ? 0 : 1);
+}
diff --git a/pstoraster/gxiodev.h b/pstoraster/gxiodev.h
new file mode 100644
index 000000000..55f1e9c3c
--- /dev/null
+++ b/pstoraster/gxiodev.h
@@ -0,0 +1,177 @@
+/* Copyright (C) 1993, 1994, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxiodev.h */
+/* Definition of an IODevice object */
+/* Requires gsmemory.h */
+#include "stat_.h"
+
+/*
+ * Note that IODevices are not the same as Ghostscript output devices.
+ * See section 3.8.2 of the PostScript Language Reference Manual,
+ * Second Edition, for more information.
+ */
+
+typedef struct gx_io_device_s gx_io_device; /* defined here */
+typedef struct gx_io_device_procs_s gx_io_device_procs; /* defined here */
+
+/* The IODevice table is defined in gconfig.c; its extern is in gscdefs.h. */
+
+#ifndef file_enum_DEFINED /* also defined in gp.h */
+# define file_enum_DEFINED
+struct file_enum_s; /* opaque to client, defined by implementors */
+typedef struct file_enum_s file_enum;
+#endif
+
+/* Define an opaque type for parameter lists. */
+#ifndef gs_param_list_DEFINED
+# define gs_param_list_DEFINED
+typedef struct gs_param_list_s gs_param_list;
+#endif
+
+/* Define an opaque type for streams. */
+#ifndef stream_DEFINED
+# define stream_DEFINED
+typedef struct stream_s stream;
+#endif
+
+/* Definition of IODevice procedures */
+/* Note that file names for fopen, delete, rename, and status */
+/* are C strings, not pointer + length. */
+/* Note also that "streams" are a higher-level concept; */
+/* the open_device and open_file procedures are normally NULL. */
+
+struct gx_io_device_procs_s {
+
+#define iodev_proc_init(proc)\
+ int proc(P2(gx_io_device *iodev, gs_memory_t *mem))
+ iodev_proc_init((*init)); /* one-time initialization */
+
+#define iodev_proc_open_device(proc)\
+ int proc(P4(gx_io_device *iodev, const char *access, stream **ps,\
+ gs_memory_t *mem))
+ iodev_proc_open_device((*open_device));
+
+#define iodev_proc_open_file(proc)\
+ int proc(P6(gx_io_device *iodev, const char *fname, uint namelen,\
+ const char *access, stream **ps, gs_memory_t *mem))
+ iodev_proc_open_file((*open_file));
+
+ /* fopen was changed in release 2.9.6, */
+ /* and again in 3.20 to return the real fname separately */
+
+#define iodev_proc_fopen(proc)\
+ int proc(P6(gx_io_device *iodev, const char *fname, const char *access,\
+ FILE **pfile, char *rfname, uint rnamelen))
+ iodev_proc_fopen((*fopen));
+
+#define iodev_proc_fclose(proc)\
+ int proc(P2(gx_io_device *iodev, FILE *file))
+ iodev_proc_fclose((*fclose));
+
+#define iodev_proc_delete_file(proc)\
+ int proc(P2(gx_io_device *iodev, const char *fname))
+ iodev_proc_delete_file((*delete_file));
+
+#define iodev_proc_rename_file(proc)\
+ int proc(P3(gx_io_device *iodev, const char *from, const char *to))
+ iodev_proc_rename_file((*rename_file));
+
+#define iodev_proc_file_status(proc)\
+ int proc(P3(gx_io_device *iodev, const char *fname, struct stat *pstat))
+ iodev_proc_file_status((*file_status));
+
+#define iodev_proc_enumerate_files(proc)\
+ file_enum *proc(P4(gx_io_device *iodev, const char *pat, uint patlen,\
+ gs_memory_t *mem))
+ iodev_proc_enumerate_files((*enumerate_files));
+
+#define iodev_proc_enumerate_next(proc)\
+ uint proc(P3(file_enum *pfen, char *ptr, uint maxlen))
+ iodev_proc_enumerate_next((*enumerate_next));
+
+#define iodev_proc_enumerate_close(proc)\
+ void proc(P1(file_enum *pfen))
+ iodev_proc_enumerate_close((*enumerate_close));
+
+ /* Added in release 2.9 */
+
+#define iodev_proc_get_params(proc)\
+ int proc(P2(gx_io_device *iodev, gs_param_list *plist))
+ iodev_proc_get_params((*get_params));
+
+#define iodev_proc_put_params(proc)\
+ int proc(P2(gx_io_device *iodev, gs_param_list *plist))
+ iodev_proc_put_params((*put_params));
+
+};
+
+/* The following typedef is needed because ansi2knr can't handle */
+/* iodev_proc_fopen((*procname)) in a formal argument list. */
+typedef iodev_proc_fopen((*iodev_proc_fopen_t));
+
+/* Default implementations of procedures */
+iodev_proc_init(iodev_no_init);
+iodev_proc_open_device(iodev_no_open_device);
+iodev_proc_open_file(iodev_no_open_file);
+iodev_proc_fopen(iodev_no_fopen);
+iodev_proc_fclose(iodev_no_fclose);
+iodev_proc_delete_file(iodev_no_delete_file);
+iodev_proc_rename_file(iodev_no_rename_file);
+iodev_proc_file_status(iodev_no_file_status);
+iodev_proc_enumerate_files(iodev_no_enumerate_files);
+iodev_proc_get_params(iodev_no_get_params);
+iodev_proc_put_params(iodev_no_put_params);
+/* The %os% implemention of fopen and fclose. */
+/* These are exported for pipes and for %null. */
+iodev_proc_fopen(iodev_os_fopen);
+iodev_proc_fclose(iodev_os_fclose);
+
+/* Get the N'th IODevice. */
+gx_io_device *gs_getiodevice(P1(int));
+#define iodev_default (gs_getiodevice(0))
+
+/* Look up an IODevice name. */
+gx_io_device *gs_findiodevice(P2(const byte *, uint));
+
+/* Get and put IODevice parameters. */
+int gs_getdevparams(P2(gx_io_device *, gs_param_list *));
+int gs_putdevparams(P2(gx_io_device *, gs_param_list *));
+
+/* Convert an OS error number to a PostScript error */
+/* if opening a file fails. */
+int gs_fopen_errno_to_code(P1(int));
+
+/* Test whether a string is equal to a character. */
+/* (This is used for access testing in file_open procedures.) */
+#define streq1(str, chr)\
+ ((str)[0] == (chr) && (str)[1] == 0)
+
+/* Finally, the IODevice structure itself. */
+#define gx_io_device_common\
+ const char *dname; /* the IODevice name */\
+ const char *dtype; /* the type returned by c'devparams */\
+ gx_io_device_procs procs
+struct gx_io_device_s {
+ gx_io_device_common;
+};
diff --git a/pstoraster/gxistate.h b/pstoraster/gxistate.h
new file mode 100644
index 000000000..218f35008
--- /dev/null
+++ b/pstoraster/gxistate.h
@@ -0,0 +1,94 @@
+/* Copyright (C) 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxistate.h */
+/* Imager state definition */
+
+#ifndef gxistate_INCLUDED
+# define gxistate_INCLUDED
+
+#include "gsropt.h"
+#include "gxfixed.h"
+#include "gxline.h"
+#include "gxmatrix.h"
+
+/*
+ * Define the subset of the PostScript graphics state that the imager
+ * library API needs. The definition of this subset is subject to change
+ * as we come to understand better the boundary between the imager and
+ * the interpreter. In particular, the imager state currently INCLUDES
+ * the following:
+ * line parameters: cap, join, miter limit, dash pattern
+ * transformation matrix (CTM)
+ * logical operation: RasterOp, transparency, rendering algorithm
+ * overprint flag
+ * rendering tweaks: flatness, fill adjustment, stroke adjust flag
+ * The imager state currently EXCLUDES the following:
+ * graphics state stack
+ * default CTM
+ * path
+ * clipping path
+ * color specification: color, color space, alpha
+ * color rendering information: halftone, halftone phase,
+ * transfer functions, black generation, undercolor removal,
+ * CIE rendering tables
+ * font
+ * device
+ * caches for many of the above
+ */
+
+#define gs_imager_state_common\
+ gx_line_params line_params;\
+ gs_matrix_fixed ctm;\
+ gs_logical_operation_t log_op;\
+ bool overprint;\
+ float flatness;\
+ gs_fixed_point fill_adjust; /* fattening for fill */\
+ bool stroke_adjust
+/* Access macros */
+#define ctm_only(pis) (*(const gs_matrix *)&(pis)->ctm)
+#define ctm_only_writable(pis) (*(gs_matrix *)&(pis)->ctm)
+#define set_ctm_only(pis, mat) (*(gs_matrix *)&(pis)->ctm = (mat))
+#define gs_init_rop(pis) ((pis)->log_op = lop_default)
+#define gs_currentlineparams_inline(pis) (&(pis)->line_params)
+
+#ifndef gs_imager_state_DEFINED
+# define gs_imager_state_DEFINED
+typedef struct gs_imager_state_s gs_imager_state;
+#endif
+
+struct gs_imager_state_s {
+ gs_imager_state_common;
+};
+
+/* Initialization for gs_imager_state */
+#define gs_imager_state_initial(scale)\
+ { gx_line_params_initial },\
+ { scale, 0.0, 0.0, -(scale), 0.0, 0.0 },\
+ lop_default, 0/*false*/, 1.0, { fixed_half, fixed_half }, 0/*false*/
+
+#define private_st_imager_state() /* in gsstate.c */\
+ gs_private_st_ptrs_add0(st_imager_state, gs_imager_state, "gs_imager_state",\
+ imager_state_enum, imager_state_reloc, st_line_params, line_params)
+
+#endif /* gxistate_INCLUDED */
diff --git a/pstoraster/gxline.h b/pstoraster/gxline.h
new file mode 100644
index 000000000..0a85a8c85
--- /dev/null
+++ b/pstoraster/gxline.h
@@ -0,0 +1,68 @@
+/* Copyright (C) 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxline.h */
+/* Private line parameter definitions */
+
+#ifndef gxline_INCLUDED
+# define gxline_INCLUDED
+
+#include "gslparam.h"
+
+/* Line parameter structures */
+/* gx_dash_params are never instantiated by themselves. */
+typedef struct gx_dash_params_s {
+ float *pattern;
+ uint pattern_size;
+ float offset;
+ /* The rest of the parameters are computed from the above */
+ bool init_ink_on; /* true if ink is initially on */
+ int init_index; /* initial index in pattern */
+ float init_dist_left;
+} gx_dash_params;
+#define gx_dash_params_initial\
+ NULL, 0, 0.0, 1/*true*/, 0, 0.0
+typedef struct gx_line_params_s {
+ float half_width; /* one-half line width */
+ gs_line_cap cap;
+ gs_line_join join;
+ float miter_limit;
+ float miter_check; /* computed from miter limit, */
+ /* see gx_set_miter_limit and */
+ /* gs_stroke */
+ gx_dash_params dash;
+} gx_line_params;
+#define gx_set_line_width(plp, wid)\
+ ((plp)->half_width = (wid) / 2)
+#define gx_current_line_width(plp)\
+ ((plp)->half_width * 2)
+int gx_set_miter_limit(P2(gx_line_params *, floatp));
+#define gx_current_miter_limit(plp)\
+ ((plp)->miter_limit)
+int gx_set_dash(P5(gx_dash_params *, const float *, uint, floatp,
+ gs_memory_t *));
+/* See gsline.c for the computation of miter_check. */
+#define gx_line_params_initial\
+ 0.0, gs_cap_butt, gs_join_miter, 10.0, 0.20305866, { gx_dash_params_initial }
+
+#endif /* gxline_INCLUDED */
diff --git a/pstoraster/gxlum.h b/pstoraster/gxlum.h
new file mode 100644
index 000000000..314b4bc21
--- /dev/null
+++ b/pstoraster/gxlum.h
@@ -0,0 +1,44 @@
+/*
+ Copyright 1993-1999 by Easy Software Products.
+ Copyright (C) 1992 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxlum.h */
+/* Luminance computation parameters for Ghostscript */
+
+/*
+ * Color weights used for computing luminance.
+ *
+ * The original ones used here were for NTSC video displays. Of course,
+ * if you want to print instead of display things are different...
+ */
+#ifdef NTSC_LUM
+# define lum_red_weight 30
+# define lum_green_weight 59
+# define lum_blue_weight 11
+#else
+# define lum_red_weight 31
+# define lum_green_weight 61
+# define lum_blue_weight 8
+#endif /* NTSC_LUN */
+#define lum_all_weights (lum_red_weight + lum_green_weight + lum_blue_weight)
diff --git a/pstoraster/gxmatrix.h b/pstoraster/gxmatrix.h
new file mode 100644
index 000000000..d34d446f9
--- /dev/null
+++ b/pstoraster/gxmatrix.h
@@ -0,0 +1,85 @@
+/* Copyright (C) 1989, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxmatrix.h */
+/* Internal matrix routines for Ghostscript library */
+
+#ifndef gxmatrix_INCLUDED
+# define gxmatrix_INCLUDED
+
+#include "gsmatrix.h"
+
+/*
+ * Define a matrix with a cached fixed-point copy of the translation.
+ * This is only used by a few routines in gscoord.c; they are responsible
+ * for ensuring the validity of the cache. Note that the floating point
+ * tx/ty values may be too large to fit in a fixed values; txy_fixed_valid
+ * is false if this is the case, and true otherwise.
+ */
+typedef struct gs_matrix_fixed_s {
+ _matrix_body;
+ fixed tx_fixed, ty_fixed;
+ bool txy_fixed_valid;
+} gs_matrix_fixed;
+
+/* Coordinate transformations to fixed point. */
+int gs_point_transform2fixed(P4(const gs_matrix_fixed *, floatp, floatp,
+ gs_fixed_point *));
+int gs_distance_transform2fixed(P4(const gs_matrix_fixed *, floatp, floatp,
+ gs_fixed_point *));
+
+/* Macro for testing whether matrix coefficients are zero, */
+/* for shortcuts when the matrix has no skew. */
+#define is_skewed(pmat) !is_fzero2((pmat)->xy, (pmat)->yx)
+
+/*
+ * Define the fixed-point coefficient structure for avoiding
+ * floating point in coordinate transformations.
+ * Currently this is used only by the Type 1 font interpreter.
+ * The setup is in gscoord.c.
+ */
+typedef struct {
+ long l;
+ fixed f;
+} coeff1;
+typedef struct {
+ coeff1 xx, xy, yx, yy;
+ int skewed;
+ int shift; /* see m_fixed */
+ int max_bits; /* max bits of coefficient */
+ fixed round; /* ditto */
+} fixed_coeff;
+/*
+ * Multiply a fixed whose integer part usually does not exceed max_bits
+ * in magnitude by a coefficient from a fixed_coeff.
+ * We can use a faster algorithm if the fixed is an integer within
+ * a range that doesn't cause the multiplication to overflow an int.
+ */
+#define m_fixed(v, c, fc, maxb)\
+ (((v) + (fixed_1 << (maxb - 1))) &\
+ ((-fixed_1 << maxb) | _fixed_fraction_v) ? /* out of range, or has fraction */\
+ (fixed2int_var(v) * (fc).c.f +\
+ arith_rshift(fixed_fraction(v) * (fc).c.f + fixed_half, _fixed_shift)) :\
+ arith_rshift(fixed2int_var(v) * (fc).c.l + (fc).round, (fc).shift))
+
+#endif /* gxmatrix_INCLUDED */
diff --git a/pstoraster/gxobj.h b/pstoraster/gxobj.h
new file mode 100644
index 000000000..23ee917c3
--- /dev/null
+++ b/pstoraster/gxobj.h
@@ -0,0 +1,214 @@
+/* Copyright (C) 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxobj.h */
+/* Memory manager implementation structures for Ghostscript */
+#include "gxbitmap.h"
+
+/* ================ Objects ================ */
+
+/*
+ * Object headers come in a number of different varieties.
+ * All arise from the same basic form, which is
+ -l- -lmsize/mark/back-
+ -size-
+ -type/reloc-
+ * l (large) is a single bit. The size of lmsize/mark/back, size, and type
+ * varies according to the environment. On machines with N:16 segmented
+ * addressing, 16-bit ints, and no alignment requirement more severe than
+ * 2 bytes, we can fit an object header into 8 bytes by making the first
+ * two fields only 16 bits wide. On all other machines, we let the
+ * lmsize/mark/back field be 1 bit shorter than a uint, and round the header
+ * size up to the next multiple of the most severe alignment restriction
+ * (4 or 8 bytes). Miraculously, we can do all this without any case testing.
+ *
+ * The mark/back field is used for the mark during the marking phase of
+ * garbage collection, and for a back pointer value during the compaction
+ * phase. Since we want to be able to collect local VM independently of
+ * global VM, we need two different distinguished mark values:
+ * - For local objects that have not been traced and should be freed
+ * (compacted out), we use 1...11 in the mark field (o_unmarked).
+ * - For global objects that have not been traced but should be kept,
+ * we use 1...10 in the mark field (o_untraced).
+ * Note that neither of these values is a possible real relocation value.
+ *
+ * The lmsize field of large objects overlaps mark and back, so we must
+ * handle these functions for large objects in some other way.
+ * Since large objects cannot be moved or relocated, we don't need the
+ * back field for them; we allocate 2 bits for the 3 mark values.
+ */
+/*
+ * The back pointer's meaning depends on whether the object is
+ * free (unmarked) or in use (marked):
+ * - In free objects, the back pointer is an offset from the object
+ * header back to a chunk_head_t structure that contains the location
+ * to which all the data in this chunk will get moved; the reloc field
+ * contains the amount by which the following run of useful objects
+ * will be relocated downwards.
+ * - In useful objects, the back pointer is an offset from the object
+ * back to the previous free object; the reloc field is not used (it
+ * overlays the type field).
+ * These two cases can be distinguished when scanning a chunk linearly,
+ * but when simply examining an object via a pointer, the chunk pointer
+ * is also needed.
+ */
+#define obj_flag_bits 1
+#define obj_mb_bits (arch_sizeof_int * 8 - obj_flag_bits)
+#define obj_ls_bits (obj_mb_bits - 2)
+#define o_unmarked (((uint)1 << obj_mb_bits) - 1)
+#define o_l_unmarked (o_unmarked & 3)
+#define o_set_unmarked_large(pp) (pp)->o_lmark = o_l_unmarked
+#define o_set_unmarked(pp)\
+ if ( (pp)->o_large ) o_set_unmarked_large(pp);\
+ else (pp)->o_smark = o_unmarked
+#define o_is_unmarked_large(pp) ((pp)->o_lmark == o_l_unmarked)
+#define o_is_unmarked(pp)\
+ ((pp)->o_large ? o_is_unmarked_large(pp) :\
+ ((pp)->o_smark == o_unmarked))
+#define o_untraced (((uint)1 << obj_mb_bits) - 2)
+#define o_l_untraced (o_untraced & 3)
+#define o_set_untraced(pp)\
+ if ( (pp)->o_large ) (pp)->o_lmark = o_l_untraced;\
+ else (pp)->o_smark = o_untraced
+#define o_is_untraced(pp)\
+ ((pp)->o_large ? (pp)->o_lmark == o_l_untraced :\
+ ((pp)->o_smark == o_untraced))
+#define o_marked 0
+#define o_mark_large(pp) (pp)->o_lmark = o_marked
+#define o_mark(pp)\
+ if ( (pp)->o_large ) o_mark_large(pp);\
+ else (pp)->o_smark = o_marked
+#define obj_back_shift obj_flag_bits
+#define obj_back_scale (1 << obj_back_shift)
+typedef struct obj_header_data_s {
+ union _f {
+ struct _h { unsigned large : 1; } h;
+ struct _l { unsigned _ : 1, lmark : 2, lsize : obj_ls_bits; } l;
+ struct _m { unsigned _ : 1, smark : obj_mb_bits; } m;
+ struct _b { unsigned _ : 1, back : obj_mb_bits; } b;
+ } f;
+ uint size;
+ union _t {
+ gs_memory_type_ptr_t type;
+ uint reloc;
+ } t;
+} obj_header_data_t;
+
+/*
+ * Define the alignment modulus for aligned objects. We assume all
+ * alignment values are powers of 2; we can avoid nested 'max'es that way.
+ * The final | is because back pointer values are divided by obj_back_scale,
+ * so objects must be aligned at least 0 mod obj_back_scale.
+ */
+#define obj_align_mod\
+ (((arch_align_long_mod - 1) | (arch_align_ptr_mod - 1) |\
+ (arch_align_double_mod - 1) | (align_bitmap_mod - 1) |\
+ (obj_back_scale - 1)) + 1)
+/* The only possible values for obj_align_mod are 4, 8, or 16.... */
+#if obj_align_mod == 4
+# define log2_obj_align_mod 2
+#else
+#if obj_align_mod == 8
+# define log2_obj_align_mod 3
+#else
+#if obj_align_mod == 16
+# define log2_obj_align_mod 4
+#endif
+#endif
+#endif
+#define obj_align_mask (obj_align_mod-1)
+#define obj_align_round(siz)\
+ (uint)(((siz) + obj_align_mask) & -obj_align_mod)
+#define obj_size_round(siz)\
+ obj_align_round((siz) + sizeof(obj_header_t))
+
+/* Define the real object header type, taking alignment into account. */
+struct obj_header_s { /* must be a struct because of forward reference */
+ union _d {
+ obj_header_data_t o;
+ byte _pad[round_up(sizeof(obj_header_data_t), obj_align_mod)];
+ } d;
+};
+
+/* Define some reasonable abbreviations for the fields. */
+#define o_large d.o.f.h.large
+#define o_lsize d.o.f.l.lsize
+#define o_lmark d.o.f.l.lmark
+#define o_back d.o.f.b.back
+#define o_smark d.o.f.m.smark
+#define o_size d.o.size
+#define o_type d.o.t.type
+#define o_nreloc d.o.t.reloc
+
+/*
+ * The macros for getting the sizes of objects all take pointers to
+ * the object header, for use when scanning storage linearly.
+ */
+#define pre_obj_small_size(pp)\
+ ((pp)->o_size)
+
+#if arch_sizeof_long > arch_sizeof_int
+
+ /* Large objects need to use o_lsize. */
+
+#define pre_obj_large_size(pp)\
+ (((ulong)(pp)->o_lsize << (arch_sizeof_int * 8)) + (pp)->o_size)
+#define pre_obj_set_large_size(pp, lsize)\
+ ((pp)->o_lsize = (lsize) >> (arch_sizeof_int * 8),\
+ (pp)->o_size = (uint)(lsize))
+#define pre_obj_contents_size(pp)\
+ ((pp)->o_large ? pre_obj_large_size(pp) : pre_obj_small_size(pp))
+
+#else
+
+ /* Large objects don't need to use o_lsize. */
+
+#define pre_obj_large_size(pp)\
+ pre_obj_small_size(pp)
+#define pre_obj_set_large_size(pp, lsize)\
+ ((pp)->o_lsize = 0,\
+ (pp)->o_size = (lsize))
+#define pre_obj_contents_size(pp)\
+ pre_obj_small_size(pp)
+
+#endif
+
+#define pre_obj_rounded_size(pp)\
+ obj_size_round(pre_obj_contents_size(pp))
+#define pre_obj_next(pp)\
+ ((obj_header_t *)((byte *)(pp) + obj_align_round(\
+ pre_obj_contents_size(pp) + sizeof(obj_header_t) )))
+
+/*
+ * Define the header that free objects point back to when relocating.
+ * Every chunk, including inner chunks, has one of these.
+ */
+typedef struct chunk_head_s {
+ byte *dest; /* destination for objects */
+#if obj_align_mod > arch_sizeof_ptr
+ byte *_pad[obj_align_mod / arch_sizeof_ptr - 1];
+#endif
+ obj_header_t free; /* header for a free object, */
+ /* in case the first real object */
+ /* is in use */
+} chunk_head_t;
diff --git a/pstoraster/gxop1.h b/pstoraster/gxop1.h
new file mode 100644
index 000000000..eb81455e5
--- /dev/null
+++ b/pstoraster/gxop1.h
@@ -0,0 +1,43 @@
+/* Copyright (C) 1991, 1992 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxop1.h */
+/* Type 1 state shared between interpreter and compiled fonts. */
+
+/*
+ * The current point (px,py) in the Type 1 interpreter state is not
+ * necessarily the same as the current position in the path being built up.
+ * Specifically, (px,py) may not reflect adjustments for hinting,
+ * whereas the current path position does reflect those adjustments.
+ */
+
+/* Define the shared Type 1 interpreter state. */
+#define max_coeff_bits 11 /* max coefficient in char space */
+typedef struct gs_op1_state_s {
+ struct gx_path_s *ppath;
+ struct gs_type1_state_s *pcis;
+ fixed_coeff fc;
+ gs_fixed_point co; /* character origin (device space) */
+ gs_fixed_point p; /* current point (device space) */
+} gs_op1_state;
+typedef gs_op1_state _ss *is_ptr;
diff --git a/pstoraster/gxpaint.c b/pstoraster/gxpaint.c
new file mode 100644
index 000000000..ce832e822
--- /dev/null
+++ b/pstoraster/gxpaint.c
@@ -0,0 +1,93 @@
+/* Copyright (C) 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxpaint.c */
+/* Graphics-state-aware fill and stroke procedures */
+#include "gx.h"
+#include "gzstate.h"
+#include "gxdevice.h"
+#include "gxhttile.h"
+#include "gxpaint.h"
+#include "gxpath.h"
+
+/* Fill a path. */
+int
+gx_fill_path(gx_path *ppath, gx_device_color *pdevc, gs_state *pgs,
+ int rule, fixed adjust_x, fixed adjust_y)
+{ gx_device *dev = gs_currentdevice_inline(pgs);
+ gx_fill_params params;
+
+ params.rule = rule;
+ params.adjust.x = adjust_x;
+ params.adjust.y = adjust_y;
+ params.flatness = (pgs->in_cachedevice > 1 ? 0.0 : pgs->flatness);
+ params.fill_zero_width = true;
+ return (*dev_proc(dev, fill_path))
+ (dev, (const gs_imager_state *)pgs, ppath, &params, pdevc,
+ pgs->clip_path);
+}
+
+/* Stroke a path for drawing or saving. */
+int
+gx_stroke_fill(gx_path *ppath, gs_state *pgs)
+{ const gx_clip_path *pcpath = pgs->clip_path;
+ gx_device *dev = gs_currentdevice_inline(pgs);
+ gx_stroke_params params;
+ gx_device_color *pdevc = pgs->dev_color;
+ int code;
+
+ params.flatness = (pgs->in_cachedevice > 1 ? 0.0 : pgs->flatness);
+ if ( dev->std_procs.stroke_path != gx_default_stroke_path &&
+ pgs->log_op == lop_default
+ )
+ { /* Give the device a chance to handle it. */
+ code = (*dev_proc(dev, stroke_path))
+ (dev, (const gs_imager_state *)pgs, ppath, &params, pdevc,
+ pcpath);
+ if ( code >= 0 )
+ return code;
+ }
+ return gx_stroke_path_only(ppath, (gx_path *)0, dev,
+ (const gs_imager_state *)pgs,
+ &params, pdevc, pcpath);
+}
+
+int
+gx_stroke_add(gx_path *ppath, gx_path *to_path, gs_state *pgs)
+{ gx_stroke_params params;
+ int code;
+
+ params.flatness = (pgs->in_cachedevice > 1 ? 0.0 : pgs->flatness);
+ code = gx_stroke_path_only(ppath, to_path, pgs->device,
+ (const gs_imager_state *)pgs,
+ &params, NULL, NULL);
+ /* I don't understand why this code used to be here: */
+#if 0
+ if ( code < 0 )
+ return code;
+ if ( ppath->subpath_open <= 0 && ppath->position_valid )
+ code = gx_path_add_point(to_path, ppath->position.x,
+ ppath->position.y);
+#endif
+ return code;
+}
diff --git a/pstoraster/gxpaint.h b/pstoraster/gxpaint.h
new file mode 100644
index 000000000..0bbd12b4e
--- /dev/null
+++ b/pstoraster/gxpaint.h
@@ -0,0 +1,123 @@
+/* Copyright (C) 1994, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxpaint.h */
+/* Device coordinate painting interface for Ghostscript library */
+/* Requires gsropt.h, gxfixed.h, gxpath.h */
+
+#ifndef gs_imager_state_DEFINED
+# define gs_imager_state_DEFINED
+typedef struct gs_imager_state_s gs_imager_state;
+#endif
+
+#ifndef gs_state_DEFINED
+# define gs_state_DEFINED
+typedef struct gs_state_s gs_state;
+#endif
+
+#ifndef gx_device_DEFINED
+# define gx_device_DEFINED
+typedef struct gx_device_s gx_device;
+#endif
+
+#ifndef gx_device_color_DEFINED
+# define gx_device_color_DEFINED
+typedef struct gx_device_color_s gx_device_color;
+#endif
+
+/* ------ Graphics-state-aware procedures ------ */
+
+/*
+ * The following procedures use information from the graphics state.
+ * They are implemented in gxpaint.c.
+ */
+
+int gx_fill_path(P6(gx_path *ppath, gx_device_color *pdevc, gs_state *pgs,
+ int rule, fixed adjust_x, fixed adjust_y));
+int gx_stroke_fill(P2(gx_path *ppath, gs_state *pgs));
+int gx_stroke_add(P3(gx_path *ppath, gx_path *to_path, gs_state *pgs));
+
+/* ------ Imager procedures ------ */
+
+/*
+ * Tweak the fill adjustment if necessary so that (nearly) empty
+ * rectangles are guaranteed to produce some output.
+ */
+void gx_adjust_if_empty(P2(const gs_fixed_rect *, gs_fixed_point *));
+
+/*
+ * Compute the amount by which to expand a stroked bounding box to account
+ * for line width, caps and joins.
+ */
+int gx_stroke_expansion(P2(const gs_imager_state *, gs_fixed_point *));
+
+/*
+ * The following procedures do not need a graphics state.
+ * These procedures are implemented in gxfill.c and gxstroke.c.
+ */
+
+/* Define the parameters passed to the imager's filling routine. */
+#ifndef gx_fill_params_DEFINED
+# define gx_fill_params_DEFINED
+typedef struct gx_fill_params_s gx_fill_params;
+#endif
+struct gx_fill_params_s {
+ int rule; /* -1 = winding #, 1 = even/odd */
+ gs_fixed_point adjust;
+ float flatness;
+ bool fill_zero_width; /* if true, make zero-width/height */
+ /* rectangles one pixel wide/high */
+};
+
+#define gx_fill_path_only(ppath, dev, pis, params, pdevc, pcpath)\
+ (*dev_proc(dev, fill_path))(dev, pis, ppath, params, pdevc, pcpath)
+
+/* Define the parameters passed to the imager's stroke routine. */
+#ifndef gx_stroke_params_DEFINED
+# define gx_stroke_params_DEFINED
+typedef struct gx_stroke_params_s gx_stroke_params;
+#endif
+struct gx_stroke_params_s {
+ float flatness;
+};
+
+int gx_stroke_path_only(P7(gx_path *ppath, gx_path *to_path, gx_device *dev,
+ const gs_imager_state *pis,
+ const gx_stroke_params *params,
+ const gx_device_color *pdevc,
+ const gx_clip_path *pcpath));
+
+/* Define rectangle operations used in implementing the above. */
+
+/* Check whether a path bounding box is within a clipping box. */
+#define rect_within(ibox, cbox)\
+ (ibox.q.y <= cbox.q.y && ibox.q.x <= cbox.q.x &&\
+ ibox.p.y >= cbox.p.y && ibox.p.x >= cbox.p.x)
+
+/* Intersect a bounding box with a clipping box. */
+#define rect_intersect(ibox, cbox)\
+ { if ( cbox.p.x > ibox.p.x ) ibox.p.x = cbox.p.x;\
+ if ( cbox.q.x < ibox.q.x ) ibox.q.x = cbox.q.x;\
+ if ( cbox.p.y > ibox.p.y ) ibox.p.y = cbox.p.y;\
+ if ( cbox.q.y < ibox.q.y ) ibox.q.y = cbox.q.y;\
+ }
diff --git a/pstoraster/gxpath.c b/pstoraster/gxpath.c
new file mode 100644
index 000000000..854ca8ef7
--- /dev/null
+++ b/pstoraster/gxpath.c
@@ -0,0 +1,543 @@
+/* Copyright (C) 1989, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxpath.c */
+/* Internal path construction routines for Ghostscript library */
+#include "gx.h"
+#include "gserrors.h"
+#include "gsstruct.h"
+#include "gxfixed.h"
+#include "gzpath.h"
+
+/* These routines all assume that all points are */
+/* already in device coordinates, and in fixed representation. */
+/* As usual, they return either 0 or a (negative) error code. */
+
+/* Forward references */
+private subpath *path_alloc_copy(P1(gx_path *));
+private int gx_path_new_subpath(P1(gx_path *));
+#ifdef DEBUG
+private void gx_print_segment(P1(const segment *));
+# define trace_segment(msg, pseg)\
+ if ( gs_debug_c('P') ) dprintf(msg), gx_print_segment(pseg);
+#else
+# define trace_segment(msg, pseg) DO_NOTHING
+#endif
+
+/* Macro for checking a point against a preset bounding box. */
+#define outside_bbox(ppath, px, py)\
+ (px < ppath->bbox.p.x || px > ppath->bbox.q.x ||\
+ py < ppath->bbox.p.y || py > ppath->bbox.q.y)
+#define check_in_bbox(ppath, px, py)\
+ if ( outside_bbox(ppath, px, py) )\
+ return_error(gs_error_rangecheck)
+
+/* Structure descriptors for paths and path segment types. */
+public_st_path();
+private_st_segment();
+private_st_line();
+private_st_line_close();
+private_st_curve();
+private_st_subpath();
+
+/* ------ Initialize/free paths ------ */
+
+/* Allocate and initialize a path. */
+gx_path *
+gx_path_alloc(gs_memory_t *mem, client_name_t cname)
+{ gx_path *ppath = gs_alloc_struct(mem, gx_path, &st_path, cname);
+ if ( ppath == 0 )
+ return 0;
+ gx_path_init(ppath, mem);
+ return ppath;
+}
+
+/* Initialize a path */
+void
+gx_path_init(gx_path *ppath, gs_memory_t *mem)
+{ ppath->memory = mem;
+ gx_path_reset(ppath);
+}
+void
+gx_path_reset(register gx_path *ppath)
+{ ppath->box_last = 0;
+ ppath->position_valid = 0;
+ ppath->first_subpath = ppath->current_subpath = 0;
+ ppath->subpath_count = 0;
+ ppath->curve_count = 0;
+ ppath->subpath_open = 0;
+ ppath->shares_segments = 0;
+ ppath->bbox_set = 0;
+}
+
+/* Release the contents of a path. We do this in reverse order */
+/* so as to maximize LIFO allocator behavior. */
+void
+gx_path_release(gx_path *ppath)
+{ segment *pseg;
+ if ( ppath->first_subpath == 0 ) return; /* empty path */
+ if ( ppath->shares_segments ) return; /* segments are shared */
+ pseg = (segment *)ppath->current_subpath->last;
+ while ( pseg )
+ { segment *prev = pseg->prev;
+ trace_segment("[P]release", pseg);
+ gs_free_object(ppath->memory, pseg, "gx_path_release");
+ pseg = prev;
+ }
+ ppath->first_subpath = 0; /* prevent re-release */
+}
+
+/* Mark a path as shared */
+void
+gx_path_share(gx_path *ppath)
+{ if ( ppath->first_subpath ) ppath->shares_segments = 1;
+}
+
+/* ------ Incremental path building ------ */
+
+/* Macro for opening the current subpath. */
+/* ppath points to the path; psub has been set to ppath->current_subpath. */
+#define path_open()\
+ if ( ppath->subpath_open <= 0 )\
+ { int code;\
+ if ( !ppath->position_valid )\
+ return_error(gs_error_nocurrentpoint);\
+ code = gx_path_new_subpath(ppath);\
+ if ( code < 0 ) return code;\
+ psub = ppath->current_subpath;\
+ }
+
+/* Macros for allocating path segments. */
+/* Note that they assume that ppath points to the path, */
+/* and that psub points to the current subpath. */
+/* We have to split the macro into two because of limitations */
+/* on the size of a single statement (sigh). */
+#define p_alloc(pseg,size)\
+ if_debug2('A', "[P]0x%lx<%lu>\n", (ulong)(pseg), (ulong)(size))
+#define path_unshare(ppath)\
+ if(ppath->shares_segments)\
+ if(!(psub = path_alloc_copy(ppath)))return_error(gs_error_VMerror)
+#define path_alloc_segment(pseg,ctype,pstype,stype,cname)\
+ path_unshare(ppath);\
+ if( !(pseg = gs_alloc_struct(ppath->memory, ctype, pstype, cname)) )\
+ return_error(gs_error_VMerror);\
+ p_alloc(pseg, sizeof(ctype));\
+ pseg->type = stype, pseg->next = 0
+#define path_alloc_link(pseg)\
+ { segment *prev = psub->last;\
+ prev->next = (segment *)pseg;\
+ pseg->prev = prev;\
+ psub->last = (segment *)pseg;\
+ }
+
+/* Open a new subpath */
+private int
+gx_path_new_subpath(gx_path *ppath)
+{ subpath *psub = ppath->current_subpath;
+ register subpath *spp;
+ path_alloc_segment(spp, subpath, &st_subpath, s_start,
+ "gx_path_new_subpath");
+ spp->last = (segment *)spp;
+ spp->curve_count = 0;
+ spp->is_closed = 0;
+ spp->pt = ppath->position;
+ ppath->subpath_open = 1;
+ if ( !psub ) /* first subpath */
+ { ppath->first_subpath = spp;
+ spp->prev = 0;
+ }
+ else
+ { segment *prev = psub->last;
+ prev->next = (segment *)spp;
+ spp->prev = prev;
+ }
+ ppath->current_subpath = spp;
+ ppath->subpath_count++;
+ trace_segment("[P]", (const segment *)spp);
+ return 0;
+}
+
+/* Add a point to the current path (moveto). */
+int
+gx_path_add_point(register gx_path *ppath, fixed x, fixed y)
+{ if ( ppath->bbox_set )
+ check_in_bbox(ppath, x, y);
+ ppath->subpath_open = -1;
+ ppath->position_valid = 1;
+ ppath->position.x = x;
+ ppath->position.y = y;
+ return 0;
+}
+
+/* Add a relative point to the current path (rmoveto). */
+int
+gx_path_add_relative_point(register gx_path *ppath, fixed dx, fixed dy)
+{ if ( !ppath->position_valid )
+ return_error(gs_error_nocurrentpoint);
+ if ( ppath->bbox_set )
+ check_in_bbox(ppath, ppath->position.x + dx, ppath->position.y + dy);
+ ppath->subpath_open = -1;
+ ppath->position.x += dx;
+ ppath->position.y += dy;
+ return 0;
+}
+
+/* Set the segment point and the current point in the path. */
+/* Assumes ppath points to the path. */
+#define path_set_point(pseg, fx, fy)\
+ (pseg)->pt.x = ppath->position.x = (fx),\
+ (pseg)->pt.y = ppath->position.y = (fy)
+
+/* Add a line to the current path (lineto). */
+int
+gx_path_add_line(gx_path *ppath, fixed x, fixed y)
+{ subpath *psub = ppath->current_subpath;
+ register line_segment *lp;
+ if ( ppath->bbox_set )
+ check_in_bbox(ppath, x, y);
+ path_open();
+ path_alloc_segment(lp, line_segment, &st_line, s_line,
+ "gx_path_add_line");
+ path_alloc_link(lp);
+ path_set_point(lp, x, y);
+ trace_segment("[P]", (segment *)lp);
+ return 0;
+}
+
+/* Add multiple lines to the current path. */
+int
+gx_path_add_lines(gx_path *ppath, const gs_fixed_point *ppts, int count)
+{ subpath *psub = ppath->current_subpath;
+ segment *prev;
+ register line_segment *lp = 0;
+ int i;
+ int code = 0;
+ if ( count <= 0 )
+ return 0;
+ path_open();
+ path_unshare(ppath);
+ prev = psub->last;
+ /* We could do better than the following, but this is a start. */
+ /* Note that we don't make any attempt to undo partial additions */
+ /* if we fail partway through; this is equivalent to what would */
+ /* happen with multiple calls on gx_path_add_line. */
+ for ( i = 0; i < count; i++ )
+ { fixed x = ppts[i].x;
+ fixed y = ppts[i].y;
+ line_segment *next;
+ if ( ppath->bbox_set && outside_bbox(ppath, x, y) )
+ { code = gs_note_error(gs_error_rangecheck);
+ break;
+ }
+ if ( !(next = gs_alloc_struct(ppath->memory, line_segment,
+ &st_line, "gx_path_add_lines"))
+ )
+ { code = gs_note_error(gs_error_VMerror);
+ break;
+ }
+ lp = next;
+ p_alloc(lp, sizeof(line_segment));
+ lp->type = s_line;
+ prev->next = (segment *)lp;
+ lp->prev = prev;
+ lp->pt.x = x;
+ lp->pt.y = y;
+ prev = (segment *)lp;
+ trace_segment("[P]", (segment *)lp);
+ }
+ if ( lp != 0 )
+ ppath->position.x = lp->pt.x,
+ ppath->position.y = lp->pt.y,
+ psub->last = (segment *)lp,
+ lp->next = 0;
+ return code;
+}
+
+/* Add a rectangle to the current path. */
+/* This is a special case of adding a closed polygon. */
+int
+gx_path_add_rectangle(gx_path *ppath, fixed x0, fixed y0, fixed x1, fixed y1)
+{ gs_fixed_point pts[3];
+ int code;
+ pts[0].x = x0;
+ pts[1].x = pts[2].x = x1;
+ pts[2].y = y0;
+ pts[0].y = pts[1].y = y1;
+ if ( (code = gx_path_add_point(ppath, x0, y0)) < 0 ||
+ (code = gx_path_add_lines(ppath, pts, 3)) < 0 ||
+ (code = gx_path_close_subpath(ppath)) < 0
+ )
+ return code;
+ return 0;
+}
+
+/* Add a curve to the current path (curveto). */
+int
+gx_path_add_curve(gx_path *ppath,
+ fixed x1, fixed y1, fixed x2, fixed y2, fixed x3, fixed y3)
+{ subpath *psub = ppath->current_subpath;
+ register curve_segment *lp;
+ if ( ppath->bbox_set )
+ { check_in_bbox(ppath, x1, y1);
+ check_in_bbox(ppath, x2, y2);
+ check_in_bbox(ppath, x3, y3);
+ }
+ path_open();
+ path_alloc_segment(lp, curve_segment, &st_curve, s_curve,
+ "gx_path_add_curve");
+ path_alloc_link(lp);
+ lp->p1.x = x1;
+ lp->p1.y = y1;
+ lp->p2.x = x2;
+ lp->p2.y = y2;
+ path_set_point(lp, x3, y3);
+ psub->curve_count++;
+ ppath->curve_count++;
+ trace_segment("[P]", (segment *)lp);
+ return 0;
+}
+
+/*
+ * Add an approximation of an arc to the current path.
+ * The current point of the path is the initial point of the arc;
+ * parameters are the final point of the arc
+ * and the point at which the extended tangents meet.
+ * We require that the arc be less than a semicircle.
+ * The arc may go either clockwise or counterclockwise.
+ * The approximation is a very simple one: a single curve
+ * whose other two control points are a fraction F of the way
+ * to the intersection of the tangents, where
+ * F = (4/3)(1 / (1 + sqrt(1+(d/r)^2)))
+ * where r is the radius and d is the distance from either tangent
+ * point to the intersection of the tangents. This produces
+ * a curve whose center point, as well as its ends, lies on
+ * the desired arc.
+ *
+ * Because F has to be computed in user space, we let the client
+ * compute it and pass it in as an argument.
+ */
+int
+gx_path_add_partial_arc(gx_path *ppath,
+ fixed x3, fixed y3, fixed xt, fixed yt, floatp fraction)
+{ fixed x0 = ppath->position.x, y0 = ppath->position.y;
+ return gx_path_add_curve(ppath,
+ x0 + (fixed)((xt - x0) * fraction),
+ y0 + (fixed)((yt - y0) * fraction),
+ x3 + (fixed)((xt - x3) * fraction),
+ y3 + (fixed)((yt - y3) * fraction),
+ x3, y3);
+}
+
+/* Append a path to another path, and reset the first path. */
+/* Currently this is only used to append a path to its parent */
+/* (the path in the previous graphics context). */
+int
+gx_path_add_path(gx_path *ppath, gx_path *ppfrom)
+{ subpath *psub;
+ path_unshare(ppfrom);
+ path_unshare(ppath);
+ if ( ppfrom->first_subpath ) /* i.e. ppfrom not empty */
+ { if ( ppath->first_subpath ) /* i.e. ppath not empty */
+ { subpath *psub = ppath->current_subpath;
+ segment *pseg = psub->last;
+ subpath *pfsub = ppfrom->first_subpath;
+ pseg->next = (segment *)pfsub;
+ pfsub->prev = pseg;
+ }
+ else
+ ppath->first_subpath = ppfrom->first_subpath;
+ ppath->current_subpath = ppfrom->current_subpath;
+ ppath->subpath_count += ppfrom->subpath_count;
+ ppath->curve_count += ppfrom->curve_count;
+ }
+ /* Transfer the remaining state. */
+ ppath->position = ppfrom->position;
+ ppath->position_valid = ppfrom->position_valid;
+ ppath->subpath_open = ppfrom->subpath_open;
+ gx_path_reset(ppfrom); /* reset the source path */
+ return 0;
+}
+
+/* Add a path or its bounding box to the enclosing path, */
+/* and reset the first path. Only used for implementing charpath and its */
+/* relatives. */
+int
+gx_path_add_char_path(gx_path *to_path, gx_path *from_path,
+ gs_char_path_mode mode)
+{ switch ( mode )
+ {
+ default: /* shouldn't happen! */
+ gx_path_reset(from_path);
+ return 0;
+ case cpm_true_charpath:
+ case cpm_false_charpath:
+ return gx_path_add_path(to_path, from_path);
+ case cpm_true_charboxpath:
+ { gs_fixed_rect bbox;
+ gx_path_bbox(from_path, &bbox);
+ return gx_path_add_rectangle(to_path, bbox.p.x, bbox.p.y,
+ bbox.q.x, bbox.q.y);
+ }
+ case cpm_false_charboxpath:
+ { gs_fixed_rect bbox;
+ int code;
+ gx_path_bbox(from_path, &bbox);
+ return
+ ((code = gx_path_add_point(to_path, bbox.p.x, bbox.p.y)) < 0 ?
+ code : gx_path_add_line(to_path, bbox.q.x, bbox.q.y));
+ }
+ }
+}
+
+/* Close the current subpath. */
+int
+gx_path_close_subpath(gx_path *ppath)
+{ subpath *psub = ppath->current_subpath;
+ register line_close_segment *lp;
+ int code;
+ switch ( ppath->subpath_open )
+ {
+ case 0:
+ return 0;
+ case -1:
+ code = gx_path_new_subpath(ppath);
+ if ( code < 0 )
+ return 0;
+ psub = ppath->current_subpath;
+ /*case 1:*/
+ }
+ path_alloc_segment(lp, line_close_segment, &st_line_close, s_line_close,
+ "gx_path_close_subpath");
+ path_alloc_link(lp);
+ path_set_point(lp, psub->pt.x, psub->pt.y);
+ lp->sub = psub;
+ psub->is_closed = 1;
+ ppath->subpath_open = 0;
+ trace_segment("[P]", (segment *)lp);
+ return 0;
+}
+
+/* Remove the last line from the current subpath, and then close it. */
+/* The Type 1 font hinting routines use this if a path ends with */
+/* a line to the start followed by a closepath. */
+int
+gx_path_pop_close_subpath(gx_path *ppath)
+{ subpath *psub = ppath->current_subpath;
+ segment *pseg;
+ segment *prev;
+ if ( psub == 0 || (pseg = psub->last) == 0 ||
+ pseg->type != s_line
+ )
+ return_error(gs_error_unknownerror);
+ prev = pseg->prev;
+ prev->next = 0;
+ psub->last = prev;
+ gs_free_object(ppath->memory, pseg, "gx_path_pop_close_subpath");
+ return gx_path_close_subpath(ppath);
+}
+
+/* ------ Internal routines ------ */
+
+/* Copy the current path, because it was shared. */
+/* Return a pointer to the current subpath, or 0. */
+private subpath *
+path_alloc_copy(gx_path *ppath)
+{ gx_path path_new;
+ int code;
+ code = gx_path_copy(ppath, &path_new, 1);
+ if ( code < 0 ) return 0;
+ *ppath = path_new;
+ ppath->shares_segments = 0;
+ return ppath->current_subpath;
+}
+
+/* ------ Debugging printout ------ */
+
+#ifdef DEBUG
+
+/* Print out a path with a label */
+void
+gx_dump_path(const gx_path *ppath, const char *tag)
+{ dprintf2("[P]Path 0x%lx %s:\n", (ulong)ppath, tag);
+ gx_path_print(ppath);
+}
+
+/* Print a path */
+void
+gx_path_print(const gx_path *ppath)
+{ const segment *pseg = (const segment *)ppath->first_subpath;
+ dprintf4(" subpaths=%d, curves=%d, point=(%f,%f)\n",
+ ppath->subpath_count, ppath->curve_count,
+ fixed2float(ppath->position.x),
+ fixed2float(ppath->position.y));
+ dprintf5(" box=(%f,%f),(%f,%f) last=0x%lx\n",
+ fixed2float(ppath->bbox.p.x), fixed2float(ppath->bbox.p.y),
+ fixed2float(ppath->bbox.q.x), fixed2float(ppath->bbox.q.y),
+ (ulong)ppath->box_last);
+ while ( pseg )
+ { gx_print_segment(pseg);
+ pseg = pseg->next;
+ }
+}
+private void
+gx_print_segment(const segment *pseg)
+{ char out[80];
+ sprintf(out, " 0x%lx<0x%lx,0x%lx>: %%s (%6g,%6g) ",
+ (ulong)pseg, (ulong)pseg->prev, (ulong)pseg->next,
+ fixed2float(pseg->pt.x), fixed2float(pseg->pt.y));
+ switch ( pseg->type )
+ {
+ case s_start:
+#define psub ((const subpath *)pseg)
+ dprintf1(out, "start");
+ dprintf2("#curves=%d last=0x%lx",
+ psub->curve_count, (ulong)psub->last);
+#undef psub
+ break;
+ case s_curve:
+ dprintf1(out, "curve");
+#define pcur ((const curve_segment *)pseg)
+ dprintf4("\n\tp1=(%f,%f) p2=(%f,%f)",
+ fixed2float(pcur->p1.x), fixed2float(pcur->p1.y),
+ fixed2float(pcur->p2.x), fixed2float(pcur->p2.y));
+#undef pcur
+ break;
+ case s_line:
+ dprintf1(out, "line");
+ break;
+ case s_line_close:
+#define plc ((const line_close_segment *)pseg)
+ dprintf1(out, "close");
+ dprintf1(" 0x%lx", (ulong)(plc->sub));
+#undef plc
+ break;
+ default:
+ { char t[20];
+ sprintf(t, "type 0x%x", pseg->type);
+ dprintf1(out, t);
+ }
+ }
+ dputc('\n');
+}
+
+#endif /* DEBUG */
diff --git a/pstoraster/gxpath.h b/pstoraster/gxpath.h
new file mode 100644
index 000000000..bb94032b2
--- /dev/null
+++ b/pstoraster/gxpath.h
@@ -0,0 +1,149 @@
+/* Copyright (C) 1989, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxpath.h */
+/* Lower-level path routines for Ghostscript library */
+/* Requires gxfixed.h */
+#include "gscpm.h"
+#include "gspenum.h"
+
+/* The routines and types in this interface use */
+/* device, rather than user, coordinates, and fixed-point, */
+/* rather than floating, representation. */
+
+/* Opaque type for a path */
+#ifndef gx_path_DEFINED
+# define gx_path_DEFINED
+typedef struct gx_path_s gx_path;
+#endif
+
+/* Define the two insideness rules */
+#define gx_rule_winding_number (-1)
+#define gx_rule_even_odd 1
+
+/* Debugging routines */
+#ifdef DEBUG
+void gx_dump_path(P2(const gx_path *, const char *));
+void gx_path_print(P1(const gx_path *));
+#endif
+
+/* Path constructors */
+
+gx_path *gx_path_alloc(P2(gs_memory_t *, client_name_t));
+void gx_path_init(P2(gx_path *, gs_memory_t *)),
+ gx_path_reset(P1(gx_path *)),
+ gx_path_release(P1(gx_path *)),
+ gx_path_share(P1(gx_path *));
+int gx_path_add_point(P3(gx_path *, fixed, fixed)),
+ gx_path_add_relative_point(P3(gx_path *, fixed, fixed)),
+ gx_path_add_line(P3(gx_path *, fixed, fixed)),
+ gx_path_add_lines(P3(gx_path *, const gs_fixed_point *, int)),
+ gx_path_add_rectangle(P5(gx_path *, fixed, fixed, fixed, fixed)),
+ gx_path_add_char_path(P3(gx_path *, gx_path *, gs_char_path_mode)),
+ gx_path_add_curve(P7(gx_path *, fixed, fixed, fixed, fixed, fixed, fixed)),
+/*
+ * gx_path_flattened_curve was removed in release 3.61.
+ gx_path_add_flattened_curve(P8(gx_path *, fixed, fixed, fixed, fixed, fixed, fixed, floatp)),
+ *
+ */
+ gx_path_add_partial_arc(P6(gx_path *, fixed, fixed, fixed, fixed, floatp)),
+ gx_path_add_path(P2(gx_path *, gx_path *)),
+ gx_path_close_subpath(P1(gx_path *)),
+ gx_path_pop_close_subpath(P1(gx_path *));
+/* The last argument to gx_path_add_partial_arc is a fraction for computing */
+/* the curve parameters. Here is the correct value for quarter-circles. */
+/* (stroke uses this to draw round caps and joins.) */
+#define quarter_arc_fraction 0.552285
+
+/* Path accessors */
+
+int gx_path_current_point(P2(const gx_path *, gs_fixed_point *)),
+ gx_path_bbox(P2(gx_path *, gs_fixed_rect *));
+bool gx_path_has_curves(P1(const gx_path *)),
+ gx_path_is_void(P1(const gx_path *)), /* no segments */
+ gx_path_is_null(P1(const gx_path *)), /* nothing at all */
+ gx_path_is_rectangle(P2(const gx_path *, gs_fixed_rect *)),
+ gx_path_is_monotonic(P1(const gx_path *));
+/* Inline versions of the above */
+#define gx_path_has_curves_inline(ppath)\
+ ((ppath)->curve_count != 0)
+#define gx_path_is_void_inline(ppath)\
+ ((ppath)->first_subpath == 0)
+#define gx_path_is_null_inline(ppath)\
+ (gx_path_is_void_inline(ppath) && !(ppath)->position_valid)
+
+/* Path transformers */
+
+/* gx_path_copy_reducing is internal. */
+int gx_path_copy_reducing(P5(const gx_path *ppath_old, gx_path *ppath_new,
+ fixed fixed_flatness, bool monotonize,
+ bool init));
+#define gx_path_copy(old, new, init)\
+ gx_path_copy_reducing(old, new, max_fixed, false, init)
+#define gx_path_flatten(old, new, flatness)\
+ gx_path_copy_reducing(old, new, float2fixed(flatness), false, true)
+#define gx_path_monotonize(old, new)\
+ gx_path_copy_reducing(old, new, max_fixed, true, true)
+int gx_path_expand_dashes(P3(const gx_path * /*old*/, gx_path * /*new*/, const gs_imager_state *)),
+ gx_path_copy_reversed(P3(const gx_path * /*old*/, gx_path * /*new*/, bool /*init*/)),
+ gx_path_translate(P3(gx_path *, fixed, fixed)),
+ gx_path_scale_exp2(P3(gx_path *, int, int));
+void gx_point_scale_exp2(P3(gs_fixed_point *, int, int)),
+ gx_rect_scale_exp2(P3(gs_fixed_rect *, int, int));
+
+/* Path enumerator */
+
+/* This interface does not make a copy of the path. */
+/* Do not use gs_path_enum_cleanup with this interface! */
+int gx_path_enum_init(P2(gs_path_enum *, const gx_path *));
+int gx_path_enum_next(P2(gs_path_enum *, gs_fixed_point [3])); /* 0 when done */
+bool gx_path_enum_backup(P1(gs_path_enum *));
+
+/* ------ Clipping paths ------ */
+
+/* Opaque type for a clipping path */
+#ifndef gx_clip_path_DEFINED
+# define gx_clip_path_DEFINED
+typedef struct gx_clip_path_s gx_clip_path;
+#endif
+
+/* Opaque type for a clip list. */
+#ifndef gx_clip_list_DEFINED
+# define gx_clip_list_DEFINED
+typedef struct gx_clip_list_s gx_clip_list;
+#endif
+
+int gx_clip_to_rectangle(P2(gs_state *, gs_fixed_rect *)),
+ gx_clip_to_path(P1(gs_state *)),
+ gx_cpath_init(P2(gx_clip_path *, gs_memory_t *)),
+ gx_cpath_from_rectangle(P3(gx_clip_path *, gs_fixed_rect *, gs_memory_t *)),
+ gx_cpath_intersect(P4(gs_state *, gx_clip_path *, gx_path *, int)),
+ gx_cpath_scale_exp2(P3(gx_clip_path *, int, int));
+void gx_cpath_release(P1(gx_clip_path *)),
+ gx_cpath_share(P1(gx_clip_path *));
+int gx_cpath_path(P2(gx_clip_path *, gx_path *));
+bool gx_cpath_inner_box(P2(const gx_clip_path *, gs_fixed_rect *)),
+ gx_cpath_outer_box(P2(const gx_clip_path *, gs_fixed_rect *)),
+ gx_cpath_includes_rectangle(P5(const gx_clip_path *, fixed, fixed, fixed, fixed));
+int gx_cpath_set_outside(P2(gx_clip_path *, bool));
+bool gx_cpath_is_outside(P1(const gx_clip_path *));
diff --git a/pstoraster/gxpath2.c b/pstoraster/gxpath2.c
new file mode 100644
index 000000000..709c65870
--- /dev/null
+++ b/pstoraster/gxpath2.c
@@ -0,0 +1,407 @@
+/* Copyright (C) 1989, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxpath2.c */
+/* Path tracing procedures for Ghostscript library */
+#include "math_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gxfixed.h"
+#include "gxarith.h"
+#include "gzpath.h"
+
+/* Define the enumeration structure. */
+private_st_path_enum();
+
+/* Read the current point of a path. */
+int
+gx_path_current_point(const gx_path *ppath, gs_fixed_point *ppt)
+{ if ( !ppath->position_valid )
+ return_error(gs_error_nocurrentpoint);
+ /* Copying the coordinates individually */
+ /* is much faster on a PC, and almost as fast on other machines.... */
+ ppt->x = ppath->position.x, ppt->y = ppath->position.y;
+ return 0;
+}
+
+/* Read the bounding box of a path. */
+/* Note that if the last element of the path is a moveto, */
+/* the bounding box does not include this point, */
+/* unless this is the only element of the path. */
+int
+gx_path_bbox(gx_path *ppath, gs_fixed_rect *pbox)
+{ if ( ppath->bbox_set )
+ { /* The bounding box was set by setbbox. */
+ *pbox = ppath->bbox;
+ return 0;
+ }
+ if ( ppath->first_subpath == 0 )
+ { /* The path is empty, use the current point if any. */
+ gx_path_current_point(ppath, &pbox->p);
+ return gx_path_current_point(ppath, &pbox->q);
+ }
+ /* The stored bounding box may not be up to date. */
+ /* Correct it now if necessary. */
+ if ( ppath->box_last == ppath->current_subpath->last )
+ { /* Box is up to date */
+ *pbox = ppath->bbox;
+ }
+ else
+ { gs_fixed_rect box;
+ register segment *pseg = ppath->box_last;
+ if ( pseg == 0 ) /* box is uninitialized */
+ { pseg = (segment *)ppath->first_subpath;
+ box.p.x = box.q.x = pseg->pt.x;
+ box.p.y = box.q.y = pseg->pt.y;
+ }
+ else
+ { box = ppath->bbox;
+ pseg = pseg->next;
+ }
+/* Macro for adjusting the bounding box when adding a point */
+#define adjust_bbox(pt)\
+ if ( (pt).x < box.p.x ) box.p.x = (pt).x;\
+ else if ( (pt).x > box.q.x ) box.q.x = (pt).x;\
+ if ( (pt).y < box.p.y ) box.p.y = (pt).y;\
+ else if ( (pt).y > box.q.y ) box.q.y = (pt).y
+ while ( pseg )
+ { switch ( pseg->type )
+ {
+ case s_curve:
+#define pcurve ((curve_segment *)pseg)
+ adjust_bbox(pcurve->p1);
+ adjust_bbox(pcurve->p2);
+#undef pcurve
+ /* falls through */
+ default:
+ adjust_bbox(pseg->pt);
+ }
+ pseg = pseg->next;
+ }
+#undef adjust_bbox
+ ppath->bbox = box;
+ ppath->box_last = ppath->current_subpath->last;
+ *pbox = box;
+ }
+ return 0;
+}
+
+/* Test if a path has any curves. */
+bool
+gx_path_has_curves(const gx_path *ppath)
+{ return ppath->curve_count != 0;
+}
+
+/* Test if a path has no segments. */
+bool
+gx_path_is_void(const gx_path *ppath)
+{ return ppath->first_subpath == 0;
+}
+
+/* Test if a path has no elements at all. */
+bool
+gx_path_is_null(const gx_path *ppath)
+{ return ppath->first_subpath == 0 && !ppath->position_valid;
+}
+
+/*
+ * Test if a subpath to be filled is a rectangle; if so, return its
+ * bounding box and the start of the next subpath.
+ * Note that this must recognize:
+ * ordinary closed rectangles (M, L, L, L, C);
+ * open rectangles (M, L, L, L);
+ * rectangles closed with lineto (Mo, L, L, L, Lo);
+ * rectangles closed with *both* lineto and closepath (bad PostScript,
+ * but unfortunately not rare) (Mo, L, L, L, Lo, C).
+ */
+bool
+gx_subpath_is_rectangle(const subpath *pseg0, gs_fixed_rect *pbox,
+ const subpath **ppnext)
+{ const segment *pseg1, *pseg2, *pseg3, *pseg4;
+ if ( pseg0->curve_count == 0 &&
+ (pseg1 = pseg0->next) != 0 &&
+ (pseg2 = pseg1->next) != 0 &&
+ (pseg3 = pseg2->next) != 0 &&
+ ((pseg4 = pseg3->next) == 0 || pseg4->type != s_line ||
+ (pseg4->pt.x == pseg0->pt.x &&
+ pseg4->pt.y == pseg0->pt.y &&
+ (pseg4->next == 0 || pseg4->next->type != s_line)))
+ )
+ { fixed x0 = pseg0->pt.x, y0 = pseg0->pt.y;
+ fixed x2 = pseg2->pt.x, y2 = pseg2->pt.y;
+ if ( (x0 == pseg1->pt.x && pseg1->pt.y == y2 &&
+ x2 == pseg3->pt.x && pseg3->pt.y == y0) ||
+ (x0 == pseg3->pt.x && pseg3->pt.y == y2 &&
+ x2 == pseg1->pt.x && pseg1->pt.y == y0)
+ )
+ { /* Path is a rectangle. Return the bounding box. */
+ if ( x0 < x2 )
+ pbox->p.x = x0, pbox->q.x = x2;
+ else
+ pbox->p.x = x2, pbox->q.x = x0;
+ if ( y0 < y2 )
+ pbox->p.y = y0, pbox->q.y = y2;
+ else
+ pbox->p.y = y2, pbox->q.y = y0;
+ while ( pseg4 != 0 && pseg4->type != s_start )
+ pseg4 = pseg4->next;
+ *ppnext = (const subpath *)pseg4;
+ return true;
+ }
+ }
+ return false;
+}
+/* Test if an entire path to be filled is a rectangle. */
+bool
+gx_path_is_rectangle(const gx_path *ppath, gs_fixed_rect *pbox)
+{ const subpath *pnext;
+ return (ppath->subpath_count == 1 &&
+ gx_subpath_is_rectangle(ppath->first_subpath, pbox, &pnext));
+}
+
+/* Translate an already-constructed path (in device space). */
+/* Don't bother to update the cbox. */
+int
+gx_path_translate(gx_path *ppath, fixed dx, fixed dy)
+{ segment *pseg;
+#define update_xy(pt)\
+ pt.x += dx, pt.y += dy
+ if ( ppath->box_last != 0 )
+ { update_xy(ppath->bbox.p);
+ update_xy(ppath->bbox.q);
+ }
+ if ( ppath->position_valid )
+ update_xy(ppath->position);
+ for ( pseg = (segment *)(ppath->first_subpath); pseg != 0;
+ pseg = pseg->next
+ )
+ switch ( pseg->type )
+ {
+ case s_curve:
+#define pcseg ((curve_segment *)pseg)
+ update_xy(pcseg->p1);
+ update_xy(pcseg->p2);
+#undef pcseg
+ default:
+ update_xy(pseg->pt);
+ }
+#undef update_xy
+ return 0;
+}
+
+/* Scale an existing path by a power of 2 (positive or negative). */
+void
+gx_point_scale_exp2(gs_fixed_point *pt, int sx, int sy)
+{ if ( sx >= 0 ) pt->x <<= sx; else pt->x >>= -sx;
+ if ( sy >= 0 ) pt->y <<= sy; else pt->y >>= -sy;
+}
+void
+gx_rect_scale_exp2(gs_fixed_rect *pr, int sx, int sy)
+{ gx_point_scale_exp2(&pr->p, sx, sy);
+ gx_point_scale_exp2(&pr->q, sx, sy);
+}
+int
+gx_path_scale_exp2(gx_path *ppath, int log2_scale_x, int log2_scale_y)
+{ segment *pseg;
+ gx_rect_scale_exp2(&ppath->bbox, log2_scale_x, log2_scale_y);
+#define update_xy(pt) gx_point_scale_exp2(&pt, log2_scale_x, log2_scale_y)
+ update_xy(ppath->position);
+ for ( pseg = (segment *)(ppath->first_subpath); pseg != 0;
+ pseg = pseg->next
+ )
+ switch ( pseg->type )
+ {
+ case s_curve:
+#define pcseg ((curve_segment *)pseg)
+ update_xy(pcseg->p1);
+ update_xy(pcseg->p2);
+#undef pcseg
+ default:
+ update_xy(pseg->pt);
+ }
+#undef update_xy
+ return 0;
+}
+
+/* Reverse a path. */
+/* We know ppath != ppath_old. */
+int
+gx_path_copy_reversed(const gx_path *ppath_old, gx_path *ppath, bool init)
+{ const subpath *psub = ppath_old->first_subpath;
+ int code;
+#ifdef DEBUG
+if ( gs_debug_c('P') )
+ gx_dump_path(ppath_old, "before reversepath");
+#endif
+ if ( init )
+ gx_path_init(ppath, ppath_old->memory);
+nsp: while ( psub )
+ { const segment *pseg = psub->last;
+ const segment *prev;
+ code = gx_path_add_point(ppath, pseg->pt.x, pseg->pt.y);
+ if ( code < 0 )
+ goto fx;
+ for ( ; ; pseg = prev )
+ { prev = pseg->prev;
+ switch ( pseg->type )
+ {
+ case s_start:
+endsp: /* Finished subpath */
+ if ( psub->is_closed )
+ { code = gx_path_close_subpath(ppath);
+ if ( code < 0 )
+ goto fx;
+ }
+ psub = (const subpath *)psub->last->next;
+ goto nsp;
+ case s_curve:
+ { const curve_segment *pc = (const curve_segment *)pseg;
+ code = gx_path_add_curve(ppath,
+ pc->p2.x, pc->p2.y,
+ pc->p1.x, pc->p1.y,
+ prev->pt.x, prev->pt.y);
+ break;
+ }
+ case s_line:
+ case s_line_close:
+ if ( prev->type == s_start && psub->is_closed )
+ { pseg = prev;
+ goto endsp;
+ }
+ code = gx_path_add_line(ppath, prev->pt.x, prev->pt.y);
+ break;
+ }
+ if ( code < 0 )
+ goto fx;
+ }
+ /* not reached */
+ }
+ if ( ppath_old->subpath_open < 0 ) /* final moveto */
+ { code = gx_path_add_point(ppath, ppath_old->position.x,
+ ppath_old->position.y);
+ if ( code < 0 )
+ goto fx;
+ }
+#ifdef DEBUG
+if ( gs_debug_c('P') )
+ gx_dump_path(ppath, "after reversepath");
+#endif
+ return 0;
+fx: gx_path_release(ppath);
+ return code;
+}
+
+/* ------ Path enumeration ------ */
+
+/* Allocate a path enumerator. */
+gs_path_enum *
+gs_path_enum_alloc(gs_memory_t *mem, client_name_t cname)
+{ return gs_alloc_struct(mem, gs_path_enum, &st_gs_path_enum, cname);
+}
+
+/* Start enumerating a path. */
+int
+gx_path_enum_init(gs_path_enum *penum, const gx_path *ppath)
+{ penum->path = ppath;
+ penum->copied_path = 0; /* not copied */
+ penum->pgs = 0;
+ penum->pseg = (const segment *)ppath->first_subpath;
+ penum->moveto_done = false;
+ return 0;
+}
+
+/* Enumerate the next element of a path. */
+/* If the path is finished, return 0; */
+/* otherwise, return the element type. */
+int
+gx_path_enum_next(gs_path_enum *penum, gs_fixed_point ppts[3])
+{ const segment *pseg = penum->pseg;
+
+ if ( pseg == 0 )
+ { /* We've enumerated all the segments, but there might be */
+ /* a trailing moveto. */
+ const gx_path *ppath = penum->path;
+ if ( ppath->subpath_open < 0 && !penum->moveto_done )
+ { /* Handle a trailing moveto */
+ penum->moveto_done = true;
+ ppts[0] = ppath->position;
+ return gs_pe_moveto;
+ }
+ return 0;
+ }
+ penum->pseg = pseg->next;
+ switch ( pseg->type )
+ {
+ case s_start:
+ ppts[0] = pseg->pt;
+ return gs_pe_moveto;
+ case s_line:
+ ppts[0] = pseg->pt;
+ return gs_pe_lineto;
+ case s_line_close:
+ ppts[0] = pseg->pt;
+ return gs_pe_closepath;
+ case s_curve:
+#define pcseg ((const curve_segment *)pseg)
+ ppts[0] = pcseg->p1;
+ ppts[1] = pcseg->p2;
+ ppts[2] = pseg->pt;
+ return gs_pe_curveto;
+#undef pcseg
+ default:
+ lprintf1("bad type %x in gx_path_enum_next!\n", pseg->type);
+ return_error(gs_error_Fatal);
+ }
+}
+
+/* Back up 1 element in the path being enumerated. */
+/* Return true if successful, false if we are at the beginning of the path. */
+/* This implementation allows backing up multiple times, */
+/* but no client currently relies on this. */
+bool
+gx_path_enum_backup(gs_path_enum *penum)
+{ const segment *pseg = penum->pseg;
+
+ if ( pseg != 0 )
+ { if ( (pseg = pseg->prev) == 0 )
+ return false;
+ penum->pseg = pseg;
+ return true;
+ }
+ /* We're at the end of the path. Check to see whether */
+ /* we need to back up over a trailing moveto. */
+ { const gx_path *ppath = penum->path;
+ if ( ppath->subpath_open < 0 && penum->moveto_done )
+ { /* Back up over the trailing moveto. */
+ penum->moveto_done = false;
+ return true;
+ }
+ { const subpath *psub = ppath->current_subpath;
+ if ( psub == 0 ) /* empty path */
+ return false;
+ /* Back up to the last segment of the last subpath. */
+ penum->pseg = psub->last;
+ return true;
+ }
+ }
+}
diff --git a/pstoraster/gxpcmap.c b/pstoraster/gxpcmap.c
new file mode 100644
index 000000000..29eb64a94
--- /dev/null
+++ b/pstoraster/gxpcmap.c
@@ -0,0 +1,511 @@
+/* Copyright (C) 1993, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxpcmap.c */
+/* Pattern color mapping for Ghostscript library */
+#include "math_.h"
+#include "memory_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gsstruct.h"
+#include "gsutil.h" /* for gs_next_ids */
+#include "gxfixed.h"
+#include "gxmatrix.h"
+#include "gxcspace.h" /* for gscolor2.h */
+#include "gxcolor2.h"
+#include "gxdcolor.h"
+#include "gxdevice.h"
+#include "gxdevmem.h"
+#include "gxpcolor.h"
+#include "gzstate.h"
+
+/* Define the default size of the Pattern cache. */
+#define max_cached_patterns_LARGE 50
+#define max_pattern_bits_LARGE 100000
+#define max_cached_patterns_SMALL 5
+#define max_pattern_bits_SMALL 1000
+uint
+gx_pat_cache_default_tiles(void)
+{
+#if arch_small_memory
+ return max_cached_patterns_SMALL;
+#else
+ return (gs_if_debug_c('.') ? max_cached_patterns_SMALL :
+ max_cached_patterns_LARGE);
+#endif
+}
+ulong
+gx_pat_cache_default_bits(void)
+{
+#if arch_small_memory
+ return max_pattern_bits_SMALL;
+#else
+ return (gs_if_debug_c('.') ? max_pattern_bits_SMALL :
+ max_pattern_bits_LARGE);
+#endif
+}
+
+/* Define the structures for Pattern rendering and caching. */
+private_st_color_tile();
+private_st_color_tile_element();
+private_st_pattern_cache();
+private_st_device_pattern_accum();
+
+/* ------ Pattern rendering ------ */
+
+/* Device procedures */
+private dev_proc_open_device(pattern_accum_open);
+private dev_proc_close_device(pattern_accum_close);
+private dev_proc_fill_rectangle(pattern_accum_fill_rectangle);
+private dev_proc_copy_mono(pattern_accum_copy_mono);
+private dev_proc_copy_color(pattern_accum_copy_color);
+
+/* The device descriptor */
+private const gx_device_pattern_accum gs_pattern_accum_device =
+{ std_device_std_body_open(gx_device_pattern_accum, 0,
+ "pattern accumulator",
+ 0, 0, 72, 72),
+ { pattern_accum_open,
+ NULL,
+ NULL,
+ NULL,
+ pattern_accum_close,
+ NULL,
+ NULL,
+ pattern_accum_fill_rectangle,
+ NULL,
+ pattern_accum_copy_mono,
+ pattern_accum_copy_color,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ gx_default_fill_path,
+ gx_default_stroke_path,
+ NULL,
+ gx_default_fill_trapezoid,
+ gx_default_fill_parallelogram,
+ gx_default_fill_triangle,
+ gx_default_draw_thin_line,
+ gx_default_begin_image,
+ gx_default_image_data,
+ gx_default_end_image
+ },
+ 0, /* target */
+ 0, 0, 0, 0 /* bitmap_memory, bits, mask, instance */
+};
+#define padev ((gx_device_pattern_accum *)dev)
+
+/* Allocate a pattern accumulator. */
+gx_device_pattern_accum *
+gx_pattern_accum_alloc(gs_memory_t *mem, client_name_t cname)
+{ gx_device_pattern_accum *adev =
+ gs_alloc_struct(mem, gx_device_pattern_accum,
+ &st_device_pattern_accum, cname);
+ if ( adev == 0 )
+ return 0;
+ *adev = gs_pattern_accum_device;
+ adev->memory = mem;
+ gx_device_forward_fill_in_procs((gx_device_forward *)adev); /* (should only do once) */
+ return adev;
+}
+
+/* Initialize a pattern accumulator. */
+/* Client must already have set instance and bitmap_memory. */
+private int
+pattern_accum_open(gx_device *dev)
+{ const gs_pattern_instance *pinst = padev->instance;
+ gs_memory_t *mem = padev->bitmap_memory;
+ gx_device_memory *mask =
+ gs_alloc_struct(mem, gx_device_memory, &st_device_memory,
+ "pattern_accum_open(mask)");
+ gx_device_memory *bits = 0;
+ gx_device *target = gs_currentdevice(pinst->saved);
+ int width = pinst->size.x;
+ int height = pinst->size.y;
+ int code;
+ if ( mask == 0 )
+ return_error(gs_error_VMerror);
+#define pdset(dev)\
+ (dev)->width = width, (dev)->height = height,\
+ (dev)->x_pixels_per_inch = target->x_pixels_per_inch,\
+ (dev)->y_pixels_per_inch = target->y_pixels_per_inch
+ pdset(padev);
+ padev->color_info = target->color_info;
+ gs_make_mem_mono_device(mask, mem, 0);
+ pdset(mask);
+ mask->bitmap_memory = mem;
+ mask->base = 0;
+ code = (*dev_proc(mask, open_device))((gx_device *)mask);
+ if ( code >= 0 )
+ { memset(mask->base, 0, mask->raster * mask->height);
+ switch ( pinst->template.PaintType )
+ {
+ case 2: /* uncolored */
+ padev->target = target;
+ break;
+ case 1: /* colored */
+ bits = gs_alloc_struct(mem, gx_device_memory,
+ &st_device_memory,
+ "pattern_accum_open(bits)");
+ if ( bits == 0 )
+ return_error(gs_error_VMerror);
+ padev->target = (gx_device *)bits;
+ gs_make_mem_device(bits, gdev_mem_device_for_bits(target->color_info.depth), mem, -1, target);
+ pdset(bits);
+#undef pdset
+ bits->color_info = target->color_info;
+ bits->bitmap_memory = mem;
+ code = (*dev_proc(bits, open_device))((gx_device *)bits);
+ }
+ }
+ if ( code < 0 )
+ { if ( bits != 0 )
+ gs_free_object(mem, bits, "pattern_accum_open(bits)");
+ (*dev_proc(mask, close_device))((gx_device *)mask);
+ gs_free_object(mem, mask, "pattern_accum_open(mask)");
+ return code;
+ }
+ padev->mask = mask;
+ padev->bits = bits;
+ return code;
+}
+
+/* Close an accumulator and free the bits. */
+private int
+pattern_accum_close(gx_device *dev)
+{ gs_memory_t *mem = padev->bitmap_memory;
+ if ( padev->bits != 0 )
+ { (*dev_proc(padev->bits, close_device))((gx_device *)padev->bits);
+ gs_free_object(mem, padev->bits, "pattern_accum_close(bits)");
+ padev->bits = 0;
+ }
+ (*dev_proc(padev->mask, close_device))((gx_device *)padev->mask);
+ gs_free_object(mem, padev->mask, "pattern_accum_close(mask)");
+ padev->mask = 0;
+ return 0;
+}
+
+/* Fill a rectangle */
+private int
+pattern_accum_fill_rectangle(gx_device *dev, int x, int y, int w, int h,
+ gx_color_index color)
+{ if ( padev->bits )
+ (*dev_proc(padev->target, fill_rectangle))(padev->target,
+ x, y, w, h, color);
+ return (*dev_proc(padev->mask, fill_rectangle))((gx_device *)padev->mask,
+ x, y, w, h, (gx_color_index)1);
+}
+
+/* Copy a monochrome bitmap. */
+private int
+pattern_accum_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 color0, gx_color_index color1)
+{ if ( padev->bits )
+ (*dev_proc(padev->target, copy_mono))(padev->target,
+ data, data_x, raster, id, x, y, w, h, color0, color1);
+ if ( color0 != gx_no_color_index )
+ color0 = 1;
+ if ( color1 != gx_no_color_index )
+ color1 = 1;
+ if ( color0 == 1 && color1 == 1 )
+ return (*dev_proc(padev->mask, fill_rectangle))((gx_device *)padev->mask,
+ x, y, w, h, (gx_color_index)1);
+ else
+ return (*dev_proc(padev->mask, copy_mono))((gx_device *)padev->mask,
+ data, data_x, raster, id, x, y, w, h, color0, color1);
+}
+
+/* Copy a color bitmap. */
+private int
+pattern_accum_copy_color(gx_device *dev, const byte *data, int data_x,
+ int raster, gx_bitmap_id id, int x, int y, int w, int h)
+{ if ( padev->bits )
+ (*dev_proc(padev->target, copy_color))(padev->target,
+ data, data_x, raster, id, x, y, w, h);
+ return (*dev_proc(padev->mask, fill_rectangle))((gx_device *)padev->mask,
+ x, y, w, h, (gx_color_index)1);
+}
+
+#undef padev
+
+/* ------ Color space implementation ------ */
+
+/* Allocate a Pattern cache. */
+gx_pattern_cache *
+gx_pattern_alloc_cache(gs_memory_t *mem, uint num_tiles, ulong max_bits)
+{ gx_pattern_cache *pcache =
+ gs_alloc_struct(mem, gx_pattern_cache, &st_pattern_cache,
+ "pattern_cache_alloc(struct)");
+ gx_color_tile *tiles =
+ gs_alloc_struct_array(mem, num_tiles, gx_color_tile,
+ &st_color_tile_element,
+ "pattern_cache_alloc(tiles)");
+ uint i;
+ if ( pcache == 0 || tiles == 0 )
+ { gs_free_object(mem, tiles, "pattern_cache_alloc(tiles)");
+ gs_free_object(mem, pcache, "pattern_cache_alloc(struct)");
+ return 0;
+ }
+ pcache->memory = mem;
+ pcache->tiles = tiles;
+ pcache->num_tiles = num_tiles;
+ pcache->tiles_used = 0;
+ pcache->next = 0;
+ pcache->bits_used = 0;
+ pcache->max_bits = max_bits;
+ for ( i = 0; i < num_tiles; tiles++, i++ )
+ { tiles->id = gx_no_bitmap_id;
+ /* Clear the pointers to pacify the GC. */
+ tiles->tbits.data = 0;
+ tiles->tmask.data = 0;
+ tiles->index = i;
+ }
+ return pcache;
+}
+/* Ensure that a gstate has a Pattern cache. */
+private int
+ensure_pattern_cache(gs_state *pgs)
+{ if ( pgs->pattern_cache == 0 )
+ { gx_pattern_cache *pcache =
+ gx_pattern_alloc_cache(pgs->memory,
+ gx_pat_cache_default_tiles(),
+ gx_pat_cache_default_bits());
+ if ( pcache == 0 )
+ return_error(gs_error_VMerror);
+ pgs->pattern_cache = pcache;
+ }
+ return 0;
+}
+
+/* Get and set the Pattern cache in a gstate. */
+gx_pattern_cache *
+gstate_pattern_cache(gs_state *pgs)
+{ return pgs->pattern_cache;
+}
+void
+gstate_set_pattern_cache(gs_state *pgs, gx_pattern_cache *pcache)
+{ pgs->pattern_cache = pcache;
+}
+
+/* Free a Pattern cache entry. */
+private void
+gx_pattern_cache_free_entry(gx_pattern_cache *pcache, gx_color_tile *ctile)
+{ gx_device_memory mdev;
+ if ( ctile->id != gx_no_bitmap_id )
+ { if ( ctile->tmask.data != 0 )
+ { mdev.width = ctile->tmask.size.x;
+ mdev.height = ctile->tmask.size.y;
+ mdev.color_info.depth = 1;
+ pcache->bits_used -= gdev_mem_bitmap_size(&mdev);
+ gs_free_object(pcache->memory, ctile->tmask.data,
+ "free_pattern_cache_entry(mask data)");
+ ctile->tmask.data = 0; /* for GC */
+ }
+ if ( ctile->tbits.data != 0 )
+ { mdev.width = ctile->tbits.size.x;
+ mdev.height = ctile->tbits.size.y;
+ mdev.color_info.depth = ctile->depth;
+ pcache->bits_used -= gdev_mem_bitmap_size(&mdev);
+ gs_free_object(pcache->memory, ctile->tbits.data,
+ "free_pattern_cache_entry(bits data)");
+ ctile->tbits.data = 0; /* for GC */
+ }
+ ctile->id = gx_no_bitmap_id;
+ pcache->tiles_used--;
+ }
+}
+
+/* Add a Pattern cache entry. This is exported for the interpreter. */
+private void make_bitmap(P3(gx_strip_bitmap *, const gx_device_memory *, gx_bitmap_id));
+int
+gx_pattern_cache_add_entry(gs_state *pgs, gx_device_pattern_accum *padev,
+ gx_color_tile **pctile)
+{ const gx_device_memory *mbits = padev->bits;
+ const gx_device_memory *mmask = padev->mask;
+ const gs_pattern_instance *pinst = padev->instance;
+ gx_pattern_cache *pcache;
+ ulong used = 0;
+ gx_bitmap_id id = pinst->id;
+ gx_color_tile *ctile;
+ int code = ensure_pattern_cache(pgs);
+ if ( code < 0 )
+ return code;
+ pcache = pgs->pattern_cache;
+ /*
+ * Check whether the pattern completely fills its box.
+ * If so, we can avoid the expensive masking operations
+ * when using the pattern.
+ */
+ { int y;
+ for ( y = 0; y < mmask->height; y++ )
+ { const byte *row = scan_line_base(mmask, y);
+ int w;
+ for ( w = mmask->width; w > 8; w -= 8 )
+ if ( *row++ != 0xff )
+ goto keep;
+ if ( (*row | (0xff >> w)) != 0xff )
+ goto keep;
+ }
+ /* We don't need a mask. */
+ mmask = 0;
+keep: ;
+ }
+ if ( mbits != 0 )
+ used += gdev_mem_bitmap_size(mbits);
+ if ( mmask != 0 )
+ used += gdev_mem_bitmap_size(mmask);
+ ctile = &pcache->tiles[id % pcache->num_tiles];
+ gx_pattern_cache_free_entry(pcache, ctile);
+ while ( pcache->bits_used + used > pcache->max_bits &&
+ pcache->bits_used != 0 /* allow 1 oversized entry (?) */
+ )
+ { pcache->next = (pcache->next + 1) % pcache->num_tiles;
+ gx_pattern_cache_free_entry(pcache, &pcache->tiles[pcache->next]);
+ }
+ ctile->id = id;
+ ctile->depth = padev->color_info.depth;
+ ctile->tiling_type = pinst->template.TilingType;
+ ctile->matrix = pinst->matrix;
+ ctile->bbox = pinst->bbox;
+ ctile->offset = pinst->offset;
+ if ( mbits != 0 )
+ make_bitmap(&ctile->tbits, mbits, gs_next_ids(1));
+ else
+ ctile->tbits.data = 0;
+ if ( mmask != 0 )
+ make_bitmap(&ctile->tmask, mmask, id);
+ else
+ ctile->tmask.data = 0;
+ ctile->is_simple = (fabs(ctile->matrix.xx) == pinst->size.x &&
+ ctile->matrix.xy == 0 &&
+ ctile->matrix.yx == 0 &&
+ fabs(ctile->matrix.yy) == pinst->size.y);
+ if_debug6('t',
+ "[t]is_simple? xstep=(%g,%g) ystep=(%g,%g) size=(%d,%d)\n",
+ ctile->matrix.xx, ctile->matrix.xy,
+ ctile->matrix.yx, ctile->matrix.yy,
+ pinst->size.x, pinst->size.y);
+ pcache->bits_used += used;
+ pcache->tiles_used++;
+ *pctile = ctile;
+ return 0;
+}
+private void
+make_bitmap(register gx_strip_bitmap *pbm, const gx_device_memory *mdev,
+ gx_bitmap_id id)
+{ pbm->data = mdev->base;
+ pbm->raster = mdev->raster;
+ pbm->rep_width = pbm->size.x = mdev->width;
+ pbm->rep_height = pbm->size.y = mdev->height;
+ pbm->id = id;
+ pbm->rep_shift = pbm->shift = 0;
+}
+
+/* Remap a Pattern color. */
+int
+gx_remap_Pattern(const gs_client_color *pc, const gs_color_space *pcs,
+ gx_device_color *pdc, const gs_state *pgs)
+{ gs_pattern_instance *pinst = pc->pattern;
+ gs_memory_t *mem = pgs->memory;
+ gs_state *saved;
+ gx_device_pattern_accum adev;
+ gx_color_tile *ctile;
+ int code;
+ if ( pinst == 0 )
+ { /* Null pattern */
+ color_set_null_pattern(pdc);
+ pdc->mask = 0;
+ return 0;
+ }
+ if ( pinst->template.PaintType == 2 ) /* uncolored */
+ { code = (*pcs->params.pattern.base_space.type->remap_color)
+ (pc, (const gs_color_space *)&pcs->params.pattern.base_space, pdc, pgs);
+ if ( code < 0 )
+ return code;
+#define replace_dc_type(t, pt)\
+ if ( pdc->type == t ) pdc->type = &pt
+ replace_dc_type(gx_dc_type_pure, gx_dc_pure_masked);
+ else
+ replace_dc_type(gx_dc_type_ht_binary, gx_dc_binary_masked);
+ else
+ replace_dc_type(gx_dc_type_ht_colored, gx_dc_colored_masked);
+ else
+ return_error(gs_error_unregistered);
+#undef replace_dc_type
+ }
+ else
+ color_set_null_pattern(pdc);
+ pdc->id = pinst->id;
+ pdc->mask = 0;
+ if ( gx_pattern_cache_lookup(pdc, pgs) )
+ return 0;
+ /* We REALLY don't like the following cast.... */
+ code = ensure_pattern_cache((gs_state *)pgs);
+ if ( code < 0 )
+ return code;
+ adev = gs_pattern_accum_device;
+ gx_device_forward_fill_in_procs((gx_device_forward *)&adev); /* (should only do once) */
+ adev.instance = pinst;
+ adev.bitmap_memory = mem;
+ code = (*dev_proc(&adev, open_device))((gx_device *)&adev);
+ if ( code < 0 )
+ return code;
+ saved = gs_gstate(pinst->saved);
+ if ( saved == 0 )
+ return_error(gs_error_VMerror);
+ if ( saved->pattern_cache == 0 )
+ saved->pattern_cache = pgs->pattern_cache;
+ gx_set_device_only(saved, (gx_device *)&adev);
+ code = (*pinst->template.PaintProc)(pc, saved);
+ gs_free_object(mem, saved, "gx_remap_Pattern(saved)");
+ if ( code < 0 )
+ { (*dev_proc(&adev, close_device))((gx_device *)&adev);
+ return code;
+ }
+ /* We REALLY don't like the following cast.... */
+ code = gx_pattern_cache_add_entry((gs_state *)pgs, &adev, &ctile);
+ if ( code < 0 )
+ (*dev_proc(&adev, close_device))((gx_device *)&adev);
+#ifdef DEBUG
+ if ( gs_debug_c('B') )
+ { debug_dump_bitmap(adev.mask->base, adev.mask->raster,
+ adev.mask->height, "[B]Pattern mask");
+ if ( adev.bits )
+ debug_dump_bitmap(((gx_device_memory *)adev.target)->base,
+ ((gx_device_memory *)adev.target)->raster,
+ adev.target->height, "[B]Pattern bits");
+ }
+#endif
+ return code;
+}
diff --git a/pstoraster/gxpcolor.h b/pstoraster/gxpcolor.h
new file mode 100644
index 000000000..7aa65c90e
--- /dev/null
+++ b/pstoraster/gxpcolor.h
@@ -0,0 +1,130 @@
+/* Copyright (C) 1993, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxpcolor.h */
+/* Internal definitions for Pattern colors */
+/* Requires gsmatrix.h, gxdevice.h, gxdevmem.h, gxcolor2.h, gxdcolor.h */
+
+/*
+ * Define the Pattern device color types. There is one type for
+ * colored patterns, and one uncolored pattern type for each non-Pattern
+ * device color type.
+ */
+extern const gx_device_color_procs
+ gx_dc_pattern, gx_dc_pure_masked,
+ gx_dc_binary_masked, gx_dc_colored_masked;
+#define gx_dc_type_pattern (&gx_dc_pattern)
+
+/*
+ * Define a color tile, an entry in the rendered Pattern cache (and
+ * eventually in the colored halftone cache). Note that the depth is
+ * not sufficient to ensure that the rendering matches a given device;
+ * however, we don't currently have an object that represents the
+ * abstraction of a 'color representation'.
+ */
+struct gx_color_tile_s {
+ /* The following are the 'key' in the cache. */
+ gx_bitmap_id id;
+ int depth;
+ /* The following are the 'value'. */
+ /* Note that if tbits and tmask both have data != 0, */
+ /* both must have the same rep_shift. */
+ /****** NON-ZERO shift VALUES ARE NOT SUPPORTED YET. ******/
+ int tiling_type; /* TilingType */
+ gs_matrix matrix; /* tiling space -> device space */
+ gs_rect bbox; /* bbox of tile in tiling space */
+ gs_point offset; /* of tile from matrix.tx/y */
+ gx_strip_bitmap tbits; /* data = 0 if uncolored */
+ gx_strip_bitmap tmask; /* data = 0 if no mask */
+ /* (i.e., the mask is all 1's) */
+ bool is_simple; /* true if xstep/ystep = tile size */
+ /* The following is neither key nor value. */
+ uint index; /* the index of the tile within */
+ /* the cache (for GC) */
+};
+#define private_st_color_tile() /* in gxpcmap.c */\
+ gs_private_st_ptrs2(st_color_tile, gx_color_tile, "gx_color_tile",\
+ color_tile_enum_ptrs, color_tile_reloc_ptrs, tbits.data, tmask.data)
+#define private_st_color_tile_element() /* in gxpcmap.c */\
+ gs_private_st_element(st_color_tile_element, gx_color_tile,\
+ "gx_color_tile[]", color_tile_elt_enum_ptrs, color_tile_elt_reloc_ptrs,\
+ st_color_tile)
+
+/*
+ * Define a cache for rendered Patterns. This is currently an open
+ * hash table with linear reprobing and round-robin replacement.
+ * Obviously, we can do better in both areas.
+ */
+typedef struct gx_pattern_cache_s {
+ gs_memory_t *memory;
+ gx_color_tile *tiles;
+ uint num_tiles;
+ uint tiles_used;
+ uint next; /* round-robin index */
+ ulong bits_used;
+ ulong max_bits;
+} gx_pattern_cache;
+#define private_st_pattern_cache() /* in gxpcmap.c */\
+ gs_private_st_ptrs1(st_pattern_cache, gx_pattern_cache,\
+ "gx_pattern_cache", pattern_cache_enum, pattern_cache_reloc, tiles)
+
+/* Allocate a Pattern cache. */
+/* We shorten the procedure names because some VMS compilers */
+/* truncate names to 23 characters. */
+uint gx_pat_cache_default_tiles(P0());
+ulong gx_pat_cache_default_bits(P0());
+gx_pattern_cache *gx_pattern_alloc_cache(P3(gs_memory_t *, uint, ulong));
+
+/* Get or set the Pattern cache in a gstate. */
+gx_pattern_cache *gstate_pattern_cache(P1(gs_state *));
+void gstate_set_pattern_cache(P2(gs_state *, gx_pattern_cache *));
+
+/*
+ * Define a device for accumulating the rendering of a Pattern.
+ * This is actually a wrapper for two other devices: one that accumulates
+ * the actual pattern image (if this is a colored pattern), and one that
+ * accumulates a mask defining which pixels in the image are set.
+ */
+typedef struct gx_device_pattern_accum_s {
+ gx_device_forward_common;
+ /* Client sets these before opening */
+ gs_memory_t *bitmap_memory;
+ const gs_pattern_instance *instance;
+ /* open sets these */
+ gx_device_memory *bits; /* target also points to bits */
+ gx_device_memory *mask;
+} gx_device_pattern_accum;
+#define private_st_device_pattern_accum() /* in gxpcmap.c */\
+ gs_private_st_suffix_add3(st_device_pattern_accum, gx_device_pattern_accum,\
+ "pattern accumulator", pattern_accum_enum, pattern_accum_reloc,\
+ st_device_forward, instance, bits, mask)
+
+/* Allocate a pattern accumulator. */
+gx_device_pattern_accum *gx_pattern_accum_alloc(P2(gs_memory_t *memory, client_name_t));
+
+/* Add an accumulated pattern to the cache. */
+int gx_pattern_cache_add_entry(P3(gs_state *, gx_device_pattern_accum *,
+ gx_color_tile **));
+
+/* Look up a pattern color in the cache. */
+bool gx_pattern_cache_lookup(P2(gx_device_color *, const gs_state *));
diff --git a/pstoraster/gxpcopy.c b/pstoraster/gxpcopy.c
new file mode 100644
index 000000000..6ac4d7f3c
--- /dev/null
+++ b/pstoraster/gxpcopy.c
@@ -0,0 +1,998 @@
+/* Copyright (C) 1992, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxpcopy.c */
+/* Path copying and flattening */
+#include "math_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gxfixed.h"
+#include "gxarith.h"
+#include "gzpath.h"
+
+/* Define whether to merge nearly collinear line segments when flattening */
+/* curves. This is very good for performance, but we feel a little */
+/* uneasy about its effects on character appearance. */
+#define MERGE_COLLINEAR_SEGMENTS 1
+
+/* Forward declarations */
+private int flatten_internal(P3(gx_path *, const curve_segment *, fixed));
+private int flatten_sample(P3(gx_path *, int, curve_segment *));
+private int monotonize_internal(P2(gx_path *, const curve_segment *));
+
+/* Copy a path, optionally flattening or monotonizing it. */
+/* If the copy fails, free the new path. */
+int
+gx_path_copy_reducing(const gx_path *ppath_old, gx_path *ppath,
+ fixed fixed_flatness, bool monotonize, bool init)
+{ gx_path old;
+ const segment *pseg;
+ int code;
+#ifdef DEBUG
+ if ( gs_debug_c('P') )
+ gx_dump_path(ppath_old, "before copy_path");
+#endif
+ old = *ppath_old;
+ if ( init )
+ gx_path_init(ppath, ppath_old->memory);
+ pseg = (const segment *)(old.first_subpath);
+ while ( pseg )
+ { switch ( pseg->type )
+ {
+ case s_start:
+ code = gx_path_add_point(ppath, pseg->pt.x, pseg->pt.y);
+ break;
+ case s_curve:
+ { const curve_segment *pc = (const curve_segment *)pseg;
+ if ( fixed_flatness == max_fixed ) /* don't flatten */
+ { if ( monotonize )
+ code = monotonize_internal(ppath, pc);
+ else
+ code = gx_path_add_curve(ppath,
+ pc->p1.x, pc->p1.y,
+ pc->p2.x, pc->p2.y,
+ pc->pt.x, pc->pt.y);
+ }
+ else
+ code = flatten_internal(ppath, pc, fixed_flatness);
+ break;
+ }
+ case s_line:
+ code = gx_path_add_line(ppath, pseg->pt.x, pseg->pt.y);
+ break;
+ case s_line_close:
+ code = gx_path_close_subpath(ppath);
+ break;
+ default: /* can't happen */
+ code = gs_note_error(gs_error_unregistered);
+ }
+ if ( code )
+ { gx_path_release(ppath);
+ if ( ppath == ppath_old )
+ *ppath = old;
+ return code;
+ }
+ pseg = pseg->next;
+ }
+ if ( old.subpath_open < 0 )
+ gx_path_add_point(ppath, old.position.x, old.position.y);
+#ifdef DEBUG
+ if ( gs_debug_c('P') )
+ gx_dump_path(ppath, "after copy_path");
+#endif
+ return 0;
+}
+
+/* ---------------- Curve flattening ---------------- */
+
+#define x1 pc->p1.x
+#define y1 pc->p1.y
+#define x2 pc->p2.x
+#define y2 pc->p2.y
+#define x3 pc->pt.x
+#define y3 pc->pt.y
+
+/*
+ * To calculate how many points to sample along a path in order to
+ * approximate it to the desired degree of flatness, we define
+ * dist((x,y)) = abs(x) + abs(y);
+ * then the number of points we need is
+ * N = 1 + sqrt(3/4 * D / flatness),
+ * where
+ * D = max(dist(p0 - 2*p1 + p2), dist(p1 - 2*p2 + p3)).
+ * Since we are going to use a power of 2 for the number of intervals,
+ * we can avoid the square root by letting
+ * N = 1 + 2^(ceiling(log2(3/4 * D / flatness) / 2)).
+ * (Reference: DEC Paris Research Laboratory report #1, May 1989.)
+ *
+ * We treat two cases specially. First, if the curve is very
+ * short, we halve the flatness, to avoid turning short shallow curves
+ * into short straight lines. Second, if the curve forms part of a
+ * character, or the flatness is less than half a pixel, we let
+ * N = 1 + 2 * max(abs(x3-x0), abs(y3-y0)).
+ * This is probably too conservative, but it produces good results.
+ */
+int
+gx_curve_log2_samples(fixed x0, fixed y0, const curve_segment *pc,
+ fixed fixed_flat)
+{ fixed
+ x03 = x3 - x0,
+ y03 = y3 - y0;
+ int k;
+
+ if ( x03 < 0 )
+ x03 = -x03;
+ if ( y03 < 0 )
+ y03 = -y03;
+ if ( (x03 | y03) < int2fixed(16) )
+ fixed_flat >>= 1;
+ if ( fixed_flat < fixed_half )
+ { /* Use the conservative method. */
+ fixed m = max(x03, y03);
+ for ( k = 1; m > fixed_1; )
+ k++, m >>= 1;
+ }
+ else
+ { const fixed
+ x12 = x1 - x2,
+ y12 = y1 - y2,
+ dx0 = x0 - x1 - x12,
+ dy0 = y0 - y1 - y12,
+ dx1 = x12 - x2 + x3,
+ dy1 = y12 - y2 + y3,
+ adx0 = any_abs(dx0),
+ ady0 = any_abs(dy0),
+ adx1 = any_abs(dx1),
+ ady1 = any_abs(dy1);
+ fixed
+ d = max(adx0, adx1) + max(ady0, ady1);
+ fixed q;
+
+ if_debug6('2', "[2]d01=%g,%g d12=%g,%g d23=%g,%g\n",
+ fixed2float(x1 - x0), fixed2float(y1 - y0),
+ fixed2float(-x12), fixed2float(-y12),
+ fixed2float(x3 - x2), fixed2float(y3 - y2));
+ if_debug2('2', " D=%f, flat=%f,",
+ fixed2float(d), fixed2float(fixed_flat));
+ d -= d >> 2; /* 3/4 * D */
+ if ( d < (fixed)1 << (sizeof(fixed) * 8 - _fixed_shift - 1) )
+ q = (d << _fixed_shift) / fixed_flat;
+ else
+ q = float2fixed((float)d / fixed_flat);
+ /* Now we want to set k = ceiling(log2(q) / 2). */
+ for ( k = 0; q > fixed_1; )
+ k++, q >>= 2;
+ if_debug1('2', " k=%d\n", k);
+ }
+ return k;
+}
+
+private int
+flatten_internal(gx_path *ppath, const curve_segment *pc, fixed fixed_flat)
+{ int k = gx_curve_log2_samples(ppath->position.x, ppath->position.y,
+ pc, fixed_flat);
+ curve_segment cseg;
+
+ cseg.p1.x = x1, cseg.p1.y = y1;
+ cseg.p2.x = x2, cseg.p2.y = y2;
+ cseg.pt.x = x3, cseg.pt.y = y3;
+ return flatten_sample(ppath, k, &cseg);
+}
+
+/*
+ * Define the maximum number of points for sampling if we want accurate
+ * rasterizing. 2^(k_sample_max*3)-1 must fit into a uint with a bit
+ * to spare; also, we must be able to compute 1/2^(3*k) by table lookup.
+ */
+#define k_sample_max min((size_of(int) * 8 - 1) / 3, 10)
+
+/*
+ * Split a curve segment into two pieces at the (parametric) midpoint.
+ * Algorithm is from "The Beta2-split: A special case of the Beta-spline
+ * Curve and Surface Representation," B. A. Barsky and A. D. DeRose, IEEE,
+ * 1985, courtesy of Crispin Goswell.
+ */
+#ifdef DEBUG
+private void
+dprint_curve(const char *str, fixed x0, fixed y0, const curve_segment *pc)
+{ dprintf9("%s p0=(%g,%g) p1=(%g,%g) p2=(%g,%g) p3=(%g,%g)\n",
+ str, fixed2float(x0), fixed2float(y0),
+ fixed2float(pc->p1.x), fixed2float(pc->p1.y),
+ fixed2float(pc->p2.x), fixed2float(pc->p2.y),
+ fixed2float(pc->pt.x), fixed2float(pc->pt.y));
+}
+#endif
+private void
+split_curve_midpoint(fixed x0, fixed y0, const curve_segment *pc,
+ curve_segment *pc1, curve_segment *pc2)
+{ /*
+ * We have to define midpoint carefully to avoid overflow.
+ * (If it overflows, something really pathological is going
+ * on, but we could get infinite recursion that way....)
+ */
+#define midpoint(a,b)\
+ (arith_rshift_1(a) + arith_rshift_1(b) + ((a) & (b) & 1) + 1)
+ fixed x12 = midpoint(x1, x2);
+ fixed y12 = midpoint(y1, y2);
+
+ /*
+ * pc1 or pc2 may be the same as pc, so we must be a little careful
+ * about the order in which we store the results.
+ */
+ pc1->p1.x = midpoint(x0, x1);
+ pc1->p1.y = midpoint(y0, y1);
+ pc2->p2.x = midpoint(x2, x3);
+ pc2->p2.y = midpoint(y2, y3);
+ pc1->p2.x = midpoint(pc1->p1.x, x12);
+ pc1->p2.y = midpoint(pc1->p1.y, y12);
+ pc2->p1.x = midpoint(x12, pc2->p2.x);
+ pc2->p1.y = midpoint(y12, pc2->p2.y);
+ if ( pc2 != pc )
+ pc2->pt.x = pc->pt.x,
+ pc2->pt.y = pc->pt.y;
+ pc1->pt.x = midpoint(pc1->p2.x, pc2->p1.x);
+ pc1->pt.y = midpoint(pc1->p2.y, pc2->p1.y);
+#undef midpoint
+}
+
+/* Initialize a cursor for rasterizing a monotonic curve. */
+void
+gx_curve_cursor_init(curve_cursor *prc, fixed x0, fixed y0,
+ const curve_segment *pc, int k)
+{ fixed v01, v12;
+ int k2 = k + k, k3 = k2 + k;
+
+#define bits_fit(v, n)\
+ (any_abs(v) <= max_fixed >> (n))
+/* The +2s are because of t3d and t2d, see below. */
+#define coeffs_fit(a, b, c)\
+ (k3 <= sizeof(fixed) * 8 - 3 &&\
+ bits_fit(a, k3 + 2) && bits_fit(b, k2 + 2) && bits_fit(c, k + 1))
+
+ prc->k = k;
+ prc->p0.x = x0, prc->p0.y = y0;
+ prc->pc = pc;
+ curve_points_to_coefficients(x0, x1, x2, x3,
+ prc->a, prc->b, prc->c, v01, v12);
+ prc->double_set = false;
+ prc->fixed_fits = coeffs_fit(prc->a, prc->b, prc->c);
+}
+
+/*
+ * Determine the X value on a monotonic curve at a given Y value. It turns
+ * out that we use so few points on the curve that it's actually faster to
+ * locate the desired point by recursive subdivision each time than to try
+ * to keep a cursor that we move incrementally. What's even more surprising
+ * is that it's faster to compute the X value at the desired point explicitly
+ * than to do the recursive subdivision on X as well as Y.
+ */
+fixed
+gx_curve_x_at_y(curve_cursor *prc, fixed y)
+{ const curve_segment *pc = prc->pc;
+#define x0 prc->p0.x
+#define y0 prc->p0.y
+ fixed cy0 = y0, cy1 = y1, cy2 = y2, cy3 = y3;
+ bool increasing = cy0 < cy3;
+ int k = prc->k;
+ int i, t;
+ fixed xl, xd;
+
+#define midpoint_fast(a,b)\
+ arith_rshift_1((a) + (b) + 1)
+ for ( t = i = 0; i < k; ++i )
+ {
+ fixed ym = midpoint_fast(cy1, cy2);
+ fixed yn = ym + arith_rshift(cy0 - cy1 - cy2 + cy3 + 4, 3);
+
+ t <<= 1;
+ if ( (y < yn) == increasing )
+ cy1 = midpoint_fast(cy0, cy1),
+ cy2 = midpoint_fast(cy1, ym),
+ cy3 = yn;
+ else
+ cy2 = midpoint_fast(cy2, cy3),
+ cy1 = midpoint_fast(ym, cy2),
+ cy0 = yn, t++;
+ }
+ { fixed a = prc->a, b = prc->b, c = prc->c;
+
+/* We must use (1 << k) >> 1 instead of 1 << (k - 1) in case k == 0. */
+#define compute_fixed(a, b, c)\
+ arith_rshift(arith_rshift(arith_rshift(a * t3, k) + b * t2, k)\
+ + c * t + ((1 << k) >> 1), k)
+#define compute_diff_fixed(a, b, c)\
+ arith_rshift(arith_rshift(arith_rshift(a * t3d, k) + b * t2d, k)\
+ + c, k)
+
+#define np2(n) (1.0 / (1L << (n)))
+ static const double k_denom[11] =
+ { np2(0), np2(1), np2(2), np2(3), np2(4),
+ np2(5), np2(6), np2(7), np2(8), np2(9), np2(10)
+ };
+ static const double k2_denom[11] =
+ { np2(0), np2(2), np2(4), np2(6), np2(8),
+ np2(10), np2(12), np2(14), np2(16), np2(18), np2(20)
+ };
+ static const double k3_denom[11] =
+ { np2(0), np2(3), np2(6), np2(9), np2(12),
+ np2(15), np2(18), np2(21), np2(24), np2(27), np2(30)
+ };
+ double den1, den2;
+#undef np2
+
+#define setup_floating(da, db, dc, a, b, c)\
+ (k >= countof(k_denom) ?\
+ (den1 = ldexp(1.0, -k),\
+ den2 = den1 * den1,\
+ da = (den2 * den1) * a,\
+ db = den2 * b,\
+ dc = den1 * c) :\
+ (da = k3_denom[k] * a,\
+ db = k2_denom[k] * b,\
+ dc = k_denom[k] * c))
+#define compute_floating(da, db, dc)\
+ ((fixed)(da * t3 + db * t2 + dc * t + 0.5))
+#define compute_diff_floating(da, db, dc)\
+ ((fixed)(da * t3d + db * t2d + dc))
+
+ if ( prc->fixed_fits )
+ { int t2 = t * t, t3 = t2 * t;
+ int t3d = (t2 + t) * 3 + 1, t2d = t + t + 1;
+ xl = compute_fixed(a, b, c) + x0;
+ xd = compute_diff_fixed(a, b, c);
+#ifdef DEBUG
+ { double fa, fb, fc;
+ fixed xlf, xdf;
+ setup_floating(fa, fb, fc, a, b, c);
+ xlf = compute_floating(fa, fb, fc) + x0;
+ xdf = compute_diff_floating(fa, fb, fc);
+ if ( any_abs(xlf - xl) > fixed_epsilon ||
+ any_abs(xdf - xd) > fixed_epsilon
+ )
+ dprintf9("Curve points differ: k=%d t=%d a,b,c=%g,%g,%g\n xl,xd fixed=%g,%g floating=%g,%g\n",
+ k, t,
+ fixed2float(a), fixed2float(b), fixed2float(c),
+ fixed2float(xl), fixed2float(xd),
+ fixed2float(xlf), fixed2float(xdf));
+/*xl = xlf, xd = xdf;*/
+ }
+#endif
+ }
+#define fa prc->da
+#define fb prc->db
+#define fc prc->dc
+#if arch_sizeof_long > arch_sizeof_int
+ else if ( t < 1L << ((sizeof(long) * 8 - 1) / 3) )
+ { /* t3 (and maybe t2) might not fit in an int, */
+ /* but they will fit in a long. */
+ long t2 = (long)t * t, t3 = t2 * t;
+ long t3d = (t2 + t) * 3 + 1, t2d = t + t + 1;
+ if ( !prc->double_set )
+ { setup_floating(fa, fb, fc, a, b, c);
+ prc->double_set = true;
+ }
+ xl = compute_floating(fa, fb, fc) + x0;
+ xd = compute_diff_floating(fa, fb, fc);
+ }
+#endif
+ else
+ { /* t3 (and maybe t2) don't even fit in a long. */
+ double t2 = (double)t * t, t3 = t2 * t;
+ double t3d = (t2 + t) * 3 + 1, t2d = t + t + 1;
+ if ( !prc->double_set )
+ { setup_floating(fa, fb, fc, a, b, c);
+ prc->double_set = true;
+ }
+ xl = compute_floating(fa, fb, fc) + x0;
+ xd = compute_diff_floating(fa, fb, fc);
+ }
+#undef fa
+#undef fb
+#undef fc
+ }
+
+ /*
+ * Now interpolate linearly between current and next.
+ */
+
+ { fixed yd = cy3 - cy0, yrel = y - cy0;
+
+ /* It's unlikely but possible that cy0 = y = cy3. */
+ /* Handle this case specially. */
+ if ( yrel == 0 )
+ return xl;
+ /* Compute in fixed point if possible. */
+ if ( any_abs(yrel) < ((fixed)1 << (sizeof(fixed) * 4 - 1)) &&
+ any_abs(xd) < ((fixed)1 << (sizeof(fixed) * 4 - 1))
+ )
+ return xd * yrel / yd + xl;
+ return fixed_mult_quo(xd, yrel, yd) + xl;
+ }
+#undef x0
+#undef y0
+}
+
+/*
+ * Flatten a segment of the path by repeated sampling.
+ * 2^k is the number of lines to produce (i.e., the number of points - 1,
+ * including the endpoints); we require k >= 1.
+ * If k or any of the coefficient values are too large,
+ * use recursive subdivision to whittle them down.
+ */
+private int
+flatten_sample(gx_path *ppath, int k, curve_segment *pc)
+{ fixed x0, y0;
+ /* x1 ... y3 were defined above */
+ fixed cx, bx, ax, cy, by, ay;
+ fixed ptx, pty;
+ fixed x, y;
+ /*
+ * We can compute successive values by finite differences,
+ * using the formulas:
+ x(t) =
+ a*t^3 + b*t^2 + c*t + d =>
+ dx(t) = x(t+e)-x(t) =
+ a*(3*t^2*e + 3*t*e^2 + e^3) + b*(2*t*e + e^2) + c*e =
+ (3*a*e)*t^2 + (3*a*e^2 + 2*b*e)*t + (a*e^3 + b*e^2 + c*e) =>
+ d2x(t) = dx(t+e)-dx(t) =
+ (3*a*e)*(2*t*e + e^2) + (3*a*e^2 + 2*b*e)*e =
+ (6*a*e^2)*t + (6*a*e^3 + 2*b*e^2) =>
+ d3x(t) = d2x(t+e)-d2x(t) =
+ 6*a*e^3;
+ x(0) = d, dx(0) = (a*e^3 + b*e^2 + c*e),
+ d2x(0) = 6*a*e^3 + 2*b*e^2;
+ * In these formulas, e = 1/2^k; of course, there are separate
+ * computations for the x and y values.
+ *
+ * There is a tradeoff in doing the above computation in fixed
+ * point. If we separate out the constant term (d) and require that
+ * all the other values fit in a long, then on a 32-bit machine with
+ * 12 bits of fraction in a fixed, k = 4 implies a maximum curve
+ * size of 128 pixels; anything larger requires subdividing the
+ * curve. On the other hand, doing the computations in explicit
+ * double precision slows down the loop by a factor of 3 or so. We
+ * found to our surprise that the latter is actually faster, because
+ * the additional subdivisions cost more than the slower loop.
+ *
+ * We represent each quantity as I+R/M, where I is an "integer" and
+ * the "remainder" R lies in the range 0 <= R < M=2^(3*k). Note
+ * that R may temporarily exceed M; for this reason, we require that
+ * M have at least one free high-order bit. To reduce the number of
+ * variables, we don't actually compute M, only M-1 (rmask). */
+ uint i;
+ uint rmask; /* M-1 */
+ fixed idx, idy, id2x, id2y, id3x, id3y; /* I */
+ uint rx, ry, rdx, rdy, rd2x, rd2y, rd3x, rd3y; /* R */
+ gs_fixed_point *ppt;
+#define max_points 50 /* arbitrary */
+ gs_fixed_point points[max_points + 1];
+
+top: x0 = ppath->position.x;
+ y0 = ppath->position.y;
+#ifdef DEBUG
+ if ( gs_debug_c('3') )
+ dprintf4("[3]x0=%f y0=%f x1=%f y1=%f\n",
+ fixed2float(x0), fixed2float(y0),
+ fixed2float(x1), fixed2float(y1)),
+ dprintf5(" x2=%f y2=%f x3=%f y3=%f k=%d\n",
+ fixed2float(x2), fixed2float(y2),
+ fixed2float(x3), fixed2float(y3), k);
+#endif
+ { fixed x01, x12, y01, y12;
+ curve_points_to_coefficients(x0, x1, x2, x3, ax, bx, cx,
+ x01, x12);
+ curve_points_to_coefficients(y0, y1, y2, y3, ay, by, cy,
+ y01, y12);
+ }
+
+ if_debug6('3', "[3]ax=%f bx=%f cx=%f\n ay=%f by=%f cy=%f\n",
+ fixed2float(ax), fixed2float(bx), fixed2float(cx),
+ fixed2float(ay), fixed2float(by), fixed2float(cy));
+#define max_fast (max_fixed / 6)
+#define min_fast (-max_fast)
+#define in_range(v) (v < max_fast && v > min_fast)
+ if ( k == 0 )
+ { /* The curve is very short, or anomalous in some way. */
+ /* Just add a line and exit. */
+ return gx_path_add_line(ppath, x3, y3);
+ }
+ if ( k <= k_sample_max &&
+ in_range(ax) && in_range(ay) &&
+ in_range(bx) && in_range(by) &&
+ in_range(cx) && in_range(cy)
+ )
+ { x = x0, y = y0;
+ rx = ry = 0;
+ ppt = points;
+ /* Fast check for n == 3, a common special case */
+ /* for small characters. */
+ if ( k == 1 )
+ {
+#define poly2(a,b,c)\
+ arith_rshift_1(arith_rshift_1(arith_rshift_1(a) + b) + c)
+ x += poly2(ax, bx, cx);
+ y += poly2(ay, by, cy);
+#undef poly2
+ if_debug2('3', "[3]dx=%f, dy=%f\n",
+ fixed2float(x - x0), fixed2float(y - y0));
+ if_debug3('3', "[3]%s x=%g, y=%g\n",
+ (((x ^ x0) | (y ^ y0)) & float2fixed(-0.5) ?
+ "add" : "skip"),
+ fixed2float(x), fixed2float(y));
+ if ( ((x ^ x0) | (y ^ y0)) & float2fixed(-0.5) )
+ ppt->x = ptx = x,
+ ppt->y = pty = y,
+ ppt++;
+ goto last;
+ }
+ else
+ { fixed bx2 = bx << 1, by2 = by << 1;
+ fixed ax6 = ((ax << 1) + ax) << 1,
+ ay6 = ((ay << 1) + ay) << 1;
+#define adjust_rem(r, q)\
+ if ( r > rmask ) q ++, r &= rmask
+ const int k2 = k << 1;
+ const int k3 = k2 + k;
+ rmask = (1 << k3) - 1;
+ /* We can compute all the remainders as ints, */
+ /* because we know they don't exceed M. */
+ /* cx/y terms */
+ idx = arith_rshift(cx, k),
+ idy = arith_rshift(cy, k);
+ rdx = ((uint)cx << k2) & rmask,
+ rdy = ((uint)cy << k2) & rmask;
+ /* bx/y terms */
+ id2x = arith_rshift(bx2, k2),
+ id2y = arith_rshift(by2, k2);
+ rd2x = ((uint)bx2 << k) & rmask,
+ rd2y = ((uint)by2 << k) & rmask;
+ idx += arith_rshift_1(id2x),
+ idy += arith_rshift_1(id2y);
+ rdx += ((uint)bx << k) & rmask,
+ rdy += ((uint)by << k) & rmask;
+ adjust_rem(rdx, idx);
+ adjust_rem(rdy, idy);
+ /* ax/y terms */
+ idx += arith_rshift(ax, k3),
+ idy += arith_rshift(ay, k3);
+ rdx += (uint)ax & rmask,
+ rdy += (uint)ay & rmask;
+ adjust_rem(rdx, idx);
+ adjust_rem(rdy, idy);
+ id2x += id3x = arith_rshift(ax6, k3),
+ id2y += id3y = arith_rshift(ay6, k3);
+ rd2x += rd3x = (uint)ax6 & rmask,
+ rd2y += rd3y = (uint)ay6 & rmask;
+ adjust_rem(rd2x, id2x);
+ adjust_rem(rd2y, id2y);
+#undef adjust_rem
+ }
+ }
+ else
+ { /*
+ * Curve is too long. Break into two pieces and recur.
+ */
+ curve_segment cseg;
+ int code;
+
+ k--;
+ split_curve_midpoint(x0, y0, pc, &cseg, pc);
+ code = flatten_sample(ppath, k, &cseg);
+ if ( code < 0 )
+ return code;
+ goto top;
+ }
+ if_debug1('2', "[2]sampling k=%d\n", k);
+ ptx = x0, pty = y0;
+ for ( i = (1 << k) - 1; ; )
+ { int code;
+#ifdef DEBUG
+ if ( gs_debug_c('3') )
+ dprintf4("[3]dx=%f+%d, dy=%f+%d\n",
+ fixed2float(idx), rdx,
+ fixed2float(idy), rdy),
+ dprintf4(" d2x=%f+%d, d2y=%f+%d\n",
+ fixed2float(id2x), rd2x,
+ fixed2float(id2y), rd2y),
+ dprintf4(" d3x=%f+%d, d3y=%f+%d\n",
+ fixed2float(id3x), rd3x,
+ fixed2float(id3y), rd3y);
+#endif
+#define accum(i, r, di, dr)\
+ if ( (r += dr) > rmask ) r &= rmask, i += di + 1;\
+ else i += di
+ accum(x, rx, idx, rdx);
+ accum(y, ry, idy, rdy);
+ if_debug3('3', "[3]%s x=%g, y=%g\n",
+ (((x ^ ptx) | (y ^ pty)) & float2fixed(-0.5) ?
+ "add" : "skip"),
+ fixed2float(x), fixed2float(y));
+ /*
+ * Skip very short segments -- those that lie entirely within
+ * a square half-pixel. Also merge nearly collinear
+ * segments -- those where one coordinate of all three points
+ * (the two endpoints and the midpoint) lie within the same
+ * half-pixel and both coordinates are monotonic.
+ * Note that ptx/y, the midpoint, is the same as ppt[-1].x/y;
+ * the previous point is ppt[-2].x/y.
+ */
+#define coord_near(v, ptv)\
+ (!( ((v) ^ (ptv)) & float2fixed(-0.5) ))
+#define coords_in_order(v0, v1, v2)\
+ ( (((v1) - (v0)) ^ ((v2) - (v1))) >= 0 )
+ if ( coord_near(x, ptx) )
+ { /* X coordinates are within a half-pixel. */
+ if ( coord_near(y, pty) )
+ goto skip; /* short segment */
+#if MERGE_COLLINEAR_SEGMENTS
+ /* Check for collinear segments. */
+ if ( ppt > points + 1 && coord_near(x, ppt[-2].x) &&
+ coords_in_order(ppt[-2].x, ptx, x) &&
+ coords_in_order(ppt[-2].y, pty, y)
+ )
+ --ppt; /* remove middle point */
+#endif
+ }
+ else if ( coord_near(y, pty) )
+ { /* Y coordinates are within a half-pixel. */
+#if MERGE_COLLINEAR_SEGMENTS
+ /* Check for collinear segments. */
+ if ( ppt > points + 1 && coord_near(y, ppt[-2].y) &&
+ coords_in_order(ppt[-2].x, ptx, x) &&
+ coords_in_order(ppt[-2].y, pty, y)
+ )
+ --ppt; /* remove middle point */
+#endif
+ }
+#undef coord_near
+#undef coords_in_order
+ /* Add a line. */
+ { if ( ppt == &points[max_points] )
+ { if ( (code = gx_path_add_lines(ppath, points, max_points)) < 0 )
+ return code;
+ ppt = points;
+ }
+ ppt->x = ptx = x;
+ ppt->y = pty = y;
+ ppt++;
+ }
+skip: if ( --i == 0 )
+ break; /* don't bother with last accum */
+ accum(idx, rdx, id2x, rd2x);
+ accum(id2x, rd2x, id3x, rd3x);
+ accum(idy, rdy, id2y, rd2y);
+ accum(id2y, rd2y, id3y, rd3y);
+#undef accum
+ }
+last: if_debug2('3', "[3]last x=%g, y=%g\n",
+ fixed2float(x3), fixed2float(y3));
+ ppt->x = x3, ppt->y = y3;
+ return gx_path_add_lines(ppath, points, (int)(ppt + 1 - points));
+}
+
+#undef x1
+#undef y1
+#undef x2
+#undef y2
+#undef x3
+#undef y3
+
+/* ---------------- Monotonic curves ---------------- */
+
+/* Test whether a path is free of non-monotonic curves. */
+bool
+gx_path_is_monotonic(const gx_path *ppath)
+{ const segment *pseg = (const segment *)(ppath->first_subpath);
+ gs_fixed_point pt0;
+
+ while ( pseg )
+ { switch ( pseg->type )
+ {
+ case s_start:
+ { const subpath *psub = (const subpath *)pseg;
+ /* Skip subpaths without curves. */
+ if ( !psub->curve_count )
+ pseg = psub->last;
+ }
+ break;
+ case s_curve:
+ { const curve_segment *pc = (const curve_segment *)pseg;
+ double t[2];
+ int nz = gx_curve_monotonic_points(pt0.y,
+ pc->p1.y, pc->p2.y, pc->pt.y, t);
+ if ( nz != 0 )
+ return false;
+ nz = gx_curve_monotonic_points(pt0.x,
+ pc->p1.x, pc->p2.x, pc->pt.x, t);
+ if ( nz != 0 )
+ return false;
+ }
+ break;
+ default:
+ ;
+ }
+ pt0 = pseg->pt;
+ pseg = pseg->next;
+ }
+ return true;
+}
+
+/* Monotonize a curve, by splitting it if necessary. */
+/* In the worst case, this could split the curve into 9 pieces. */
+private int
+monotonize_internal(gx_path *ppath, const curve_segment *pc)
+{ fixed x0 = ppath->position.x, y0 = ppath->position.y;
+ double t[2];
+#define max_segs 9
+ curve_segment cs[max_segs];
+ const curve_segment *pcs;
+ curve_segment *pcd;
+ int i, j, nseg;
+ int nz;
+
+ /* Monotonize in Y. */
+ nz = gx_curve_monotonic_points(y0, pc->p1.y, pc->p2.y, pc->pt.y, t);
+ nseg = max_segs - 1 - nz;
+ pcd = cs + nseg;
+ if ( nz == 0 )
+ *pcd = *pc;
+ else
+ { gx_curve_split(x0, y0, pc, t[0], pcd, pcd + 1);
+ if ( nz == 2 )
+ gx_curve_split(pcd->pt.x, pcd->pt.y, pcd + 1, t[1] * (1 - t[0]),
+ pcd + 1, pcd + 2);
+ }
+
+ /* Monotonize in X. */
+ for ( pcs = pcd, pcd = cs, i = 0, j = nseg; j < max_segs; ++pcs, ++j )
+ { nz = gx_curve_monotonic_points(x0, pcs->p1.x, pcs->p2.x,
+ pcs->pt.x, t);
+
+ if ( nz == 0 )
+ *pcd = *pcs;
+ else
+ { gx_curve_split(x0, y0, pcs, t[0], pcd, pcd + 1);
+ if ( nz == 2 )
+ gx_curve_split(pcd->pt.x, pcd->pt.y, pcd + 1,
+ t[1] * (1 - t[0]), pcd + 1, pcd + 2);
+ }
+ pcd += nz + 1;
+ x0 = pcd[-1].pt.x;
+ }
+ nseg = pcd - cs;
+
+ /* Add the segment(s) to the output. */
+#ifdef DEBUG
+ if ( gs_debug_c('2') )
+ { int pi;
+ gs_fixed_point pp0;
+
+ pp0 = ppath->position;
+ if ( nseg == 1 )
+ dprint_curve("[2]No split", pp0.x, pp0.y, pc);
+ else
+ { dprintf1("[2]Split into %d segments:\n", nseg);
+ dprint_curve("[2]Original", pp0.x, pp0.y, pc);
+ for ( pi = 0; pi < nseg; ++pi )
+ { dprint_curve("[2] =>", pp0.x, pp0.y, cs + pi);
+ pp0 = cs[pi].pt;
+ }
+ }
+ }
+#endif
+ for ( pcs = cs, i = 0; i < nseg; ++pcs, ++i )
+ { int code = gx_path_add_curve(ppath, pcs->p1.x, pcs->p1.y,
+ pcs->p2.x, pcs->p2.y,
+ pcs->pt.x, pcs->pt.y);
+ if ( code < 0 )
+ return code;
+ }
+
+ return 0;
+}
+
+/*
+ * Split a curve if necessary into pieces that are monotonic in X or Y as a
+ * function of the curve parameter t. This will eventually allow us to
+ * rasterize curves on the fly. This takes a fair amount of analysis....
+ * Store the values of t of the split points in pst[0] and pst[1]. Return
+ * the number of split points (0, 1, or 2).
+ */
+int
+gx_curve_monotonic_points(fixed v0, fixed v1, fixed v2, fixed v3,
+ double pst[2])
+{
+ /*
+ Let
+ v(t) = a*t^3 + b*t^2 + c*t + d, 0 <= t <= 1.
+ Then
+ dv(t) = 3*a*t^2 + 2*b*t + c.
+ v(t) has a local minimum or maximum (or inflection point)
+ precisely where dv(t) = 0. Now the roots of dv(t) = 0 (i.e.,
+ the zeros of dv(t)) are at
+ t = ( -2*b +/- sqrt(4*b^2 - 12*a*c) ) / 6*a
+ = ( -b +/- sqrt(b^2 - 3*a*c) ) / 3*a
+ (Note that real roots exist iff b^2 >= 3*a*c.)
+ We want to know if these lie in the range (0..1).
+ (The endpoints don't count.) Call such a root a "valid zero."
+ Since computing the roots is expensive, we would like to have
+ some cheap tests to filter out cases where they don't exist
+ (i.e., where the curve is already monotonic).
+ */
+ fixed v01, v12, a, b, c, b2, a3;
+ fixed dv_end, b2abs, a3abs;
+
+ curve_points_to_coefficients(v0, v1, v2, v3, a, b, c, v01, v12);
+ b2 = b << 1;
+ a3 = (a << 1) + a;
+ /*
+ If a = 0, the only possible zero is t = -c / 2*b.
+ This zero is valid iff sign(c) != sign(b) and 0 < |c| < 2*|b|.
+ */
+ if ( a == 0 )
+ { if ( (b ^ c) < 0 && any_abs(c) < any_abs(b2) && c != 0 )
+ { *pst = (double)(-c) / b2;
+ return 1;
+ }
+ else
+ return 0;
+ }
+ /*
+ Iff a curve is horizontal at t = 0, c = 0. In this case,
+ there can be at most one other zero, at -2*b / 3*a.
+ This zero is valid iff sign(a) != sign(b) and 0 < 2*|b| < 3*|a|.
+ */
+ if ( c == 0 )
+ { if ( (a ^ b) < 0 && any_abs(b2) < any_abs(a3) && b != 0 )
+ { *pst = (double)(-b2) / a3;
+ return 1;
+ }
+ else
+ return 0;
+ }
+ /*
+ Similarly, iff a curve is horizontal at t = 1, 3*a + 2*b + c = 0.
+ In this case, there can be at most one other zero,
+ at -1 - 2*b / 3*a, iff sign(a) != sign(b) and 1 < -2*b / 3*a < 2,
+ i.e., 3*|a| < 2*|b| < 6*|a|.
+ */
+ else if ( (dv_end = a3 + b2 + c) == 0 )
+ { if ( (a ^ b) < 0 &&
+ (b2abs = any_abs(b2)) > (a3abs = any_abs(a3)) &&
+ b2abs < a3abs << 1
+ )
+ { *pst = (double)(-b2 - a3) / a3;
+ return 1;
+ }
+ else
+ return 0;
+ }
+ /*
+ If sign(dv_end) != sign(c), at least one valid zero exists,
+ since dv(0) and dv(1) have opposite signs and hence
+ dv(t) must be zero somewhere in the interval [0..1].
+ */
+ else if ( (dv_end ^ c) < 0 )
+ ;
+ /*
+ If sign(a) = sign(b), no valid zero exists,
+ since dv is monotonic on [0..1] and has the same sign
+ at both endpoints.
+ */
+ else if ( (a ^ b) >= 0 )
+ return 0;
+ /*
+ Otherwise, dv(t) may be non-monotonic on [0..1]; it has valid zeros
+ iff its sign anywhere in this interval is different from its sign
+ at the endpoints, which occurs iff it has an extremum in this
+ interval and the extremum is of the opposite sign from c.
+ To find this out, we look for the local extremum of dv(t)
+ by observing
+ d2v(t) = 6*a*t + 2*b
+ which has a zero only at
+ t1 = -b / 3*a
+ Now if t1 <= 0 or t1 >= 1, no valid zero exists.
+ Note that we just determined that sign(a) != sign(b), so we know t1 > 0.
+ */
+ else if ( any_abs(b) >= any_abs(a3) )
+ return 0;
+ /*
+ Otherwise, we just go ahead with the computation of the roots,
+ and test them for being in the correct range. Note that a valid
+ zero is an inflection point of v(t) iff d2v(t) = 0; we don't
+ bother to check for this case, since it's rare.
+ */
+ { double nbf = (double)(-b);
+ double a3f = (double)a3;
+ double radicand = nbf * nbf - a3f * c;
+
+ if ( radicand < 0 )
+ { if_debug1('2', "[2]negative radicand = %g\n", radicand);
+ return 0;
+ }
+ { double root = sqrt(radicand);
+ int nzeros = 0;
+ double z = (nbf + root) / a3f;
+
+ if_debug2('2', "[2]zeros at %g, %g\n", z, (nbf - root) / a3f);
+ if ( z > 0 && z < 1 )
+ *pst = z, nzeros = 1;
+ if ( root != 0 )
+ { z = (nbf - root) / a3f;
+ if ( z > 0 && z < 1 )
+ pst[nzeros] = z, nzeros++;
+ }
+ return nzeros;
+ }
+ }
+}
+
+/*
+ * Split a curve at an arbitrary point t. The above midpoint split is a
+ * special case of this with t = 0.5.
+ */
+void
+gx_curve_split(fixed x0, fixed y0, const curve_segment *pc, double t,
+ curve_segment *pc1, curve_segment *pc2)
+{ /*
+ * If the original function was v(t), we want to compute the points
+ * for the functions v1(T) = v(t * T) and v2(T) = v(t + (1 - t) * T).
+ * Straightforwardly,
+ * v1(T) = a*t^3*T^3 + b*t^2*T^2 + c*t*T + d
+ * i.e.
+ * a1 = a*t^3, b1 = b*t^2, c1 = c*t, d1 = d.
+ * Similarly,
+ * v2(T) = a*[t + (1-t)*T]^3 + b*[t + (1-t)*T]^2 +
+ * c*[t + (1-t)*T] + d
+ * = a*[(1-t)^3*T^3 + 3*t*(1-t)^2*T^2 + 3*t^2*(1-t)*T +
+ * t^3] + b*[(1-t)^2*T^2 + 2*t*(1-t)*T + t^2] +
+ * c*[(1-t)*T + t] + d
+ * = a*(1-t)^3*T^3 + [a*3*t + b]*(1-t)^2*T^2 +
+ * [a*3*t^2 + b*2*t + c]*(1-t)*T +
+ * a*t^3 + b*t^2 + c*t + d
+ * We do this in the simplest way, namely, we convert the points to
+ * coefficients, do the arithmetic, and convert back. It would
+ * obviously be faster to do the arithmetic directly on the points,
+ * as the midpoint code does; this is just an implementation issue
+ * that we can revisit if necessary.
+ */
+ double t2 = t * t, t3 = t2 * t;
+ double omt = 1 - t, omt2 = omt * omt, omt3 = omt2 * omt;
+ fixed v01, v12, a, b, c, na, nb, nc;
+
+ if_debug1('2', "[2]splitting at t = %g\n", t);
+#define compute_seg(v0, v)\
+ curve_points_to_coefficients(v0, pc->p1.v, pc->p2.v, pc->pt.v,\
+ a, b, c, v01, v12);\
+ na = (fixed)(a * t3), nb = (fixed)(b * t2), nc = (fixed)(c * t);\
+ curve_coefficients_to_points(na, nb, nc, v0,\
+ pc1->p1.v, pc1->p2.v, pc1->pt.v);\
+ na = (fixed)(a * omt3);\
+ nb = (fixed)((a * t * 3 + b) * omt2);\
+ nc = (fixed)((a * t2 * 3 + b * 2 * t + c) * omt);\
+ curve_coefficients_to_points(na, nb, nc, pc1->pt.v,\
+ pc2->p1.v, pc2->p2.v, pc2->pt.v)
+ compute_seg(x0, x);
+ compute_seg(y0, y);
+#undef compute_seg
+}
diff --git a/pstoraster/gxpdash.c b/pstoraster/gxpdash.c
new file mode 100644
index 000000000..b3d5d35ae
--- /dev/null
+++ b/pstoraster/gxpdash.c
@@ -0,0 +1,143 @@
+/* Copyright (C) 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxpdash.c */
+/* Dash expansion for paths */
+#include "math_.h"
+#include "gx.h"
+#include "gsmatrix.h" /* for gscoord.h */
+#include "gscoord.h"
+#include "gxfixed.h"
+#include "gsline.h"
+#include "gzline.h"
+#include "gzpath.h"
+
+/* Expand a dashed path into explicit segments. */
+/* The path contains no curves. */
+private int subpath_expand_dashes(P3(const subpath *, gx_path *,
+ const gs_imager_state *));
+int
+gx_path_expand_dashes(const gx_path *ppath_old, gx_path *ppath,
+ const gs_imager_state *pis)
+{ const subpath *psub;
+ if ( gs_currentlineparams(pis)->dash.pattern_size == 0 )
+ return gx_path_copy(ppath_old, ppath, true);
+ gx_path_init(ppath, ppath_old->memory);
+ for ( psub = ppath_old->first_subpath; psub != 0;
+ psub = (const subpath *)psub->last->next
+ )
+ { int code = subpath_expand_dashes(psub, ppath, pis);
+ if ( code < 0 )
+ { gx_path_release(ppath);
+ return code;
+ }
+ }
+ return 0;
+}
+private int
+subpath_expand_dashes(const subpath *psub, gx_path *ppath,
+ const gs_imager_state *pis)
+{ const gx_dash_params *dash = &gs_currentlineparams(pis)->dash;
+ const float *pattern = dash->pattern;
+ int count, ink_on, index;
+ float dist_left;
+ fixed x0 = psub->pt.x, y0 = psub->pt.y;
+ fixed x, y;
+ const segment *pseg;
+ int wrap = (dash->init_ink_on && psub->is_closed ? -1 : 0);
+ int drawing = wrap;
+ int code;
+ if ( (code = gx_path_add_point(ppath, x0, y0)) < 0 )
+ return code;
+ /* To do the right thing at the beginning of a closed path, */
+ /* we have to skip any initial line, and then redo it at */
+ /* the end of the path. Drawing = -1 while skipping, */
+ /* 0 while drawing normally, and 1 on the second round. */
+top: count = dash->pattern_size;
+ ink_on = dash->init_ink_on;
+ index = dash->init_index;
+ dist_left = dash->init_dist_left;
+ x = x0, y = y0;
+ pseg = (const segment *)psub;
+ while ( (pseg = pseg->next) != 0 && pseg->type != s_start )
+ { fixed sx = pseg->pt.x, sy = pseg->pt.y;
+ fixed udx = sx - x, udy = sy - y;
+ float length, dx, dy;
+ float dist;
+ if ( !(udx | udy) ) /* degenerate */
+ dx = 0, dy = 0, length = 0;
+ else
+ { gs_point d;
+ dx = udx, dy = udy; /* scaled as fixed */
+ gs_imager_idtransform(pis, dx, dy, &d);
+ length = hypot(d.x, d.y) * (1 / (float)int2fixed(1));
+ }
+ dist = length;
+ while ( dist > dist_left )
+ { /* We are using up the dash element */
+ float fraction = dist_left / length;
+ fixed nx = x + (fixed)(dx * fraction);
+ fixed ny = y + (fixed)(dy * fraction);
+ if ( ink_on )
+ { if ( drawing >= 0 )
+ code = gx_path_add_line(ppath, nx, ny);
+ }
+ else
+ { if ( drawing > 0 ) return 0; /* done */
+ code = gx_path_add_point(ppath, nx, ny);
+ drawing = 0;
+ }
+ if ( code < 0 ) return code;
+ dist -= dist_left;
+ ink_on = !ink_on;
+ if ( ++index == count ) index = 0;
+ dist_left = pattern[index];
+ x = nx, y = ny;
+ }
+ dist_left -= dist;
+ /* Handle the last dash of a segment. */
+ if ( ink_on )
+ { if ( drawing >= 0 )
+ code =
+ (pseg->type == s_line_close && drawing > 0 ?
+ gx_path_close_subpath(ppath) :
+ gx_path_add_line(ppath, sx, sy));
+ }
+ else
+ { if ( drawing > 0 ) return 0; /* done */
+ code = gx_path_add_point(ppath, sx, sy);
+ drawing = 0;
+ }
+ if ( code < 0 ) return code;
+ x = sx, y = sy;
+ }
+ /* Check for wraparound. */
+ if ( wrap && drawing <= 0 )
+ { /* We skipped some initial lines. */
+ /* Go back and do them now. */
+ drawing = 1;
+ goto top;
+ }
+ return 0;
+}
+
diff --git a/pstoraster/gxstate.h b/pstoraster/gxstate.h
new file mode 100644
index 000000000..9abd1fe5e
--- /dev/null
+++ b/pstoraster/gxstate.h
@@ -0,0 +1,63 @@
+/* Copyright (C) 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxstate.h */
+/* Internal graphics state API */
+
+#ifndef gxstate_INCLUDED
+# define gxstate_INCLUDED
+
+/* Opaque type for a graphics state */
+#ifndef gs_state_DEFINED
+# define gs_state_DEFINED
+typedef struct gs_state_s gs_state;
+#endif
+
+/*
+ * The interfaces in this file are for internal use only, primarily by the
+ * interpreter. They are not guaranteed to remain stable from one release
+ * to another.
+ */
+
+/* Memory and save/restore management */
+gs_memory_t *gs_state_memory(P1(const gs_state *));
+gs_state *gs_state_saved(P1(const gs_state *));
+gs_state *gs_state_swap_saved(P2(gs_state *, gs_state *));
+gs_memory_t *gs_state_swap_memory(P2(gs_state *, gs_memory_t *));
+
+/* "Client data" interface for graphics states. */
+typedef void *(*gs_state_alloc_proc_t)(P1(gs_memory_t *mem));
+typedef int (*gs_state_copy_proc_t)(P2(void *to, const void *from));
+typedef void (*gs_state_free_proc_t)(P2(void *old, gs_memory_t *mem));
+typedef struct gs_state_client_procs_s {
+ gs_state_alloc_proc_t alloc;
+ gs_state_copy_proc_t copy;
+ gs_state_free_proc_t free;
+} gs_state_client_procs;
+void gs_state_set_client(P3(gs_state *, void *, const gs_state_client_procs *));
+/* gzstate.h redefines the following: */
+#ifndef gs_state_client_data
+void *gs_state_client_data(P1(const gs_state *));
+#endif
+
+#endif /* gxstate_INCLUDED */
diff --git a/pstoraster/gxstroke.c b/pstoraster/gxstroke.c
new file mode 100644
index 000000000..fb5db18a1
--- /dev/null
+++ b/pstoraster/gxstroke.c
@@ -0,0 +1,1060 @@
+/* Copyright (C) 1989, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxstroke.c */
+/* Path stroking procedures for Ghostscript library */
+#include "math_.h"
+#include "gx.h"
+#include "gpcheck.h"
+#include "gserrors.h"
+#include "gsdcolor.h"
+#include "gxfixed.h"
+#include "gxarith.h"
+#include "gxmatrix.h"
+#include "gscoord.h"
+#include "gxdevice.h"
+#include "gxhttile.h"
+#include "gxistate.h"
+#include "gzline.h"
+#include "gzpath.h"
+#include "gzcpath.h"
+#include "gxpaint.h"
+
+/*
+ * We don't really know whether it's a good idea to take fill adjustment
+ * into account for stroking. Disregarding it means that strokes
+ * come out thinner than fills; observing it produces heavy-looking
+ * strokes at low resolutions. But in any case, we must disregard it
+ * when stroking zero-width lines.
+ */
+#define USE_FILL_ADJUSTMENT
+
+#ifdef USE_FILL_ADJUSTMENT
+# define stroke_adjustment(thin, pis, xy)\
+ (thin ? fixed_0 : (pis)->fill_adjust.xy)
+#else
+# define stroke_adjustment(thin, pis, xy) fixed_0
+#endif
+
+/*
+ * Compute the amount by which to expand a stroked bounding box to account
+ * for line width, caps and joins. Because of square caps and miter joins,
+ * the maximum expansion on each side is
+ * max(sqrt(2), miter_limit) * line_width/2,
+ * computed in user space.
+ */
+int
+gx_stroke_expansion(const gs_imager_state *pis, gs_fixed_point *ppt)
+{ double expand = max(1.415, pis->line_params.miter_limit) *
+ pis->line_params.half_width;
+ /* Short-cut gs_bbox_transform. */
+ float cx1 = pis->ctm.xx + pis->ctm.yx;
+ float cy1 = pis->ctm.xy + pis->ctm.yy;
+ float cx2 = pis->ctm.xx - pis->ctm.yx;
+ float cy2 = pis->ctm.xy - pis->ctm.yy;
+
+ if ( cx1 < 0 ) cx1 = -cx1;
+ if ( cy1 < 0 ) cy1 = -cy1;
+ if ( cx2 < 0 ) cx2 = -cx2;
+ if ( cy2 < 0 ) cy2 = -cy2;
+ ppt->x = float2fixed(expand * max(cx1, cx2));
+ ppt->y = float2fixed(expand * max(cy1, cy2));
+ return 0;
+}
+
+/*
+ * Structure for a partial line (passed to the drawing routine).
+ * Two of these are required to do joins right.
+ * Each endpoint includes the two ends of the cap as well,
+ * and the deltas for square, round, and triangular cap computation.
+ *
+ * The two base values for computing the caps of a partial line are the
+ * width and the end cap delta. The width value is one-half the line
+ * width (suitably transformed) at 90 degrees counter-clockwise
+ * (in device space, but with "90 degrees" interpreted in *user*
+ * coordinates) at the end (as opposed to the origin) of the line.
+ * The cdelta value is one-half the transformed line width in the same
+ * direction as the line. From these, we compute two other values at each
+ * end of the line: co and ce, which are the ends of the cap.
+ * Note that the cdelta values at o are the negatives of the values at e,
+ * as are the offsets from p to co and ce.
+ *
+ * Initially, only o.p, e.p, e.cdelta, width, and thin are set.
+ * compute_caps fills in the rest.
+ */
+typedef gs_fixed_point _ss *p_ptr;
+typedef struct endpoint_s {
+ gs_fixed_point p; /* the end of the line */
+ gs_fixed_point co, ce; /* ends of the cap, p +/- width */
+ gs_fixed_point cdelta; /* +/- cap length */
+} endpoint;
+typedef endpoint _ss *ep_ptr;
+typedef const endpoint _ss *const_ep_ptr;
+typedef struct partial_line_s {
+ endpoint o; /* starting coordinate */
+ endpoint e; /* ending coordinate */
+ gs_fixed_point width; /* one-half line width, see above */
+ bool thin; /* true if minimum-width line */
+} partial_line;
+typedef partial_line _ss *pl_ptr;
+
+/* Assign a point. Some compilers would do this with very slow code */
+/* if we simply implemented it as an assignment. */
+#define assign_point(pp, p)\
+ ((pp)->x = (p).x, (pp)->y = (p).y)
+
+/* Other forward declarations */
+private void near adjust_stroke(P3(pl_ptr, const gs_imager_state *, bool));
+private int near line_join_points(P4(const gx_line_params *pgs_lp,
+ pl_ptr plp, pl_ptr nplp,
+ gs_fixed_point _ss *join_points));
+private void near compute_caps(P1(pl_ptr));
+private int near add_points(P4(gx_path *, const gs_fixed_point _ss *,
+ int, bool));
+private int near add_round_cap(P2(gx_path *, const_ep_ptr));
+private int near cap_points(P3(gs_line_cap, const_ep_ptr,
+ gs_fixed_point _ss * /*[3]*/));
+
+/* Define the default implementation of the device stroke_path procedure. */
+int
+gx_default_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)
+{ return gx_stroke_path_only(ppath, (gx_path *)0, dev, pis, params,
+ pdcolor, pcpath);
+}
+
+/* Fill a partial stroked path. Free variables: */
+/* to_path, stroke_path_body, fill_params, always_thin, pis, dev, pdevc, */
+/* pcpath, code, ppath, exit(label). */
+#define fill_stroke_path(thin)\
+if(to_path==&stroke_path_body && !gx_path_is_void_inline(&stroke_path_body))\
+{ fill_params.adjust.x = stroke_adjustment(thin, pis, x);\
+ fill_params.adjust.y = stroke_adjustment(thin, pis, y);\
+ code = gx_fill_path_only(to_path, dev, pis, &fill_params, pdevc, pcpath);\
+ gx_path_release(to_path);\
+ if ( code < 0 ) goto exit;\
+ gx_path_init(to_path, ppath->memory);\
+}
+
+/*
+ * Define the internal procedures that stroke a partial_line
+ * (the first pl_ptr argument). If both partial_lines are non-null,
+ * the procedure creates an appropriate join; otherwise, the procedure
+ * creates an end cap. If the first int is 0, the procedure also starts
+ * with an appropriate cap.
+ */
+#define stroke_line_proc(proc)\
+ int near proc(P9(gx_path *, int, pl_ptr, pl_ptr, const gx_device_color *,\
+ gx_device *, const gs_imager_state *,\
+ const gx_stroke_params *, const gs_fixed_rect *))
+typedef stroke_line_proc((*stroke_line_proc_t));
+
+private stroke_line_proc(stroke_add);
+private stroke_line_proc(stroke_fill);
+
+/* Define the orientations we handle specially. */
+typedef enum {
+ orient_other = 0,
+ orient_portrait, /* [xx 0 0 yy tx ty] */
+ orient_landscape /* [0 xy yx 0 tx ty] */
+} orientation;
+
+/* Stroke a path. If to_path != 0, append the stroke outline to it; */
+/* if to_path == 0, draw the strokes on dev. */
+int
+gx_stroke_path_only(gx_path *ppath, gx_path *to_path, gx_device *pdev,
+ const gs_imager_state *pis, const gx_stroke_params *params,
+ const gx_device_color *pdevc, const gx_clip_path *pcpath)
+{ stroke_line_proc_t line_proc =
+ (to_path == 0 ? stroke_fill : stroke_add);
+ gs_fixed_rect ibox, cbox;
+ gx_device_clip cdev;
+ gx_device *dev = pdev;
+ gx_device *save_dev = dev;
+ int code = 0;
+ gx_fill_params fill_params;
+ const gx_line_params *pgs_lp = gs_currentlineparams_inline(pis);
+ int dash_count = pgs_lp->dash.pattern_size;
+ gx_path fpath, dpath;
+ gx_path stroke_path_body;
+ const gx_path *spath;
+ float xx = pis->ctm.xx, xy = pis->ctm.xy;
+ float yx = pis->ctm.yx, yy = pis->ctm.yy;
+ /*
+ * We are dealing with a reflected coordinate system
+ * if transform(1,0) is counter-clockwise from transform(0,1).
+ * See the note in stroke_add for the algorithm.
+ */
+ int uniform;
+ bool reflected;
+ orientation orient =
+ (/*is_fzero2(xy, yx) ?
+ (uniform = (xx == yy ? 1 : xx == -yy ? -1 : 0),
+ reflected = (uniform ? uniform < 0 : (xx < 0) != (yy < 0)),
+ orient_portrait) :
+ is_fzero2(xx, yy) ?
+ (uniform = (xy == yx ? -1 : xy == -yx ? 1 : 0),
+ reflected = (uniform ? uniform < 0 : (xy < 0) == (yx < 0)),
+ orient_landscape) :*/
+ (uniform = 0,
+ reflected = xy * yx > xx * yy,
+ orient_other));
+ float line_width = pgs_lp->half_width; /* (*half* the line width) */
+ bool always_thin;
+ double line_width_and_scale, device_line_width_scale;
+ const segment *pseg;
+
+#ifdef DEBUG
+if ( gs_debug_c('o') )
+ { int count = pgs_lp->dash.pattern_size;
+ int i;
+ dprintf3("[o]half_width=%f, cap=%d, join=%d,\n",
+ pgs_lp->half_width, (int)pgs_lp->cap, (int)pgs_lp->join);
+ dprintf2(" miter_limit=%f, miter_check=%f,\n",
+ pgs_lp->miter_limit, pgs_lp->miter_check);
+ dprintf1(" dash pattern=%d", count);
+ for ( i = 0; i < count; i++ )
+ dprintf1(",%f", pgs_lp->dash.pattern[i]);
+ dprintf4(",\n offset=%f, init(ink_on=%d, index=%d, dist_left=%f)\n",
+ pgs_lp->dash.offset, pgs_lp->dash.init_ink_on,
+ pgs_lp->dash.init_index, pgs_lp->dash.init_dist_left);
+ }
+#endif
+
+ gx_path_bbox(ppath, &ibox);
+ /* Expand the path bounding box by the scaled line width. */
+ { gs_fixed_point expansion;
+ gx_stroke_expansion(pis, &expansion);
+ expansion.x += pis->fill_adjust.x;
+ expansion.y += pis->fill_adjust.y;
+ ibox.p.x -= expansion.x;
+ ibox.p.y -= expansion.y;
+ ibox.q.x += expansion.x;
+ ibox.q.y += expansion.y;
+ }
+ /* Check the expanded bounding box against the clipping regions. */
+ if ( pcpath != NULL &&
+ (gx_cpath_inner_box(pcpath, &cbox), !rect_within(ibox, cbox))
+ )
+ { /* Intersect the path box and the clip bounding box. */
+ /* If the intersection is empty, this call is a no-op. */
+ gs_fixed_rect bbox;
+
+ gx_cpath_outer_box(pcpath, &bbox);
+ if_debug4('f', " outer_box=(%g,%g),(%g,%g)\n",
+ fixed2float(bbox.p.x), fixed2float(bbox.p.y),
+ fixed2float(bbox.q.x), fixed2float(bbox.q.y));
+ rect_intersect(ibox, bbox);
+ if ( ibox.p.x >= ibox.q.x || ibox.p.y >= ibox.q.y )
+ { /* Intersection of boxes is empty! */
+ return 0;
+ }
+ /*
+ * The path is neither entirely inside the inner clip box
+ * nor entirely outside the outer clip box.
+ * If we had to flatten the path, this is where we would
+ * recompute its bbox and make the tests again,
+ * but we don't bother right now.
+ *
+ * Set up a clipping device.
+ */
+ dev = (gx_device *)&cdev;
+ gx_make_clip_device(&cdev, &cdev, &pcpath->list);
+ cdev.target = save_dev;
+ cdev.max_fill_band = save_dev->max_fill_band;
+ (*dev_proc(dev, open_device))(dev);
+ }
+ fill_params.rule = gx_rule_winding_number;
+ fill_params.flatness = pis->flatness;
+ fill_params.fill_zero_width = true;
+ if ( line_width < 0 )
+ line_width = -line_width;
+ line_width_and_scale = line_width * (double)int2fixed(1);
+ if ( is_fzero(line_width) )
+ always_thin = true;
+ else
+ { float xa, ya;
+ switch ( orient )
+ {
+ case orient_portrait:
+ xa = xx, ya = yy;
+ goto sat;
+ case orient_landscape:
+ xa = xy, ya = yx;
+sat: if ( xa < 0 ) xa = -xa;
+ if ( ya < 0 ) ya = -ya;
+ always_thin = (max(xa, ya) * line_width < 0.5);
+ if ( !always_thin && uniform )
+ { /* Precompute a value we'll need later. */
+ device_line_width_scale = line_width_and_scale * xa;
+ }
+ break;
+ default:
+ { /* The check is more complicated, but it's worth it. */
+ float xsq = xx * xx + xy * xy;
+ float ysq = yx * yx + yy * yy;
+ float cross = xx * yx + xy * yy;
+ if ( cross < 0 ) cross = 0;
+ always_thin =
+ ((max(xsq, ysq) + cross) * line_width * line_width
+ < 0.25);
+ }
+ }
+ }
+ if_debug5('o', "[o]ctm=(%g,%g,%g,%g) thin=%d\n",
+ xx, xy, yx, yy, always_thin);
+ /* Start by flattening the path. We should do this on-the-fly.... */
+ if ( !ppath->curve_count ) /* don't need to flatten */
+ { if ( !ppath->first_subpath )
+ return 0;
+ spath = ppath;
+ }
+ else
+ { if ( (code = gx_path_flatten(ppath, &fpath, params->flatness)) < 0
+ )
+ return code;
+ spath = &fpath;
+ }
+ if ( dash_count )
+ { code = gx_path_expand_dashes(spath, &dpath, pis);
+ if ( code < 0 )
+ goto exf;
+ spath = &dpath;
+ }
+ if ( to_path == 0 )
+ { /* We might try to defer this if it's expensive.... */
+ to_path = &stroke_path_body;
+ gx_path_init(to_path, ppath->memory);
+ }
+ for ( pseg = (const segment *)spath->first_subpath; pseg != 0; )
+ { int first = 0;
+ int index = 0;
+ fixed x = pseg->pt.x;
+ fixed y = pseg->pt.y;
+ bool is_closed = ((const subpath *)pseg)->is_closed;
+ partial_line pl, pl_prev, pl_first;
+ while ( (pseg = pseg->next) != 0 && pseg->type != s_start )
+ { /* Compute the width parameters in device space. */
+ /* We work with unscaled values, for speed. */
+ fixed sx = pseg->pt.x, udx = sx - x;
+ fixed sy = pseg->pt.y, udy = sy - y;
+ pl.o.p.x = x, pl.o.p.y = y;
+d: pl.e.p.x = sx, pl.e.p.y = sy;
+ if ( !(udx | udy) ) /* degenerate */
+ { /*
+ * If this is the first segment of the subpath,
+ * check the entire subpath for degeneracy.
+ * Otherwise, ignore the degenerate segment.
+ */
+ if ( index != 0 )
+ continue;
+ /* Check for a degenerate subpath. */
+ while ( (pseg = pseg->next) != 0 &&
+ pseg->type != s_start
+ )
+ { sx = pseg->pt.x, udx = sx - x;
+ sy = pseg->pt.y, udy = sy - y;
+ if ( (udx | udy) )
+ goto d;
+ }
+ /*
+ * The entire subpath is degenerate, but it includes
+ * more than one point. If we are using round caps,
+ * draw the cap, otherwise do nothing.
+ */
+ if ( pgs_lp->cap == gs_cap_round )
+ { /*
+ * Set up cdelta; don't bother with efficiency.
+ * This is the same computation as for the slow case
+ * below, except that we arbitrarily choose the
+ * direction so that dpt.x = fixed_1, dpt.y = 0.
+ */
+ double dptx = line_width_and_scale;
+ pl.e.cdelta.x = (fixed)(dptx * xx);
+ pl.e.cdelta.y = (fixed)(dptx * xy);
+ if ( !reflected )
+ dptx = -dptx;
+ pl.width.x = -(fixed)(dptx * yx);
+ pl.width.y = -(fixed)(dptx * yy);
+ pl.thin = false;
+ compute_caps(&pl);
+ /* To produce a complete dot, we need two */
+ /* round caps. */
+ code = gx_path_add_point(to_path, pl.e.co.x,
+ pl.e.co.y);
+ if ( code < 0 )
+ goto exit;
+ code = add_round_cap(to_path, &pl.e);
+ if ( code < 0 )
+ goto exit;
+ code = add_round_cap(to_path, &pl.o);
+ if ( code < 0 )
+ goto exit;
+ fill_stroke_path(false);
+ }
+ break;
+ }
+ if ( !always_thin )
+ { if ( uniform != 0 )
+ { /* We can save a lot of work in this case. */
+ /* We know orient != orient_other. */
+ float dpx = udx, dpy = udy;
+ float wl = device_line_width_scale /
+ hypot(dpx, dpy);
+ pl.e.cdelta.x = (fixed)(dpx * wl);
+ pl.e.cdelta.y = (fixed)(dpy * wl);
+ /* The width is the cap delta rotated by */
+ /* 90 degrees. */
+ pl.width.x = -pl.e.cdelta.y,
+ pl.width.y = pl.e.cdelta.x;
+ pl.thin = false; /* if not always_thin, */
+ /* then never thin. */
+ }
+ else
+ { gs_point dpt; /* unscaled */
+ float wl;
+ gs_imager_idtransform(pis,
+ (float)udx, (float)udy, &dpt);
+ wl = line_width_and_scale /
+ hypot(dpt.x, dpt.y);
+ /* Construct the width vector in */
+ /* user space, still unscaled. */
+ dpt.x *= wl;
+ dpt.y *= wl;
+ /*
+ * We now compute both perpendicular
+ * and (optionally) parallel half-widths,
+ * as deltas in device space. We use
+ * a fixed-point, unscaled version of
+ * gs_dtransform. The second computation
+ * folds in a 90-degree rotation (in user
+ * space, before transforming) in the
+ * direction that corresponds to counter-
+ * clockwise in device space.
+ */
+ pl.e.cdelta.x = (fixed)(dpt.x * xx);
+ pl.e.cdelta.y = (fixed)(dpt.y * yy);
+ if ( orient != orient_portrait )
+ pl.e.cdelta.x += (fixed)(dpt.y * yx),
+ pl.e.cdelta.y += (fixed)(dpt.x * xy);
+ if ( !reflected )
+ dpt.x = -dpt.x, dpt.y = -dpt.y;
+ pl.width.x = (fixed)(dpt.y * xx),
+ pl.width.y = -(fixed)(dpt.x * yy);
+ if ( orient != orient_portrait )
+ pl.width.x -= (fixed)(dpt.x * yx),
+ pl.width.y += (fixed)(dpt.y * xy);
+ pl.thin =
+ any_abs(pl.width.x) + any_abs(pl.width.y) <
+ float2fixed(0.75);
+ }
+ if ( !pl.thin )
+ { adjust_stroke(&pl, pis, false);
+ compute_caps(&pl);
+ }
+ }
+ else /* always_thin */
+ pl.e.cdelta.x = pl.e.cdelta.y = 0,
+ pl.width.x = pl.width.y = 0,
+ pl.thin = true;
+ if ( first++ == 0 ) pl_first = pl;
+ if ( index++ )
+ { code = (*line_proc)(to_path,
+ (is_closed ? 1 : index - 2),
+ &pl_prev, &pl, pdevc, dev, pis,
+ params, &cbox);
+ if ( code < 0 )
+ goto exit;
+ fill_stroke_path(always_thin);
+ }
+ pl_prev = pl;
+ x = sx, y = sy;
+ }
+ if ( index )
+ { /* If closed, join back to start, else cap. */
+ /* For some reason, the Borland compiler requires the cast */
+ /* in the following line. */
+ pl_ptr lptr = (is_closed ? (pl_ptr)&pl_first : (pl_ptr)0);
+ code = (*line_proc)(to_path, index - 1, &pl_prev, lptr,
+ pdevc, dev, pis, params, &cbox);
+ if ( code < 0 )
+ goto exit;
+ fill_stroke_path(always_thin);
+ }
+ }
+exit: if ( to_path == &stroke_path_body )
+ gx_path_release(to_path); /* (only needed if error) */
+ if ( dash_count )
+ gx_path_release(&dpath);
+exf: if ( ppath->curve_count )
+ gx_path_release(&fpath);
+ return code;
+}
+
+/* ------ Internal routines ------ */
+
+/* Adjust the endpoints and width of a stroke segment */
+/* to achieve more uniform rendering. */
+/* Only o.p, e.p, e.cdelta, and width have been set. */
+private void near
+adjust_stroke(pl_ptr plp, const gs_imager_state *pis, bool thin)
+{ fixed _ss *pw;
+ fixed _ss *pov;
+ fixed _ss *pev;
+ fixed w, w2;
+ fixed adj2;
+ if ( !pis->stroke_adjust && plp->width.x != 0 && plp->width.y != 0 )
+ return; /* don't adjust */
+ if ( any_abs(plp->width.x) < any_abs(plp->width.y) )
+ { /* More horizontal stroke */
+ pw = &plp->width.y, pov = &plp->o.p.y, pev = &plp->e.p.y;
+ adj2 = stroke_adjustment(thin, pis, y) << 1;
+ }
+ else
+ { /* More vertical stroke */
+ pw = &plp->width.x, pov = &plp->o.p.x, pev = &plp->e.p.x;
+ adj2 = stroke_adjustment(thin, pis, x) << 1;
+ }
+ /* Round the larger component of the width up or down, */
+ /* whichever way produces a result closer to the correct width. */
+ /* Note that just rounding the larger component */
+ /* may not produce the correct result. */
+ w = *pw;
+ w2 = fixed_rounded(w << 1); /* full line width */
+ if ( w2 == 0 && *pw != 0 )
+ { /* Make sure thin lines don't disappear. */
+ w2 = (*pw < 0 ? -fixed_1 + adj2 : fixed_1 - adj2);
+ *pw = arith_rshift_1(w2);
+ }
+ /* Only adjust the endpoints if the line is horizontal or vertical. */
+ if ( *pov == *pev )
+ { /* We're going to round the endpoint coordinates, so */
+ /* take the fill adjustment into account now. */
+ if ( w >= 0 ) w2 += adj2;
+ else w2 = adj2 - w2;
+ if ( w2 & fixed_1 ) /* odd width, move to half-pixel */
+ { *pov = *pev = fixed_floor(*pov) + fixed_half;
+ }
+ else /* even width, move to pixel */
+ { *pov = *pev = fixed_rounded(*pov);
+ }
+ }
+}
+
+/* Compute the intersection of two lines. This is a messy algorithm */
+/* that somehow ought to be useful in more places than just here.... */
+/* If the lines are (nearly) parallel, return -1 without setting *pi; */
+/* otherwise, return 0 if the intersection is beyond *pp1 and *pp2 in */
+/* the direction determined by *pd1 and *pd2, and 1 otherwise. */
+private int
+line_intersect(
+ p_ptr pp1, /* point on 1st line */
+ p_ptr pd1, /* slope of 1st line (dx,dy) */
+ p_ptr pp2, /* point on 2nd line */
+ p_ptr pd2, /* slope of 2nd line */
+ p_ptr pi) /* return intersection here */
+{ /* We don't have to do any scaling, the factors all work out right. */
+ float u1 = pd1->x, v1 = pd1->y;
+ float u2 = pd2->x, v2 = pd2->y;
+ double denom = u1 * v2 - u2 * v1;
+ float xdiff = pp2->x - pp1->x;
+ float ydiff = pp2->y - pp1->y;
+ double f1;
+ double max_result = any_abs(denom) * (double)max_fixed;
+#ifdef DEBUG
+if ( gs_debug_c('O') )
+ { dprintf4("[o]Intersect %f,%f(%f/%f)",
+ fixed2float(pp1->x), fixed2float(pp1->y),
+ fixed2float(pd1->x), fixed2float(pd1->y));
+ dprintf4(" & %f,%f(%f/%f),\n",
+ fixed2float(pp2->x), fixed2float(pp2->y),
+ fixed2float(pd2->x), fixed2float(pd2->y));
+ dprintf3("\txdiff=%f ydiff=%f denom=%f ->\n",
+ xdiff, ydiff, denom);
+ }
+#endif
+ /* Check for degenerate result. */
+ if ( any_abs(xdiff) >= max_result || any_abs(ydiff) >= max_result )
+ { /* The lines are nearly parallel, */
+ /* or one of them has zero length. Punt. */
+ if_debug0('O', "\tdegenerate!\n");
+ return -1;
+ }
+ f1 = (v2 * xdiff - u2 * ydiff) / denom;
+ pi->x = pp1->x + (fixed)(f1 * u1);
+ pi->y = pp1->y + (fixed)(f1 * v1);
+ if_debug2('O', "\t%f,%f\n",
+ fixed2float(pi->x), fixed2float(pi->y));
+ return (f1 >= 0 && (v1 * xdiff >= u1 * ydiff ? denom >= 0 : denom < 0) ? 0 : 1);
+}
+
+#define lix plp->o.p.x
+#define liy plp->o.p.y
+#define litox plp->e.p.x
+#define litoy plp->e.p.y
+
+/* Set up the width and delta parameters for a thin line. */
+/* We only approximate the width and height. */
+private void near
+set_thin_widths(register pl_ptr plp)
+{ fixed dx = litox - lix, dy = litoy - liy;
+
+#define trsign(pos, c) ((pos) ? (c) : -(c))
+ if ( any_abs(dx) > any_abs(dy) )
+ { plp->width.x = plp->e.cdelta.y = 0;
+ plp->width.y = plp->e.cdelta.x = trsign(dx >= 0, fixed_half);
+ }
+ else
+ { plp->width.y = plp->e.cdelta.x = 0;
+ plp->width.x = -(plp->e.cdelta.y = trsign(dy >= 0, fixed_half));
+ }
+#undef trsign
+}
+
+/* Draw a line on the device. */
+private int near
+stroke_fill(gx_path *ppath, int first, register pl_ptr plp, pl_ptr nplp,
+ const gx_device_color *pdevc, gx_device *dev, const gs_imager_state *pis,
+ const gx_stroke_params *params, const gs_fixed_rect *pbbox)
+{ if ( plp->thin )
+ { /* Minimum-width line, don't have to be careful. */
+ /* We do have to check for the entire line being */
+ /* within the clipping rectangle, allowing for some */
+ /* slop at the ends. */
+ fixed x0 = lix, y0 = liy;
+ fixed x1 = litox, y1 = litoy;
+ fixed t;
+
+#define slop int2fixed(2)
+ if ( x0 > x1 )
+ t = x0, x0 = x1 - slop, x1 = t + slop;
+ else
+ x0 -= slop, x1 += slop;
+ if ( y0 > y1 )
+ t = y0, y0 = y1 - slop, y1 = t + slop;
+ else
+ y0 -= slop, y1 += slop;
+#undef slop
+
+ if ( pbbox->p.x <= x0 && x1 <= pbbox->q.x &&
+ pbbox->p.y <= y0 && y1 <= pbbox->q.y
+ )
+ return (*dev_proc(dev, draw_thin_line))(dev,
+ lix, liy, litox, litoy,
+ pdevc, pis->log_op);
+ /* We didn't set up the endpoint parameters before, */
+ /* because the line was thin. stroke_add will do this. */
+ }
+ /* Check for being able to fill directly. */
+ { const gx_line_params *pgs_lp = gs_currentlineparams_inline(pis);
+ gs_line_cap cap = pgs_lp->cap;
+ gs_line_join join = pgs_lp->join;
+ if ( !plp->thin
+ && ((first != 0 && nplp != 0) || cap == gs_cap_butt
+ || cap == gs_cap_square)
+ && (join == gs_join_bevel || join == gs_join_miter ||
+ join == gs_join_none)
+ && (pis->fill_adjust.x | pis->fill_adjust.y) == 0
+ && lop_is_idempotent(pis->log_op)
+ )
+ { gs_fixed_point points[6];
+ int npoints, code;
+ gs_fixed_point *bevel = points + 2;
+
+ npoints = cap_points((first == 0 ? cap : gs_cap_butt),
+ &plp->o, points);
+ if ( nplp == 0 )
+ code = cap_points(cap, &plp->e, points + npoints);
+ else
+ code = line_join_points(pgs_lp, plp, nplp, points + npoints);
+ if ( code < 0 )
+ return code;
+ if ( nplp != 0 && join != gs_join_none )
+ { if ( join == gs_join_miter )
+ { /* Make sure we have a bevel and not a miter. */
+ if ( !(points[2].x == plp->e.co.x &&
+ points[2].y == plp->e.co.y &&
+ points[5].x == plp->e.ce.x &&
+ points[5].y == plp->e.ce.y)
+ )
+ goto fill;
+ }
+ /* Identify which 3 points define the bevel triangle. */
+ if ( points[3].x == nplp->o.p.x &&
+ points[3].y == nplp->o.p.y
+ )
+ ++bevel;
+ /* Fill the bevel. */
+ code = (*dev_proc(dev, fill_triangle))(dev,
+ bevel->x, bevel->y,
+ bevel[1].x - bevel->x,
+ bevel[1].y - bevel->y,
+ bevel[2].x - bevel->x,
+ bevel[2].y - bevel->y,
+ pdevc, pis->log_op);
+ if ( code < 0 )
+ return code;
+ }
+ /* Fill the body of the stroke. */
+ return (*dev_proc(dev, fill_parallelogram))(dev,
+ points[1].x, points[1].y,
+ points[0].x - points[1].x,
+ points[0].y - points[1].y,
+ points[2].x - points[1].x,
+ points[2].y - points[1].y,
+ pdevc, pis->log_op);
+fill: code = add_points(ppath, points, npoints + code, true);
+ if ( code < 0 )
+ return code;
+ return gx_path_close_subpath(ppath);
+ }
+ }
+ /* General case: construct a path for the fill algorithm. */
+ return stroke_add(ppath, first, plp, nplp, pdevc, dev, pis, params,
+ pbbox);
+}
+
+#undef lix
+#undef liy
+#undef litox
+#undef litoy
+
+/* Add a segment to the path. This handles all the complex cases. */
+private int near
+stroke_add(gx_path *ppath, int first, register pl_ptr plp, pl_ptr nplp,
+ const gx_device_color *pdevc, gx_device *dev, const gs_imager_state *pis,
+ const gx_stroke_params *params, const gs_fixed_rect *ignore_pbbox)
+{ const gx_line_params *pgs_lp = gs_currentlineparams_inline(pis);
+ gs_fixed_point points[8];
+ int npoints;
+ int code;
+ bool moveto_first = true;
+
+ if ( plp->thin )
+ { /* We didn't set up the endpoint parameters before, */
+ /* because the line was thin. Do it now. */
+ set_thin_widths(plp);
+ adjust_stroke(plp, pis, true);
+ compute_caps(plp);
+ }
+ /* Create an initial cap if desired. */
+ if ( first == 0 && pgs_lp->cap == gs_cap_round )
+ { if ( (code = gx_path_add_point(ppath, plp->o.co.x, plp->o.co.y)) < 0 ||
+ (code = add_round_cap(ppath, &plp->o)) < 0
+ )
+ return code;
+ npoints = 0;
+ moveto_first = false;
+ }
+ else
+ { if ( (npoints = cap_points((first == 0 ? pgs_lp->cap : gs_cap_butt), &plp->o, points)) < 0 )
+ return npoints;
+ }
+ if ( nplp == 0 )
+ { /* Add a final cap. */
+ if ( pgs_lp->cap == gs_cap_round )
+ { assign_point(&points[npoints], plp->e.co);
+ ++npoints;
+ if ( (code = add_points(ppath, points, npoints, moveto_first)) < 0 )
+ return code;
+ code = add_round_cap(ppath, &plp->e);
+ goto done;
+ }
+ code = cap_points(pgs_lp->cap, &plp->e, points + npoints);
+ }
+ else if ( pgs_lp->join == gs_join_round )
+ { assign_point(&points[npoints], plp->e.co);
+ ++npoints;
+ if ( (code = add_points(ppath, points, npoints, moveto_first)) < 0 )
+ return code;
+ code = add_round_cap(ppath, &plp->e);
+ goto done;
+ }
+ else if ( nplp->thin ) /* no join */
+ code = cap_points(gs_cap_butt, &plp->e, points + npoints);
+ else /* non-round join */
+ code = line_join_points(pgs_lp, plp, nplp, points + npoints);
+ if ( code < 0 )
+ return code;
+ code = add_points(ppath, points, npoints + code, moveto_first);
+done: if ( code < 0 )
+ return code;
+ return gx_path_close_subpath(ppath);
+}
+
+/* Add lines with a possible initial moveto. */
+private int near
+add_points(gx_path *ppath, const gs_fixed_point _ss *points, int npoints,
+ bool moveto_first)
+{ if ( moveto_first )
+ { int code = gx_path_add_point(ppath, points[0].x, points[0].y);
+ if ( code < 0 )
+ return code;
+ return gx_path_add_lines(ppath, points + 1, npoints - 1);
+ }
+ else
+ return gx_path_add_lines(ppath, points, npoints);
+}
+
+/* ---------------- Join computation ---------------- */
+
+/* Compute the points for a bevel, miter, or triangle join. */
+private int near
+line_join_points(const gx_line_params *pgs_lp, pl_ptr plp, pl_ptr nplp,
+ gs_fixed_point _ss *join_points)
+{ int num_points;
+#define jp1 join_points[0]
+#define np1 join_points[1]
+#define np2 join_points[2]
+#define jp2 join_points[3]
+#define jpx join_points[4]
+ /*
+ * Set np to whichever of nplp->o.co or .ce is outside
+ * the current line. We observe that the point (x2,y2)
+ * is counter-clockwise from (x1,y1), relative to the origin,
+ * iff
+ * (arctan(y2/x2) - arctan(y1/x1)) mod 2*pi < pi,
+ * taking the signs of xi and yi into account to determine
+ * the quadrants of the results. It turns out that
+ * even though arctan is monotonic only in the 4th/1st
+ * quadrants and the 2nd/3rd quadrants, case analysis on
+ * the signs of xi and yi demonstrates that this test
+ * is equivalent to the much less expensive test
+ * x1 * y2 > x2 * y1
+ * in all cases.
+ *
+ * In the present instance, x1,y1 are plp->width,
+ * x2,y2 are nplp->width, and the origin is
+ * their common point (plp->e.p, nplp->o.p).
+ */
+ /* We make the test using double arithmetic only because */
+ /* the !@#&^*% C language doesn't give us access to */
+ /* the double-width-result multiplication operation */
+ /* that almost all CPUs provide! */
+ bool ccw =
+ (double)(plp->width.x) * /* x1 */
+ (nplp->width.y) > /* y2 */
+ (double)(nplp->width.x) * /* x2 */
+ (plp->width.y);
+ p_ptr outp, np;
+
+ /* Initialize for a bevel join. */
+ assign_point(&jp1, plp->e.co);
+
+ if ( pgs_lp->join == gs_join_none )
+ { /* No join at all, things are simple. */
+ assign_point(&join_points[1], plp->e.ce);
+ return 2;
+ }
+
+ assign_point(&jp2, plp->e.ce);
+
+ /* Because of stroke adjustment, it is possible that */
+ /* plp->e.p != nplp->o.p. For that reason, we must use */
+ /* nplp->o.p as np1 or np2. */
+ if ( !ccw )
+ { outp = &jp2;
+ assign_point(&np2, nplp->o.co);
+ assign_point(&np1, nplp->o.p);
+ np = &np2;
+ }
+ else
+ { outp = &jp1;
+ assign_point(&np1, nplp->o.ce);
+ assign_point(&np2, nplp->o.p);
+ np = &np1;
+ }
+ if_debug1('O', "[o]use %s\n", (ccw ? "co (ccw)" : "ce (cw)"));
+
+ /* Handle triangular joins now. */
+ if ( pgs_lp->join == gs_join_triangle )
+ { fixed tpx = outp->x - nplp->o.p.x + np->x;
+ fixed tpy = outp->y - nplp->o.p.y + np->y;
+ assign_point(&jpx, jp2);
+ if ( !ccw )
+ { /* Insert tp between np2 and jp2. */
+ jp2.x = tpx, jp2.y = tpy;
+ }
+ else
+ { /* Insert tp between jp1 and np1. */
+ assign_point(&jp2, np2);
+ assign_point(&np2, np1);
+ np1.x = tpx, np1.y = tpy;
+ }
+ num_points = 5;
+ goto jadd;
+ }
+ num_points = 4;
+
+ /* Don't bother with the miter check if the two */
+ /* points to be joined are very close together, */
+ /* namely, in the same square half-pixel. */
+ if ( pgs_lp->join == gs_join_miter &&
+ !(fixed2long(outp->x << 1) == fixed2long(np->x << 1) &&
+ fixed2long(outp->y << 1) == fixed2long(np->y << 1))
+ )
+ { /*
+ * Check whether a miter join is appropriate.
+ * Let a, b be the angles of the two lines.
+ * We check tan(a-b) against the miter_check
+ * by using the following formula:
+ * If tan(a)=u1/v1 and tan(b)=u2/v2, then
+ * tan(a-b) = (u1*v2 - u2*v1) / (u1*u2 + v1*v2).
+ * We can do all the computations unscaled,
+ * because we're only concerned with ratios.
+ */
+ float u1 = plp->e.cdelta.y, v1 = plp->e.cdelta.x;
+ float u2 = nplp->o.cdelta.y, v2 = nplp->o.cdelta.x;
+ double num = u1 * v2 - u2 * v1;
+ double denom = u1 * u2 + v1 * v2;
+ float check = pgs_lp->miter_check;
+ /*
+ * We will want either tan(a-b) or tan(b-a)
+ * depending on the orientations of the lines.
+ * Fortunately we know the relative orientations already.
+ */
+ if ( !ccw ) /* have plp - nplp, want vice versa */
+ num = -num;
+#ifdef DEBUG
+if ( gs_debug_c('O') )
+ { dprintf4("[o]Miter check: u1/v1=%f/%f, u2/v2=%f/%f,\n",
+ u1, v1, u2, v2);
+ dprintf3(" num=%f, denom=%f, check=%f\n",
+ num, denom, check);
+ }
+#endif
+ /*
+ * If we define T = num / denom, then we want to use
+ * a miter join iff arctan(T) >= arctan(check).
+ * We know that both of these angles are in the 1st
+ * or 2nd quadrant, and since arctan is monotonic
+ * within each quadrant, we can do the comparisons
+ * on T and check directly, taking signs into account
+ * as follows:
+ * sign(T) sign(check) atan(T) >= atan(check)
+ * ------- ----------- ----------------------
+ * + + T >= check
+ * - + true
+ * + - false
+ * - - T >= check
+ */
+ if ( denom < 0 )
+ num = -num, denom = -denom;
+ /* Now denom >= 0, so sign(num) = sign(T). */
+ if ( check > 0 ?
+ (num < 0 || num >= denom * check) :
+ (num < 0 && num >= denom * check)
+ )
+ { /* OK to use a miter join. */
+ gs_fixed_point mpt;
+ if_debug0('O', " ... passes.\n");
+ /* Compute the intersection of */
+ /* the extended edge lines. */
+ if ( line_intersect(outp, &plp->e.cdelta, np,
+ &nplp->o.cdelta, &mpt) == 0
+ )
+ assign_point(outp, mpt);
+ }
+ }
+jadd: return num_points;
+}
+/* ---------------- Cap computations ---------------- */
+
+/* Compute the endpoints of the two caps of a segment. */
+/* Only o.p, e.p, width, and cdelta have been set. */
+private void near
+compute_caps(register pl_ptr plp)
+{ fixed wx2 = plp->width.x;
+ fixed wy2 = plp->width.y;
+ plp->o.co.x = plp->o.p.x + wx2, plp->o.co.y = plp->o.p.y + wy2;
+ plp->o.cdelta.x = -plp->e.cdelta.x,
+ plp->o.cdelta.y = -plp->e.cdelta.y;
+ plp->o.ce.x = plp->o.p.x - wx2, plp->o.ce.y = plp->o.p.y - wy2;
+ plp->e.co.x = plp->e.p.x - wx2, plp->e.co.y = plp->e.p.y - wy2;
+ plp->e.ce.x = plp->e.p.x + wx2, plp->e.ce.y = plp->e.p.y + wy2;
+#ifdef DEBUG
+if ( gs_debug_c('O') )
+ dprintf4("[o]Stroke o=(%f,%f) e=(%f,%f)\n",
+ fixed2float(plp->o.p.x), fixed2float(plp->o.p.y),
+ fixed2float(plp->e.p.x), fixed2float(plp->e.p.y)),
+ dprintf4("\twxy=(%f,%f) lxy=(%f,%f)\n",
+ fixed2float(wx2), fixed2float(wy2),
+ fixed2float(plp->e.cdelta.x), fixed2float(plp->e.cdelta.y));
+#endif
+}
+
+#define px endp->p.x
+#define py endp->p.y
+#define xo endp->co.x
+#define yo endp->co.y
+#define xe endp->ce.x
+#define ye endp->ce.y
+#define cdx endp->cdelta.x
+#define cdy endp->cdelta.y
+
+/* Add a round cap to a path. */
+/* Assume the current point is the cap origin (endp->co). */
+private int near
+add_round_cap(gx_path *ppath, const_ep_ptr endp)
+{ fixed xm = px + cdx;
+ fixed ym = py + cdy;
+ int code;
+
+ if ( (code = gx_path_add_partial_arc(ppath, xm, ym,
+ xo + cdx, yo + cdy, quarter_arc_fraction)) < 0 ||
+ (code = gx_path_add_partial_arc(ppath, xe, ye,
+ xe + cdx, ye + cdy, quarter_arc_fraction)) < 0
+ )
+ return code;
+ return 0;
+}
+
+/* Compute the points for a non-round cap. */
+/* Return the number of points. */
+private int near
+cap_points(gs_line_cap type, const_ep_ptr endp,
+ gs_fixed_point _ss *pts /* [3] */)
+{
+#define put_point(i, px, py)\
+ pts[i].x = (px), pts[i].y = (py)
+ switch ( type )
+ {
+ case gs_cap_butt:
+ put_point(0, xo, yo);
+ put_point(1, xe, ye);
+ return 2;
+ case gs_cap_square:
+ put_point(0, xo + cdx, yo + cdy);
+ put_point(1, xe + cdx, ye + cdy);
+ return 2;
+ case gs_cap_triangle: /* (not supported by PostScript) */
+ put_point(0, xo, yo);
+ put_point(1, px + cdx, py + cdy);
+ put_point(2, xe, ye);
+ return 3;
+ default: /* can't happen */
+ return_error(gs_error_unregistered);
+ }
+#undef put_point
+}
diff --git a/pstoraster/gxtmap.h b/pstoraster/gxtmap.h
new file mode 100644
index 000000000..22e219e4e
--- /dev/null
+++ b/pstoraster/gxtmap.h
@@ -0,0 +1,42 @@
+/* Copyright (C) 1994 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxtmap.h */
+/* Definition of transfer mapping function */
+/* (also used for black generation and undercolor removal) */
+
+#ifndef gxtmap_INCLUDED
+# define gxtmap_INCLUDED
+
+/* Common definition for mapping procedures. */
+/* These are used for transfer functions, black generation, */
+/* and undercolor removal. */
+/* gx_transfer_map should probably be renamed gx_mapping_cache.... */
+
+/* Define an abstract type for a transfer map. */
+typedef struct gx_transfer_map_s gx_transfer_map;
+
+/* Define the type of a mapping procedure. */
+typedef float (*gs_mapping_proc)(P2(floatp, const gx_transfer_map *));
+
+#endif /* gxtmap_INCLUDED */
diff --git a/pstoraster/gxtype1.h b/pstoraster/gxtype1.h
new file mode 100644
index 000000000..20ad19280
--- /dev/null
+++ b/pstoraster/gxtype1.h
@@ -0,0 +1,222 @@
+/* Copyright (C) 1990, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxtype1.h */
+/* Private Adobe Type 1 font definitions for Ghostscript library */
+#include "gscrypt1.h"
+#include "gstype1.h"
+
+/* This file defines the structures for the state of a Type 1 interpreter. */
+
+/*
+ * Because of oversampling, one pixel in the Type 1 interpreter may
+ * correspond to several device pixels. This is also true of the hint data,
+ * since the CTM reflects the transformation to the oversampled space.
+ * To help keep the font level hints separated from the character level hints,
+ * we store the scaling factor separately with each set of hints.
+ */
+typedef struct pixel_scale_s {
+ fixed unit; /* # of pixels per device pixel */
+ fixed half; /* unit / 2 */
+ int log2_unit; /* log2(unit / fixed_1) */
+} pixel_scale;
+typedef struct point_scale_s {
+ pixel_scale x, y;
+} point_scale;
+#define set_pixel_scale(pps, log2)\
+ (pps)->unit = ((pps)->half = fixed_half << ((pps)->log2_unit = log2)) << 1
+#define scaled_rounded(v, pps)\
+ (((v) + (pps)->half) & -(pps)->unit)
+
+/* ------ Font level hints ------ */
+
+/* Define the standard stem width tables. */
+/* Each table is sorted, since the StemSnap arrays are sorted. */
+#define max_snaps (1 + max_StemSnap)
+typedef struct {
+ int count;
+ fixed data[max_snaps];
+} stem_snap_table;
+
+/* Define the alignment zone structure. */
+/* These are in device coordinates also. */
+#define max_a_zones (max_BlueValues + max_OtherBlues)
+typedef struct {
+ int is_top_zone;
+ fixed v0, v1; /* range for testing */
+ fixed flat; /* flat position */
+} alignment_zone;
+
+/* Define the structure for hints that depend only on the font and CTM, */
+/* not on the individual character. Eventually these should be cached */
+/* with the font/matrix pair. */
+typedef struct font_hints_s {
+ bool axes_swapped; /* true if x & y axes interchanged */
+ /* (only set if using hints) */
+ bool x_inverted, y_inverted; /* true if axis is inverted */
+ bool use_x_hints; /* true if we should use hints */
+ /* for char space x coords (vstem) */
+ bool use_y_hints; /* true if we should use hints */
+ /* for char space y coords (hstem) */
+ point_scale scale; /* oversampling scale */
+ stem_snap_table snap_h; /* StdHW, StemSnapH */
+ stem_snap_table snap_v; /* StdVW, StemSnapV */
+ fixed blue_fuzz, blue_shift; /* alignment zone parameters */
+ /* in device pixels */
+ bool suppress_overshoot; /* (computed from BlueScale) */
+ int a_zone_count; /* # of alignment zones */
+ alignment_zone a_zones[max_a_zones]; /* the alignment zones */
+} font_hints;
+
+/* ------ Character level hints ------ */
+
+/* Define the stem hint tables. */
+/* Each stem hint table is kept sorted. */
+/* Stem hints are in device coordinates. */
+/* We must set max_stems large enough to handle complex Asian characters. */
+#define max_stems 16 /* arbitrary */
+typedef struct {
+ fixed v0, v1; /* coordinates (widened a little) */
+ fixed dv0, dv1; /* adjustment values */
+} stem_hint;
+typedef struct {
+ int count;
+ int current; /* cache cursor for search */
+ stem_hint data[max_stems];
+} stem_hint_table;
+
+/* ------ Interpreter state ------ */
+
+/* Define the control state of the interpreter. */
+/* This is what must be saved and restored */
+/* when calling a CharString subroutine. */
+typedef struct {
+ const byte *ip;
+ crypt_state dstate;
+ gs_const_string char_string; /* original CharString or Subr, */
+ /* for GC */
+} ip_state;
+
+#ifndef gx_path_DEFINED
+# define gx_path_DEFINED
+typedef struct gx_path_s gx_path;
+#endif
+
+#ifndef segment_DEFINED
+# define segment_DEFINED
+typedef struct segment_s segment;
+#endif
+
+/* This is the full state of the Type 1 interpreter. */
+#define ostack_size 24 /* per documentation */
+#define ipstack_size 10 /* per documentation */
+struct gs_type1_state_s {
+ /* The following are set at initialization */
+ gs_font_type1 *pfont; /* font-specific data */
+ gs_imager_state *pis; /* imager state */
+ gx_path *path; /* path for appending */
+ bool charpath_flag; /* false if show, true if charpath */
+ int paint_type; /* 0/3 for fill, 1/2 for stroke */
+ fixed_coeff fc; /* cached fixed coefficients */
+ float flatness; /* flatness for character curves */
+ point_scale scale; /* oversampling scale */
+ font_hints fh; /* font-level hints */
+ gs_fixed_point origin; /* character origin */
+ /* The following are updated dynamically */
+ fixed ostack[ostack_size]; /* the Type 1 operand stack */
+ int os_count; /* # of occupied stack entries */
+ ip_state ipstack[ipstack_size+1]; /* control stack */
+ int ips_count; /* # of occupied entries */
+ int init_done; /* -1 if not done & not needed, */
+ /* 0 if not done & needed, 1 if done */
+ bool sb_set; /* true if lsb is preset */
+ bool width_set; /* true if width is set (for */
+ /* seac components) */
+ gs_fixed_point lsb; /* left side bearing (char coords) */
+ gs_fixed_point width; /* character width (char coords) */
+ int seac_base; /* base character code for seac, */
+ /* or -1 */
+ gs_fixed_point adxy; /* seac accent displacement, */
+ /* needed to adjust currentpoint */
+ fixed asb_diff; /* seac asb - accented char lsb.x, */
+ /* needed to adjust Flex endpoint */
+ gs_fixed_point position; /* save unadjusted position */
+ /* when returning temporarily */
+ /* to caller */
+ int flex_path_was_open; /* record whether path was open */
+ /* at start of Flex section */
+#define flex_max 8
+ gs_fixed_point flex_points[flex_max]; /* points for Flex */
+ int flex_count;
+ int ignore_pops; /* # of pops to ignore (after */
+ /* a known othersubr call) */
+ /* The following are set dynamically. */
+#define dotsection_in 0
+#define dotsection_out (-1)
+ int dotsection_flag; /* 0 if inside dotsection, */
+ /* -1 if outside */
+ bool vstem3_set; /* true if vstem3 seen */
+ gs_fixed_point vs_offset; /* device space offset for centering */
+ /* middle stem of vstem3 */
+ int hints_initial; /* hints applied to initial point */
+ /* of subpath */
+ gs_fixed_point unmoved_start; /* original initial point of subpath */
+ segment *hint_next; /* last segment where hints have */
+ /* been applied, 0 means none of */
+ /* current subpath has been hinted */
+ int hints_pending; /* hints applied to end of hint_next */
+ gs_fixed_point unmoved_end; /* original hint_next->pt */
+ stem_hint_table hstem_hints; /* horizontal stem hints */
+ stem_hint_table vstem_hints; /* vertical stem hints */
+};
+extern_st(st_gs_type1_state);
+#define public_st_gs_type1_state() /* in gstype1.c */\
+ gs_public_st_composite(st_gs_type1_state, gs_type1_state, "gs_type1_state",\
+ gs_type1_state_enum_ptrs, gs_type1_state_reloc_ptrs)
+
+
+/* ------ Interface between main Type 1 interpreter and hint routines ------ */
+
+/* Font level hints */
+void reset_font_hints(P2(font_hints *, const gs_log2_scale_point *));
+void compute_font_hints(P4(font_hints *, const gs_matrix_fixed *,
+ const gs_log2_scale_point *,
+ const gs_type1_data *));
+/* Character level hints */
+void reset_stem_hints(P1(gs_type1_state *)),
+ update_stem_hints(P1(gs_type1_state *)),
+ type1_apply_path_hints(P3(gs_type1_state *, bool, gx_path *)),
+#define apply_path_hints(pcis, closing)\
+ type1_apply_path_hints(pcis, closing, pcis->path)
+ type1_do_hstem(P4(gs_type1_state *, fixed, fixed,
+ const gs_matrix_fixed *)),
+#define type1_hstem(pcis, y, dy)\
+ type1_do_hstem(pcis, y, dy, &(pcis)->pis->ctm)
+ type1_do_vstem(P4(gs_type1_state *, fixed, fixed,
+ const gs_matrix_fixed *)),
+#define type1_vstem(pcis, x, dx)\
+ type1_do_vstem(pcis, x, dx, &(pcis)->pis->ctm)
+ type1_do_center_vstem(P4(gs_type1_state *, fixed, fixed,
+ const gs_matrix_fixed *));
+#define center_vstem(pcis, x0, dx)\
+ type1_do_center_vstem(pcis, x0, dx, &(pcis)->pis->ctm)
diff --git a/pstoraster/gxxfont.h b/pstoraster/gxxfont.h
new file mode 100644
index 000000000..90a6e6436
--- /dev/null
+++ b/pstoraster/gxxfont.h
@@ -0,0 +1,172 @@
+/* Copyright (C) 1992, 1993, 1994 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gxxfont.h */
+/* External font interface for Ghostscript library */
+#include "gsccode.h"
+#include "gsmatrix.h"
+#include "gsuid.h"
+#include "gsxfont.h"
+
+/*
+ * Design issues for external fonts
+ *
+ * 1. Where do xfonts come from: a device or a font service?
+ *
+ * 2. Is a given xfont associated with a particular device, or with a
+ * class of devices, which may have different output media?
+ * (Specifically, Windows displays vs. printers.)
+ *
+ * 3. Is an xfont a handle that must be interpreted by its originator,
+ * or an object with its own set of operations?
+ *
+ * 4. Are xfonts always transformation-specific, or is there such a thing
+ * as a scalable xfont?
+ *
+ * 5. What is the meaning of the transformation matrix supplied when
+ * asking for an xfont?
+ *
+ * Answers (for the current design)
+ *
+ * 1. Devices supply xfonts. Internal devices (image, null, clipping,
+ * command list, tracing) forward font requests to a real underlying
+ * device. File format devices should do the same, but right now
+ * they don't.
+ *
+ * 2. An xfont is not associated with anything: it just provides bitmaps.
+ * Since xfonts are only used at small sizes and low resolutions,
+ * tuning differences for different output media aren't likely to be
+ * an issue.
+ *
+ * 3. Xfonts are objects. They are allocated by their originator, and
+ * (currently) only freed by `restore'.
+ *
+ * 4. Xfonts are always transformation-specific. This may lead to some
+ * clutter, but it's very unlikely that a document will have enough
+ * different transformed versions of a single font for this to be a
+ * problem in practice.
+ *
+ * 5. The transformation matrix is the CTM within the BuildChar or BuildGlyph
+ * procedure. This maps a 1000x1000 square to the intended character size
+ * (assuming the base font uses the usual 1000-unit scaling).
+ */
+
+/* The definitions for xfonts are very similar to those for devices. */
+
+/* Structure for generic xfonts. */
+typedef struct gx_xfont_common_s {
+ gx_xfont_procs *procs;
+} gx_xfont_common;
+/* A generic xfont. */
+struct gx_xfont_s {
+ gx_xfont_common common;
+};
+
+/* Definition of xfont procedures. */
+
+struct gx_xfont_procs_s {
+
+ /* Look up a font name, UniqueID, and matrix, and return */
+ /* an xfont. */
+
+ /* NOTE: even though this is defined as an xfont_proc, */
+ /* it is actually a `factory' procedure, the only one that */
+ /* does not take an xfont * as its first argument. */
+
+#define xfont_proc_lookup_font(proc)\
+ gx_xfont *proc(P7(gx_device *dev, const byte *fname, uint len,\
+ int encoding_index, const gs_uid *puid, const gs_matrix *pmat,\
+ gs_memory_t *mem))
+ xfont_proc_lookup_font((*lookup_font));
+
+ /* Convert a character name to an xglyph code. */
+ /* encoding_index is 0 for StandardEncoding, */
+ /* 1 for ISOLatin1Encoding, 2 for SymbolEncoding, */
+ /* and -1 for any other encoding. Either chr or glyph */
+ /* may be absent (gs_no_char/glyph), but not both. */
+ /* OBSOLETE as of release 3.43, but still supported. */
+
+#define xfont_proc_char_xglyph(proc)\
+ gx_xglyph proc(P5(gx_xfont *xf, gs_char chr, int encoding_index,\
+ gs_glyph glyph, gs_proc_glyph_name((*glyph_name))))
+ xfont_proc_char_xglyph((*char_xglyph));
+
+ /* Get the metrics for a character. */
+ /* Note: pwidth changed in release 2.9.7. */
+
+#define xfont_proc_char_metrics(proc)\
+ int proc(P5(gx_xfont *xf, gx_xglyph xg, int wmode,\
+ gs_point *pwidth, gs_int_rect *pbbox))
+ xfont_proc_char_metrics((*char_metrics));
+
+ /* Render a character. */
+ /* (x,y) corresponds to the character origin. */
+ /* The target may be any Ghostscript device. */
+
+#define xfont_proc_render_char(proc)\
+ int proc(P7(gx_xfont *xf, gx_xglyph xg, gx_device *target,\
+ int x, int y, gx_color_index color, int required))
+ xfont_proc_render_char((*render_char));
+
+ /* Release any external resources associated with an xfont. */
+ /* If mprocs is not NULL, also free any storage */
+ /* allocated by lookup_font (including the xfont itself). */
+
+#define xfont_proc_release(proc)\
+ int proc(P2(gx_xfont *xf, gs_memory_t *mem))
+ xfont_proc_release((*release));
+
+ /* Convert a character name to an xglyph code. */
+ /* This is the same as char_xglyph, except that */
+ /* it takes a vector of callback procedures. */
+ /* (New in release 3.43.) */
+
+#define xfont_proc_char_xglyph2(proc)\
+ gx_xglyph proc(P5(gx_xfont *xf, gs_char chr, int encoding_index,\
+ gs_glyph glyph, const gx_xfont_callbacks *callbacks))
+ xfont_proc_char_xglyph2((*char_xglyph2));
+
+};
+
+/*
+ * Since xfonts are garbage-collectable, they need structure descriptors.
+ * Fortunately, the common part of an xfont contains no pointers to
+ * GC-managed space, so simple xfonts can use gs_private_st_simple.
+ * The following macro will serve for an xfont with only one pointer,
+ * to its device:
+ */
+#define gs__st_dev_ptrs1(scope_st, stname, stype, sname, penum, preloc, de)\
+ private ENUM_PTRS_BEGIN(penum) return 0;\
+ case 0: *pep = gx_device_enum_ptr((gx_device *)(((stype *)vptr)->de)); break;\
+ ENUM_PTRS_END\
+ private RELOC_PTRS_BEGIN(preloc) ;\
+ ((stype *)vptr)->de = (void *)gx_device_reloc_ptr((gx_device *)(((stype *)vptr)->de), gcst);\
+ RELOC_PTRS_END\
+ gs__st_composite_only(scope_st, stname, stype, sname, penum, preloc)
+/*
+ * We probably don't ever want xfont descriptors to be public....
+#define gs_public_st_dev_ptrs1(stname, stype, sname, penum, preloc, de)\
+ gs__st_dev_ptrs1(public_st, stname, stype, sname, penum, preloc, de)
+ */
+#define gs_private_st_dev_ptrs1(stname, stype, sname, penum, preloc, de)\
+ gs__st_dev_ptrs1(private_st, stname, stype, sname, penum, preloc, de)
diff --git a/pstoraster/gzacpath.h b/pstoraster/gzacpath.h
new file mode 100644
index 000000000..550dd3d7c
--- /dev/null
+++ b/pstoraster/gzacpath.h
@@ -0,0 +1,44 @@
+/* Copyright (C) 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gzacpath.h */
+/* Private representation of clipping path accumulator */
+/* Requires gxdevice.h, gzcpath.h */
+
+/* Device for accumulating a rectangle list. */
+typedef struct gx_device_cpath_accum_s {
+ gx_device_common;
+ gs_memory_t *list_memory; /* set by client */
+ gs_int_rect bbox;
+ gx_clip_list list;
+} gx_device_cpath_accum;
+
+/* Start accumulating a clipping path. */
+void gx_cpath_accum_begin(P2(gx_device_cpath_accum *padev, gs_memory_t *mem));
+
+/* Finish accumulating a clipping path. */
+int gx_cpath_accum_end(P2(const gx_device_cpath_accum *padev,
+ gx_clip_path *pcpath));
+
+/* Discard an accumulator in case of error. */
+void gx_cpath_accum_discard(P1(gx_device_cpath_accum *padev));
diff --git a/pstoraster/gzcpath.h b/pstoraster/gzcpath.h
new file mode 100644
index 000000000..43bce459f
--- /dev/null
+++ b/pstoraster/gzcpath.h
@@ -0,0 +1,52 @@
+/* Copyright (C) 1994, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gzcpath.h */
+/* Private representation of clipping paths for Ghostscript library */
+/* Requires gzpath.h. */
+#include "gxcpath.h"
+
+/* gx_clip_path is a 'subclass' of gx_path. */
+struct gx_clip_path_s {
+ gx_path path;
+ int rule; /* rule for insideness of path */
+ /* Anything within the inner_box is guaranteed to fall */
+ /* entirely within the clipping path. */
+ gs_fixed_rect inner_box;
+ /* Anything outside the outer_box is guaranteed to fall */
+ /* entirely outside the clipping path. This is the same */
+ /* as the path bounding box, widened to pixel boundaries. */
+ gs_fixed_rect outer_box;
+ gx_clip_list list;
+ char segments_valid; /* segment representation is valid */
+ char shares_list; /* if true, this path shares its */
+ /* clip list storage with the one in */
+ /* the previous saved graphics state */
+ /* The id changes whenever the clipping region changes. */
+ gs_id id;
+};
+extern_st(st_clip_path);
+#define public_st_clip_path() /* in gxcpath.c */\
+ gs_public_st_composite(st_clip_path, gx_clip_path, "clip_path",\
+ clip_path_enum_ptrs, clip_path_reloc_ptrs)
+#define st_clip_path_max_ptrs (st_path_max_ptrs + st_clip_list_max_ptrs)
diff --git a/pstoraster/gzht.h b/pstoraster/gzht.h
new file mode 100644
index 000000000..43e8bccc1
--- /dev/null
+++ b/pstoraster/gzht.h
@@ -0,0 +1,147 @@
+/* Copyright (C) 1989, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gzht.h */
+/* Private halftone representation for Ghostscript */
+/* Requires gxdevice.h, gxdcolor.h */
+#include "gxht.h"
+#include "gxfmap.h"
+#include "gxdht.h"
+#include "gxhttile.h"
+
+/* Sort a sampled halftone order by sample value. */
+void gx_sort_ht_order(P2(gx_ht_bit *, uint));
+
+/* (Internal) procedures for constructing halftone orders. */
+int gx_ht_alloc_order(P6(gx_ht_order *porder, uint width, uint height,
+ uint strip_shift, uint num_levels, gs_memory_t *mem));
+void gx_ht_construct_spot_order(P1(gx_ht_order *));
+void gx_ht_construct_threshold_order(P2(gx_ht_order *, const byte *));
+void gx_ht_construct_bits(P1(gx_ht_order *));
+
+/* Halftone enumeration structure */
+struct gs_screen_enum_s {
+ gs_halftone halftone; /* supplied by client */
+ gx_ht_order order;
+ gs_matrix mat; /* for mapping device x,y to rotated cell */
+ int x, y;
+ int strip, shift;
+ gs_state *pgs;
+};
+#define private_st_gs_screen_enum() /* in gshtscr.c */\
+ gs_private_st_composite(st_gs_screen_enum, gs_screen_enum,\
+ "gs_screen_enum", screen_enum_enum_ptrs, screen_enum_reloc_ptrs)
+/* order.levels, order.bits, pgs)*/
+
+/* Prepare a device halftone for installation, but don't install it. */
+int gs_sethalftone_prepare(P3(gs_state *, gs_halftone *,
+ gx_device_halftone *));
+
+/* Allocate and initialize a spot screen. */
+/* This is the first half of gs_screen_init_accurate. */
+int gs_screen_order_init(P4(gx_ht_order *, const gs_state *,
+ gs_screen_halftone *, bool));
+
+/* Prepare to sample a spot screen. */
+/* This is the second half of gs_screen_init_accurate. */
+int gs_screen_enum_init(P4(gs_screen_enum *, const gx_ht_order *,
+ gs_state *, gs_screen_halftone *));
+
+/*
+ * We don't want to remember all the values of the halftone screen,
+ * because they would take up space proportional to P^3, where P is
+ * the number of pixels in a cell. Instead, we pick some number N of
+ * patterns to cache. Each cache slot covers a range of (P+1)/N
+ * different gray levels: we "slide" the contents of the slot back and
+ * forth within this range by incrementally adding and dropping 1-bits.
+ * N>=0 (obviously); N<=P+1 (likewise); also, so that we can simplify things
+ * by preallocating the bookkeeping information for the cache, we define
+ * a constant max_cached_tiles which is an a priori maximum value for N.
+ *
+ * Note that the raster for each tile must be a multiple of bitmap_align_mod,
+ * to satisfy the copy_mono device routine, even though a multiple of
+ * sizeof(ht_mask_t) would otherwise be sufficient.
+ */
+
+struct gx_ht_cache_s {
+ /* The following are set when the cache is created. */
+ byte *bits; /* the base of the bits */
+ uint bits_size; /* the space available for bits */
+ gx_ht_tile *ht_tiles; /* the base of the tiles */
+ uint num_tiles; /* the number of tiles allocated */
+ /* The following are reset each time the cache is initialized */
+ /* for a new screen. */
+ gx_ht_order order; /* the cached order vector */
+ int num_cached; /* actual # of cached tiles */
+ int levels_per_tile; /* # of levels per cached tile */
+ gx_bitmap_id base_id; /* the base id, to which */
+ /* we add the halftone level */
+};
+/* We don't mark from the tiles pointer, and we relocate the tiles en masse. */
+#define private_st_ht_tiles() /* in gxht.c */\
+ gs_private_st_composite(st_ht_tiles, gx_ht_tile, "ht tiles",\
+ ht_tiles_enum_ptrs, ht_tiles_reloc_ptrs)
+#define private_st_ht_cache() /* in gxht.c */\
+ gs_private_st_ptrs_add2(st_ht_cache, gx_ht_cache, "ht cache",\
+ ht_cache_enum_ptrs, ht_cache_reloc_ptrs,\
+ st_ht_order, order, bits, ht_tiles)
+
+/* Compute a fractional color for dithering, the correctly rounded */
+/* quotient f * max_gx_color_value / maxv. */
+#define frac_color_(f, maxv)\
+ (gx_color_value)(((f) * (0xffffL * 2) + maxv) / (maxv * 2))
+extern const gx_color_value _ds *fc_color_quo[8];
+#define fractional_color(f, maxv)\
+ ((maxv) <= 7 ? fc_color_quo[maxv][f] : frac_color_(f, maxv))
+
+/* ------ Halftone cache procedures ------ */
+
+/* Allocate a halftone cache. */
+uint gx_ht_cache_default_tiles(P0());
+uint gx_ht_cache_default_bits(P0());
+gx_ht_cache *gx_ht_alloc_cache(P3(gs_memory_t *, uint, uint));
+
+/* Clear a halftone cache. */
+#define gx_ht_clear_cache(pcache)\
+ ((pcache)->order.levels = 0, (pcache)->order.bits = 0,\
+ (pcache)->ht_tiles[0].tiles.data = 0)
+
+/* Initialize a halftone cache with a given order. */
+void gx_ht_init_cache(P2(gx_ht_cache *, const gx_ht_order *));
+
+/* Install a halftone in the graphics state. */
+int gx_ht_install(P3(gs_state *,
+ const gs_halftone *, const gx_device_halftone *));
+
+/* Make the cache order current, and return whether */
+/* there is room for all possible tiles in the cache. */
+bool gx_check_tile_cache(P1(gs_state *));
+
+/* Determine whether a given (width, y, height) might fit into a */
+/* single tile. If so, return the byte offset of the appropriate row */
+/* from the beginning of the tile, and set *ppx to the x phase offset */
+/* within the tile; if not, return -1. */
+int gx_check_tile_size(P5(gs_state *pgs, int w, int y, int h, int *ppx));
+
+/* Make a given level current in a halftone cache. */
+gx_ht_tile *gx_render_ht(P2(gx_ht_cache *, int));
diff --git a/pstoraster/gzline.h b/pstoraster/gzline.h
new file mode 100644
index 000000000..b9570a45f
--- /dev/null
+++ b/pstoraster/gzline.h
@@ -0,0 +1,33 @@
+/* Copyright (C) 1989, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gzline.h */
+/* Line parameter implementation */
+#include "gxline.h"
+
+#define private_st_line_params() /* in gsstate.c */\
+ gs_private_st_ptrs1(st_line_params, gx_line_params, "line_params",\
+ line_params_enum_ptrs, line_params_reloc_ptrs, dash.pattern)
+
+/* Internal accessor for line parameters in graphics state */
+const gx_line_params *gs_currentlineparams(P1(const gs_imager_state *));
diff --git a/pstoraster/gzpath.h b/pstoraster/gzpath.h
new file mode 100644
index 000000000..ef8f7fba8
--- /dev/null
+++ b/pstoraster/gzpath.h
@@ -0,0 +1,221 @@
+/* Copyright (C) 1989, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gzpath.h */
+/* Private representation of paths for Ghostscript library */
+/* Requires gxfixed.h */
+#include "gxpath.h"
+#include "gsstruct.h" /* for extern_st */
+
+/* Paths are represented as a linked list of line or curve segments, */
+/* similar to what pathforall reports. */
+
+/* Definition of a path segment: a segment start, a line, */
+/* or a Bezier curve. */
+typedef enum {
+ s_start,
+ s_line,
+ s_line_close,
+ s_curve
+} segment_type;
+#define segment_common\
+ segment *prev;\
+ segment *next;\
+ segment_type type;\
+ gs_fixed_point pt; /* initial point for starts, */\
+ /* final point for others */
+/* Forward declarations for structure types */
+#ifndef segment_DEFINED
+# define segment_DEFINED
+typedef struct segment_s segment;
+#endif
+typedef struct subpath_s subpath;
+
+/* A generic segment. This is never instantiated, */
+/* but we define a descriptor anyway for the benefit of subclasses. */
+struct segment_s {
+ segment_common
+};
+#define private_st_segment() /* in gxpath.c */\
+ gs_private_st_ptrs2(st_segment, struct segment_s, "segment",\
+ segment_enum_ptrs, segment_reloc_ptrs, prev, next)
+
+/* Line segments have no special data. */
+typedef struct {
+ segment_common
+} line_segment;
+#define private_st_line() /* in gxpath.c */\
+ gs_private_st_suffix_add0(st_line, line_segment, "line",\
+ line_enum_ptrs, line_reloc_ptrs, st_segment)
+
+/* Line_close segments are for the lines appended by closepath. */
+/* They point back to the subpath being closed. */
+typedef struct {
+ segment_common
+ subpath *sub;
+} line_close_segment;
+#define private_st_line_close() /* in gxpath.c */\
+ gs_private_st_suffix_add1(st_line_close, line_close_segment, "close",\
+ close_enum_ptrs, close_reloc_ptrs, st_segment, sub)
+
+/*
+ * We use two different representations for curve segments: one defined by
+ * two endpoints (p0, p3) and two control points (p1, p2), and one defined
+ * by two sets of parametric cubic coefficients (ax ... dy). Here is how
+ * they are related (v = x or y). We spell out some multiplies by 3 for
+ * the benefit of compilers too simple to optimize this.
+ */
+#define curve_points_to_coefficients(v0, v1, v2, v3, a, b, c, t01, t12)\
+ (/*d = (v0),*/\
+ t01 = (v1) - (v0), c = (t01 << 1) + t01,\
+ t12 = (v2) - (v1), b = (t12 << 1) + t12 - c,\
+ a = (v3) - b - c - (v0))
+/*
+ * or conversely
+ */
+#define curve_coefficients_to_points(a, b, c, d, v1, v2, v3)\
+ (/*v0 = (d),*/\
+ v1 = (d) + ((c) / 3),\
+ v2 = v1 + (((b) + (c)) / 3),\
+ v3 = (a) + (b) + (c) + (d))
+
+/* Curve segments store the control points. */
+typedef struct {
+ segment_common
+ gs_fixed_point p1, p2;
+} curve_segment;
+#define private_st_curve() /* in gxpath.c */\
+ gs_private_st_composite_only(st_curve, curve_segment, "curve",\
+ segment_enum_ptrs, segment_reloc_ptrs)
+
+/* A start segment. This serves as the head of a subpath. */
+/* The closer is only used temporarily when filling, */
+/* to close an open subpath. */
+struct subpath_s {
+ segment_common
+ segment *last; /* last segment of subpath, */
+ /* points back to here if empty */
+ int curve_count; /* # of curves */
+ line_close_segment closer;
+ char/*bool*/ is_closed; /* true if subpath is closed */
+};
+#define private_st_subpath() /* in gxpath.c */\
+ gs_private_st_suffix_add1(st_subpath, subpath, "subpath",\
+ subpath_enum_ptrs, subpath_reloc_ptrs, st_segment, last)
+
+/* Test whether a subpath is a rectangle; if so, also return */
+/* the start of the next subpath. */
+bool gx_subpath_is_rectangle(P3(const subpath *pstart, gs_fixed_rect *pbox,
+ const subpath **ppnext));
+
+/* Curve manipulation */
+
+/* Return the smallest value k such that 2^k segments will approximate */
+/* the curve to within the desired flatness. */
+int gx_curve_log2_samples(P4(fixed, fixed, const curve_segment *, fixed));
+
+/* Return up to 2 values of t which split the curve into monotonic parts. */
+int gx_curve_monotonic_points(P5(fixed, fixed, fixed, fixed, double [2]));
+
+/* Split a curve at an arbitrary value of t. */
+void gx_curve_split(P6(fixed, fixed, const curve_segment *, double,
+ curve_segment *, curve_segment *));
+
+/* Initialize a cursor for rasterizing a monotonic curve. */
+/* Currently this has no dynamic elements, but it might someday. */
+typedef struct curve_cursor_s {
+ /* Following are set at initialization */
+ int k; /* 2^k segments */
+ gs_fixed_point p0; /* starting point */
+ const curve_segment *pc; /* other points */
+ fixed a, b, c; /* curve coefficients */
+ double da, db, dc; /* double versions of a, b, c */
+ bool double_set; /* true if da/b/c set */
+ bool fixed_fits; /* true if compute in fixed point */
+} curve_cursor;
+void gx_curve_cursor_init(P5(curve_cursor *prc, fixed x0, fixed y0,
+ const curve_segment *pc, int k));
+
+/* Return the value of X at a given Y value on a monotonic curve. */
+/* y must lie between prc->p0.y and prc->pt.y. */
+fixed gx_curve_x_at_y(P2(curve_cursor *prc, fixed y));
+
+/*
+ * Here is the actual structure of a path.
+ * The path state reflects the most recent operation on the path as follows:
+ * Operation position_valid subpath_open
+ * newpath 0 0
+ * moveto 1 -1
+ * lineto/curveto 1 1
+ * closepath 1 0
+ */
+struct gx_path_s {
+ gs_memory_t *memory;
+ gs_fixed_rect bbox; /* bounding box (in device space) */
+ segment *box_last; /* bbox incorporates segments */
+ /* up to & including this one */
+ subpath *first_subpath;
+ subpath *current_subpath;
+ int subpath_count;
+ int curve_count;
+ gs_fixed_point position; /* current position */
+ int subpath_open; /* 0 = newpath or closepath, */
+ /* -1 = moveto, 1 = lineto/curveto */
+ char/*bool*/ position_valid;
+ char/*bool*/ bbox_set; /* true if setbbox is in effect */
+ char/*bool*/ shares_segments; /* if true, this path shares its */
+ /* segment storage with the one in */
+ /* the previous saved graphics state */
+};
+extern_st(st_path);
+#define public_st_path() /* in gxpath.c */\
+ gs_public_st_ptrs3(st_path, gx_path, "path",\
+ path_enum_ptrs, path_reloc_ptrs, box_last, first_subpath, current_subpath)
+#define st_path_max_ptrs 3
+
+/* Path enumeration structure */
+struct gs_path_enum_s {
+ const segment *pseg;
+ const gs_state *pgs;
+ const gx_path *path; /* path being enumerated */
+ gx_path *copied_path; /* if the path was copied, this is the */
+ /* the same as path, to be released */
+ /* when done enumerating */
+ bool moveto_done; /* have we reported a final moveto yet? */
+};
+#define private_st_path_enum() /* in gxpath2.c */\
+ gs_private_st_ptrs4(st_gs_path_enum, gs_path_enum, "gs_path_enum",\
+ path_enum_enum_ptrs, path_enum_reloc_ptrs, pseg, pgs, path, copied_path)
+
+/* Macros equivalent to a few heavily used procedures. */
+/* Be aware that these macros may evaluate arguments more than once. */
+#define gx_path_current_point_inline(ppath,ppt)\
+ ( !ppath->position_valid ? gs_note_error(gs_error_nocurrentpoint) :\
+ ((ppt)->x = ppath->position.x, (ppt)->y = ppath->position.y, 0) )
+/* ...rel_point rather than ...relative_point is because */
+/* some compilers dislike identifiers of >31 characters. */
+#define gx_path_add_rel_point_inline(ppath,dx,dy)\
+ ( !ppath->position_valid || ppath->bbox_set ?\
+ gx_path_add_relative_point(ppath, dx, dy) :\
+ (ppath->position.x += dx, ppath->position.y += dy,\
+ ppath->subpath_open = -1, 0) )
diff --git a/pstoraster/gzstate.h b/pstoraster/gzstate.h
new file mode 100644
index 000000000..3b066fdd9
--- /dev/null
+++ b/pstoraster/gzstate.h
@@ -0,0 +1,154 @@
+/* Copyright (C) 1989, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* gzstate.h */
+/* Private graphics state definition for Ghostscript library */
+#include "gxdcolor.h"
+#include "gxistate.h"
+#include "gsstate.h"
+#include "gxstate.h"
+#include "gxcvalue.h"
+#include "gxtmap.h"
+
+/* Opaque types referenced by the graphics state. */
+#ifndef gs_font_DEFINED
+# define gs_font_DEFINED
+typedef struct gs_font_s gs_font;
+#endif
+#ifndef gs_halftone_DEFINED
+# define gs_halftone_DEFINED
+typedef struct gs_halftone_s gs_halftone;
+#endif
+#ifndef gx_device_halftone_DEFINED
+# define gx_device_halftone_DEFINED
+typedef struct gx_device_halftone_s gx_device_halftone;
+#endif
+
+/* Composite components of the graphics state. */
+typedef struct gx_transfer_colored_s {
+ /* The components must be in this order: */
+ gx_transfer_map *red;
+ gx_transfer_map *green;
+ gx_transfer_map *blue;
+ gx_transfer_map *gray;
+} gx_transfer_colored;
+typedef union gx_transfer_s {
+ gx_transfer_map *indexed[4];
+ gx_transfer_colored colored;
+} gx_transfer;
+
+/*
+ * We allocate a large subset of the graphics state as a single object.
+ * Its type is opaque here, defined in gsstate.c.
+ * The components marked with @ must be allocated in the contents.
+ * (Consult gsstate.c for more details about gstate storage management.)
+ */
+typedef struct gs_state_contents_s gs_state_contents;
+
+/* Graphics state structure. */
+
+struct gs_state_s {
+ gs_imager_state_common; /* imager state, must be first */
+ gs_memory_t *memory;
+ gs_state *saved; /* previous state from gsave */
+ gs_state_contents *contents;
+
+ /* Transformation: */
+
+ gs_matrix ctm_inverse;
+ bool ctm_inverse_valid; /* true if ctm_inverse = ctm^-1 */
+ gs_matrix ctm_default;
+ bool ctm_default_set; /* if true, use ctm_default; */
+ /* if false, ask device */
+
+ /* Paths: */
+
+ struct gx_path_s *path;
+ struct gx_clip_path_s *clip_path; /* @ */
+ int clip_rule;
+
+ /* Halftone screen: */
+
+ gs_halftone *halftone;
+ gx_device_halftone *dev_ht;
+ gs_int_point ht_phase;
+ struct gx_ht_cache_s *ht_cache; /* shared by all gstates */
+
+ /* Color (device-independent): */
+
+ struct gs_color_space_s *color_space;
+ struct gs_client_color_s *ccolor;
+ gx_color_value alpha;
+
+ /* Color (device-dependent): */
+
+ struct gs_cie_render_s *cie_render;
+ gx_transfer_map *black_generation; /* may be 0 */
+ gx_transfer_map *undercolor_removal; /* may be 0 */
+ /* set_transfer holds the transfer functions specified by */
+ /* set[color]transfer; effective_transfer includes the */
+ /* effects of overrides by TransferFunctions in halftone */
+ /* dictionaries. (In Level 1 systems, set_transfer and */
+ /* effective_transfer are always the same.) */
+ gx_transfer set_transfer;
+ gx_transfer effective_transfer;
+
+ /* Color caches: */
+
+ gx_device_color *dev_color;
+ struct gx_cie_joint_caches_s *cie_joint_caches;
+ const struct gx_color_map_procs_s *cmap_procs;
+ struct gx_pattern_cache_s *pattern_cache; /* shared by all GCs */
+
+ /* Font: */
+
+ gs_font *font;
+ gs_font *root_font;
+ gs_matrix_fixed char_tm; /* font matrix * ctm */
+#define char_tm_only(pgs) *(gs_matrix *)&(pgs)->char_tm
+ bool char_tm_valid; /* true if char_tm is valid */
+ byte in_cachedevice; /* 0 if not in setcachedevice, */
+ /* 1 if in setcachdevice but not */
+ /* actually caching, */
+ /* 2 if in setcachdevice and */
+ /* actually caching */
+ byte /*gs_char_path_mode*/ in_charpath; /* (see gscpm.h) */
+ gs_state *show_gstate; /* gstate when show was invoked */
+ /* (so charpath can append to path) */
+
+ /* Other stuff: */
+
+ int level; /* incremented by 1 per gsave */
+ gx_device *device;
+#undef gs_currentdevice_inline
+#define gs_currentdevice_inline(pgs) ((pgs)->device)
+
+ /* Client data: */
+
+ void *client_data;
+#define gs_state_client_data(pgs) ((pgs)->client_data)
+ gs_state_client_procs client_procs;
+};
+#define private_st_gs_state() /* in gsstate.c */\
+ gs_private_st_composite(st_gs_state, gs_state, "gs_state",\
+ gs_state_enum_ptrs, gs_state_reloc_ptrs)
diff --git a/pstoraster/ialloc.c b/pstoraster/ialloc.c
new file mode 100644
index 000000000..e357311f1
--- /dev/null
+++ b/pstoraster/ialloc.c
@@ -0,0 +1,261 @@
+/* Copyright (C) 1993, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* ialloc.c */
+/* Memory allocator for Ghostscript interpreter */
+#include "gx.h"
+#include "memory_.h"
+#include "errors.h"
+#include "gsstruct.h"
+#include "gxarith.h" /* for small_exact_log2 */
+#include "iref.h" /* must precede iastate.h */
+#include "iastate.h"
+#include "ivmspace.h"
+#include "store.h"
+
+/*
+ * Define global and local instances.
+ */
+gs_dual_memory_t gs_imemory;
+
+/* Imported from gsalloc.c */
+gs_ref_memory_t *ialloc_alloc_state(P2(gs_memory_t *, uint));
+
+#define imem ((gs_ref_memory_t *)mem)
+
+/* Initialize the allocator */
+void
+ialloc_init(gs_memory_t *mem, uint chunk_size, bool level2)
+{ gs_ref_memory_t *ilmem = ialloc_alloc_state(mem, chunk_size);
+ gs_ref_memory_t *igmem =
+ (level2 ?
+ ialloc_alloc_state(mem, chunk_size) :
+ ilmem);
+ gs_ref_memory_t *ismem = ialloc_alloc_state(mem, chunk_size);
+ int i;
+
+ for ( i = 0; i < countof(gs_imemory.spaces.indexed); i++ )
+ gs_imemory.spaces.indexed[i] = 0;
+ gs_imemory.space_local = ilmem;
+ gs_imemory.space_global = igmem;
+ gs_imemory.space_system = ismem;
+ gs_imemory.reclaim = 0;
+ /* Level 1 systems have only local VM. */
+ igmem->space = avm_global;
+ ilmem->space = avm_local; /* overrides if ilmem == igmem */
+ igmem->global = ilmem->global = igmem;
+ ismem->space = avm_system;
+ ialloc_set_space(&gs_imemory, avm_global);
+}
+
+/* ================ Local/global VM ================ */
+
+/* Get the space attribute of an allocator */
+uint
+imemory_space(gs_ref_memory_t *iimem)
+{ return iimem->space;
+}
+
+/* Select the allocation space. */
+void
+ialloc_set_space(gs_dual_memory_t *dmem, uint space)
+{ gs_ref_memory_t *mem = dmem->spaces.indexed[space >> r_space_shift];
+ dmem->current = mem;
+ dmem->current_space = mem->space;
+}
+
+/* Reset the requests. */
+void
+ialloc_reset_requested(gs_dual_memory_t *dmem)
+{ dmem->space_system->gc_status.requested = 0;
+ dmem->space_global->gc_status.requested = 0;
+ dmem->space_local->gc_status.requested = 0;
+}
+
+/* ================ Refs ================ */
+
+/*
+ * As noted in iastate.h, every run of refs has an extra ref at the end
+ * to hold relocation information for the garbage collector;
+ * since sizeof(ref) % obj_align_mod == 0, we never need to
+ * allocate any additional padding space at the end of the block.
+ */
+
+/* Allocate an array of refs. */
+int
+gs_alloc_ref_array(gs_ref_memory_t *mem, ref *parr, uint attrs,
+ uint num_refs, client_name_t cname)
+{ ref *obj;
+ /* If we're allocating a run of refs already, use it. */
+ if ( mem->cc.rtop == mem->cc.cbot &&
+ num_refs < (mem->cc.ctop - mem->cc.cbot) / sizeof(ref)
+ )
+ { obj = (ref *)mem->cc.rtop - 1; /* back up over last ref */
+ if_debug4('A', "[a%d:+$ ]%s(%u) = 0x%lx\n", imem->space,
+ client_name_string(cname), num_refs, (ulong)obj);
+ mem->cc.rcur[-1].o_size += num_refs * sizeof(ref);
+ { ref *end = (ref *)(mem->cc.rtop = mem->cc.cbot +=
+ num_refs * sizeof(ref));
+ make_mark(end - 1);
+ }
+ }
+ else
+ { /*
+ * Allocate a new run. We have to distinguish 3 cases:
+ * - Same chunk: pcc unchanged, end == cc.cbot.
+ * - Large chunk: pcc unchanged, end != cc.cbot.
+ * - New chunk: pcc changed.
+ */
+ chunk_t *pcc = mem->pcc;
+ ref *end;
+ obj = gs_alloc_struct_array((gs_memory_t *)mem, num_refs + 1,
+ ref, &st_refs, cname);
+ if ( obj == 0 )
+ return_error(e_VMerror);
+ /* Set the terminating ref now. */
+ end = (ref *)obj + num_refs;
+ make_mark(end);
+ /* Set has_refs in the chunk. */
+ if ( mem->pcc != pcc || mem->cc.cbot == (byte *)(end + 1) )
+ { /* Ordinary chunk. */
+ mem->cc.rcur = (obj_header_t *)obj;
+ mem->cc.rtop = (byte *)(end + 1);
+ mem->cc.has_refs = true;
+ }
+ else
+ { /* Large chunk. */
+ /* This happens only for very large arrays, */
+ /* so it doesn't need to be cheap. */
+ chunk_locator_t cl;
+ cl.memory = mem;
+ cl.cp = mem->clast;
+ chunk_locate_ptr(obj, &cl);
+ cl.cp->has_refs = true;
+ }
+ }
+ make_array(parr, attrs | mem->space, num_refs, obj);
+ return 0;
+}
+
+/* Resize an array of refs. Currently this is only implemented */
+/* for shrinking, not for growing. */
+int
+gs_resize_ref_array(gs_ref_memory_t *mem, ref *parr,
+ uint new_num_refs, client_name_t cname)
+{ uint old_num_refs = r_size(parr);
+ uint diff;
+ ref *obj = parr->value.refs;
+ if ( new_num_refs > old_num_refs || !r_has_type(parr, t_array) )
+ return_error(e_Fatal);
+ diff = old_num_refs - new_num_refs;
+ /* Check for LIFO. See gs_free_ref_array for more details. */
+ if ( mem->cc.rtop == mem->cc.cbot &&
+ (byte *)(obj + (old_num_refs + 1)) == mem->cc.rtop
+ )
+ { /* Shorten the refs object. */
+ ref *end = (ref *)(mem->cc.cbot = mem->cc.rtop -=
+ diff * sizeof(ref));
+ if_debug4('A', "[a%d:<$ ]%s(%u) 0x%lx\n", imem->space,
+ client_name_string(cname), diff, (ulong)obj);
+ mem->cc.rcur[-1].o_size -= diff * sizeof(ref);
+ make_mark(end - 1);
+ }
+ else
+ { /* Punt. */
+ if_debug4('A', "[a%d:<$#]%s(%u) 0x%lx\n", imem->space,
+ client_name_string(cname), diff, (ulong)obj);
+ imem->freed_lost += diff * sizeof(ref);
+ }
+ r_set_size(parr, new_num_refs);
+ return 0;
+}
+
+/* Deallocate an array of refs. Only do this if LIFO, or if */
+/* the array occupies an entire chunk by itself. */
+void
+gs_free_ref_array(gs_ref_memory_t *mem, ref *parr, client_name_t cname)
+{ uint num_refs = r_size(parr);
+ ref *obj = parr->value.refs;
+ /*
+ * Compute the storage size of the array, and check for LIFO
+ * freeing or a separate chunk. Note that the array might be packed;
+ * for the moment, if it's anything but a t_array, punt.
+ * The +1s are for the extra ref for the GC.
+ */
+ if ( !r_has_type(parr, t_array) )
+ ; /* don't look for special cases */
+ else if ( mem->cc.rtop == mem->cc.cbot &&
+ (byte *)(obj + (num_refs + 1)) == mem->cc.rtop
+ )
+ { if ( (obj_header_t *)obj == mem->cc.rcur )
+ { /* Deallocate the entire refs object. */
+ gs_free_object((gs_memory_t *)mem, obj, cname);
+ mem->cc.rcur = 0;
+ mem->cc.rtop = 0;
+ }
+ else
+ { /* Deallocate it at the end of the refs object. */
+ if_debug4('A', "[a%d:-$ ]%s(%u) 0x%lx\n",
+ imem->space, client_name_string(cname),
+ num_refs, (ulong)obj);
+ mem->cc.rcur[-1].o_size -= num_refs * sizeof(ref);
+ mem->cc.rtop = mem->cc.cbot = (byte *)(obj + 1);
+ make_mark(obj);
+ }
+ return;
+ }
+ else if ( num_refs >= (mem->large_size / arch_sizeof_ref - 1) )
+ { /* See if this array has a chunk all to itself. */
+ /* We only make this check when freeing very large objects, */
+ /* so it doesn't need to be cheap. */
+ chunk_locator_t cl;
+ cl.memory = imem;
+ cl.cp = imem->clast;
+ if ( chunk_locate_ptr(obj, &cl) &&
+ obj == (ref *)((obj_header_t *)(cl.cp->cbase) + 1) &&
+ (byte *)(obj + (num_refs + 1)) == cl.cp->cend
+ )
+ { /* Free the chunk. */
+ if_debug4('a', "[a%d:-$L]%s(%u) 0x%lx\n",
+ imem->space, client_name_string(cname),
+ num_refs, (ulong)obj);
+ alloc_free_chunk(cl.cp, imem);
+ return;
+ }
+ }
+ /* Punt. */
+ if_debug4('A', "[a%d:-$#]%s(%u) 0x%lx\n", imem->space,
+ client_name_string(cname), num_refs, (ulong)obj);
+ imem->freed_lost += num_refs * sizeof(ref);
+}
+
+/* Allocate a string ref. */
+int
+gs_alloc_string_ref(gs_ref_memory_t *mem, ref *psref,
+ uint attrs, uint nbytes, client_name_t cname)
+{ byte *str = gs_alloc_string((gs_memory_t *)mem, nbytes, cname);
+ if ( str == 0 )
+ return_error(e_VMerror);
+ make_string(psref, attrs | mem->space, nbytes, str);
+ return 0;
+}
diff --git a/pstoraster/ialloc.h b/pstoraster/ialloc.h
new file mode 100644
index 000000000..d082ebeb4
--- /dev/null
+++ b/pstoraster/ialloc.h
@@ -0,0 +1,122 @@
+/* Copyright (C) 1989, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* ialloc.h */
+/* Interface to Ghostscript interpreter memory allocator */
+
+#ifndef ialloc_INCLUDED
+# define ialloc_INCLUDED
+
+#include "imemory.h"
+
+/*
+ * Define the interpreter memory manager instance.
+ */
+extern gs_dual_memory_t gs_imemory;
+#define idmemory (&gs_imemory)
+#define iimemory (gs_imemory.current)
+#define imemory ((gs_memory_t *)iimemory)
+#define iimemory_local (gs_imemory.space_local)
+#define imemory_local ((gs_memory_t *)iimemory_local)
+#define iimemory_global (gs_imemory.space_global)
+#define imemory_global ((gs_memory_t *)iimemory_global)
+#define iimemory_system (gs_imemory.space_system)
+#define imemory_system ((gs_memory_t *)iimemory_system)
+
+/*
+ * Aliases for invoking the standard allocator interface.
+ */
+#define ialloc_bytes(nbytes, cname)\
+ gs_alloc_bytes(imemory, nbytes, cname)
+#define ialloc_struct(typ, pstype, cname)\
+ gs_alloc_struct(imemory, typ, pstype, cname)
+#define ialloc_byte_array(nelts, esize, cname)\
+ gs_alloc_byte_array(imemory, nelts, esize, cname)
+#define ialloc_struct_array(nelts, typ, pstype, cname)\
+ gs_alloc_struct_array(imemory, nelts, typ, pstype, cname)
+#define ifree_object(data, cname)\
+ gs_free_object(imemory, data, cname)
+#define ialloc_string(nbytes, cname)\
+ gs_alloc_string(imemory, nbytes, cname)
+#define ifree_string(data, nbytes, cname)\
+ gs_free_string(imemory, data, nbytes, cname)
+
+/* Initialize the interpreter's allocator. */
+void ialloc_init(P3(gs_memory_t *, uint, bool));
+
+/* Resize a string. */
+#define iresize_string(data, oldn, newn, cname)\
+ gs_resize_string(imemory, data, oldn, newn, cname)
+
+/* ------ Internal routines ------ */
+
+/* Reset the request values that identify the cause of a GC. */
+void ialloc_reset_requested(P1(gs_dual_memory_t *));
+
+/* Validate the contents of memory. */
+void ialloc_validate_spaces(P1(const gs_dual_memory_t *));
+#define ivalidate_spaces() ialloc_validate_spaces(idmemory)
+
+/*
+ * Local/global VM management.
+ */
+
+/* Get the space attribute of the current allocator. */
+#define ialloc_space(dmem) ((dmem)->current_space)
+#define icurrent_space ialloc_space(idmemory)
+extern uint imemory_space(P1(gs_ref_memory_t *));
+
+/* Select the allocation space. */
+void ialloc_set_space(P2(gs_dual_memory_t *, uint));
+
+/*
+ * Ref-related facilities.
+ */
+
+#ifdef r_type /* i.e., we know about refs */
+
+/* Allocate and free ref arrays. */
+#define ialloc_ref_array(paref, attrs, nrefs, cname)\
+ gs_alloc_ref_array(iimemory, paref, attrs, nrefs, cname)
+#define iresize_ref_array(paref, nrefs, cname)\
+ gs_resize_ref_array(iimemory, paref, nrefs, cname)
+#define ifree_ref_array(paref, cname)\
+ gs_free_ref_array(iimemory, paref, cname)
+
+/* Allocate a string ref. */
+#define ialloc_string_ref(psref, attrs, nbytes, cname)\
+ gs_alloc_string_ref(iimemory, psref, attrs, nbytes, cname)
+
+/* Make a ref for a newly allocated structure. */
+#define make_istruct(pref,attrs,ptr)\
+ make_struct(pref, (attrs) | icurrent_space, ptr)
+#define make_istruct_new(pref,attrs,ptr)\
+ make_struct_new(pref, (attrs) | icurrent_space, ptr)
+#define make_iastruct(pref,attrs,ptr)\
+ make_astruct(pref, (attrs) | icurrent_space, ptr)
+#define make_iastruct_new(pref,attrs,ptr)\
+ make_astruct_new(pref, (attrs) | icurrent_space, ptr)
+
+#endif /* ifdef r_type */
+
+#endif /* ialloc_INCLUDED */
diff --git a/pstoraster/iastate.h b/pstoraster/iastate.h
new file mode 100644
index 000000000..3c2e9bae6
--- /dev/null
+++ b/pstoraster/iastate.h
@@ -0,0 +1,29 @@
+/* Copyright (C) 1993, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* iastate.h */
+/* Interpreter memory manager internal definitions */
+/* Requires gsmemory.h, gsstruct.h */
+#include "gxalloc.h"
+#include "istruct.h"
+#include "ialloc.h"
diff --git a/pstoraster/iastruct.h b/pstoraster/iastruct.h
new file mode 100644
index 000000000..609b34d17
--- /dev/null
+++ b/pstoraster/iastruct.h
@@ -0,0 +1,27 @@
+/* Copyright (C) 1993, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* iastruct.h */
+/* Interpreter memory manager implementation structures */
+#include "gxobj.h"
+#include "ialloc.h"
diff --git a/pstoraster/ibnum.c b/pstoraster/ibnum.c
new file mode 100644
index 000000000..47afc9788
--- /dev/null
+++ b/pstoraster/ibnum.c
@@ -0,0 +1,213 @@
+/* Copyright (C) 1990, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* ibnum.c */
+/* Level 2 encoded number reading utilities for Ghostscript */
+#include "math_.h"
+#include "memory_.h"
+#include "ghost.h"
+#include "errors.h"
+#include "stream.h"
+#include "ibnum.h"
+#include "imemory.h" /* for iutil.h */
+#include "iutil.h"
+
+/* Define the number of bytes for a given format of encoded number. */
+const byte enc_num_bytes[] = { enc_num_bytes_values };
+
+/* ------ Encoded number reading ------ */
+
+/* Set up to read from an encoded number array/string. */
+/* Return <0 for error, or a number format. */
+int
+num_array_format(const ref *op)
+{ switch ( r_type(op) )
+ {
+ case t_string:
+ { /* Check that this is a legitimate encoded number string. */
+ const byte *bp = op->value.bytes;
+ int format = bp[1];
+
+ if ( r_size(op) < 4 || bp[0] != bt_num_array_value ||
+ !num_is_valid(format)
+ )
+ return_error(e_rangecheck);
+ if ( sdecodeshort(bp + 2, format) !=
+ (r_size(op) - 4) / encoded_number_bytes(format)
+ )
+ return_error(e_rangecheck);
+ return format;
+ }
+ case t_array:
+ case t_mixedarray:
+ case t_shortarray:
+ return num_array;
+ default:
+ return_error(e_typecheck);
+ }
+}
+
+/* Get the number of elements in an encoded number array/string. */
+uint
+num_array_size(const ref *op, int format)
+{ return (format == num_array ? r_size(op) :
+ (r_size(op) - 4) / encoded_number_bytes(format));
+}
+
+/* Get an encoded number from an array/string according to the given format. */
+/* Put the value in np->value.{intval,realval}. */
+/* Return t_int if integer, t_real if real, t_null if end of stream, */
+/* or an error if the format is invalid. */
+int
+num_array_get(const ref *op, int format, uint index, ref *np)
+{ if ( format == num_array )
+ { int code = array_get(op, (long)index, np);
+ if ( code < 0 )
+ return t_null;
+ switch ( r_type(np) )
+ {
+ case t_integer:
+ return t_integer;
+ case t_real:
+ return t_real;
+ default:
+ return_error(e_rangecheck);
+ }
+ }
+ else
+ { uint nbytes = encoded_number_bytes(format);
+ if ( index >= (r_size(op) - 4) / nbytes )
+ return t_null;
+ return sdecode_number(op->value.bytes + 4 + index * nbytes,
+ format, np);
+ }
+}
+
+/* Internal routine to decode a number in a given format. */
+/* Same returns as sget_encoded_number. */
+static const double binary_scale[32] = {
+#define expn2(n) (0.5 / (1L << (n-1)))
+ 1.0, expn2(1), expn2(2), expn2(3),
+ expn2(4), expn2(5), expn2(6), expn2(7),
+ expn2(8), expn2(9), expn2(10), expn2(11),
+ expn2(12), expn2(13), expn2(14), expn2(15),
+ expn2(16), expn2(17), expn2(18), expn2(19),
+ expn2(20), expn2(21), expn2(22), expn2(23),
+ expn2(24), expn2(25), expn2(26), expn2(27),
+ expn2(28), expn2(29), expn2(30), expn2(31)
+#undef expn2
+};
+int
+sdecode_number(const byte *str, int format, ref *np)
+{ switch ( format & 0x170 )
+ {
+ case num_int32: case num_int32 + 16:
+ if ( (format & 31) == 0 )
+ { np->value.intval = sdecodelong(str, format);
+ return t_integer;
+ }
+ else
+ { np->value.realval =
+ (double)sdecodelong(str, format) *
+ binary_scale[format & 31];
+ return t_real;
+ }
+ case num_int16:
+ if ( (format & 15) == 0 )
+ { np->value.intval = sdecodeshort(str, format);
+ return t_integer;
+ }
+ else
+ { np->value.realval =
+ sdecodeshort(str, format) *
+ binary_scale[format & 15];
+ return t_real;
+ }
+ case num_float:
+ np->value.realval = sdecodefloat(str, format);
+ return t_real;
+ default:
+ return_error(e_syntaxerror); /* invalid format?? */
+ }
+}
+
+/* ------ Decode number ------ */
+
+/* Decode encoded numbers from a string according to format. */
+
+/* Decode a (16-bit, signed) short. */
+short
+sdecodeshort(register const byte *p, int format)
+{ int a = p[0], b = p[1];
+ short v = (num_is_lsb(format) ? (b << 8) + a : (a << 8) + b);
+#if arch_sizeof_short == 2
+ return v;
+#else
+ /* Sign-extend if sizeof(short) > 2. */
+ return (v & 0x7fff) - (v & 0x8000);
+#endif
+}
+
+/* Decode a (32-bit, signed) long. */
+long
+sdecodelong(register const byte *p, int format)
+{ int a = p[0], b = p[1], c = p[2], d = p[3];
+ long v = (num_is_lsb(format) ?
+ ((long)d << 24) + ((long)c << 16) + (b << 8) + a :
+ ((long)a << 24) + ((long)b << 16) + (c << 8) + d);
+#if arch_sizeof_long == 4
+ return v;
+#else
+ /* Sign-extend if sizeof(long) > 4. */
+ return (v & 0x7fffffffL) - (v & 0x80000000L);
+#endif
+}
+
+/* Decode a float. We don't handle non-IEEE native representations yet. */
+float
+sdecodefloat(register const byte *p, int format)
+{ float fnum;
+ if ( format != num_float_native )
+ { bits32 lnum = (bits32)sdecodelong(p, format);
+ /* We know IEEE floats take 32 bits. */
+#if !arch_floats_are_IEEE
+ /* Convert IEEE float to native float. */
+ int sign_expt = lnum >> 23;
+ int expt = sign_expt & 0xff;
+ long mant = lnum & 0x7fffff;
+ if ( expt == 0 && mant == 0 )
+ fnum = 0;
+ else
+ { mant += 0x800000;
+ fnum = (float)ldexp((float)mant, expt - 127 - 24);
+ }
+ if ( sign_expt & 0x100 )
+ fnum = -fnum;
+#else
+ fnum = *(float *)&lnum;
+#endif
+ }
+ else
+ memcpy(&fnum, p, sizeof(float));
+ return fnum;
+}
diff --git a/pstoraster/ibnum.h b/pstoraster/ibnum.h
new file mode 100644
index 000000000..62766d252
--- /dev/null
+++ b/pstoraster/ibnum.h
@@ -0,0 +1,64 @@
+/* Copyright (C) 1990, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* ibnum.h */
+/* Interface to Level 2 number readers */
+/* Requires stream.h */
+
+/* Define the byte that begins an encoded number string. */
+/* (This is the same as the value of bt_num_array in btoken.h.) */
+#define bt_num_array_value 149
+
+/* Homogenous number array formats. */
+/* The default for numbers is big-endian. */
+#define num_int32 0 /* [0..31] */
+#define num_int16 32 /* [32..47] */
+#define num_float 48
+#define num_float_IEEE num_float
+#define num_float_native (num_float + 1)
+#define num_msb 0
+#define num_lsb 128
+#define num_is_lsb(format) ((format) >= num_lsb)
+#define num_is_valid(format) (((format) & 127) <= 49)
+/* Special "format" for reading from an array. */
+/* num_msb/lsb is not used in this case. */
+#define num_array 256
+/* Define the number of bytes for a given format of encoded number. */
+extern const byte enc_num_bytes[]; /* in ibnum.c */
+#define enc_num_bytes_values\
+ 4, 4, 2, 4, 0, 0, 0, 0,\
+ 4, 4, 2, 4, 0, 0, 0, 0,\
+ sizeof(ref)
+#define encoded_number_bytes(format)\
+ (enc_num_bytes[(format) >> 4])
+
+/* Read from an array or encoded number string. */
+int num_array_format(P1(const ref *)); /* returns format or error */
+uint num_array_size(P2(const ref *, int));
+int num_array_get(P4(const ref *, int, uint, ref *));
+
+/* Decode a number from a string with appropriate byte swapping. */
+int sdecode_number(P3(const byte *, int, ref *));
+short sdecodeshort(P2(const byte *, int));
+long sdecodelong(P2(const byte *, int));
+float sdecodefloat(P2(const byte *, int));
diff --git a/pstoraster/iccinit0.c b/pstoraster/iccinit0.c
new file mode 100644
index 000000000..9c57f02c9
--- /dev/null
+++ b/pstoraster/iccinit0.c
@@ -0,0 +1,30 @@
+/* Copyright (C) 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* iccinit0.c */
+/* Initialization string for non-compiled initialization */
+#include "stdpre.h"
+
+/* gsmain.c recognizes an empty init string specially. */
+const byte gs_init_string[] = { 0 };
+const uint gs_init_string_sizeof = 0;
diff --git a/pstoraster/ichar.h b/pstoraster/ichar.h
new file mode 100644
index 000000000..b5bc8a5af
--- /dev/null
+++ b/pstoraster/ichar.h
@@ -0,0 +1,65 @@
+/* Copyright (C) 1994, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* ichar.h */
+/* Shared definitions for text operators */
+/* Requires gxchar.h */
+
+/*
+ * All the character rendering operators use the execution stack
+ * for loop control -- see estack.h for details.
+ * The information pushed by these operators is as follows:
+ * the enumerator (t_struct, a gs_show_enum);
+ * a slot for the procedure for kshow or cshow (probably t_array) or
+ * the string or array for [x][y]show (t_string or t_array);
+ * a slot for the string/array index for [x][y]show (t_integer);
+ * a slot for the saved o-stack depth for cshow or stringwidth
+ * (t_integer);
+ * a slot for the saved d-stack depth ditto (t_integer);
+ * the procedure to be called at the end of the enumeration
+ * (t_operator, but called directly, not by the interpreter);
+ * the usual e-stack mark (t_null).
+ */
+#define snumpush 7
+#define esenum(ep) r_ptr(ep, gs_show_enum)
+#define senum esenum(esp)
+#define esslot(ep) ((ep)[-1])
+#define sslot esslot(esp)
+#define essindex(ep) ((ep)[-2])
+#define ssindex essindex(esp)
+#define esodepth(ep) ((ep)[-3])
+#define sodepth esodepth(esp)
+#define esddepth(ep) ((ep)[-4])
+#define sddepth esddepth(esp)
+#define eseproc(ep) ((ep)[-5])
+#define seproc eseproc(esp)
+
+/* Procedures exported by zchar.c for zchar1.c and/or zchar2.c. */
+gs_show_enum *op_show_find(P0());
+int op_show_setup(P2(os_ptr, gs_show_enum **));
+int op_show_enum_setup(P2(os_ptr, gs_show_enum **));
+void op_show_finish_setup(P3(gs_show_enum *, int, op_proc_p));
+int op_show_continue(P1(os_ptr));
+int op_show_continue_dispatch(P2(os_ptr, int));
+int op_show_return_width(P3(os_ptr, uint, float *));
+void op_show_free(P0());
diff --git a/pstoraster/icharout.h b/pstoraster/icharout.h
new file mode 100644
index 000000000..049fe3471
--- /dev/null
+++ b/pstoraster/icharout.h
@@ -0,0 +1,53 @@
+/* Copyright (C) 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* icharout.h */
+/* Interface to zcharout.c */
+
+/* Execute an outline defined by a PostScript procedure. */
+int zchar_exec_char_proc(P1(os_ptr));
+
+/*
+ * Get the metrics for a character from the Metrics dictionary of a base
+ * font. If present, store the l.s.b. in psbw[0,1] and the width in
+ * psbw[2,3].
+ */
+typedef enum {
+ metricsNone = 0,
+ metricsWidthOnly = 1,
+ metricsSideBearingAndWidth = 2
+} metrics_present;
+int /*metrics_present*/
+ zchar_get_metrics(P3(const gs_font_base *pbfont, const ref *pcnref,
+ float psbw[4]));
+
+/*
+ * Consult Metrics2 and CDevProc, and call setcachedevice[2]. Return
+ * o_push_estack if we had to call a CDevProc, or if we are skipping the
+ * rendering process (only getting the metrics).
+ */
+int zchar_set_cache(P8(os_ptr op, const gs_font_base *pbfont,
+ const ref *pcnref, const float psb[2],
+ const float pwidth[2], const gs_rect *pbbox,
+ int (*cont_fill)(P1(os_ptr)),
+ int (*cont_stroke)(P1(os_ptr))));
diff --git a/pstoraster/icie.h b/pstoraster/icie.h
new file mode 100644
index 000000000..0a8d5e7b2
--- /dev/null
+++ b/pstoraster/icie.h
@@ -0,0 +1,77 @@
+/* Copyright (C) 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* icie.h */
+/* Internal definitions for interpreter CIE color handling */
+
+/*
+ * All of the routines below are exported by zcie.c for zcrd.c,
+ * except for cie_cache_joint which is exported by zcrd.c for zcie.c.
+ */
+
+/* ------ Parameter acquisition ------ */
+
+/* Get a range array parameter from a dictionary. */
+/* We know that count <= 4. */
+int dict_ranges_param(P4(const ref *pdref, const char _ds *kstr, int count,
+ gs_range *prange));
+
+/* Get 3 ranges from a dictionary. */
+#define dict_range3_param(pdref, kstr, prange3)\
+ dict_ranges_param(pdref, kstr, 3, (prange3)->ranges)
+
+/* Get a 3x3 matrix parameter from a dictionary. */
+#define dict_matrix3_param(op, kstr, pmat)\
+ dict_float_array_param(op, kstr, 9, (float *)pmat, (const float *)&Matrix3_default)
+#define matrix3_ok 9
+
+/* Get an array of procedures from a dictionary. */
+/* We know count <= countof(empty_procs). */
+int dict_proc_array_param(P4(const ref *pdict, const char _ds *kstr,
+ uint count, ref *pparray));
+
+/* Get 3 procedures from a dictionary. */
+#define dict_proc3_param(op, kstr, pparray)\
+ dict_proc_array_param(op, kstr, 3, pparray)
+
+/* Get WhitePoint and BlackPoint values. */
+int cie_points_param(P2(const ref *pdref, gs_cie_wb *pwb));
+
+/* Process a 3- or 4-dimensional lookup table from a dictionary. */
+/* The caller has set pclt->n and pclt->m. */
+/* ptref is known to be a readable array of size at least n+1. */
+int cie_table_param(P3(const ref *ptable, gx_color_lookup_table *pclt,
+ gs_memory_t *mem));
+
+/* ------ Internal routines ------ */
+
+int cie_cache_push_finish(P3(int (*)(P1(os_ptr)), gs_state *, void *));
+int cie_prepare_cache(P6(const gs_range *, const ref *,
+ cie_cache_floats *, void *, const gs_state *, client_name_t));
+int cie_prepare_caches_3(P8(const gs_range3 *, const ref *,
+ cie_cache_floats *, cie_cache_floats *, cie_cache_floats *,
+ void *, const gs_state *, client_name_t));
+#define cie_prepare_cache3(d3,p3,c3,pcie,pgs,cname)\
+ cie_prepare_caches_3(d3, p3, &(c3)->floats, &(c3)[1].floats, &(c3)[2].floats, pcie, pgs, cname)
+
+int cie_cache_joint(P2(const ref_cie_render_procs *, gs_state *));
diff --git a/pstoraster/icolor.h b/pstoraster/icolor.h
new file mode 100644
index 000000000..76a69e9bd
--- /dev/null
+++ b/pstoraster/icolor.h
@@ -0,0 +1,54 @@
+/* Copyright (C) 1994 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* icolor.h */
+/* Declarations for transfer function & similar cache remapping */
+
+/*
+ * All caches use the same mapping function for the library layer;
+ * it simply looks up the value in the cache.
+ */
+float gs_mapped_transfer(P2(floatp, const gx_transfer_map *));
+
+/* Define the number of stack slots needed for zcolor_remap_one. */
+/* The client is responsible for doing check_e/ostack or the equivalent */
+/* before calling zcolor_remap_one. */
+extern const int zcolor_remap_one_ostack;
+extern const int zcolor_remap_one_estack;
+
+/* Schedule the sampling and reloading of a cache. */
+int zcolor_remap_one(P5(const ref *, os_ptr, gx_transfer_map *,
+ const gs_state *, int (*)(P1(os_ptr))));
+
+/* Reload a cache with entries in [0..1] after sampling. */
+int zcolor_remap_one_finish(P1(os_ptr));
+
+/* Reload a cache with entries in [-1..1] after sampling. */
+int zcolor_remap_one_signed_finish(P1(os_ptr));
+
+/* Recompute the effective transfer functions and invalidate the current */
+/* color after cache reloading. */
+int zcolor_reset_transfer(P1(os_ptr));
+
+/* Invalidate the current color after cache reloading. */
+int zcolor_remap_color(P1(os_ptr));
diff --git a/pstoraster/iconf.c b/pstoraster/iconf.c
new file mode 100644
index 000000000..80627a0b0
--- /dev/null
+++ b/pstoraster/iconf.c
@@ -0,0 +1,73 @@
+/* Copyright (C) 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* iconf.c */
+/* Configuration-dependent tables and initialization for interpreter */
+#include "stdio_.h" /* stdio for stream.h */
+#include "gsmemory.h" /* for gscdefs.h */
+#include "gscdefs.h"
+#include "iref.h"
+#include "ivmspace.h"
+#include "opdef.h"
+#include "stream.h" /* for files.h */
+#include "files.h"
+#include "imain.h"
+
+/* Set up the .ps file name string array. */
+/* We fill in the lengths at initialization time. */
+#define ref_(t) struct { struct tas_s tas; t value; }
+#define string_(s)\
+ { { (t_string<<r_type_shift) + a_readonly + avm_foreign, 0 }, s },
+#define psfile_(fns) string_(fns)
+ref_(const char *) gs_init_file_array[] = {
+#include "gconfig.h"
+ string_(0)
+};
+#undef psfile_
+
+/* Set up the emulator name string array similarly. */
+#define emulator_(ems) string_(ems)
+ref_(const char *) gs_emulator_name_array[] = {
+#include "gconfig.h"
+ string_(0)
+};
+#undef emulator_
+
+/* Initialize the operators. */
+extern op_def_ptr
+ /* Initialization operators */
+#define oper_(defs) defs(P0()),
+#include "gconfig.h"
+#undef oper_
+ /* Interpreter operators */
+ interp_op_defs(P0());
+op_def_ptr (*(op_defs_all[]))(P0()) = {
+ /* Initialization operators */
+#define oper_(defs) defs,
+#include "gconfig.h"
+#undef oper_
+ /* Interpreter operators */
+ interp_op_defs,
+ /* end marker */
+ 0
+};
diff --git a/pstoraster/icsmap.h b/pstoraster/icsmap.h
new file mode 100644
index 000000000..f910a61c6
--- /dev/null
+++ b/pstoraster/icsmap.h
@@ -0,0 +1,39 @@
+/* Copyright (C) 1994 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* icsmap.h */
+/* Interface to shared routines for loading the cached color space maps. */
+
+/*
+ * Set up to load a cached map for an Indexed or substituted Separation
+ * color space. The implementation is in zcsindex.c. When the map1
+ * procedure is called, the following structure is on the e_stack:
+ */
+#define num_csme 5
+# define csme_num_components (-4) /* t_integer */
+# define csme_map (-3) /* t_struct (bytes) */
+# define csme_proc (-2) /* -procedure- */
+# define csme_hival (-1) /* t_integer */
+# define csme_index 0 /* t_integer */
+int zcs_begin_map(P5(gs_indexed_map **pmap, const ref *pproc, int num_entries,
+ const gs_base_color_space *base_space, int (*map1)(P1(os_ptr))));
diff --git a/pstoraster/idebug.c b/pstoraster/idebug.c
new file mode 100644
index 000000000..029b3f0e0
--- /dev/null
+++ b/pstoraster/idebug.c
@@ -0,0 +1,262 @@
+/* Copyright (C) 1989, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* idebug.c */
+/* Debugging support for Ghostscript interpreter */
+/* This file must always be compiled with DEBUG set. */
+#undef DEBUG
+#define DEBUG
+#include "string_.h"
+#include "ghost.h"
+#include "ialloc.h" /* for imemory for getting struct type */
+#include "idebug.h" /* defines interface */
+#include "idict.h"
+#include "iname.h"
+#include "ipacked.h"
+#include "istack.h"
+#include "iutil.h"
+#include "ivmspace.h"
+#include "ostack.h" /* for opdef.h */
+#include "opdef.h"
+#include "store.h" /* for make_oper for opdef.h */
+
+/* Table of type name strings */
+static const char *type_strings[] = { type_print_strings };
+
+/* First unassigned type index */
+extern const int tx_next_index; /* in interp.c */
+
+/* Print a name. */
+void
+debug_print_name(const ref *pnref)
+{ ref sref;
+ name_string_ref(pnref, &sref);
+ debug_print_string(sref.value.const_bytes, r_size(&sref));
+}
+
+/* Print a ref. */
+private void
+debug_print_full_ref(const ref *pref)
+{ unsigned size = r_size(pref);
+ ref nref;
+ dprintf1("(%x)", r_type_attrs(pref));
+ switch ( r_type(pref) )
+ {
+ case t_array:
+ dprintf2("array(%u)0x%lx", size, (ulong)pref->value.refs); break;
+ case t_astruct:
+ goto strct;
+ case t_boolean:
+ dprintf1("boolean %x", pref->value.boolval); break;
+ case t_device:
+ dprintf1("device 0x%lx", (ulong)pref->value.pdevice); break;
+ case t_dictionary:
+ dprintf3("dict(%u/%u)0x%lx",
+ dict_length(pref), dict_maxlength(pref),
+ (ulong)pref->value.pdict);
+ break;
+ case t_file:
+ dprintf1("file 0x%lx", (ulong)pref->value.pfile); break;
+ case t_fontID:
+ goto strct;
+ case t_integer:
+ dprintf1("int %ld", pref->value.intval); break;
+ case t_mark:
+ dprintf("mark"); break;
+ case t_mixedarray:
+ dprintf2("mixed packedarray(%u)0x%lx", size,
+ (ulong)pref->value.packed); break;
+ case t_name:
+ dprintf2("name(0x%lx#%x)", (ulong)pref->value.pname,
+ name_index(pref));
+ debug_print_name(pref);
+ break;
+ case t_null:
+ dprintf("null"); break;
+ case t_oparray:
+ dprintf2("op_array(%u)0x%lx:", size, (ulong)pref->value.const_refs);
+ { const op_array_table *opt = op_index_op_array_table(size);
+ name_index_ref(opt->nx_table[size - opt->base_index], &nref);
+ }
+ debug_print_name(&nref);
+ break;
+ case t_operator:
+ dprintf1("op(%u", size);
+ if ( size > 0 && size < op_def_count ) /* just in case */
+ dprintf1(":%s", (const char *)(op_def_table[size]->oname + 1));
+ dprintf1(")0x%lx", (ulong)pref->value.opproc);
+ break;
+ case t_real:
+ dprintf1("real %f", pref->value.realval); break;
+ case t_save:
+ dprintf1("save %lu", pref->value.saveid); break;
+ case t_shortarray:
+ dprintf2("short packedarray(%u)0x%lx", size,
+ (ulong)pref->value.packed); break;
+ case t_string:
+ dprintf2("string(%u)0x%lx", size, (ulong)pref->value.bytes); break;
+ case t_struct:
+strct: { obj_header_t *obj = (obj_header_t *)pref->value.pstruct;
+ dprintf2("struct %s 0x%lx",
+ (r_is_foreign(pref) ? "-foreign-" :
+ gs_struct_type_name_string(gs_object_type(imemory, obj))),
+ (ulong)obj);
+ }
+ break;
+ default: dprintf1("type 0x%x", r_type(pref));
+ }
+}
+private void
+debug_print_packed_ref(const ref_packed *pref)
+{ ushort elt = *pref & packed_value_mask;
+ ref nref;
+ switch ( *pref >> r_packed_type_shift )
+ {
+ case pt_executable_operator:
+ dprintf("<op_name>");
+ op_index_ref(elt, &nref);
+ debug_print_ref(&nref);
+ break;
+ case pt_integer:
+ dprintf1("<int> %d", (int)elt + packed_min_intval);
+ break;
+ case pt_literal_name:
+ dprintf("<lit_name>"); goto ptn;
+ case pt_executable_name:
+ dprintf("<exec_name>");
+ptn: name_index_ref(elt, &nref);
+ dprintf2("(0x%lx#%x)", (ulong)nref.value.pname, elt);
+ debug_print_name(&nref);
+ break;
+ default:
+ dprintf2("<packed_%d?>0x%x", *pref >> r_packed_type_shift, elt);
+ }
+}
+void
+debug_print_ref(const ref *pref)
+{ if ( r_is_packed(pref) )
+ debug_print_packed_ref((const ref_packed *)pref);
+ else
+ debug_print_full_ref(pref);
+ fflush(dstderr);
+}
+
+/* Dump one ref. */
+void
+debug_dump_one_ref(const ref *p)
+{ uint attrs = r_type_attrs(p);
+ uint type = r_type(p);
+ static const attr_print_mask apm[] = {attr_print_masks, {0,0,0}};
+ const attr_print_mask *ap = apm;
+#define buf_size 30
+ char buf[buf_size + 1];
+ uint plen;
+
+ if ( type >= tx_next_index )
+ dprintf1("0x%02x?? ", type);
+ else if ( type >= t_next_index )
+ dprintf("opr* ");
+ else
+ dprintf1("%s ", type_strings[type]);
+ for ( ; ap->mask; ++ap )
+ if ( (attrs & ap->mask) == ap->value )
+ dputc(ap->print);
+ dprintf2(" 0x%04x 0x%08lx", r_size(p), *(const ulong *)&p->value);
+ if ( obj_cvs(p, (byte *)buf, countof(buf) - 1, &plen, NULL) >= 0 &&
+ ((buf[plen] = 0), strcmp(buf, "--nostringval--"))
+ )
+ dprintf1(" = %s", buf);
+ fflush(dstderr);
+}
+
+/* Dump a region of memory containing refs. */
+void
+debug_dump_refs(const ref *from, uint size, const char *msg)
+{ const ref *p = from;
+ uint count = size;
+ if ( size && msg )
+ dprintf2("%s at 0x%lx:\n", msg, (ulong)from);
+ while ( count-- )
+ { dprintf2("..%04x: 0x%02x ", (uint)p & 0xffff, r_type(p));
+ debug_dump_one_ref(p);
+ dputc('\n');
+ p++;
+ }
+}
+
+/* Dump a stack. */
+void
+debug_dump_stack(const ref_stack *pstack, const char *msg)
+{ uint i;
+ const char *m = msg;
+ for ( i = ref_stack_count(pstack); i != 0; )
+ { const ref *p = ref_stack_index(pstack, --i);
+ if ( m )
+ dprintf2("%s at 0x%lx:\n", m, (ulong)pstack),
+ m = NULL;
+ dprintf2("0x%lx: 0x%02x ", (ulong)p, r_type(p));
+ debug_dump_one_ref(p);
+ dputc('\n');
+ }
+}
+
+/* Dump an array. */
+void
+debug_dump_array(const ref *array)
+{ const ref_packed *pp;
+ unsigned int type = r_type(array);
+ uint len;
+
+ switch (type)
+ {
+ default:
+ dprintf2 ("%s at 0x%lx isn't an array.\n",
+ (type < countof(type_strings) ?
+ type_strings[type] : "????"),
+ (ulong)array);
+ return;
+ case t_oparray:
+ /* This isn't really an array, but we'd like to see */
+ /* its contents anyway. */
+ debug_dump_array(array->value.const_refs);
+ return;
+ case t_array:
+ case t_mixedarray:
+ case t_shortarray:
+ ;
+ }
+
+ /* This "packed" loop works for all array-types. */
+ for ( len = r_size (array), pp = array->value.packed;
+ len > 0;
+ len--, pp = packed_next(pp))
+ { ref temp;
+ packed_get(pp, &temp);
+ dprintf3("..%04x%c 0x%02x ",
+ (uint)pp & 0xffff,
+ ((r_is_packed(pp)) ? '*' : ':'),
+ r_type(&temp));
+ debug_dump_one_ref(&temp);
+ dputc ('\n');
+ }
+}
diff --git a/pstoraster/idebug.h b/pstoraster/idebug.h
new file mode 100644
index 000000000..d4642d0c7
--- /dev/null
+++ b/pstoraster/idebug.h
@@ -0,0 +1,41 @@
+/* Copyright (C) 1994, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* idebug.h */
+/* Prototypes for debugging procedures in idebug.c */
+
+/* Print individual values. */
+void debug_print_name(P1(const ref *));
+void debug_print_ref(P1(const ref *));
+
+/* Dump regions of memory. */
+void debug_dump_one_ref(P1(const ref *));
+void debug_dump_refs(P3(const ref *from, uint size, const char *msg));
+void debug_dump_array(P1(const ref *array));
+
+/* Dump a stack. Using this requires istack.h. */
+#ifndef ref_stack_DEFINED
+typedef struct ref_stack_s ref_stack; /* also defined in istack.h */
+# define ref_stack_DEFINED
+#endif
+void debug_dump_stack(P2(const ref_stack *pstack, const char *msg));
diff --git a/pstoraster/idict.c b/pstoraster/idict.c
new file mode 100644
index 000000000..9e7adbbcb
--- /dev/null
+++ b/pstoraster/idict.c
@@ -0,0 +1,974 @@
+/*
+ Copyright 1993-1999 by Easy Software Products.
+ Copyright (C) 1989, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* idict.c */
+/* Dictionaries for Ghostscript */
+#include "string_.h" /* for strlen */
+#include "ghost.h"
+#include "errors.h"
+#include "ialloc.h"
+#include "idebug.h" /* for debug_print_name */
+#include "inamedef.h"
+#include "ipacked.h"
+#include "isave.h" /* for value cache in names */
+#include "store.h"
+#include "idict.h" /* interface definition */
+#include "dstack.h" /* interface & some implementation */
+#include "iutil.h"
+#include "ivmspace.h" /* for store check */
+
+/*
+ * A dictionary of capacity M is a structure of four elements (refs):
+ *
+ * keys - a t_shortarray or t_array of M+1 elements, containing
+ * the keys.
+ *
+ * values - a t_array of M+1 elements, containing the values.
+ *
+ * count - a t_integer whose value tells how many entries are
+ * occupied (N).
+ *
+ * maxlength - a t_integer whose value gives the client's view of
+ * the capacity (C). C may be less than M (see below).
+ *
+ * C < M is possible because on large-memory systems, we round up M so that
+ * M is a power of 2; this allows us to use masking rather than division
+ * for computing the initial hash probe. However, C is always the
+ * maxlength specified by the client, so clients get a consistent story.
+ *
+ * As noted above, the keys may be either in packed or unpacked form.
+ * The markers for unused and deleted entries are different in the two forms.
+ * In the packed form:
+ * unused entries contain packed_key_empty;
+ * deleted entries contain packed_key_deleted.
+ * In the unpacked form:
+ * unused entries contain a literal null;
+ * deleted entries contain an executable null.
+ *
+ * The first entry is always marked deleted, to reduce the cost of the
+ * wrap-around check.
+ *
+ * Note that if the keys slot in the dictionary is new,
+ * all the key slots are new (more recent than the last save).
+ * We use this fact to avoid saving stores into packed keys
+ * for newly created dictionaries.
+ *
+ * Note that name keys with indices above packed_name_max_index require using
+ * the unpacked form.
+ */
+#define dict_is_packed(dct) r_has_type(&(dct)->keys, t_shortarray)
+#define packed_key_empty (pt_tag(pt_integer) + 0)
+#define packed_key_deleted (pt_tag(pt_integer) + 1)
+#define packed_key_impossible pt_tag(pt_full_ref) /* never matches */
+#define packed_name_key(nidx)\
+ ((nidx) <= packed_name_max_index ? pt_tag(pt_literal_name) + (nidx) :\
+ packed_key_impossible)
+/*
+ * Using a special mark for deleted entries causes lookup time to degrade
+ * as entries are inserted and deleted. This is not a problem, because
+ * entries are almost never deleted.
+ */
+#define d_maxlength(dct) ((uint)((dct)->maxlength.value.intval))
+#define d_set_maxlength(dct,siz) ((dct)->maxlength.value.intval = (siz))
+#define nslots(dct) r_size(&(dct)->values)
+#define npairs(dct) (nslots(dct) - 1)
+#define d_length(dct) ((uint)((dct)->count.value.intval))
+
+/*
+ * Define the size of the largest valid dictionary.
+ * This is limited by the size field of the keys and values refs,
+ * and by the enumeration interface, which requires the size to
+ * fit in an int. As it happens, max_array_size will always be
+ * smaller than max_int.
+ */
+const uint dict_max_size = max_array_size - 1;
+
+/* Define whether dictionaries expand automatically when full. */
+bool dict_auto_expand = false;
+
+/* Define whether dictionaries are packed by default. */
+bool dict_default_pack = true;
+
+/* Cached values from the top element of the dictionary stack. */
+/* See dstack.h for details. */
+int dsspace; /* see dstack.h */
+const ref_packed *dtop_keys;
+uint dtop_npairs;
+ref *dtop_values;
+
+/* Forward references */
+private int dict_create_contents(P3(uint size, const ref *pdref, bool pack));
+
+/* Debugging statistics */
+#ifdef DEBUG
+long dn_lookups; /* total lookups */
+long dn_1probe; /* successful lookups on only 1 probe */
+long dn_2probe; /* successful lookups on 2 probes */
+/* Wrappers for dict_find and dict_find_name_by_index */
+int real_dict_find(P3(const ref *pdref, const ref *key, ref **ppvalue));
+int
+dict_find(const ref *pdref, const ref *pkey, ref **ppvalue)
+{ dict *pdict = pdref->value.pdict;
+ int code = real_dict_find(pdref, pkey, ppvalue);
+
+ dn_lookups++;
+ if ( r_has_type(pkey, t_name) && dict_is_packed(pdict) )
+ { uint nidx = name_index(pkey);
+ uint hash =
+ hash_mod(dict_name_index_hash(nidx), npairs(pdict)) + 1;
+ if ( pdict->keys.value.packed[hash] ==
+ pt_tag(pt_literal_name) + nidx
+ )
+ dn_1probe++;
+ else if ( pdict->keys.value.packed[hash - 1] ==
+ pt_tag(pt_literal_name) + nidx
+ )
+ dn_2probe++;
+ }
+ /* Do the cheap flag test before the expensive remainder test. */
+ if ( gs_debug_c('d') && !(dn_lookups % 1000) )
+ dprintf3("[d]lookups=%ld 1probe=%ld 2probe=%ld\n",
+ dn_lookups, dn_1probe, dn_2probe);
+ return code;
+}
+#define dict_find real_dict_find
+ref *real_dict_find_name_by_index(P1(uint nidx));
+ref *
+dict_find_name_by_index(uint nidx)
+{ ref *pvalue = real_dict_find_name_by_index(nidx);
+ dict *pdict = dsp->value.pdict;
+
+ dn_lookups++;
+ if ( dict_is_packed(pdict) )
+ { uint hash =
+ hash_mod(dict_name_index_hash(nidx), npairs(pdict)) + 1;
+ if ( pdict->keys.value.packed[hash] ==
+ pt_tag(pt_literal_name) + nidx
+ )
+ dn_1probe++;
+ else if ( pdict->keys.value.packed[hash - 1] ==
+ pt_tag(pt_literal_name) + nidx
+ )
+ dn_2probe++;
+ }
+ /* Do the cheap flag test before the expensive remainder test. */
+ if ( gs_debug_c('d') && !(dn_lookups % 1000) )
+ dprintf3("[d]lookups=%ld 1probe=%ld 2probe=%ld\n",
+ dn_lookups, dn_1probe, dn_2probe);
+ return pvalue;
+}
+#define dict_find_name_by_index real_dict_find_name_by_index
+#endif
+
+/* Create a dictionary in the current VM space. */
+int
+dict_create(uint size, ref *pdref)
+{ ref arr;
+ int code = ialloc_ref_array(&arr, a_all, sizeof(dict) / sizeof(ref),
+ "dict_create");
+ ref dref;
+
+ if ( code < 0 )
+ return code;
+ make_tav_new(&dref, t_dictionary, r_space(&arr) | a_all,
+ pdict, (dict *)arr.value.refs);
+ code = dict_create_contents(size, &dref, dict_default_pack);
+ if ( code < 0 )
+ return code;
+ *pdref = dref;
+ return 0;
+}
+/* Create unpacked keys for a dictionary. */
+/* The keys are allocated in the same VM space as the dictionary. */
+private int
+dict_create_unpacked_keys(uint asize, const ref *pdref)
+{ dict *pdict = pdref->value.pdict;
+ uint space = ialloc_space(idmemory);
+ int code;
+
+ ialloc_set_space(idmemory, r_space(pdref));
+ code = ialloc_ref_array(&pdict->keys, a_all, asize, "dict create unpacked keys");
+ if ( code >= 0 )
+ { ref *kp = pdict->keys.value.refs;
+ ref_mark_new(&pdict->keys);
+ refset_null(kp, asize);
+ r_set_attrs(kp, a_executable); /* wraparound entry */
+ }
+ ialloc_set_space(idmemory, space);
+ return code;
+}
+/* Create the contents (keys and values) of a newly allocated dictionary. */
+/* Allocate in the current VM space, which is assumed to be the same as */
+/* the VM space where the dictionary is allocated. */
+private int
+dict_create_contents(uint size, const ref *pdref, bool pack)
+{ dict *pdict = pdref->value.pdict;
+ uint asize = (size == 0 ? 1 : size);
+ int code;
+ uint i;
+
+ /* If appropriate, round up the actual allocated size to the next */
+ /* higher power of 2, so we can use & instead of %. */
+ dict_round_size(asize);
+ asize++; /* allow room for wraparound entry */
+ code = ialloc_ref_array(&pdict->values, a_all, asize,
+ "dict_create(values)");
+ if ( code < 0 )
+ return code;
+ ref_mark_new(&pdict->values);
+ refset_null(pdict->values.value.refs, asize);
+ if ( pack )
+ { uint ksize = (asize + packed_per_ref - 1) / packed_per_ref;
+ ref arr;
+ ref_packed *pkp;
+ ref_packed *pzp;
+ code = ialloc_ref_array(&arr, a_all, ksize,
+ "dict_create(packed keys)");
+ if ( code < 0 )
+ return code;
+ pkp = (ref_packed *)arr.value.refs;
+ make_tasv_new(&pdict->keys, t_shortarray,
+ r_space(&arr) | a_all,
+ asize, packed, pkp);
+ /* MRS - unrolled loop to avoid SGI compiler bug */
+ for (pzp = pkp, i = 0; i < asize; i++ )
+ *pzp++ = packed_key_empty;
+ for (; i % packed_per_ref; i++ )
+ *pzp++ = packed_key_empty;
+ *pkp = packed_key_deleted; /* wraparound entry */
+ }
+ else /* not packed */
+ { int code = dict_create_unpacked_keys(asize, pdref);
+ if ( code < 0 ) return code;
+ }
+ make_int_new(&pdict->count, 0);
+ make_int_new(&pdict->maxlength, size);
+ return 0;
+}
+
+/*
+ * Ensure that a dictionary uses the unpacked representation for keys.
+ * We can't just use dict_resize, because the values slots mustn't move.
+ */
+int
+dict_unpack(ref *pdref)
+{ dict *pdict = pdref->value.pdict;
+ if ( !dict_is_packed(pdict) )
+ return 0; /* nothing to do */
+ { uint count = nslots(pdict);
+ const ref_packed *okp = pdict->keys.value.packed;
+ ref old_keys;
+ int code;
+ ref *nkp;
+ old_keys = pdict->keys;
+ if ( ref_must_save(&old_keys) )
+ ref_do_save(pdref, &pdict->keys, "dict_unpack(keys)");
+ code = dict_create_unpacked_keys(count, pdref);
+ if ( code < 0 )
+ return code;
+ for ( nkp = pdict->keys.value.refs; count--; okp++, nkp++ )
+ if ( r_packed_is_name(okp) )
+ { packed_get(okp, nkp);
+ ref_mark_new(nkp);
+ }
+ if ( !ref_must_save(&old_keys) )
+ ifree_ref_array(&old_keys, "dict_unpack(old keys)");
+ dict_set_top(); /* just in case */
+ }
+ return 0;
+}
+
+/*
+ * Define a macro for searching a packed dictionary. Free variables:
+ * ref_packed kpack - holds the packed key.
+ * uint hash - holds the hash of the name.
+ * dict *pdict - points to the dictionary.
+ * uint size - holds npairs(pdict).
+ * Note that the macro is *not* enclosed in {}, so that we can access
+ * the values of kbot and kp after leaving the loop.
+ *
+ * We break the macro into two to avoid overflowing some preprocessors.
+ */
+/* packed_search_body also uses kp and kbot as free variables. */
+#define packed_search_body(del,pre,post,miss)\
+ { if_debug2('D', "[D]probe 0x%lx: 0x%x\n", (ulong)kp, *kp);\
+ if ( *kp == kpack )\
+ { pre (pdict->values.value.refs + (kp - kbot));\
+ post;\
+ }\
+ else if ( !r_packed_is_name(kp) )\
+ { /* Empty, deleted, or wraparound. Figure out which. */\
+ if ( *kp == packed_key_empty ) miss;\
+ if ( kp == kbot ) break; /* wrap */\
+ else { del; }\
+ }\
+ }
+#define packed_search_1(del,pre,post,miss)\
+ const ref_packed *kbot = pdict->keys.value.packed;\
+ register const ref_packed *kp;\
+ for ( kp = kbot + hash_mod(hash, size) + 1; ; kp-- )\
+ packed_search_body(del,pre,post,miss)
+#define packed_search_2(del,pre,post,miss)\
+ for ( kp += size; ; kp-- )\
+ packed_search_body(del,pre,post,miss)
+
+/*
+ * Look up a key in a dictionary. Store a pointer to the value slot
+ * where found, or to the (value) slot for inserting.
+ * Return 1 if found, 0 if not and there is room for a new entry,
+ * or e_dictfull if the dictionary is full and the key is missing.
+ * The caller is responsible for ensuring key is not a null.
+ */
+int
+dict_find(const ref *pdref, const ref *pkey,
+ ref **ppvalue /* result is stored here */)
+{ dict *pdict = pdref->value.pdict;
+ uint size = npairs(pdict);
+ register int etype;
+ uint nidx;
+ ref_packed kpack;
+ uint hash;
+ int ktype;
+ /* Compute hash. The only types we bother with are strings, */
+ /* names, and (unlikely, but worth checking for) integers. */
+ switch ( r_type(pkey) )
+ {
+ case t_name:
+ nidx = name_index(pkey);
+nh: hash = dict_name_index_hash(nidx);
+ kpack = packed_name_key(nidx);
+ ktype = t_name;
+ break;
+ case t_string: /* convert to a name first */
+ { ref nref;
+ int code;
+ if ( !r_has_attr(pkey, a_read) )
+ return_error(e_invalidaccess);
+ code = name_ref(pkey->value.bytes, r_size(pkey), &nref, 1);
+ if ( code < 0 )
+ return code;
+ nidx = name_index(&nref);
+ } goto nh;
+ case t_integer:
+ hash = (uint)pkey->value.intval * 30503;
+ kpack = packed_key_impossible;
+ ktype = -1;
+ nidx = 0; /* only to pacify gcc */
+ break;
+ case t_null: /* not allowed as a key */
+ return_error(e_typecheck);
+ default:
+ hash = r_btype(pkey) * 99; /* yech */
+ kpack = packed_key_impossible;
+ ktype = -1;
+ nidx = 0; /* only to pacify gcc */
+ }
+ /* Search the dictionary */
+ if ( dict_is_packed(pdict) )
+ { const ref_packed *pslot = 0;
+ packed_search_1(if ( pslot == 0 ) pslot = kp,
+ *ppvalue =, return 1, goto miss);
+ packed_search_2(if ( pslot == 0 ) pslot = kp,
+ *ppvalue =, return 1, goto miss);
+ /*
+ * Double wraparound, dict is full.
+ * Note that even if there was an empty slot (pslot != 0),
+ * we must return dictfull if length = maxlength.
+ */
+ if ( pslot == 0 || d_length(pdict) == d_maxlength(pdict) )
+ return(e_dictfull);
+ *ppvalue = pdict->values.value.refs + (pslot - kbot);
+ return 0;
+miss: /* Key is missing, not double wrap. See above re dictfull. */
+ if ( d_length(pdict) == d_maxlength(pdict) )
+ return(e_dictfull);
+ if ( pslot == 0 )
+ pslot = kp;
+ *ppvalue = pdict->values.value.refs + (pslot - kbot);
+ return 0;
+ }
+ else
+ { ref *kbot = pdict->keys.value.refs;
+ register ref *kp;
+ ref *pslot = 0;
+ int wrap = 0;
+ for ( kp = kbot + hash_mod(hash, size) + 2; ; )
+ { --kp;
+ if ( (etype = r_type(kp)) == ktype )
+ { /* Fast comparison if both keys are names */
+ if ( name_index(kp) == nidx )
+ { *ppvalue = pdict->values.value.refs + (kp - kbot);
+ return 1;
+ }
+ }
+ else if ( etype == t_null )
+ { /* Empty, deleted, or wraparound. */
+ /* Figure out which. */
+ if ( kp == kbot ) /* wrap */
+ { if ( wrap++ ) /* wrapped twice */
+ { if ( pslot == 0 )
+ return(e_dictfull);
+ break;
+ }
+ kp += size + 1;
+ }
+ else if ( r_has_attr(kp, a_executable) )
+ { /* Deleted entry, save the slot. */
+ if ( pslot == 0 )
+ pslot = kp;
+ }
+ else /* key not found */
+ break;
+ }
+ else
+ { if ( obj_eq(kp, pkey) )
+ { *ppvalue = pdict->values.value.refs + (kp - kbot);
+ return 1;
+ }
+ }
+ }
+ if ( d_length(pdict) == d_maxlength(pdict) )
+ return(e_dictfull);
+ *ppvalue = pdict->values.value.refs +
+ ((pslot != 0 ? pslot : kp) - kbot);
+ return 0;
+ }
+}
+
+/*
+ * Look up a (constant) C string in a dictionary.
+ * Return 1 if found, <= 0 if not.
+ */
+int
+dict_find_string(const ref *pdref, const char _ds *kstr, ref **ppvalue)
+{ int code;
+ ref kname;
+ if ( (code = name_ref((const byte *)kstr, strlen(kstr), &kname, -1)) < 0 )
+ return code;
+ return dict_find(pdref, &kname, ppvalue);
+}
+
+/* Check whether a dictionary is one of the permanent ones on the d-stack. */
+bool
+dict_is_permanent_on_dstack(const ref *pdref)
+{ dict *pdict = pdref->value.pdict;
+ int i;
+ if ( d_stack.extension_size == 0 )
+ { /* Only one block of d-stack. */
+ for ( i = 0; i < min_dstack_size; ++i )
+ if ( dsbot[i].value.pdict == pdict )
+ return true;
+ }
+ else
+ { /* More than one block of d-stack. */
+ uint count = ref_stack_count(&d_stack);
+ for ( i = count - min_dstack_size; i < count; ++i )
+ if ( ref_stack_index(&d_stack, i)->value.pdict == pdict )
+ return true;
+ }
+ return false;
+}
+
+/*
+ * Look up a name on the dictionary stack.
+ * Return the pointer to the value if found, 0 if not.
+ */
+ref *
+dict_find_name_by_index(uint nidx)
+{ ds_ptr pdref = dsp;
+/* Since we know the hash function is the identity function, */
+/* there's no point in allocating a separate variable for it. */
+#define hash dict_name_index_hash(nidx)
+ ref_packed kpack = packed_name_key(nidx);
+ do
+ { dict *pdict = pdref->value.pdict;
+ uint size = npairs(pdict);
+#ifdef DEBUG
+ if ( gs_debug_c('D') )
+ { ref dnref;
+ name_index_ref(nidx, &dnref);
+ dputs("[D]lookup ");
+ debug_print_name(&dnref);
+ dprintf3(" in 0x%lx(%u/%u)\n",
+ (ulong)pdict, dict_length(pdref),
+ dict_maxlength(pdref));
+ }
+#endif
+ if ( dict_is_packed(pdict) )
+ { packed_search_1(DO_NOTHING, return,
+ DO_NOTHING, goto miss);
+ packed_search_2(DO_NOTHING, return,
+ DO_NOTHING, break);
+ miss: ;
+ }
+ else
+ { ref *kbot = pdict->keys.value.refs;
+ register ref *kp;
+ int wrap = 0;
+ /* Search the dictionary */
+ for ( kp = kbot + hash_mod(hash, size) + 2; ; )
+ { --kp;
+ if ( r_has_type(kp, t_name) )
+ { if ( name_index(kp) == nidx )
+ return pdict->values.value.refs +
+ (kp - kbot);
+ }
+ else if ( r_has_type(kp, t_null) )
+ { /* Empty, deleted, or wraparound. */
+ /* Figure out which. */
+ if ( !r_has_attr(kp, a_executable) )
+ break;
+ if ( kp == kbot ) /* wrap */
+ { if ( wrap++ )
+ break; /* 2 wraps */
+ kp += size + 1;
+ }
+ }
+ }
+ }
+ }
+ while ( pdref-- > dsbot );
+ /* The name isn't in the top dictionary block. */
+ /* If there are other blocks, search them now (more slowly). */
+ if ( !d_stack.extension_size ) /* no more blocks */
+ return (ref *)0;
+ { /* We could use the STACK_LOOP macros, but for now, */
+ /* we'll do things the simplest way. */
+ ref key;
+ uint i = dsp + 1 - dsbot;
+ uint size = ref_stack_count(&d_stack);
+ ref *pvalue;
+ name_index_ref(nidx, &key);
+ for ( ; i < size; i++ )
+ { if ( dict_find(ref_stack_index(&d_stack, i),
+ &key, &pvalue) > 0
+ )
+ return pvalue;
+ }
+ }
+ return (ref *)0;
+#undef hash
+}
+
+/*
+ * Enter a key-value pair in a dictionary.
+ * See idict.h for the possible return values.
+ */
+int
+dict_put(ref *pdref /* t_dictionary */, const ref *pkey, const ref *pvalue)
+{ int rcode = 0;
+ int code;
+ ref *pvslot;
+ /* Check the value. */
+ store_check_dest(pdref, pvalue);
+top: if ( (code = dict_find(pdref, pkey, &pvslot)) <= 0 ) /* not found */
+ { /* Check for overflow */
+ dict *pdict = pdref->value.pdict;
+ ref kname;
+ uint index;
+ switch ( code )
+ {
+ case 0:
+ break;
+ case e_dictfull:
+ if ( !dict_auto_expand )
+ return_error(e_dictfull);
+ code = dict_grow(pdref);
+ if ( code < 0 )
+ return code;
+ goto top; /* keep things simple */
+ default: /* e_typecheck */
+ return code;
+ }
+ index = pvslot - pdict->values.value.refs;
+ /* If the key is a string, convert it to a name. */
+ if ( r_has_type(pkey, t_string) )
+ { int code;
+ if ( !r_has_attr(pkey, a_read) )
+ return_error(e_invalidaccess);
+ code = name_from_string(pkey, &kname);
+ if ( code < 0 )
+ return code;
+ pkey = &kname;
+ }
+ if ( dict_is_packed(pdict) )
+ { ref_packed *kp;
+ if ( !r_has_type(pkey, t_name) ||
+ name_index(pkey) > packed_name_max_index
+ )
+ { /* Change to unpacked representation. */
+ int code = dict_unpack(pdref);
+ if ( code < 0 )
+ return code;
+ goto top;
+ }
+ kp = (ref_packed *)(pdict->keys.value.packed + index);
+ if ( ref_must_save(&pdict->keys) )
+ { /* See initial comment for why it is safe */
+ /* not to save the change if the keys */
+ /* array itself is new. */
+ ref_do_save(&pdict->keys, kp, "dict_put(key)");
+ }
+ *kp = pt_tag(pt_literal_name) + name_index(pkey);
+ }
+ else
+ { ref *kp = pdict->keys.value.refs + index;
+ if_debug2('d', "[d]0x%lx: fill key at 0x%lx\n",
+ (ulong)pdict, (ulong)kp);
+ store_check_dest(pdref, pkey);
+ ref_assign_old(&pdict->keys, kp, pkey,
+ "dict_put(key)"); /* set key of pair */
+ }
+ ref_save(pdref, &pdict->count, "dict_put(count)");
+ pdict->count.value.intval++;
+ /* If the key is a name, update its 1-element cache. */
+ if ( r_has_type(pkey, t_name) )
+ { name *pname = pkey->value.pname;
+ if ( pname->pvalue == pv_no_defn &&
+ (pdict == systemdict->value.pdict ||
+ dict_is_permanent_on_dstack(pdref)) &&
+ /* Only set the cache if we aren't inside */
+ /* a save. This way, we never have to */
+ /* undo setting the cache. */
+ alloc_save_level(idmemory) == 0
+ )
+ { /* Set the cache. */
+ if_debug0('d', "[d]set cache\n");
+ pname->pvalue = pvslot;
+ }
+ else
+ { /* The cache can't be used. */
+ if_debug0('d', "[d]no cache\n");
+ pname->pvalue = pv_other;
+ }
+ }
+ rcode = 1;
+ }
+ if_debug8('d', "[d]0x%lx: put key 0x%lx 0x%lx\n value at 0x%lx: old 0x%lx 0x%lx, new 0x%lx 0x%lx\n",
+ (ulong)pdref->value.pdict,
+ ((const ulong *)pkey)[0], ((const ulong *)pkey)[1],
+ (ulong)pvslot,
+ ((const ulong *)pvslot)[0], ((const ulong *)pvslot)[1],
+ ((const ulong *)pvalue)[0], ((const ulong *)pvalue)[1]);
+ ref_assign_old(&pdref->value.pdict->values, pvslot, pvalue,
+ "dict_put(value)");
+ return rcode;
+}
+
+/*
+ * Enter a key-value pair where the key is a (constant) C string.
+ */
+int
+dict_put_string(ref *pdref, const char *kstr, const ref *pvalue)
+{ int code;
+ ref kname;
+ if ( (code = name_ref((const byte *)kstr, strlen(kstr), &kname, 0)) < 0 )
+ return code;
+ return dict_put(pdref, &kname, pvalue);
+}
+
+/* Remove an element from a dictionary. */
+int
+dict_undef(ref *pdref, const ref *pkey)
+{ ref *pvslot;
+ dict *pdict;
+ uint index;
+ if ( dict_find(pdref, pkey, &pvslot) <= 0 )
+ return(e_undefined);
+ /* Remove the entry from the dictionary. */
+ pdict = pdref->value.pdict;
+ index = pvslot - pdict->values.value.refs;
+ if ( dict_is_packed(pdict) )
+ { ref_packed *pkp =
+ (ref_packed *)(pdict->keys.value.packed + index);
+ /* See the initial comment for why it is safe not to save */
+ /* the change if the keys array itself is new. */
+ if ( ref_must_save(&pdict->keys) )
+ ref_do_save(&pdict->keys, pkp, "dict_undef(key)");
+ /* Accumulating deleted entries slows down lookup. */
+ /* Detect the easy case where we can use an empty entry */
+ /* rather than a deleted one, namely, when the next entry */
+ /* in the probe order is empty. */
+ if ( pkp[-1] == packed_key_empty )
+ *pkp = packed_key_empty;
+ else
+ *pkp = packed_key_deleted;
+ }
+ else /* not packed */
+ { ref *kp = pdict->keys.value.refs + index;
+ make_null_old(&pdict->keys, kp, "dict_undef(key)");
+ /* Accumulating deleted entries slows down lookup. */
+ /* Detect the easy case where we can use an empty entry */
+ /* rather than a deleted one, namely, when the next entry */
+ /* in the probe order is empty. */
+ if ( !r_has_type(kp - 1, t_null) || /* full entry */
+ r_has_attr(kp - 1, a_executable) /* deleted or wraparound */
+ )
+ r_set_attrs(kp, a_executable); /* mark as deleted */
+ }
+ ref_save(pdref, &pdict->count, "dict_undef(count)");
+ pdict->count.value.intval--;
+ /* If the key is a name, update its 1-element cache. */
+ if ( r_has_type(pkey, t_name) )
+ { name *pname = pkey->value.pname;
+ if ( pv_valid(pname->pvalue) )
+ {
+#ifdef DEBUG
+ /* Check the the cache is correct. */
+ if ( !dict_is_permanent_on_dstack(pdref) )
+ lprintf1("dict_undef: cached name value pointer 0x%lx is incorrect!\n",
+ (ulong)pname->pvalue);
+#endif
+ /* Clear the cache */
+ pname->pvalue = pv_no_defn;
+ }
+ }
+ make_null_old(&pdict->values, pvslot, "dict_undef(value)");
+ return 0;
+}
+
+/* Return the number of elements in a dictionary. */
+uint
+dict_length(const ref *pdref /* t_dictionary */)
+{ return d_length(pdref->value.pdict);
+}
+
+/* Return the capacity of a dictionary. */
+uint
+dict_maxlength(const ref *pdref /* t_dictionary */)
+{ return d_maxlength(pdref->value.pdict);
+}
+
+/* Copy one dictionary into another. */
+/* If new_only is true, only copy entries whose keys */
+/* aren't already present in the destination. */
+int
+dict_copy_entries(const ref *pdrfrom /* t_dictionary */,
+ ref *pdrto /* t_dictionary */, bool new_only)
+{ int space = r_space(pdrto);
+ int index;
+ ref elt[2];
+ ref *pvslot;
+ int code;
+ if ( space != avm_max )
+ { /* Do the store check before starting the copy. */
+ index = dict_first(pdrfrom);
+ while ( (index = dict_next(pdrfrom, index, elt)) >= 0 )
+ if ( !new_only || dict_find(pdrto, &elt[0], &pvslot) <= 0 )
+ { store_check_space(space, &elt[0]);
+ store_check_space(space, &elt[1]);
+ }
+ }
+ /* Now copy the contents. */
+ index = dict_first(pdrfrom);
+ while ( (index = dict_next(pdrfrom, index, elt)) >= 0 )
+ { if ( new_only && dict_find(pdrto, &elt[0], &pvslot) > 0 )
+ continue;
+ if ( (code = dict_put(pdrto, &elt[0], &elt[1])) < 0 )
+ return code;
+ }
+ return 0;
+}
+
+/* Set the cached values computed from the top entry on the dstack. */
+/* See dstack.h for details. */
+private const ref_packed no_packed_keys[2] =
+ { packed_key_deleted, packed_key_empty };
+void
+dict_set_top(void)
+{ dict *pdict = dsp->value.pdict;
+ if_debug3('d', "[d]dsp = 0x%lx -> 0x%lx, key array type = %d\n",
+ (ulong)dsp, (ulong)pdict, r_type(&pdict->keys));
+ if ( dict_is_packed(pdict) &&
+ r_has_attr(dict_access_ref(dsp), a_read)
+ )
+ { dtop_keys = pdict->keys.value.packed;
+ dtop_npairs = npairs(pdict);
+ dtop_values = pdict->values.value.refs;
+ }
+ else
+ { dtop_keys = no_packed_keys;
+ dtop_npairs = 1;
+ }
+ if ( !r_has_attr(dict_access_ref(dsp), a_write) )
+ dsspace = -1;
+ else
+ dsspace = r_space(dsp);
+}
+
+/* Resize a dictionary. */
+int
+dict_resize(ref *pdref, uint new_size)
+{ dict *pdict = pdref->value.pdict;
+ dict dnew;
+ ref drto;
+ int code;
+ uint space;
+ if ( new_size < d_length(pdict) )
+ { if ( !dict_auto_expand )
+ return_error(e_dictfull);
+ new_size = d_length(pdict);
+ }
+ space = ialloc_space(idmemory);
+ ialloc_set_space(idmemory, r_space(pdref));
+ make_tav_new(&drto, t_dictionary, r_space(pdref) | a_all,
+ pdict, &dnew);
+ if ( (code = dict_create_contents(new_size, &drto, dict_is_packed(pdict))) < 0 )
+ { ialloc_set_space(idmemory, space);
+ return code;
+ }
+ /* We must suppress the store check, in case we are expanding */
+ /* systemdict or another global dictionary that is allowed */
+ /* to reference local objects. */
+ r_set_space(&drto, avm_local);
+ dict_copy(pdref, &drto); /* can't fail */
+ /* Save or free the old dictionary. */
+ if ( ref_must_save(&pdict->values) )
+ ref_do_save(pdref, &pdict->values, "dict_resize(values)");
+ else
+ ifree_ref_array(&pdict->values, "dict_resize(old values)");
+ if ( ref_must_save(&pdict->keys) )
+ ref_do_save(pdref, &pdict->keys, "dict_resize(keys)");
+ else
+ ifree_ref_array(&pdict->keys, "dict_resize(old keys)");
+ ref_assign(&pdict->keys, &dnew.keys);
+ ref_assign(&pdict->values, &dnew.values);
+ ref_save(pdref, &pdict->maxlength, "dict_resize(maxlength)");
+ d_set_maxlength(pdict, new_size);
+ ialloc_set_space(idmemory, space);
+ dict_set_top(); /* just in case this is the top dict */
+ return 0;
+}
+
+/* Grow a dictionary for dict_put. */
+int
+dict_grow(ref *pdref)
+{ dict *pdict = pdref->value.pdict;
+ /* We might have maxlength < npairs, if */
+ /* dict_round_size is true. */
+ ulong new_size = d_maxlength(pdict) * 3 / 2 + 2;
+ if ( new_size > dict_max_size )
+ { if ( d_maxlength(pdict) == dict_max_size )
+ return_error(e_dictfull);
+ new_size = dict_max_size;
+ }
+ if ( new_size > npairs(pdict) )
+ { int code = dict_resize(pdref, (uint)new_size);
+ if ( code < 0 )
+ return code;
+ }
+ else
+ { /* maxlength < npairs, we can grow in place */
+ ref_save(pdref, &pdict->maxlength, "dict_put(maxlength)");
+ d_set_maxlength(pdict, new_size);
+ }
+ return 0;
+}
+
+/* Prepare to enumerate a dictionary. */
+int
+dict_first(const ref *pdref)
+{ return (int)nslots(pdref->value.pdict);
+}
+
+/* Enumerate the next element of a dictionary. */
+int
+dict_next(const ref *pdref, int index, ref *eltp /* ref eltp[2] */)
+{ dict *pdict = pdref->value.pdict;
+ ref *vp = pdict->values.value.refs + index;
+ while ( vp--, --index >= 0 )
+ { array_get(&pdict->keys, (long)index, eltp);
+ /* Make sure this is a valid entry. */
+ if ( r_has_type(eltp, t_name) ||
+ (!dict_is_packed(pdict) && !r_has_type(eltp, t_null))
+ )
+ { eltp[1] = *vp;
+ if_debug6('d', "[d]0x%lx: index %d: %lx %lx, %lx %lx\n",
+ (ulong)pdict, index,
+ ((ulong *)eltp)[0], ((ulong *)eltp)[1],
+ ((ulong *)vp)[0], ((ulong *)vp)[1]);
+ return index;
+ }
+ }
+ return -1; /* no more elements */
+}
+
+/* Return the index of a value within a dictionary. */
+int
+dict_value_index(const ref *pdref, const ref *pvalue)
+{ return (int)(pvalue - pdref->value.pdict->values.value.refs - 1);
+}
+
+/* Return the entry at a given index within a dictionary. */
+/* If the index designates an unoccupied entry, return e_undefined. */
+int
+dict_index_entry(const ref *pdref, int index, ref *eltp /* ref eltp[2] */)
+{ const dict *pdict = pdref->value.pdict;
+ array_get(&pdict->keys, (long)(index + 1), eltp);
+ if ( r_has_type(eltp, t_name) ||
+ (!dict_is_packed(pdict) && !r_has_type(eltp, t_null))
+ )
+ { eltp[1] = pdict->values.value.refs[index + 1];
+ return 0;
+ }
+ return e_undefined;
+}
+
+/* After a garbage collection, scan the permanent dictionaries and */
+/* update the cached value pointers in names. */
+void
+dstack_gc_cleanup(void)
+{ uint count = ref_stack_count(&d_stack);
+ uint dsi;
+ for ( dsi = min_dstack_size; dsi > 0; --dsi )
+ { const dict *pdict =
+ ref_stack_index(&d_stack, count - dsi)->value.pdict;
+ uint size = nslots(pdict);
+ ref *pvalue = pdict->values.value.refs;
+ uint i;
+ for ( i = 0; i < size; ++i, ++pvalue )
+ { ref key;
+ ref *old_pvalue;
+ array_get(&pdict->keys, (long)i, &key);
+ if ( r_has_type(&key, t_name) &&
+ pv_valid(old_pvalue = key.value.pname->pvalue)
+ )
+ { /*
+ * The name only has a single definition,
+ * so it must be this one. Check to see if
+ * no relocation is actually needed; if so,
+ * we can skip the entire dictionary.
+ */
+ if ( old_pvalue == pvalue )
+ { if_debug1('d', "[d]skipping dstack entry %d\n",
+ dsi - 1);
+ break;
+ }
+ /* Update the value pointer. */
+ key.value.pname->pvalue = pvalue;
+ }
+ }
+ }
+}
diff --git a/pstoraster/idict.h b/pstoraster/idict.h
new file mode 100644
index 000000000..d08321544
--- /dev/null
+++ b/pstoraster/idict.h
@@ -0,0 +1,183 @@
+/* Copyright (C) 1989, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* idict.h */
+/* Interfaces for Ghostscript dictionary package */
+
+/*
+ * Contrary to our usual practice, we expose the (first-level)
+ * representation of a dictionary in the interface file,
+ * because it is so important that access checking go fast.
+ * The access attributes for the dictionary are stored in
+ * the values ref.
+ */
+struct dict_s {
+ ref values; /* t_array, values */
+ ref keys; /* t_shortarray or t_array, keys */
+ ref count; /* t_integer, count of occupied entries */
+ /* (length) */
+ ref maxlength; /* t_integer, maxlength as seen by client. */
+};
+
+/*
+ * Define the maximum size of a dictionary.
+ */
+extern const uint dict_max_size;
+
+/*
+ * Define whether dictionaries expand automatically when full. Note that
+ * if dict_auto_expand is true, dict_put, dict_copy, dict_resize, and
+ * dict_grow cannot return e_dictfull; however, they can return e_VMerror.
+ * (dict_find can return e_dictfull even if dict_auto_expand is true.)
+ */
+extern bool dict_auto_expand;
+
+/*
+ * Create a dictionary.
+ */
+int dict_create(P2(uint maxlength, ref *pdref));
+
+/*
+ * Return a pointer to a ref that holds the access attributes
+ * for a dictionary.
+ */
+#define dict_access_ref(pdref) (&(pdref)->value.pdict->values)
+/*
+ * Check a dictionary for read or write permission.
+ * Note: this does NOT check the type of its operand!
+ */
+#define check_dict_read(dref) check_read(*dict_access_ref(&dref))
+#define check_dict_write(dref) check_write(*dict_access_ref(&dref))
+
+/*
+ * Look up a key in a dictionary. Store a pointer to the value slot
+ * where found, or to the (value) slot for inserting.
+ * The caller is responsible for checking that the dictionary is readable.
+ * Return 1 if found, 0 if not and there is room for a new entry,
+ * Failure returns:
+ * e_typecheck if the key is null;
+ * e_invalidaccess if the key is a string lacking read access;
+ * e_VMerror or e_limitcheck if the key is a string and the corresponding
+ * error occurs from attempting to convert it to a name;
+ * e_dictfull if the dictionary is full and the key is missing.
+ */
+int dict_find(P3(const ref *pdref, const ref *key, ref **ppvalue));
+
+/*
+ * Look up a (constant) C string in a dictionary.
+ * Return 1 if found, <= 0 if not.
+ */
+int dict_find_string(P3(const ref *pdref, const char _ds *kstr,
+ ref **ppvalue));
+
+/*
+ * Enter a key-value pair in a dictionary.
+ * The caller is responsible for checking that the dictionary is writable.
+ * Return 1 if this was a new entry, 0 if this replaced an existing entry.
+ * Failure returns are as for dict_find, except that e_dictfull doesn't
+ * occur if the dictionary is full but expandable, plus:
+ * e_invalidaccess for an attempt to store a younger key or value into
+ * an older dictionary;
+ * e_VMerror if a VMerror occurred while trying to expand the
+ * dictionary.
+ */
+int dict_put(P3(ref *pdref, const ref *key, const ref *pvalue));
+
+/*
+ * Enter a key-value pair where the key is a (constant) C string.
+ */
+int dict_put_string(P3(ref *pdref, const char *kstr, const ref *pvalue));
+
+/*
+ * Remove a key-value pair from a dictionary.
+ * Return 0 or e_undefined.
+ */
+int dict_undef(P2(ref *pdref, const ref *key));
+
+/*
+ * Return the number of elements in a dictionary.
+ */
+uint dict_length(P1(const ref *pdref));
+
+/*
+ * Return the capacity of a dictionary.
+ */
+uint dict_maxlength(P1(const ref *pdref));
+
+/*
+ * Copy one dictionary into another.
+ * Return 0 or e_dictfull.
+ * If new_only is true, only copy entries whose keys
+ * aren't already present in the destination.
+ */
+int dict_copy_entries(P3(const ref *dfrom, ref *dto, bool new_only));
+#define dict_copy(dfrom, dto) dict_copy_entries(dfrom, dto, false)
+#define dict_copy_new(dfrom, dto) dict_copy_entries(dfrom, dto, true)
+
+/*
+ * Grow or shrink a dictionary.
+ * Return 0, e_dictfull, or e_VMerror.
+ */
+int dict_resize(P2(ref *pdref, uint newmaxlength));
+
+/*
+ * Grow a dictionary in the same way as dict_put does.
+ * We export this for some special-case code in zop_def.
+ */
+int dict_grow(P1(ref *pdref));
+
+/*
+ * Ensure that a dictionary uses the unpacked representation for keys.
+ * (This is of no interest to ordinary clients.)
+ */
+int dict_unpack(P1(ref *pdref));
+
+/*
+ * Prepare to enumerate a dictionary.
+ * Return an integer suitable for the first call to dict_next.
+ */
+int dict_first(P1(const ref *pdref));
+
+/*
+ * Enumerate the next element of a dictionary.
+ * index is initially the result of a call on dict_first.
+ * Either store a key and value at eltp[0] and eltp[1]
+ * and return an updated index, or return -1
+ * to signal that there are no more elements in the dictionary.
+ */
+int dict_next(P3(const ref *pdref, int index, ref *eltp));
+
+/*
+ * Given a value pointer return by dict_find, return an index that
+ * identifies the entry within the dictionary. (This may, but need not,
+ * be the same as the index returned by dict_next.)
+ * The index is in the range [0..maxlength-1].
+ */
+int dict_value_index(P2(const ref *pdref, const ref *pvalue));
+
+/*
+ * Given an index in [0..maxlength-1], as returned by dict_value_index,
+ * return the key and value, as returned by dict_next.
+ * If the index designates an unoccupied entry, return e_undefined.
+ */
+int dict_index_entry(P3(const ref *pdref, int index, ref *eltp));
diff --git a/pstoraster/idparam.c b/pstoraster/idparam.c
new file mode 100644
index 000000000..76346048b
--- /dev/null
+++ b/pstoraster/idparam.c
@@ -0,0 +1,347 @@
+/* Copyright (C) 1992, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* idparam.c */
+/* Utilities for getting parameters out of dictionaries. */
+#include "memory_.h"
+#include "string_.h" /* for strlen */
+#include "ghost.h"
+#include "errors.h"
+#include "gsmatrix.h" /* for dict_matrix_param */
+#include "gsuid.h"
+#include "idict.h"
+#include "idparam.h" /* interface definition */
+#include "ilevel.h"
+#include "imemory.h" /* for iutil.h */
+#include "iname.h"
+#include "iutil.h"
+#include "oper.h" /* for check_proc */
+#include "store.h" /* for making empty proc */
+
+/* Get a Boolean parameter from a dictionary. */
+/* Return 0 if found, 1 if defaulted, <0 if wrong type. */
+int
+dict_bool_param(const ref *pdict, const char _ds *kstr,
+ bool defaultval, bool *pvalue)
+{ ref *pdval;
+ if ( pdict == 0 || dict_find_string(pdict, kstr, &pdval) <= 0 )
+ { *pvalue = defaultval;
+ return 1;
+ }
+ if ( !r_has_type(pdval, t_boolean) )
+ return_error(e_typecheck);
+ *pvalue = pdval->value.boolval;
+ return 0;
+}
+
+/* Get an integer or null parameter from a dictionary. */
+/* Return 0 if found, 1 if defaulted, <0 if invalid. */
+/* If the parameter is null, return 2 without setting *pvalue. */
+/* Note that the default value may be out of range, in which case */
+/* a missing value will return e_rangecheck rather than 1. */
+int
+dict_int_null_param(const ref *pdict, const char _ds *kstr, int minval,
+ int maxval, int defaultval, int *pvalue)
+{ ref *pdval;
+ int code;
+ long ival;
+ if ( pdict == 0 || dict_find_string(pdict, kstr, &pdval) <= 0 )
+ { ival = defaultval;
+ code = 1;
+ }
+ else
+ { switch ( r_type(pdval) )
+ {
+ case t_integer:
+ ival = pdval->value.intval;
+ break;
+ case t_real:
+ /* Allow an integral real, because Fontographer */
+ /* (which violates the Adobe specs in other ways */
+ /* as well) sometimes generates output that */
+ /* needs this. */
+ if ( pdval->value.realval < minval || pdval->value.realval > maxval )
+ return_error(e_rangecheck);
+ ival = (long)pdval->value.realval;
+ if ( ival != pdval->value.realval )
+ return_error(e_rangecheck);
+ break;
+ case t_null:
+ return 2;
+ default:
+ return_error(e_typecheck);
+ }
+ code = 0;
+ }
+ if ( ival < minval || ival > maxval )
+ return_error(e_rangecheck);
+ *pvalue = (int)ival;
+ return code;
+}
+/* Get an integer parameter from a dictionary. */
+/* Return like dict_int_null_param, but return e_typecheck for null. */
+int
+dict_int_param(const ref *pdict, const char _ds *kstr, int minval, int maxval,
+ int defaultval, int *pvalue)
+{ int code = dict_int_null_param(pdict, kstr, minval, maxval,
+ defaultval, pvalue);
+ return (code == 2 ? gs_note_error(e_typecheck) : code);
+}
+
+/* Get an unsigned integer parameter from a dictionary. */
+/* Return 0 if found, 1 if defaulted, <0 if invalid. */
+/* Note that the default value may be out of range, in which case */
+/* a missing value will return e_rangecheck rather than 1. */
+int
+dict_uint_param(const ref *pdict, const char _ds *kstr,
+ uint minval, uint maxval, uint defaultval, uint *pvalue)
+{ ref *pdval;
+ int code;
+ uint ival;
+ if ( pdict == 0 || dict_find_string(pdict, kstr, &pdval) <= 0 )
+ { ival = defaultval;
+ code = 1;
+ }
+ else
+ { check_type_only(*pdval, t_integer);
+ if ( pdval->value.intval != (uint)pdval->value.intval )
+ return_error(e_rangecheck);
+ ival = (uint)pdval->value.intval;
+ code = 0;
+ }
+ if ( ival < minval || ival > maxval )
+ return_error(e_rangecheck);
+ *pvalue = ival;
+ return code;
+}
+
+/* Get a float parameter from a dictionary. */
+/* Return 0 if found, 1 if defaulted, <0 if wrong type. */
+int
+dict_float_param(const ref *pdict, const char _ds *kstr,
+ floatp defaultval, float *pvalue)
+{ ref *pdval;
+ if ( pdict == 0 || dict_find_string(pdict, kstr, &pdval) <= 0 )
+ { *pvalue = defaultval;
+ return 1;
+ }
+ switch ( r_type(pdval) )
+ {
+ case t_integer: *pvalue = pdval->value.intval; return 0;
+ case t_real: *pvalue = pdval->value.realval; return 0;
+ }
+ return_error(e_typecheck);
+}
+
+/* Get an integer array from a dictionary. */
+/* Return the element count if OK, 0 if missing, <0 if invalid. */
+int
+dict_int_array_param(const ref *pdict, const char _ds *kstr,
+ uint maxlen, int *ivec)
+{ ref *pdval;
+ const ref *pa;
+ int *pi = ivec;
+ uint size;
+ int i;
+ if ( pdict == 0 || dict_find_string(pdict, kstr, &pdval) <= 0 )
+ return 0;
+ if ( !r_has_type(pdval, t_array) )
+ return_error(e_typecheck);
+ size = r_size(pdval);
+ if ( size > maxlen )
+ return_error(e_limitcheck);
+ pa = pdval->value.const_refs;
+ for ( i = 0; i < size; i++, pa++, pi++ )
+ { /* See dict_int_param above for why we allow reals here. */
+ switch ( r_type(pa) )
+ {
+ case t_integer:
+ if ( pa->value.intval != (int)pa->value.intval )
+ return_error(e_rangecheck);
+ *pi = (int)pa->value.intval;
+ break;
+ case t_real:
+ if ( pa->value.realval < min_int ||
+ pa->value.realval > max_int ||
+ pa->value.realval != (int)pa->value.realval
+ )
+ return_error(e_rangecheck);
+ *pi = (int)pa->value.realval;
+ break;
+ default:
+ return_error(e_typecheck);
+ }
+ }
+ return size;
+}
+
+/* Get a float array from a dictionary. */
+/* Return the element count if OK, <0 if invalid. */
+/* If the parameter is missing, then if defaultvec is NULL, return 0; */
+/* if defaultvec is not NULL, copy it into fvec (maxlen elements) */
+/* and return maxlen. */
+int
+dict_float_array_param(const ref *pdict, const char _ds *kstr,
+ uint maxlen, float *fvec, const float *defaultvec)
+{ ref *pdval;
+ uint size;
+ int code;
+ if ( pdict == 0 || dict_find_string(pdict, kstr, &pdval) <= 0 )
+ { if ( defaultvec == NULL ) return 0;
+ memcpy(fvec, defaultvec, maxlen * sizeof(float));
+ return maxlen;
+ }
+ if ( !r_has_type(pdval, t_array) )
+ return_error(e_typecheck);
+ size = r_size(pdval);
+ if ( size > maxlen )
+ return_error(e_limitcheck);
+ code = num_params(pdval->value.refs + size - 1, size, fvec);
+ return (code >= 0 ? size : code);
+}
+
+/*
+ * Get a procedure from a dictionary. If the key is missing,
+ * defaultval = false means substitute t__invalid;
+ * defaultval = true means substitute an empty procedure.
+ * In either case, return 1.
+ */
+int
+dict_proc_param(const ref *pdict, const char _ds *kstr, ref *pproc,
+ bool defaultval)
+{ ref *pdval;
+ if ( pdict == 0 || dict_find_string(pdict, kstr, &pdval) <= 0 )
+ { if ( defaultval )
+ make_empty_const_array(pproc, a_readonly + a_executable);
+ else
+ make_t(pproc, t__invalid);
+ return 1;
+ }
+ check_proc(*pdval);
+ *pproc = *pdval;
+ return 0;
+}
+
+/* Get a matrix from a dictionary. */
+int
+dict_matrix_param(const ref *pdict, const char _ds *kstr, gs_matrix *pmat)
+{ ref *pdval;
+ if ( pdict == 0 || dict_find_string(pdict, kstr, &pdval) <= 0 )
+ return_error(e_typecheck);
+ return read_matrix(pdval, pmat);
+}
+
+/* Get a UniqueID or XUID from a dictionary. */
+/* Return 0 if UniqueID, 1 if XUID, <0 if error. */
+/* If there is no uid, return default. */
+int
+dict_uid_param(const ref *pdict, gs_uid *puid, int defaultval,
+ gs_memory_t *mem)
+{ ref *puniqueid;
+ if ( pdict == 0 )
+ { uid_set_invalid(puid);
+ return defaultval;
+ }
+ /* In a Level 2 environment, check for XUID first. */
+ if ( level2_enabled &&
+ dict_find_string(pdict, "XUID", &puniqueid) > 0
+ )
+ { long *xvalues;
+ uint size, i;
+ if ( !r_has_type(puniqueid, t_array) )
+ return_error(e_typecheck);
+ size = r_size(puniqueid);
+ if ( size == 0 )
+ return_error(e_rangecheck);
+ xvalues = (long *)gs_alloc_byte_array(mem, size, sizeof(long),
+ "get XUID");
+ if ( xvalues == 0 )
+ return_error(e_VMerror);
+ /* Get the values from the XUID array. */
+ for ( i = 0; i < size; i++ )
+ { const ref *pvalue = puniqueid->value.const_refs + i;
+ if ( !r_has_type(pvalue, t_integer) )
+ { gs_free_object(mem, xvalues, "get XUID");
+ return_error(e_typecheck);
+ }
+ xvalues[i] = pvalue->value.intval;
+ }
+ uid_set_XUID(puid, xvalues, size);
+ return 1;
+ }
+ /* If no UniqueID entry, set the UID to invalid, */
+ /* because UniqueID need not be present in all fonts, */
+ /* and if it is, the legal range is 0 to 2^24-1. */
+ if ( dict_find_string(pdict, "UniqueID", &puniqueid) <= 0 )
+ { uid_set_invalid(puid);
+ return defaultval;
+ }
+ else
+ { if ( !r_has_type(puniqueid, t_integer) ||
+ puniqueid->value.intval < 0 ||
+ puniqueid->value.intval > 0xffffffL
+ )
+ return_error(e_rangecheck);
+ /* Apparently fonts created by Fontographer often have */
+ /* a UniqueID of 0, contrary to Adobe's specifications. */
+ /* Treat 0 as equivalent to -1 (no UniqueID). */
+ if ( puniqueid->value.intval == 0 )
+ { uid_set_invalid(puid);
+ return defaultval;
+ }
+ else
+ uid_set_UniqueID(puid, puniqueid->value.intval);
+ }
+ return 0;
+}
+
+/* Check that a UID in a dictionary is equal to an existing, valid UID. */
+bool
+dict_check_uid_param(const ref *pdict, const gs_uid *puid)
+{ ref *puniqueid;
+ if ( uid_is_XUID(puid) )
+ { uint size = uid_XUID_size(puid);
+ uint i;
+ if ( dict_find_string(pdict, "XUID", &puniqueid) <= 0 )
+ return false;
+ if ( !r_has_type(puniqueid, t_array) ||
+ r_size(puniqueid) != size
+ )
+ return false;
+ for ( i = 0; i < size; i++ )
+ { const ref *pvalue = puniqueid->value.const_refs + i;
+ if ( !r_has_type(pvalue, t_integer) )
+ return false;
+ if ( pvalue->value.intval != uid_XUID_values(puid)[i] )
+ return false;
+ }
+ return true;
+ }
+ else
+ { if ( dict_find_string(pdict, "UniqueID", &puniqueid) <= 0 )
+ return false;
+ return (r_has_type(puniqueid, t_integer) &&
+ puniqueid->value.intval == puid->id);
+ }
+}
+
diff --git a/pstoraster/idparam.h b/pstoraster/idparam.h
new file mode 100644
index 000000000..133461726
--- /dev/null
+++ b/pstoraster/idparam.h
@@ -0,0 +1,80 @@
+/* Copyright (C) 1992, 1993, 1994 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* idparam.h */
+/* Interface to idparam.c */
+
+#ifndef gs_matrix_DEFINED
+# define gs_matrix_DEFINED
+typedef struct gs_matrix_s gs_matrix;
+#endif
+
+#ifndef gs_uid_DEFINED
+# define gs_uid_DEFINED
+typedef struct gs_uid_s gs_uid;
+#endif
+
+/*
+ * Unless otherwise noted, all the following routines return 0 for
+ * a valid parameter, 1 for a defaulted parameter, or <0 on error.
+ *
+ * Note that all the dictionary parameter routines take a C string,
+ * not a t_name ref *. Even though this is slower, it means that
+ * the GC doesn't have to worry about finding statically declared
+ * name refs, and we have that many fewer static variables.
+ *
+ * All these routines allow pdict == NULL, which they treat the same as
+ * pdict referring to an empty dictionary. Routines with "null" in their
+ * name return 2 if the parameter is null, without setting *pvalue.
+ */
+int dict_bool_param(P4(const ref *pdict, const char _ds *kstr,
+ bool defaultval, bool *pvalue));
+int dict_int_param(P6(const ref *pdict, const char _ds *kstr,
+ int minval, int maxval, int defaultval, int *pvalue));
+int dict_int_null_param(P6(const ref *pdict, const char _ds *kstr,
+ int minval, int maxval, int defaultval,
+ int *pvalue));
+int dict_uint_param(P6(const ref *pdict, const char _ds *kstr,
+ uint minval, uint maxval, uint defaultval,
+ uint *pvalue));
+int dict_float_param(P4(const ref *pdict, const char _ds *kstr,
+ floatp defaultval, float *pvalue));
+int dict_int_array_param(P4(const ref *pdict, const char _ds *kstr,
+ uint maxlen, int *ivec));
+int dict_float_array_param(P5(const ref *pdict, const char _ds *kstr,
+ uint maxlen, float *fvec,
+ const float *defaultvec));
+/*
+ * For dict_proc_param,
+ * defaultval = false means substitute t__invalid;
+ * defaultval = true means substitute an empty procedure.
+ * In either case, return 1.
+ */
+int dict_proc_param(P4(const ref *pdict, const char _ds *kstr, ref *pproc,
+ bool defaultval));
+int dict_matrix_param(P3(const ref *pdict, const char _ds *kstr,
+ gs_matrix *pmat));
+int dict_uid_param(P4(const ref *pdict, gs_uid *puid, int defaultval,
+ gs_memory_t *mem));
+/* Check that a UID in a dictionary is equal to an existing, valid UID. */
+bool dict_check_uid_param(P2(const ref *pdict, const gs_uid *puid));
diff --git a/pstoraster/ifilter.h b/pstoraster/ifilter.h
new file mode 100644
index 000000000..bb73591fb
--- /dev/null
+++ b/pstoraster/ifilter.h
@@ -0,0 +1,77 @@
+/* Copyright (C) 1994, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* ifilter.h */
+/* Filter creation utilities for Ghostscript */
+/* Requires oper.h, stream.h, strimpl.h */
+#include "istream.h"
+#include "ivmspace.h"
+
+/*
+ * Define the utility procedures for creating filters.
+ * Note that a filter will be allocated in global VM iff the source/target
+ * and all relevant parameters (if any) are in global VM.
+ */
+int filter_read(P5(
+ /* Operand stack pointer that was passed to zfxxx operator */
+ os_ptr op,
+ /* # of parameters to pop off o-stack, */
+ /* not counting the source/target */
+ int npop,
+ /* Template for stream */
+ const stream_template *template,
+ /* Initialized s_xxx_state, 0 if no separate state */
+ stream_state *st,
+ /* Max of space attributes of all parameters referenced by */
+ /* the state, 0 if no such parameters */
+ uint space
+ ));
+int filter_write(P5(os_ptr op, int npop, const stream_template *template,
+ stream_state *st, uint space));
+/*
+ * Define a simplified interface for streams with no parameters or state.
+ * These procedures also pop the top o-stack element if it is a dictionary.
+ */
+int filter_read_simple(P2(os_ptr op, const stream_template *template));
+int filter_write_simple(P2(os_ptr op, const stream_template *template));
+
+/* Mark a filter stream as temporary. */
+/* See stream.h for the meaning of is_temp. */
+void filter_mark_temp(P2(const ref *fop, int is_temp));
+
+/*
+ * Define the state of a procedure-based stream.
+ * Note that procedure-based streams are defined at the Ghostscript
+ * interpreter level, unlike all other stream types which depend only
+ * on the stream package and the memory manager.
+ */
+typedef struct stream_proc_state_s {
+ stream_state_common;
+ bool eof;
+ uint index; /* current index within data */
+ ref proc;
+ ref data;
+} stream_proc_state;
+#define private_st_stream_proc_state() /* in zfproc.c */\
+ gs_private_st_complex_only(st_sproc_state, stream_proc_state,\
+ "procedure stream state", sproc_clear_marks, sproc_enum_ptrs, sproc_reloc_ptrs, 0)
diff --git a/pstoraster/ifont.h b/pstoraster/ifont.h
new file mode 100644
index 000000000..6d1a3b194
--- /dev/null
+++ b/pstoraster/ifont.h
@@ -0,0 +1,83 @@
+/* Copyright (C) 1989, 1991, 1993, 1994, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* ifont.h */
+/* Interpreter internal font representation */
+
+#include "gsstruct.h" /* for extern_st */
+
+/* The external definition of fonts is given in the PostScript manual, */
+/* pp. 91-93. */
+
+/* The structure given below is 'client data' from the viewpoint */
+/* of the library. font-type objects (t_struct/st_font, "t_fontID") */
+/* point directly to a gs_font. */
+
+typedef struct font_data_s {
+ ref dict; /* font dictionary object */
+ ref BuildChar;
+ ref BuildGlyph;
+ ref Encoding;
+ ref CharStrings;
+ union _fs {
+ struct _f1 {
+ ref OtherSubrs; /* from Private dictionary */
+ ref Subrs; /* from Private dictionary */
+ } type1;
+ struct _f42 {
+ ref sfnts;
+ } type42;
+ } u;
+} font_data;
+/*
+ * Even though the interpreter's part of the font data actually
+ * consists of refs, allocating it as refs tends to create sandbars;
+ * since it is always allocated and freed as a unit, we can treat it
+ * as an ordinary structure.
+ */
+/* st_font_data is exported for zdefault_make_font in zfont.c. */
+extern_st(st_font_data);
+#define public_st_font_data() /* in zfont2.c */\
+ gs_public_st_ref_struct(st_font_data, font_data, "font_data")
+#define pfont_data(pfont) ((font_data *)((pfont)->client_data))
+#define pfont_dict(pfont) (&pfont_data(pfont)->dict)
+
+/* Registered encodings, for the benefit of platform fonts, `seac', */
+/* and compiled font initialization. */
+/* This is a t_array ref that points to the encodings. */
+#define registered_Encodings_countof 5
+extern ref registered_Encodings;
+#define registered_Encoding(i) (registered_Encodings.value.refs[i])
+#define StandardEncoding registered_Encoding(0)
+
+/* Internal procedures shared between modules */
+
+/* In zchar.c */
+int font_bbox_param(P2(const ref *pfdict, float bbox[4]));
+
+/* In zfont.c */
+#ifndef gs_font_DEFINED
+# define gs_font_DEFINED
+typedef struct gs_font_s gs_font;
+#endif
+int font_param(P2(const ref *pfdict, gs_font **ppfont));
diff --git a/pstoraster/igc.c b/pstoraster/igc.c
new file mode 100644
index 000000000..e306eb12b
--- /dev/null
+++ b/pstoraster/igc.c
@@ -0,0 +1,1092 @@
+/* Copyright (C) 1993, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* igc.c */
+/* Garbage collector for Ghostscript */
+#include "memory_.h"
+#include "ghost.h"
+#include "errors.h"
+#include "gsexit.h"
+#include "gsmdebug.h"
+#include "gsstruct.h"
+#include "gsutil.h"
+#include "iastate.h"
+#include "isave.h"
+#include "isstate.h"
+#include "idict.h"
+#include "ipacked.h"
+#include "istruct.h"
+#include "igc.h"
+#include "igcstr.h"
+#include "inamedef.h"
+#include "opdef.h" /* for marking oparray names */
+
+/* Import preparation and cleanup routines. */
+extern void name_gc_cleanup(P1(gc_state_t *));
+
+/* Define an entry on the mark stack. */
+typedef struct { void *ptr; uint index; bool is_refs; } ms_entry;
+
+/* Define (a segment of) the mark stack. */
+/* entries[0] has ptr = 0 to indicate the bottom of the stack. */
+/* count additional entries follow this structure. */
+typedef struct gc_mark_stack_s gc_mark_stack;
+struct gc_mark_stack_s {
+ gc_mark_stack *prev;
+ gc_mark_stack *next;
+ uint count;
+ bool on_heap; /* if true, allocated with gs_malloc */
+ ms_entry entries[1];
+};
+/* Define the mark stack sizing parameters. */
+#define ms_size_default 100 /* default, allocated on C stack */
+/* This should probably be defined as a parameter somewhere.... */
+#define ms_size_desired /* for gs_malloc */\
+ ((max_ushort - sizeof(gc_mark_stack)) / sizeof(ms_entry) - 10)
+#define ms_size_min 50 /* min size for segment in free block */
+
+/* Forward references */
+private void gc_init_mark_stack(P2(gc_mark_stack *, uint));
+private void gc_objects_clear_marks(P1(chunk_t *));
+private void gc_unmark_names(P0());
+private int gc_trace(P3(gs_gc_root_t *, gc_state_t *, gc_mark_stack *));
+private int gc_trace_chunk(P3(chunk_t *, gc_state_t *, gc_mark_stack *));
+private bool gc_trace_finish(P1(gc_state_t *));
+private void gc_clear_reloc(P1(chunk_t *));
+private void gc_objects_set_reloc(P1(chunk_t *));
+private void gc_do_reloc(P3(chunk_t *, gs_ref_memory_t *, gc_state_t *));
+private void gc_objects_compact(P2(chunk_t *, gc_state_t *));
+private void gc_free_empty_chunks(P1(gs_ref_memory_t *));
+
+/* Forward references for pointer types */
+private ptr_proc_unmark(ptr_struct_unmark);
+private ptr_proc_mark(ptr_struct_mark);
+private ptr_proc_unmark(ptr_string_unmark);
+private ptr_proc_mark(ptr_string_mark);
+/*ptr_proc_unmark(ptr_ref_unmark);*/ /* in igc.h */
+/*ptr_proc_mark(ptr_ref_mark);*/ /* in igc.h */
+/*ptr_proc_reloc(gs_reloc_struct_ptr, void);*/ /* in gsstruct.h */
+/*ptr_proc_reloc(gs_reloc_ref_ptr, ref_packed);*/ /* in istruct.h */
+
+/* Pointer type descriptors. */
+/* Note that the trace/mark routine has special knowledge of ptr_ref_type */
+/* and ptr_struct_type -- it assumes that no other types have embedded */
+/* pointers. Note also that the reloc procedures for string and ref */
+/* pointers are never called. */
+typedef ptr_proc_reloc((*ptr_proc_reloc_t), void);
+const gs_ptr_procs_t ptr_struct_procs =
+ { ptr_struct_unmark, ptr_struct_mark, (ptr_proc_reloc_t)gs_reloc_struct_ptr };
+const gs_ptr_procs_t ptr_string_procs =
+ { ptr_string_unmark, ptr_string_mark, NULL };
+const gs_ptr_procs_t ptr_const_string_procs =
+ { ptr_string_unmark, ptr_string_mark, NULL };
+const gs_ptr_procs_t ptr_ref_procs =
+ { ptr_ref_unmark, ptr_ref_mark, NULL };
+
+/* ------ Main program ------ */
+
+/* Top level of garbage collector. */
+#ifdef DEBUG
+private void
+end_phase(const char _ds *str)
+{ if ( gs_debug_c('6') )
+ { dprintf1("[6]---------------- end %s ----------------\n",
+ (const char *)str);
+ fflush(dstderr);
+ }
+}
+#else
+# define end_phase(str) DO_NOTHING
+#endif
+void
+gc_top_level(gs_dual_memory_t *dmem, bool global)
+{
+#define nspaces 3
+ gs_ref_memory_t *spaces[nspaces];
+ gs_gc_root_t space_roots[nspaces];
+ int ntrace, ncollect, ispace;
+ gs_ref_memory_t *mem;
+ chunk_t *cp;
+ gs_gc_root_t *rp;
+ gc_state_t state;
+ struct _msd {
+ gc_mark_stack stack;
+ ms_entry body[ms_size_default];
+ } ms_default;
+ gc_mark_stack *mark_stack = &ms_default.stack;
+
+ /* Determine how many spaces we are collecting. */
+
+ spaces[0] = dmem->space_local;
+ spaces[1] = dmem->space_system;
+ spaces[2] = dmem->space_global;
+ if ( dmem->space_global != dmem->space_local )
+ ntrace = 3;
+ else
+ ntrace = 2;
+ ncollect = (global ? ntrace : 1);
+
+#define for_spaces(i, n)\
+ for ( i = 0; i < n; i++ )
+#define for_space_mems(i, mem)\
+ for ( mem = spaces[i]; mem != 0; mem = &mem->saved->state )
+#define for_mem_chunks(mem, cp)\
+ for ( cp = (mem)->cfirst; cp != 0; cp = cp->cnext )
+#define for_space_chunks(i, mem, cp)\
+ for_space_mems(i, mem) for_mem_chunks(mem, cp)
+#define for_chunks(n, mem, cp)\
+ for_spaces(ispace, n) for_space_chunks(ispace, mem, cp)
+#define for_roots(n, mem, rp)\
+ for_spaces(ispace, n)\
+ for ( mem = spaces[ispace], rp = mem->roots; rp != 0; rp = rp->next )
+
+ /* Initialize the state. */
+ state.loc.memory = spaces[0]; /* either one will do */
+ state.loc.cp = 0;
+ state.space_local = spaces[0];
+ state.space_system = spaces[1];
+ state.space_global = spaces[2];
+
+ /* Register the allocators themselves as roots, */
+ /* so we mark and relocate the change and save lists properly. */
+
+ for_spaces(ispace, ntrace)
+ gs_register_struct_root((gs_memory_t *)spaces[ispace],
+ &space_roots[ispace],
+ (void **)&spaces[ispace],
+ "gc_top_level");
+
+ end_phase("register space roots");
+
+#ifdef DEBUG
+
+ /* Pre-validate the state. This shouldn't be necessary.... */
+
+ for_spaces(ispace, ntrace)
+ ialloc_validate_memory(spaces[ispace], &state);
+
+ end_phase("pre-validate pointers");
+
+#endif
+
+ /* Clear marks in spaces to be collected; set them, */
+ /* and clear relocation, in spaces that are only being traced. */
+
+ for_chunks(ncollect, mem, cp)
+ { gc_objects_clear_marks(cp);
+ gc_strings_set_marks(cp, false);
+ }
+ for ( ispace = ncollect; ispace < ntrace; ispace++ )
+ for_space_chunks(ispace, mem, cp)
+ gc_clear_reloc(cp);
+
+ end_phase("clear chunk marks");
+
+ /* Clear the marks of roots. We must do this explicitly, */
+ /* since some roots are not in any chunk. */
+
+ for_roots(ntrace, mem, rp)
+ { void *vptr = *rp->p;
+ if_debug_root('6', "[6]unmarking root", rp);
+ (*rp->ptype->unmark)(vptr, &state);
+ }
+
+ end_phase("clear root marks");
+
+ gc_unmark_names();
+
+ /* Initialize the (default) mark stack. */
+
+ gc_init_mark_stack(&ms_default.stack, ms_size_default);
+ ms_default.stack.prev = 0;
+ ms_default.stack.on_heap = false;
+
+ /* Add all large-enough free blocks to the mark stack. */
+ /* Also initialize the rescan pointers. */
+
+ { gc_mark_stack *end = mark_stack;
+ for_chunks(ntrace, mem, cp)
+ { uint avail = cp->ctop - cp->cbot;
+ if ( avail >= sizeof(gc_mark_stack) + sizeof(ms_entry) *
+ ms_size_min &&
+ !cp->inner_count
+ )
+ { gc_mark_stack *pms = (gc_mark_stack *)cp->cbot;
+ gc_init_mark_stack(pms, (avail - sizeof(gc_mark_stack)) /
+ sizeof(ms_entry));
+ end->next = pms;
+ pms->prev = end;
+ pms->on_heap = false;
+ if_debug2('6', "[6]adding free 0x%lx(%u) to mark stack\n",
+ (ulong)pms, pms->count);
+ }
+ cp->rescan_bot = cp->cend;
+ cp->rescan_top = cp->cbase;
+ }
+ }
+
+ /* Mark from roots. */
+
+ { int more = 0;
+ for_roots(ntrace, mem, rp)
+ { if_debug_root('6', "[6]marking root", rp);
+ more |= gc_trace(rp, &state, mark_stack);
+ }
+
+ end_phase("mark");
+
+ while ( more < 0 ) /* stack overflowed */
+ { more = 0;
+ for_chunks(ntrace, mem, cp)
+ more |= gc_trace_chunk(cp, &state, mark_stack);
+ }
+
+ end_phase("mark overflow");
+ }
+
+ /* Free the mark stack. */
+
+ { gc_mark_stack *pms = mark_stack;
+ while ( pms->next )
+ pms = pms->next;
+ while ( pms )
+ { gc_mark_stack *prev = pms->prev;
+ uint size = sizeof(*pms) + sizeof(ms_entry) * pms->count;
+ if ( pms->on_heap )
+ gs_free(pms, 1, size, "gc mark stack");
+ else
+ gs_alloc_fill(pms, gs_alloc_fill_free, size);
+ pms = prev;
+ }
+ }
+
+ gc_trace_finish(&state);
+
+ end_phase("finish trace");
+
+ /* Set the relocation of roots outside any chunk to o_untraced, */
+ /* so we won't try to relocate pointers to them. */
+ /* (Currently, there aren't any.) */
+
+ /* Disable freeing in the allocators of the spaces we are */
+ /* collecting, so finalization procedures won't cause problems. */
+ { int i;
+ for_spaces(i, ncollect)
+ gs_enable_free((gs_memory_t *)spaces[i], false);
+ }
+
+ /* Compute relocation based on marks, in the spaces */
+ /* we are going to compact. Also finalize freed objects. */
+
+ for_chunks(ncollect, mem, cp)
+ { gc_objects_set_reloc(cp);
+ gc_strings_set_reloc(cp);
+ }
+
+ /* Re-enable freeing. */
+ { int i;
+ for_spaces(i, ncollect)
+ gs_enable_free((gs_memory_t *)spaces[i], true);
+ }
+
+ end_phase("set reloc");
+
+ /* Remove unmarked names, and relocate name string pointers. */
+
+ name_gc_cleanup(&state);
+
+ end_phase("clean up names");
+
+ /* Relocate pointers. */
+
+ for_chunks(ntrace, mem, cp)
+ gc_do_reloc(cp, mem, &state);
+
+ end_phase("relocate chunks");
+
+ for_roots(ntrace, mem, rp)
+ { if_debug3('6', "[6]relocating root 0x%lx: 0x%lx -> 0x%lx\n",
+ (ulong)rp, (ulong)rp->p, (ulong)*rp->p);
+ if ( rp->ptype == ptr_ref_type )
+ { ref *pref = (ref *)*rp->p;
+ gs_reloc_refs((ref_packed *)pref,
+ (ref_packed *)(pref + 1),
+ &state);
+ }
+ else
+ *rp->p = (*rp->ptype->reloc)(*rp->p, &state);
+ }
+
+ end_phase("relocate roots");
+
+ /* Compact data. We only do this for spaces we are collecting. */
+
+ for_spaces(ispace, ncollect)
+ { for_space_mems(ispace, mem)
+ { for_mem_chunks(mem, cp)
+ { if_debug_chunk('6', "[6]compacting chunk", cp);
+ gc_objects_compact(cp, &state);
+ gc_strings_compact(cp);
+ if_debug_chunk('6', "[6]after compaction:", cp);
+ if ( mem->pcc == cp )
+ mem->cc = *cp;
+ }
+ mem->saved = mem->reloc_saved;
+ ialloc_reset_free(mem);
+ }
+ }
+
+ end_phase("compact");
+
+ /* Free empty chunks. */
+
+ for_spaces(ispace, ncollect)
+ for_space_mems(ispace, mem)
+ gc_free_empty_chunks(mem);
+
+ end_phase("free empty chunks");
+
+ /*
+ * Update previous_status to reflect any freed chunks,
+ * and set inherited to the negative of allocated,
+ * so it has no effect. We must update previous_status by
+ * working back-to-front along the save chain, using pointer reversal.
+ * (We could update inherited in any order, since it only uses
+ * information local to the individual save level.)
+ */
+
+ for_spaces(ispace, ncollect)
+ { /* Reverse the pointers. */
+ alloc_save_t *curr;
+ alloc_save_t *prev = 0;
+ alloc_save_t *next;
+ gs_memory_status_t total;
+ for ( curr = spaces[ispace]->saved; curr != 0;
+ prev = curr, curr = next
+ )
+ { next = curr->state.saved;
+ curr->state.saved = prev;
+ }
+ /* Now work the other way, accumulating the values. */
+ total.allocated = 0, total.used = 0;
+ for ( curr = prev, prev = 0; curr != 0;
+ prev = curr, curr = next
+ )
+ { mem = &curr->state;
+ next = mem->saved;
+ mem->saved = prev;
+ mem->previous_status = total;
+ if_debug3('6',
+ "[6]0x%lx previous allocated=%lu, used=%lu\n",
+ (ulong)mem, total.allocated, total.used);
+ gs_memory_status((gs_memory_t *)mem, &total);
+ mem->gc_allocated = mem->allocated + total.allocated;
+ mem->inherited = -mem->allocated;
+ }
+ mem = spaces[ispace];
+ mem->previous_status = total;
+ mem->gc_allocated = mem->allocated + total.allocated;
+ if_debug3('6', "[6]0x%lx previous allocated=%lu, used=%lu\n",
+ (ulong)mem, total.allocated, total.used);
+ }
+
+ end_phase("update stats");
+
+ /* Clear marks in spaces we didn't compact. */
+
+ for ( ispace = ncollect; ispace < ntrace; ispace++ )
+ for_space_chunks(ispace, mem, cp)
+ gc_objects_clear_marks(cp);
+
+ end_phase("post-clear marks");
+
+ /* Unregister the allocator roots. */
+
+ for_spaces(ispace, ntrace)
+ gs_unregister_root((gs_memory_t *)spaces[ispace],
+ &space_roots[ispace], "gc_top_level");
+
+ end_phase("unregister space roots");
+
+#ifdef DEBUG
+
+ /* Validate the state. This shouldn't be necessary.... */
+
+ for_spaces(ispace, ntrace)
+ ialloc_validate_memory(spaces[ispace], &state);
+
+ end_phase("validate pointers");
+
+#endif
+}
+
+/* ------ Debugging utilities ------ */
+
+/* Validate a pointer to an object header. */
+#ifdef DEBUG
+# define debug_check_object(pre, cp, gcst)\
+ ialloc_validate_object((pre) + 1, cp, gcst)
+#else
+# define debug_check_object(pre, cp, gcst) DO_NOTHING
+#endif
+
+/* ------ Unmarking phase ------ */
+
+/* Unmark a single struct. */
+private void
+ptr_struct_unmark(void *vptr, gc_state_t *ignored)
+{ if ( vptr != 0 )
+ o_set_unmarked(((obj_header_t *)vptr - 1));
+}
+
+/* Unmark a single string. */
+private void
+ptr_string_unmark(void *vptr, gc_state_t *gcst)
+{ discard(gc_string_mark(((gs_string *)vptr)->data,
+ ((gs_string *)vptr)->size,
+ false, gcst));
+}
+
+/* Unmark the objects in a chunk. */
+private void
+gc_objects_clear_marks(chunk_t *cp)
+{ if_debug_chunk('6', "[6]unmarking chunk", cp);
+ SCAN_CHUNK_OBJECTS(cp)
+ DO_ALL
+ struct_proc_clear_marks((*proc)) =
+ pre->o_type->clear_marks;
+ debug_check_object(pre, cp, NULL);
+ if_debug3('7', " [7](un)marking %s(%lu) 0x%lx\n",
+ struct_type_name_string(pre->o_type),
+ (ulong)size, (ulong)pre);
+ o_set_unmarked(pre);
+ if ( proc != 0 )
+ (*proc)(pre + 1, size);
+ END_OBJECTS_SCAN
+}
+
+/* Mark 0- and 1-character names, and those referenced from the */
+/* op_array_nx_table, and unmark all the rest. */
+private void
+gc_unmark_names()
+{ register uint i;
+ name_unmark_all();
+ for ( i = 0; i < op_array_table_global.count; i++ )
+ { uint nidx = op_array_table_global.nx_table[i];
+ name_index_ptr(nidx)->mark = 1;
+ }
+ for ( i = 0; i < op_array_table_local.count; i++ )
+ { uint nidx = op_array_table_local.nx_table[i];
+ name_index_ptr(nidx)->mark = 1;
+ }
+}
+
+/* ------ Marking phase ------ */
+
+/* Initialize (a segment of) the mark stack. */
+private void
+gc_init_mark_stack(gc_mark_stack *pms, uint count)
+{ pms->next = 0;
+ pms->count = count;
+ pms->entries[0].ptr = 0;
+ pms->entries[0].index = 0;
+ pms->entries[0].is_refs = false;
+}
+
+/* Mark starting from all marked objects in the interval of a chunk */
+/* needing rescanning. */
+private int
+gc_trace_chunk(chunk_t *cp, gc_state_t *pstate, gc_mark_stack *pmstack)
+{ byte *sbot = cp->rescan_bot;
+ byte *stop = cp->rescan_top;
+ gs_gc_root_t root;
+ void *comp;
+ int more = 0;
+
+ if ( sbot > stop )
+ return 0;
+ root.p = &comp;
+ if_debug_chunk('6', "[6]marking from chunk", cp);
+ cp->rescan_bot = cp->cend;
+ cp->rescan_top = cp->cbase;
+ SCAN_CHUNK_OBJECTS(cp)
+ DO_ALL
+ if ( (byte *)(pre + 1) + size < sbot )
+ ;
+ else if ( (byte *)(pre + 1) > stop )
+ return more; /* 'break' won't work here */
+ else
+ { if_debug2('7', " [7]scanning/marking 0x%lx(%lu)\n",
+ (ulong)pre, (ulong)size);
+ if ( pre->o_type == &st_refs )
+ { ref_packed *rp = (ref_packed *)(pre + 1);
+ char *end = (char *)rp + size;
+ root.ptype = ptr_ref_type;
+ while ( (char *)rp < end )
+ { comp = rp;
+ if ( r_is_packed(rp) )
+ { if ( r_has_pmark(rp) )
+ { r_clear_pmark(rp);
+ more |= gc_trace(&root, pstate,
+ pmstack);
+ }
+ rp++;
+ }
+ else
+ { if ( r_has_attr((ref *)rp, l_mark) )
+ { r_clear_attrs((ref *)rp, l_mark);
+ more |= gc_trace(&root, pstate,
+ pmstack);
+ }
+ rp += packed_per_ref;
+ }
+ }
+ }
+ else if ( !o_is_unmarked(pre) )
+ { struct_proc_clear_marks((*proc)) =
+ pre->o_type->clear_marks;
+ root.ptype = ptr_struct_type;
+ comp = pre + 1;
+ if ( !o_is_untraced(pre) )
+ o_set_unmarked(pre);
+ if ( proc != 0 )
+ (*proc)(comp, size);
+ more |= gc_trace(&root, pstate, pmstack);
+ }
+ }
+ END_OBJECTS_SCAN
+ return more;
+}
+
+/* Recursively mark from a (root) pointer. */
+/* Return -1 if we overflowed the mark stack, */
+/* 0 if we completed successfully without marking any new objects, */
+/* 1 if we completed and marked some new objects. */
+private int
+gc_trace(gs_gc_root_t *rp, gc_state_t *pstate, gc_mark_stack *pmstack)
+{ gc_mark_stack *pms = pmstack;
+ ms_entry *sp = pms->entries + 1;
+ /* We stop the mark stack 1 entry early, because we store into */
+ /* the entry beyond the top. */
+ ms_entry *stop = sp + pms->count - 2;
+#ifdef DEBUG
+ ulong prev_depth = 0;
+#endif
+ int new = 0;
+ void *nptr = *rp->p;
+#define mark_name(nidx, pname)\
+ { if ( !pname->mark )\
+ { pname->mark = 1;\
+ new |= 1;\
+ if_debug2('8', " [8]marked name 0x%lx(%u)\n", (ulong)pname, nidx);\
+ }\
+ }
+
+ if ( nptr == 0 )
+ return 0;
+
+ /* Initialize the stack */
+ sp->ptr = nptr;
+ if ( rp->ptype == ptr_ref_type )
+ sp->index = 1, sp->is_refs = true;
+ else
+ { sp->index = 0, sp->is_refs = false;
+ if ( (*rp->ptype->mark)(nptr, pstate) )
+ new |= 1;
+ }
+ for ( ; ; )
+ { gs_ptr_type_t ptp;
+#ifdef DEBUG
+ static const char *dots = "..........";
+ ulong dot_depth;
+#define depth_dots\
+ ((dot_depth = sp - pms->entries - 1 + prev_depth) >= 10 ? dots :\
+ dots + 10 - dot_depth)
+#endif
+ if ( !sp->is_refs ) /* struct */
+ { obj_header_t *ptr = sp->ptr;
+ ulong osize;
+ struct_proc_enum_ptrs((*mproc));
+
+ if ( ptr == 0 )
+ { /* We've reached the bottom of a stack segment. */
+ pms = pms->prev;
+ if ( pms == 0 )
+ break; /* all done */
+#ifdef DEBUG
+ prev_depth -= pms->count - 1;
+#endif
+ stop = pms->entries + pms->count - 1;
+ sp = stop;
+ continue;
+ }
+ debug_check_object(ptr - 1, NULL, NULL);
+ osize = pre_obj_contents_size(ptr - 1);
+ if_debug4('7', " [7]%smarking %s 0x%lx[%u]",
+ depth_dots,
+ struct_type_name_string(ptr[-1].o_type),
+ (ulong)ptr, sp->index);
+ mproc = ptr[-1].o_type->enum_ptrs;
+ ptp = (mproc == 0 ? (gs_ptr_type_t)0 :
+ (*mproc)(ptr, osize, sp->index++, &nptr));
+ if ( ptp == NULL ) /* done with structure */
+ { if_debug0('7', " - done\n");
+ sp--;
+ continue;
+ }
+ if_debug1('7', " = 0x%lx\n", (ulong)nptr);
+ /* Descend into nptr, whose pointer type is ptp. */
+ if ( ptp == ptr_ref_type )
+ { sp[1].index = 1;
+ sp[1].is_refs = true;
+ }
+ else if ( ptp != ptr_struct_type )
+ { /* We assume this is some non-pointer- */
+ /* containing type. */
+ if ( (*ptp->mark)(nptr, pstate) )
+ new |= 1;
+ continue;
+ }
+ else
+ { sp[1].index = 0;
+ sp[1].is_refs = false;
+ }
+ }
+ else /* refs */
+ { ref_packed *pptr = sp->ptr;
+
+ if ( !sp->index )
+ { --sp;
+ continue;
+ }
+ --(sp->index);
+ if_debug3('8', " [8]%smarking refs 0x%lx[%u]\n",
+ depth_dots, (ulong)pptr, sp->index);
+#define rptr ((ref *)pptr)
+ if ( r_is_packed(rptr) )
+ { sp->ptr = pptr + 1;
+ if ( r_has_pmark(pptr) )
+ continue;
+ r_set_pmark(pptr);
+ new |= 1;
+ if ( r_packed_is_name(pptr) )
+ { uint nidx = packed_name_index(pptr);
+ name *pname = name_index_ptr(nidx);
+ mark_name(nidx, pname);
+ }
+ continue;
+ }
+ sp->ptr = rptr + 1;
+ if ( r_has_attr(rptr, l_mark) )
+ continue;
+ r_set_attrs(rptr, l_mark);
+ new |= 1;
+ switch ( r_type(rptr) )
+ {
+ /* Struct cases */
+ case t_file:
+ nptr = rptr->value.pfile;
+rs: if ( r_is_foreign(rptr) )
+ continue;
+ sp[1].is_refs = false;
+ sp[1].index = 0;
+ ptp = ptr_struct_type;
+ break;
+ case t_device:
+ nptr = rptr->value.pdevice; goto rs;
+ case t_fontID:
+ case t_struct:
+ case t_astruct:
+ nptr = rptr->value.pstruct; goto rs;
+ /* Non-trivial non-struct cases */
+ case t_dictionary:
+ nptr = rptr->value.pdict;
+ sp[1].index = sizeof(dict) / sizeof(ref);
+ goto rrp;
+ case t_array:
+ nptr = rptr->value.refs;
+rr: if ( (sp[1].index = r_size(rptr)) == 0 )
+ { /* Set the base pointer to 0, */
+ /* so we never try to relocate it. */
+ rptr->value.refs = 0;
+ continue;
+ }
+rrp: if ( r_is_foreign(rptr) )
+ continue;
+rrc: sp[1].is_refs = true;
+ break;
+ case t_mixedarray: case t_shortarray:
+ nptr = (void *)rptr->value.packed; /* discard const */
+ goto rr;
+ case t_name:
+ mark_name(name_index(rptr), rptr->value.pname);
+ continue;
+ case t_string:
+ if ( r_is_foreign(rptr) )
+ continue;
+ if ( gc_string_mark(rptr->value.bytes, r_size(rptr), true, pstate) )
+ new |= 1;
+ continue;
+ case t_oparray:
+ nptr = (void *)rptr->value.const_refs; /* discard const */
+ sp[1].index = 1;
+ goto rrc;
+ default: /* includes packed refs */
+ continue;
+ }
+#undef rptr
+ }
+ if ( sp == stop )
+ { /* The current segment is full. */
+ if ( pms->next == 0 )
+ { /* Try to allocate another segment. */
+ uint count;
+ for ( count = ms_size_desired;
+ count >= ms_size_min;
+ count >>= 1
+ )
+ { pms->next =
+ gs_malloc(1, sizeof(gc_mark_stack) +
+ sizeof(ms_entry) * count,
+ "gc mark stack");
+ if ( pms->next != 0 )
+ break;
+ }
+ if ( pms->next == 0 )
+ { /* The mark stack overflowed. */
+ byte *cptr = sp->ptr; /* container */
+ chunk_t *cp = gc_locate(cptr, pstate);
+
+ if ( cp == 0 )
+ { /* We were tracing outside collectible */
+ /* storage. This can't happen. */
+ lprintf1("mark stack overflowed while outside collectible space at 0x%lx!\n",
+ (ulong)cptr);
+ gs_abort();
+ }
+ if ( cptr < cp->rescan_bot )
+ cp->rescan_bot = cptr, new = -1;
+ if ( cptr > cp->rescan_top )
+ cp->rescan_top = cptr, new = -1;
+ new |= 1;
+ continue;
+ }
+ gc_init_mark_stack(pms->next, count);
+ pms->next->prev = pms;
+ pms->next->on_heap = true;
+ }
+#ifdef DEBUG
+ prev_depth += pms->count - 1;
+#endif
+ pms = pms->next;
+ stop = pms->entries + pms->count - 1;
+ pms->entries[1] = sp[1];
+ sp = pms->entries;
+ }
+ /* index and is_refs are already set */
+ if ( !sp[1].is_refs )
+ { if ( !(*ptp->mark)(nptr, pstate) )
+ continue;
+ new |= 1;
+ }
+ (++sp)->ptr = nptr;
+ }
+ return new;
+}
+
+/* Mark a struct. Return true if new mark. */
+private bool
+ptr_struct_mark(void *vptr, gc_state_t *ignored)
+{ obj_header_t *ptr = vptr;
+ if ( vptr == 0 )
+ return false;
+ ptr--; /* point to header */
+ if ( !o_is_unmarked(ptr) )
+ return false;
+ o_mark(ptr);
+ return true;
+}
+
+/* Mark a string. Return true if new mark. */
+private bool
+ptr_string_mark(void *vptr, gc_state_t *gcst)
+{ return gc_string_mark(((gs_string *)vptr)->data,
+ ((gs_string *)vptr)->size,
+ true, gcst);
+}
+
+/* Finish tracing by marking names. */
+private bool
+gc_trace_finish(gc_state_t *pstate)
+{ uint nidx = 0;
+ bool marked = false;
+ while ( (nidx = name_next_valid_index(nidx)) != 0 )
+ { name *pname = name_index_ptr(nidx);
+ if ( pname->mark )
+ { if ( !pname->foreign_string && gc_string_mark(pname->string_bytes, pname->string_size, true, pstate) )
+ marked = true;
+ marked |= ptr_struct_mark(name_index_ptr_sub_table(nidx, pname), pstate);
+ }
+ }
+ return marked;
+}
+
+/* ------ Relocation planning phase ------ */
+
+/* Initialize the relocation information in the chunk header. */
+private void
+gc_init_reloc(chunk_t *cp)
+{ chunk_head_t *chead = cp->chead;
+ chead->dest = cp->cbase;
+ chead->free.o_back =
+ offset_of(chunk_head_t, free) >> obj_back_shift;
+ chead->free.o_size = sizeof(obj_header_t);
+ chead->free.o_nreloc = 0;
+}
+
+/* Set marks and clear relocation for chunks that won't be compacted. */
+private void
+gc_clear_reloc(chunk_t *cp)
+{ gc_init_reloc(cp);
+ SCAN_CHUNK_OBJECTS(cp)
+ DO_ALL
+ const struct_shared_procs_t _ds *procs =
+ pre->o_type->shared;
+ if ( procs != 0 )
+ (*procs->clear_reloc)(pre, size);
+ o_set_untraced(pre);
+ END_OBJECTS_SCAN
+ gc_strings_set_marks(cp, true);
+ gc_strings_clear_reloc(cp);
+}
+
+/* Set the relocation for the objects in a chunk. */
+/* This will never be called for a chunk with any o_untraced objects. */
+private void
+gc_objects_set_reloc(chunk_t *cp)
+{ uint reloc = 0;
+ chunk_head_t *chead = cp->chead;
+ byte *pfree = (byte *)&chead->free; /* most recent free object */
+ if_debug_chunk('6', "[6]setting reloc for chunk", cp);
+ gc_init_reloc(cp);
+ SCAN_CHUNK_OBJECTS(cp)
+ DO_ALL
+ struct_proc_finalize((*finalize));
+ const struct_shared_procs_t _ds *procs =
+ pre->o_type->shared;
+ if ( (procs == 0 ? o_is_unmarked(pre) :
+ !(*procs->set_reloc)(pre, reloc, size))
+ )
+ { /* Free object */
+ reloc += sizeof(obj_header_t) + obj_align_round(size);
+ if ( (finalize = pre->o_type->finalize) != 0 )
+ { if_debug2('u', "[u]GC finalizing %s 0x%lx\n",
+ struct_type_name_string(pre->o_type),
+ (ulong)(pre + 1));
+ (*finalize)(pre + 1);
+ }
+ if ( pre->o_large )
+ { /* We should chop this up into small */
+ /* free blocks, but there's no value */
+ /* in doing this right now. */
+ o_set_unmarked_large(pre);
+ }
+ else
+ { pfree = (byte *)pre;
+ pre->o_back =
+ (pfree - (byte *)chead) >> obj_back_shift;
+ pre->o_nreloc = reloc;
+ }
+ if_debug3('7', " [7]at 0x%lx, unmarked %lu, new reloc = %u\n",
+ (ulong)pre, (ulong)size, reloc);
+ }
+ else
+ { /* Useful object */
+ debug_check_object(pre, cp, NULL);
+ if ( pre->o_large )
+ { if ( o_is_unmarked_large(pre) )
+ o_mark_large(pre);
+ }
+ else
+ pre->o_back =
+ ((byte *)pre - pfree) >> obj_back_shift;
+ }
+ END_OBJECTS_SCAN
+#ifdef DEBUG
+ if ( reloc != 0 )
+ { if_debug1('6', "[6]freed %u", reloc);
+ if_debug_chunk('6', " in", cp);
+ }
+#endif
+}
+
+/* ------ Relocation phase ------ */
+
+/* Relocate the pointers in all the objects in a chunk. */
+private void
+gc_do_reloc(chunk_t *cp, gs_ref_memory_t *mem, gc_state_t *pstate)
+{ chunk_head_t *chead = cp->chead;
+ if_debug_chunk('6', "[6]relocating in chunk", cp);
+ SCAN_CHUNK_OBJECTS(cp)
+ DO_ALL
+ /* We need to relocate the pointers in an object iff */
+ /* it is o_untraced, or it is a useful object. */
+ /* An object is free iff its back pointer points to */
+ /* the chunk_head structure. */
+ if ( o_is_untraced(pre) ||
+ (pre->o_large ? !o_is_unmarked(pre) :
+ pre->o_back << obj_back_shift !=
+ (byte *)pre - (byte *)chead)
+ )
+ { struct_proc_reloc_ptrs((*proc)) =
+ pre->o_type->reloc_ptrs;
+ if_debug3('7',
+ " [7]relocating ptrs in %s(%lu) 0x%lx\n",
+ struct_type_name_string(pre->o_type),
+ (ulong)size, (ulong)pre);
+ if ( proc != 0 )
+ (*proc)(pre + 1, size, pstate);
+ }
+ END_OBJECTS_SCAN
+}
+
+/* Print pointer relocation if debugging. */
+/* We have to provide this procedure even if DEBUG is not defined, */
+/* in case one of the other GC modules was compiled with DEBUG. */
+void *
+print_reloc_proc(const void *obj, const char *cname, void *robj)
+{ if_debug3('9', " [9]relocate %s * 0x%lx to 0x%lx\n",
+ cname, (ulong)obj, (ulong)robj);
+ return robj;
+}
+
+/* Relocate a pointer to an (aligned) object. */
+/* See gsmemory.h for why the argument is const and the result is not. */
+void /*obj_header_t*/ *
+gs_reloc_struct_ptr(const void /*obj_header_t*/ *obj, gc_state_t *gcst)
+{ const void *robj;
+
+ if ( obj == 0 )
+ return print_reloc(obj, "NULL", 0);
+#define optr ((const obj_header_t *)obj)
+ debug_check_object(optr - 1, NULL, gcst);
+ /* The following should be a conditional expression, */
+ /* but Sun's cc compiler can't handle it. */
+ if ( optr[-1].o_large )
+ robj = obj;
+ else
+ { uint back = optr[-1].o_back;
+ if ( back == o_untraced )
+ robj = obj;
+ else
+ {
+#ifdef DEBUG
+ /* Do some sanity checking. */
+ if ( back > gcst->space_local->chunk_size >> obj_back_shift )
+ { lprintf2("Invalid back pointer %u at 0x%lx!\n",
+ back, (ulong)obj);
+ gs_abort();
+ }
+#endif
+ { const obj_header_t *pfree = (const obj_header_t *)
+ ((const char *)(optr - 1) -
+ (back << obj_back_shift));
+ const chunk_head_t *chead = (const chunk_head_t *)
+ ((const char *)pfree -
+ (pfree->o_back << obj_back_shift));
+ robj = chead->dest +
+ ((const char *)obj - (const char *)(chead + 1) -
+ pfree->o_nreloc);
+ }
+ }
+ }
+ return print_reloc(obj,
+ struct_type_name_string(optr[-1].o_type),
+ (void *)robj); /* discard const */
+#undef optr
+}
+
+/* ------ Compaction phase ------ */
+
+/* Compact the objects in a chunk. */
+/* This will never be called for a chunk with any o_untraced objects. */
+private void
+gc_objects_compact(chunk_t *cp, gc_state_t *gcst)
+{ chunk_head_t *chead = cp->chead;
+ obj_header_t *dpre = (obj_header_t *)chead->dest;
+ SCAN_CHUNK_OBJECTS(cp)
+ DO_ALL
+ /* An object is free iff its back pointer points to */
+ /* the chunk_head structure. */
+ if ( (pre->o_large ? !o_is_unmarked(pre) :
+ pre->o_back << obj_back_shift !=
+ (byte *)pre - (byte *)chead)
+ )
+ { const struct_shared_procs_t _ds *procs =
+ pre->o_type->shared;
+ debug_check_object(pre, cp, gcst);
+ if_debug4('7',
+ " [7]compacting %s 0x%lx(%lu) to 0x%lx\n",
+ struct_type_name_string(pre->o_type),
+ (ulong)pre, (ulong)size, (ulong)dpre);
+ if ( procs == 0 )
+ { if ( dpre != pre )
+ memmove(dpre, pre,
+ sizeof(obj_header_t) + size);
+ }
+ else
+ (*procs->compact)(pre, dpre, size);
+ dpre = (obj_header_t *)
+ ((byte *)dpre + obj_size_round(size));
+ }
+ END_OBJECTS_SCAN
+ if ( cp->outer == 0 && chead->dest != cp->cbase )
+ dpre = (obj_header_t *)cp->cbase; /* compacted this chunk into another */
+ gs_alloc_fill(dpre, gs_alloc_fill_collected, cp->cbot - (byte *)dpre);
+ cp->cbot = (byte *)dpre;
+ cp->rcur = 0;
+ cp->rtop = 0; /* just to be sure */
+}
+
+/* ------ Cleanup ------ */
+
+/* Free empty chunks. */
+private void
+gc_free_empty_chunks(gs_ref_memory_t *mem)
+{ chunk_t *cp;
+ chunk_t *csucc;
+ /* Free the chunks in reverse order, */
+ /* to encourage LIFO behavior. */
+ for ( cp = mem->clast; cp != 0; cp = csucc )
+ { /* Make sure this isn't an inner chunk, */
+ /* or a chunk that has inner chunks. */
+ csucc = cp->cprev; /* save before freeing */
+ if ( cp->cbot == cp->cbase && cp->ctop == cp->climit &&
+ cp->outer == 0 && cp->inner_count == 0
+ )
+ { alloc_free_chunk(cp, mem);
+ if ( mem->pcc == cp )
+ mem->pcc = 0;
+ }
+ }
+}
diff --git a/pstoraster/igc.h b/pstoraster/igc.h
new file mode 100644
index 000000000..f2f6d50eb
--- /dev/null
+++ b/pstoraster/igc.h
@@ -0,0 +1,82 @@
+/* Copyright (C) 1994, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* igc.h */
+/* Internal interfaces in Ghostscript GC */
+
+/* Define the procedures shared among a "genus" of structures. */
+/* Currently there are only two genera: refs, and all other structures. */
+struct struct_shared_procs_s {
+
+ /* Clear the relocation information in an object. */
+
+#define gc_proc_clear_reloc(proc)\
+ void proc(P2(obj_header_t *pre, uint size))
+ gc_proc_clear_reloc((*clear_reloc));
+
+ /* Compute any internal relocation for a marked object. */
+ /* Return true if the object should be kept. */
+ /* The reloc argument shouldn't be required, */
+ /* but we need it for ref objects. */
+
+#define gc_proc_set_reloc(proc)\
+ bool proc(P3(obj_header_t *pre, uint reloc, uint size))
+ gc_proc_set_reloc((*set_reloc));
+
+ /* Compact an object. */
+
+#define gc_proc_compact(proc)\
+ void proc(P3(obj_header_t *pre, obj_header_t *dpre, uint size))
+ gc_proc_compact((*compact));
+
+};
+
+/* Define the structure for holding GC state. */
+/*typedef struct gc_state_s gc_state_t;*/ /* in gsstruct.h */
+struct gc_state_s {
+ chunk_locator_t loc;
+ vm_spaces spaces;
+};
+
+/* Exported by igcref.c for igc.c */
+void gs_mark_refs(P3(ref_packed *, ref *, bool));
+void ptr_ref_unmark(P2(void *, gc_state_t *));
+bool ptr_ref_mark(P2(void *, gc_state_t *));
+/*ref_packed *gs_reloc_ref_ptr(P2(const ref_packed *, gc_state_t *));*/
+
+/* Exported by ilocate.c for igc.c */
+void ialloc_validate_memory(P2(const gs_ref_memory_t *, gc_state_t *));
+void ialloc_validate_chunk(P2(const chunk_t *, gc_state_t *));
+void ialloc_validate_object(P3(const obj_header_t *, const chunk_t *,
+ gc_state_t *));
+
+/* Macro for returning a relocated pointer */
+#ifdef DEBUG
+void *print_reloc_proc(P3(const void *obj, const char *cname, void *robj));
+# define print_reloc(obj, cname, nobj)\
+ (gs_debug_c('9') ? print_reloc_proc(obj, cname, nobj) :\
+ (void *)(nobj))
+#else
+# define print_reloc(obj, cname, nobj)\
+ (void *)(nobj)
+#endif
diff --git a/pstoraster/igcref.c b/pstoraster/igcref.c
new file mode 100644
index 000000000..169d4cb85
--- /dev/null
+++ b/pstoraster/igcref.c
@@ -0,0 +1,562 @@
+/* Copyright (C) 1994, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* igcref.c */
+/* ref garbage collector for Ghostscript */
+#include "memory_.h"
+#include "ghost.h"
+#include "gsexit.h"
+#include "gsstruct.h" /* for gxalloc.h */
+#include "iname.h"
+#include "iastate.h"
+#include "idebug.h"
+#include "igc.h"
+#include "ipacked.h"
+#include "store.h" /* for ref_assign_inline */
+
+/*
+ * Define the 'structure' type descriptor for refs.
+ * This is special because it has different shared procs.
+ */
+private gc_proc_clear_reloc(refs_clear_reloc);
+private gc_proc_set_reloc(refs_set_reloc);
+private gc_proc_compact(refs_compact);
+private const struct_shared_procs_t refs_shared_procs =
+ { refs_clear_reloc, refs_set_reloc, refs_compact };
+private struct_proc_clear_marks(refs_clear_marks);
+private struct_proc_reloc_ptrs(refs_do_reloc);
+const gs_memory_struct_type_t st_refs =
+ { sizeof(ref), "refs", &refs_shared_procs, refs_clear_marks, 0, refs_do_reloc };
+
+/*
+ * Define the GC procedures for structs that actually contain refs.
+ * These are special because the shared refs_* procedures
+ * are never called. Instead, we unmark the individual refs in clear_marks,
+ * disregard refs_*_reloc (because we will never relocate a ptr_ref_type
+ * pointer pointing into the structure), disregard refs_compact (because
+ * compaction is never required), and remove the marks in reloc_ptrs.
+ * See also the comment about ptr_ref_type in imemory.h.
+ */
+CLEAR_MARKS_PROC(ref_struct_clear_marks) {
+ ref *pref = (ref *)vptr;
+ ref *end = (ref *)((char *)vptr + size);
+ for ( ; pref < end; pref++ )
+ r_clear_attrs(pref, l_mark);
+}
+ENUM_PTRS_BEGIN_PROC(ref_struct_enum_ptrs) {
+ if ( index >= size / sizeof(ref) )
+ return 0;
+ *pep = (ref *)vptr + index;
+ return ptr_ref_type;
+ENUM_PTRS_END_PROC }
+RELOC_PTRS_BEGIN(ref_struct_reloc_ptrs) {
+ ref *beg = vptr;
+ ref *end = (ref *)((char *)vptr + size);
+ gs_reloc_refs((ref_packed *)beg, (ref_packed *)end, gcst);
+ ref_struct_clear_marks(vptr, size);
+} RELOC_PTRS_END
+
+/* ------ Unmarking phase ------ */
+
+/* Unmark a single ref. */
+void
+ptr_ref_unmark(void *vptr, gc_state_t *ignored)
+{ if ( r_is_packed((ref *)vptr) )
+ r_clear_pmark((ref_packed *)vptr);
+ else
+ r_clear_attrs((ref *)vptr, l_mark);
+}
+
+/* Unmarking routine for ref objects. */
+private void
+refs_clear_marks(void /*obj_header_t*/ *vptr, uint size)
+{ gs_mark_refs((ref_packed *)vptr,
+ (ref *)((byte *)vptr + size),
+ false);
+}
+/* Mark or unmark a block of refs. */
+/* The last ref must be full-size, and is never marked. */
+void
+gs_mark_refs(ref_packed *from, ref *to, bool mark)
+{ ref_packed *rp = from;
+ ushort pmark = (mark ? lp_mark : 0);
+ ushort rmark = (mark ? l_mark : 0);
+ /* Since the last ref is full-size, we only need to check for */
+ /* the end of the block when we see one of those. */
+ for ( ; ; )
+ { if ( r_is_packed(rp) )
+ {
+#ifdef DEBUG
+if ( gs_debug_c('8') )
+{ dprintf1(" [8]unmark packed 0x%lx ", (ulong)rp);
+ debug_print_ref((const ref *)rp);
+ dprintf("\n");
+}
+#endif
+ r_store_pmark(rp, pmark);
+ rp++;
+ }
+ else /* full-size ref */
+ {
+#ifdef DEBUG
+if ( gs_debug_c('8') )
+{ dprintf1(" [8]unmark ref 0x%lx ", (ulong)rp);
+ debug_print_ref((ref *)rp);
+ dprintf("\n");
+}
+#endif
+ r_store_attrs((ref *)rp, l_mark, rmark);
+ rp += packed_per_ref;
+ if ( rp >= (ref_packed *)to )
+ { /* Ensure the last ref is not marked. */
+ r_clear_attrs((ref *)rp - 1, l_mark);
+ break;
+ }
+ }
+ }
+}
+
+/* ------ Marking phase ------ */
+
+/* Mark a ref. Return true if new mark. */
+bool
+ptr_ref_mark(void *vptr, gc_state_t *ignored)
+{ if ( r_is_packed(vptr) )
+ { if ( r_has_pmark((ref_packed *)vptr) )
+ return false;
+ r_set_pmark((ref_packed *)vptr);
+ }
+ else
+ { if ( r_has_attr((ref *)vptr, l_mark) )
+ return false;
+ r_set_attrs((ref *)vptr, l_mark);
+ }
+ return true;
+}
+
+/* ------ Relocation planning phase ------ */
+
+/*
+ * We store relocation in the size field of refs that don't use it,
+ * so that we don't have to scan all the way to an unmarked object.
+ * We must avoid nulls, which sometimes have useful information
+ * in their size fields, and the types above t_next_index, which are
+ * actually operators in disguise and also use the size field.
+ */
+#define case_types_using_size\
+ case t_null: case_types_with_size
+
+/* Clear the relocation for a ref object. */
+private void
+refs_clear_reloc(obj_header_t *hdr, uint size)
+{ register ref_packed *rp = (ref_packed *)(hdr + 1);
+ ref_packed *end = (ref_packed *)((byte *)rp + size);
+ while ( rp < end )
+ { if ( r_is_packed(rp) )
+ rp++;
+ else /* full-size ref */
+ { uint type;
+ /* Store the relocation here if possible. */
+ switch ( type = r_type((ref *)rp) )
+ { case_types_using_size:
+ break;
+ default:
+ if ( type >= t_next_index )
+ break;
+ if_debug1('8', " [8]clearing reloc at 0x%lx\n",
+ (ulong)rp);
+ r_set_size((ref *)rp, 0);
+ }
+ rp += packed_per_ref;
+ }
+ }
+}
+
+/* Set the relocation for a ref object. */
+private bool
+refs_set_reloc(obj_header_t *hdr, uint reloc, uint size)
+{ ref_packed *rp = (ref_packed *)(hdr + 1);
+ ref_packed *end = (ref_packed *)((byte *)rp + size);
+ uint freed = 0;
+ /*
+ * We have to be careful to keep refs aligned properly.
+ * For the moment, we do this by either keeping or discarding
+ * an entire (aligned) block of align_packed_per_ref packed elements
+ * as a unit. We know that align_packed_per_ref <= packed_per_ref,
+ * and we also know that packed refs are always allocated in blocks
+ * of align_packed_per_ref, so this makes things relatively easy.
+ */
+ while ( rp < end )
+ { if ( r_is_packed(rp) )
+ {
+#if align_packed_per_ref == 1
+ if ( r_has_pmark(rp) )
+ { if_debug1('8',
+ " [8]packed ref 0x%lx is marked\n",
+ (ulong)rp);
+ rp++;
+ }
+#else
+ uint marked = *rp;
+ int i;
+ for ( i = 1; i < align_packed_per_ref; i++ )
+ marked |= rp[i];
+ if ( marked & lp_mark )
+ { /* At least one packed_ref in the block */
+ /* is marked: Keep the whole block. */
+ for ( ; i; i--, rp++ )
+ { r_set_pmark(rp);
+ if_debug1('8',
+ " [8]packed ref 0x%lx is marked\n",
+ (ulong)rp);
+ }
+ }
+#endif
+ else
+ { if_debug2('8', " [8]%d packed ref(s) at 0x%lx are unmarked\n",
+ align_packed_per_ref, (ulong)rp);
+ rp += align_packed_per_ref;
+ freed += sizeof(ref_packed) * align_packed_per_ref;
+ }
+ }
+ else /* full-size ref */
+ { uint rel = reloc + freed;
+ /* The following assignment is logically */
+ /* unnecessary; we do it only for convenience */
+ /* in debugging. */
+ ref *pref = (ref *)rp;
+ if ( !r_has_attr(pref, l_mark) )
+ { if_debug1('8', " [8]ref 0x%lx is unmarked\n",
+ (ulong)pref);
+ /* Change this to a mark so we can */
+ /* store the relocation. */
+ r_set_type(pref, t_mark);
+ r_set_size(pref, rel);
+ freed += sizeof(ref);
+ }
+ else
+ { uint type;
+ if_debug1('8', " [8]ref 0x%lx is marked\n",
+ (ulong)pref);
+ /* Store the relocation here if possible. */
+ switch ( type = r_type(pref) )
+ {
+ case_types_using_size:
+ break;
+ default:
+ if ( type >= t_next_index )
+ break;
+ if_debug2('8', " [8]storing reloc %u at 0x%lx\n",
+ rel, (ulong)pref);
+ r_set_size(pref, rel);
+ }
+ }
+ rp += packed_per_ref;
+ }
+ }
+ if_debug3('7', " [7]at end of refs 0x%lx, size = %u, freed = %u\n",
+ (ulong)(hdr + 1), size, freed);
+ if ( freed == size )
+ return false;
+#if arch_sizeof_int > arch_sizeof_short
+ /*
+ * If the final relocation can't fit in the r_size field
+ * (which can't happen if the object shares a chunk with
+ * any other objects, so we know reloc = 0 in this case),
+ * we have to keep the entire object unless there are no
+ * references to any ref in it.
+ */
+ if ( freed <= max_ushort )
+ return true;
+ /*
+ * We have to mark all surviving refs, but we also must
+ * overwrite any non-surviving refs with something that
+ * doesn't contain any pointers.
+ */
+ rp = (ref_packed *)(hdr + 1);
+ while ( rp < end )
+ { if ( r_is_packed(rp) )
+ { if ( !r_has_pmark(rp) )
+ *rp = pt_tag(pt_integer) | lp_mark;
+ ++rp;
+ }
+ else
+ { /* The following assignment is logically */
+ /* unnecessary; we do it only for convenience */
+ /* in debugging. */
+ ref *pref = (ref *)rp;
+ if ( !r_has_attr(pref, l_mark) )
+ { r_set_type_attrs(pref, t_mark, l_mark);
+ r_set_size(pref, reloc);
+ }
+ else
+ { uint type;
+ switch ( type = r_type(pref) )
+ {
+ case_types_using_size:
+ break;
+ default:
+ if ( type >= t_next_index )
+ break;
+ r_set_size(pref, reloc);
+ }
+ }
+ rp += packed_per_ref;
+ }
+ }
+ /* The last ref has to remain unmarked. */
+ r_clear_attrs((ref *)rp - 1, l_mark);
+#endif
+ return true;
+}
+
+/* ------ Relocation phase ------ */
+
+/* Relocate all the pointers in a block of refs. */
+private void
+refs_do_reloc(void /*obj_header_t*/ *vptr, uint size, gc_state_t *gcst)
+{ gs_reloc_refs((ref_packed *)vptr,
+ (ref_packed *)((char *)vptr + size),
+ gcst);
+}
+/* Relocate the contents of a block of refs. */
+void
+gs_reloc_refs(ref_packed *from, ref_packed *to, gc_state_t *gcst)
+{ ref_packed *rp = from;
+ while ( rp < to )
+ { ref *pref;
+ if ( r_is_packed(rp) )
+ { rp++;
+ continue;
+ }
+ /* The following assignment is logically unnecessary; */
+ /* we do it only for convenience in debugging. */
+ pref = (ref *)rp;
+ if_debug3('8', " [8]relocating %s %d ref at 0x%lx\n",
+ (r_has_attr(pref, l_mark) ? "marked" : "unmarked"),
+ r_btype(pref), (ulong)pref);
+ if ( r_has_attr(pref, l_mark) && !r_is_foreign(pref) )
+ switch ( r_type(pref) )
+ {
+ /* Struct cases */
+#define ref_case(v, t)\
+ pref->value.v =\
+ (t *)gs_reloc_struct_ptr((obj_header_t *)pref->value.v, gcst)
+ case t_file:
+ ref_case(pfile, struct stream_s); break;
+ case t_device:
+ ref_case(pdevice, struct gx_device_s); break;
+ case t_fontID:
+ case t_struct:
+ case t_astruct:
+ ref_case(pstruct, void); break;
+#undef ref_case
+ /* Non-trivial non-struct cases */
+ case t_dictionary:
+ pref->value.pdict =
+ (dict *)gs_reloc_ref_ptr((ref_packed *)pref->value.pdict, gcst);
+ break;
+ case t_array:
+ if ( r_size(pref) != 0 ) /* value.refs might be NULL */
+ pref->value.refs =
+ (ref *)gs_reloc_ref_ptr((ref_packed *)pref->value.refs, gcst);
+ break;
+ case t_mixedarray: case t_shortarray:
+ if ( r_size(pref) != 0 ) /* value.refs might be NULL */
+ pref->value.packed =
+ gs_reloc_ref_ptr(pref->value.packed, gcst);
+ break;
+ case t_name:
+ { void *psub = name_ref_sub_table(pref);
+ void *rsub = gs_reloc_struct_ptr(psub, gcst);
+ pref->value.pname = (name *)
+ ((char *)rsub + ((char *)pref->value.pname - (char *)psub));
+ } break;
+ case t_string:
+ { gs_string str;
+ str.data = pref->value.bytes;
+ str.size = r_size(pref);
+ gs_reloc_string(&str, gcst);
+ pref->value.bytes = str.data;
+ } break;
+ case t_oparray:
+ pref->value.const_refs =
+ (const ref *)gs_reloc_ref_ptr((const ref_packed *)pref->value.const_refs, gcst);
+ break;
+ }
+ rp += packed_per_ref;
+ }
+}
+
+/* Relocate a pointer to a ref. */
+/* See gsmemory.h for why the argument is const and the result is not. */
+ref_packed *
+gs_reloc_ref_ptr(const ref_packed *prp, gc_state_t *ignored)
+{ /* Search forward for relocation. */
+ /* This algorithm is intrinsically very inefficient; */
+ /* we hope eventually to replace it with a better one. */
+ register const ref_packed *rp = prp;
+ uint dec = 0;
+
+ for ( ; ; )
+ { uint type;
+ if ( r_is_packed(rp) )
+ { /* For each unmarked packed ref we pass over, */
+ /* we have to decrement the final relocation. */
+ if ( r_is_packed(rp + 1) )
+ { /* Almost all packed refs are marked, */
+ /* so test both at the same time. */
+ if ( !(*rp & rp[1] & lp_mark) )
+ { if ( (*rp | rp[1]) & lp_mark )
+ dec += sizeof(ref_packed);
+ else
+ dec += sizeof(ref_packed) * 2;
+ }
+ rp += 2;
+ continue;
+ }
+ else if ( !r_has_pmark(rp) )
+ dec += sizeof(ref_packed);
+ rp++; /* fall through */
+ }
+ switch ( type = r_type((const ref *)rp) )
+ {
+ default: /* reloc is in r_size */
+ if ( type < t_next_index )
+ { /* These refs might be in a space */
+ /* that isn't being compacted. If so, */
+ /* the relocation value here will be zero. */
+ return print_reloc(prp, "ref",
+ (ref_packed *)
+ (r_size((const ref *)rp) == 0 ? prp :
+ (const ref_packed *)((const char *)prp - r_size((const ref *)rp) + dec)));
+ }
+ /* falls through */
+ case_types_using_size:
+ rp += packed_per_ref;
+ }
+ }
+}
+
+/* ------ Compaction phase ------ */
+
+/* Compact a ref object. */
+/* Remove the marks at the same time. */
+private void
+refs_compact(obj_header_t *pre, obj_header_t *dpre, uint size)
+{ ref_packed *dest;
+ ref_packed *src;
+ ref_packed *end;
+ uint new_size;
+ src = (ref_packed *)(pre + 1);
+ end = (ref_packed *)((byte *)src + size);
+ /*
+ * We know that a block of refs always ends with an unmarked
+ * full-size ref, so we only need to check for reaching the end
+ * of the block when we see one of those.
+ */
+ if ( dpre == pre ) /* Loop while we don't need to copy. */
+ for ( ; ; )
+ { if ( r_is_packed(src) )
+ { if ( !r_has_pmark(src) )
+ break;
+ if_debug1('8', " [8]packed ref 0x%lx \"copied\"\n",
+ (ulong)src);
+ *src &= ~lp_mark;
+ src++;
+ }
+ else /* full-size ref */
+ { if ( !r_has_attr((ref *)src, l_mark) )
+ break;
+ if_debug1('8', " [8]ref 0x%lx \"copied\"\n",
+ (ulong)src);
+ r_clear_attrs((ref *)src, l_mark);
+ src += packed_per_ref;
+ }
+ }
+ else
+ *dpre = *pre;
+ dest = (ref_packed *)((char *)dpre + ((char *)src - (char *)pre));
+ for ( ; ; )
+ { if ( r_is_packed(src) )
+ { if ( r_has_pmark(src) )
+ { if_debug2('8', " [8]packed ref 0x%lx copied to 0x%lx\n",
+ (ulong)src, (ulong)dest);
+ *dest++ = *src & ~lp_mark;
+ }
+ src++;
+ }
+ else /* full-size ref */
+ { if ( r_has_attr((ref *)src, l_mark) )
+ { ref rtemp;
+ if_debug2('8', " [8]ref 0x%lx copied to 0x%lx\n",
+ (ulong)src, (ulong)dest);
+ /* We can't just use ref_assign_inline, */
+ /* because the source and destination */
+ /* might overlap! */
+ ref_assign_inline(&rtemp, (ref *)src);
+ r_clear_attrs(&rtemp, l_mark);
+ ref_assign_inline((ref *)dest, &rtemp);
+ dest += packed_per_ref;
+ src += packed_per_ref;
+ }
+ else /* check for end of block */
+ { src += packed_per_ref;
+ if ( src >= end )
+ break;
+ }
+ }
+ }
+ new_size = (byte *)dest - (byte *)(dpre + 1) + sizeof(ref);
+#ifdef DEBUG
+ /* Check that the relocation came out OK. */
+ /* NOTE: this check only works within a single chunk. */
+ if ( (byte *)src - (byte *)dest != r_size((ref *)src - 1) + sizeof(ref) )
+ { lprintf3("Reloc error for refs 0x%lx: reloc = %lu, stored = %u\n",
+ (ulong)dpre, (ulong)((byte *)src - (byte *)dest),
+ (uint)r_size((ref *)src - 1));
+ gs_exit(1);
+ }
+#endif
+ /* Pad to a multiple of sizeof(ref). */
+ while ( new_size & (sizeof(ref) - 1) )
+ *dest++ = pt_tag(pt_integer),
+ new_size += sizeof(ref_packed);
+ /* We want to make the newly freed space into a free block, */
+ /* but we can only do this if we have enough room. */
+ if ( size - new_size < sizeof(obj_header_t) )
+ { /* Not enough room. Pad to original size. */
+ while ( new_size < size )
+ *dest++ = pt_tag(pt_integer),
+ new_size += sizeof(ref_packed);
+ }
+ else
+ { obj_header_t *pfree = (obj_header_t *)((ref *)dest + 1);
+ pfree->o_large = 0;
+ pfree->o_size = size - new_size - sizeof(obj_header_t);
+ pfree->o_type = &st_bytes;
+ }
+ /* Re-create the final ref. */
+ r_set_type((ref *)dest, t_integer);
+ dpre->o_size = new_size;
+}
diff --git a/pstoraster/igcstr.c b/pstoraster/igcstr.c
new file mode 100644
index 000000000..233898204
--- /dev/null
+++ b/pstoraster/igcstr.c
@@ -0,0 +1,353 @@
+/* Copyright (C) 1994, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* igcstr.c */
+/* String GC routines for Ghostscript */
+#include "memory_.h"
+#include "ghost.h"
+#include "gsmdebug.h"
+#include "gsstruct.h"
+#include "iastate.h"
+#include "igcstr.h"
+
+/* (Un)mark the strings in a chunk. */
+void
+gc_strings_set_marks(chunk_t *cp, bool mark)
+{ if ( cp->smark != 0 )
+ { if_debug3('6', "[6]clearing string marks 0x%lx[%u] to %d\n",
+ (ulong)cp->smark, cp->smark_size, (int)mark);
+ memset(cp->smark, (mark ? 0xff : 0), cp->smark_size);
+ }
+}
+
+/* We mark strings a word at a time. */
+typedef string_mark_unit bword;
+#define bword_log2_bytes log2_sizeof_string_mark_unit
+#define bword_bytes (1 << bword_log2_bytes)
+#define bword_log2_bits (bword_log2_bytes + 3)
+#define bword_bits (1 << bword_log2_bits)
+#define bword_1s (~(bword)0)
+/* Compensate for byte order reversal if necessary. */
+#if arch_is_big_endian
+# if bword_bytes == 2
+# define bword_swap_bytes(m) m = (m << 8) | (m >> 8)
+# else /* bword_bytes == 4 */
+# define bword_swap_bytes(m)\
+ m = (m << 24) | ((m & 0xff00) << 8) | ((m >> 8) & 0xff00) | (m >> 24)
+# endif
+#else
+# define bword_swap_bytes(m) DO_NOTHING
+#endif
+
+/* Mark a string. Return true if any new marks. */
+bool
+gc_string_mark(const byte *ptr, uint size, bool set, gc_state_t *gcst)
+{ uint left = size;
+ bword *bp;
+ uint bn;
+ bword m;
+ const chunk_t *cp;
+ uint offset;
+ bword marks = 0;
+
+ if ( size == 0 )
+ return false;
+#define dprintstr()\
+ dputc('('); fwrite(ptr, 1, min(size, 20), dstderr);\
+ dputs((size <= 20 ? ")" : "...)"))
+ if ( !(cp = gc_locate(ptr, gcst)) ) /* not in a chunk */
+ {
+#ifdef DEBUG
+ if ( gs_debug_c('5') )
+ { dprintf2("[5]0x%lx[%u]", (ulong)ptr, size);
+ dprintstr();
+ dputs(" not in a chunk\n");
+ }
+#endif
+ return false;
+ }
+ if ( cp->smark == 0 ) /* not marking strings */
+ return false;
+#ifdef DEBUG
+ if ( ptr < cp->ctop )
+ { lprintf4("String pointer 0x%lx[%u] outside [0x%lx..0x%lx)\n",
+ (ulong)ptr, size, (ulong)cp->ctop, (ulong)cp->climit);
+ return false;
+ }
+ else if ( ptr + size > cp->climit )
+ { /*
+ * If this is the bottommost string in a chunk that has
+ * an inner chunk, the string's starting address is both
+ * cp->ctop of the outer chunk and cp->climit of the inner;
+ * gc_locate may incorrectly attribute the string to the
+ * inner chunk because of this. This doesn't affect
+ * marking or relocation, since the machinery for these
+ * is all associated with the outermost chunk,
+ * but it can cause the validity check to fail.
+ * Check for this case now.
+ */
+ const chunk_t *scp = cp;
+ while ( ptr == scp->climit && scp->outer != 0 )
+ scp = scp->outer;
+ if ( ptr + size > scp->climit )
+ { lprintf4("String pointer 0x%lx[%u] outside [0x%lx..0x%lx)\n",
+ (ulong)ptr, size,
+ (ulong)scp->ctop, (ulong)scp->climit);
+ return false;
+ }
+ }
+#endif
+ offset = ptr - cp->sbase;
+ bp = (bword *)(cp->smark + ((offset & -bword_bits) >> 3));
+ bn = offset & (bword_bits - 1);
+ m = bword_1s << bn;
+ bword_swap_bytes(m);
+ if ( set )
+ { if ( left + bn >= bword_bits )
+ { marks |= ~*bp & m;
+ *bp |= m;
+ m = bword_1s, left -= bword_bits - bn, bp++;
+ while ( left >= bword_bits )
+ { marks |= ~*bp;
+ *bp = bword_1s;
+ left -= bword_bits, bp++;
+ }
+ }
+ if ( left )
+ { bword_swap_bytes(m);
+ m -= m << left;
+ bword_swap_bytes(m);
+ marks |= ~*bp & m;
+ *bp |= m;
+ }
+ }
+ else
+ { if ( left + bn >= bword_bits )
+ { *bp &= ~m;
+ m = bword_1s, left -= bword_bits - bn, bp++;
+ if ( left >= bword_bits * 5 )
+ { memset(bp, 0, (left & -bword_bits) >> 3);
+ bp += left >> bword_log2_bits;
+ left &= bword_bits - 1;
+ }
+ else
+ while ( left >= bword_bits )
+ { *bp = 0;
+ left -= bword_bits, bp++;
+ }
+ }
+ if ( left )
+ { bword_swap_bytes(m);
+ m -= m << left;
+ bword_swap_bytes(m);
+ *bp &= ~m;
+ }
+ }
+#ifdef DEBUG
+ if ( gs_debug_c('5') )
+ { dprintf4("[5]%s%smarked 0x%lx[%u]",
+ (marks ? "" : "already "), (set ? "" : "un"),
+ (ulong)ptr, size);
+ dprintstr();
+ dputc('\n');
+ }
+#endif
+ return marks != 0;
+}
+
+/* Clear the relocation for strings. */
+void
+gc_strings_clear_reloc(chunk_t *cp)
+{ if ( cp->sreloc != 0 )
+ { uint sreloc_size =
+ ((cp->smark_size + (string_data_quantum / 8 - 1)) >>
+ (log2_string_data_quantum - 3)) *
+ sizeof(string_reloc_offset);
+ if_debug2('6', "[6]clearing string reloc 0x%lx[%u]\n",
+ (ulong)cp->sreloc, sreloc_size);
+ memset(cp->sreloc, 0, sreloc_size);
+ }
+}
+
+/* Count the 0-bits in a byte. */
+private const byte count_zero_bits_table[256] = {
+#define o4(n) n,n-1,n-1,n-2
+#define o16(n) o4(n),o4(n-1),o4(n-1),o4(n-2)
+#define o64(n) o16(n),o16(n-1),o16(n-1),o16(n-2)
+ o64(8), o64(7), o64(7), o64(6)
+};
+#define byte_count_zero_bits(byt)\
+ (uint)(count_zero_bits_table[byt])
+#define byte_count_one_bits(byt)\
+ (uint)(8 - count_zero_bits_table[byt])
+
+/* Set the relocation for the strings in a chunk. */
+/* The sreloc table stores the relocated offset from climit for */
+/* the beginning of each block of string_data_quantum characters. */
+void
+gc_strings_set_reloc(chunk_t *cp)
+{ if ( cp->sreloc != 0 && cp->smark != 0 )
+ { byte *bot = cp->ctop;
+ byte *top = cp->climit;
+ uint count =
+ (top - bot + (string_data_quantum - 1)) >>
+ log2_string_data_quantum;
+ string_reloc_offset *relp =
+ cp->sreloc +
+ (cp->smark_size >> (log2_string_data_quantum - 3));
+ register byte *bitp = cp->smark + cp->smark_size;
+ register string_reloc_offset reloc = 0;
+ while ( count-- )
+ { bitp -= string_data_quantum / 8;
+ reloc += string_data_quantum -
+ byte_count_zero_bits(bitp[0]);
+ reloc -= byte_count_zero_bits(bitp[1]);
+ reloc -= byte_count_zero_bits(bitp[2]);
+ reloc -= byte_count_zero_bits(bitp[3]);
+#if log2_string_data_quantum > 5
+ reloc -= byte_count_zero_bits(bitp[4]);
+ reloc -= byte_count_zero_bits(bitp[5]);
+ reloc -= byte_count_zero_bits(bitp[6]);
+ reloc -= byte_count_zero_bits(bitp[7]);
+#endif
+ *--relp = reloc;
+ }
+ }
+ cp->sdest = cp->climit;
+}
+
+/* Relocate a string pointer. */
+void
+gs_reloc_string(gs_string *sptr, gc_state_t *gcst)
+{ byte *ptr;
+ const chunk_t *cp;
+ uint offset;
+ uint reloc;
+ const byte *bitp;
+ byte byt;
+ if ( sptr->size == 0 )
+ { sptr->data = 0;
+ return;
+ }
+ ptr = sptr->data;
+ if ( !(cp = gc_locate(ptr, gcst)) ) /* not in a chunk */
+ return;
+ if ( cp->sreloc == 0 || cp->smark == 0 ) /* not marking strings */
+ return;
+ offset = ptr - cp->sbase;
+ reloc = cp->sreloc[offset >> log2_string_data_quantum];
+ bitp = &cp->smark[offset >> 3];
+ switch ( offset & (string_data_quantum - 8) )
+ {
+#if log2_string_data_quantum > 5
+ case 56: reloc -= byte_count_one_bits(bitp[-7]);
+ case 48: reloc -= byte_count_one_bits(bitp[-6]);
+ case 40: reloc -= byte_count_one_bits(bitp[-5]);
+ case 32: reloc -= byte_count_one_bits(bitp[-4]);
+#endif
+ case 24: reloc -= byte_count_one_bits(bitp[-3]);
+ case 16: reloc -= byte_count_one_bits(bitp[-2]);
+ case 8: reloc -= byte_count_one_bits(bitp[-1]);
+ }
+ byt = *bitp & (0xff >> (8 - (offset & 7)));
+ reloc -= byte_count_one_bits(byt);
+ if_debug2('5', "[5]relocate string 0x%lx to 0x%lx\n",
+ (ulong)ptr, (ulong)(cp->sdest - reloc));
+ sptr->data = cp->sdest - reloc;
+}
+void
+gs_reloc_const_string(gs_const_string *sptr, gc_state_t *gcst)
+{ /* We assume the representation of byte * and const byte * is */
+ /* the same.... */
+ gs_reloc_string((gs_string *)sptr, gcst);
+}
+
+/* Compact the strings in a chunk. */
+void
+gc_strings_compact(chunk_t *cp)
+{ if ( cp->smark != 0 )
+ { byte *hi = cp->climit;
+ byte *lo = cp->ctop;
+ register const byte *from = hi;
+ register byte *to = hi;
+ register const byte *bp = cp->smark + cp->smark_size;
+#ifdef DEBUG
+ if ( gs_debug_c('4') || gs_debug_c('5') )
+ { byte *base = cp->sbase;
+ uint i = (lo - base) & -string_data_quantum;
+ uint n = round_up(hi - base, string_data_quantum);
+#define R 16
+ for ( ; i < n; i += R )
+ { uint j;
+ dprintf1("[4]0x%lx: ", (ulong)(base + i));
+ for ( j = i; j < i + R; j++ )
+ { byte ch = base[j];
+ if ( ch <= 31 )
+ { dputc('^'); dputc(ch + 0100); }
+ else
+ dputc(ch);
+ }
+ dputc(' ');
+ for ( j = i; j < i + R; j++ )
+ dputc((cp->smark[j >> 3] & (1 << (j & 7)) ?
+ '+' : '.'));
+#undef R
+ if ( !(i & (string_data_quantum - 1)) )
+ dprintf1(" %u", cp->sreloc[i >> log2_string_data_quantum]);
+ dputc('\n');
+ }
+ }
+#endif
+ while ( from > lo )
+ { register byte b = *--bp;
+ from -= 8;
+ switch ( b )
+ {
+ case 0xff:
+ to -= 8;
+ if ( to != from )
+ { to[7] = from[7]; to[6] = from[6];
+ to[5] = from[5]; to[4] = from[4];
+ to[3] = from[3]; to[2] = from[2];
+ to[1] = from[1]; to[0] = from[0];
+ }
+ break;
+ default:
+ if ( b & 0x80 ) *--to = from[7];
+ if ( b & 0x40 ) *--to = from[6];
+ if ( b & 0x20 ) *--to = from[5];
+ if ( b & 0x10 ) *--to = from[4];
+ if ( b & 8 ) *--to = from[3];
+ if ( b & 4 ) *--to = from[2];
+ if ( b & 2 ) *--to = from[1];
+ if ( b & 1 ) *--to = from[0];
+ /* falls through */
+ case 0:
+ ;
+ }
+ }
+ gs_alloc_fill(cp->ctop, gs_alloc_fill_collected,
+ to - cp->ctop);
+ cp->ctop = to;
+ }
+}
diff --git a/pstoraster/igcstr.h b/pstoraster/igcstr.h
new file mode 100644
index 000000000..7e45a52d6
--- /dev/null
+++ b/pstoraster/igcstr.h
@@ -0,0 +1,35 @@
+/* Copyright (C) 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* igcstr.h */
+/* Internal interface to string garbage collector */
+
+/* Exported by ilocate.c for igcstr.c */
+chunk_t *gc_locate(P2(const void *, gc_state_t *));
+
+/* Exported by igcstr.c for igc.c */
+void gc_strings_set_marks(P2(chunk_t *, bool));
+bool gc_string_mark(P4(const byte *, uint, bool, gc_state_t *));
+void gc_strings_clear_reloc(P1(chunk_t *));
+void gc_strings_set_reloc(P1(chunk_t *));
+void gc_strings_compact(P1(chunk_t *));
diff --git a/pstoraster/igstate.h b/pstoraster/igstate.h
new file mode 100644
index 000000000..e8bd5c7b9
--- /dev/null
+++ b/pstoraster/igstate.h
@@ -0,0 +1,161 @@
+/* Copyright (C) 1989, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* igstate.h */
+/* Ghostscript interpreter graphics state definition */
+#include "gsstate.h"
+#include "gxstate.h" /* for 'client data' access */
+#include "istruct.h" /* for gstate obj definition */
+
+/*
+ * From the interpreter's point of view, the graphics state is largely opaque,
+ * i.e., the interpreter is just another client of the library.
+ * The interpreter does require additional items in the graphics state;
+ * these are "client data" from the library's point of view.
+ * Most of the complexity in this added state comes from
+ * the parameters associated with the various Level 2 color spaces.
+ * Note that the added information consists entirely of refs.
+ */
+
+/*
+ * The interpreter represents graphics state objects in a slightly
+ * unnatural way, namely, by a t_astruct ref that points to an object
+ * of type st_igstate_obj, which is essentially a t_struct ref that in turn
+ * points to a real graphics state (object of type st_gs_state).
+ * We do this so that save and restore can manipulate the intermediate
+ * object and not have to worry about copying entire gs_states.
+ *
+ * Because a number of different operators must test whether an object
+ * is a gstate, we make an exception to our convention of declaring
+ * structure descriptors only in the place where the structure itself
+ * is defined (see gsstruct.h for more information on this).
+ */
+typedef struct igstate_obj_s {
+ ref gstate; /* t_struct / st_gs_state */
+} igstate_obj;
+extern_st(st_igstate_obj);
+#define public_st_igstate_obj() /* in zdps1.c */\
+ gs_public_st_ref_struct(st_igstate_obj, igstate_obj, "gstatetype")
+#define igstate_ptr(rp) r_ptr(&r_ptr(rp, igstate_obj)->gstate, gs_state)
+
+/* CIE transformation procedures */
+typedef struct ref_cie_procs_s {
+ union {
+ ref DEFG;
+ ref DEF;
+ } PreDecode;
+ union {
+ ref ABC;
+ ref A;
+ } Decode;
+ ref DecodeLMN;
+} ref_cie_procs;
+/* CIE rendering transformation procedures */
+typedef struct ref_cie_render_procs_s {
+ ref TransformPQR, EncodeLMN, EncodeABC, RenderTableT;
+} ref_cie_render_procs;
+
+/* Separation name and tint transform */
+typedef struct ref_separation_params_s {
+ ref layer_name, tint_transform;
+} ref_separation_params;
+
+/* All color space parameters. */
+/* All of these are optional. */
+/* Note that they may actually be the parameters for an underlying or */
+/* alternate space for a special space. */
+typedef struct ref_color_procs_s {
+ ref_cie_procs cie;
+ union {
+ ref_separation_params separation;
+ ref index_proc;
+ } special;
+} ref_color_procs;
+typedef struct ref_colorspace_s {
+ ref array; /* color space (array), */
+ /* only relevant if the current */
+ /* color space has parameters associated with it. */
+ ref_color_procs procs; /* associated procedures/parameters, */
+ /* only relevant for CIE, Separation, Indexed/CIE, */
+ /* Indexed with procedure, or a Pattern with one of these. */
+} ref_colorspace;
+
+typedef struct int_gstate_s {
+ ref dash_pattern; /* (array) */
+ /* Screen_procs are only relevant if setscreen was */
+ /* executed more recently than sethalftone */
+ /* (for this graphics context). */
+ union {
+ ref indexed[4];
+ struct {
+ /* The components must be in this order: */
+ ref red, green, blue, gray;
+ } colored;
+ } screen_procs, /* halftone screen procedures */
+ transfer_procs; /* transfer procedures */
+ ref black_generation; /* (procedure) */
+ ref undercolor_removal; /* (procedure) */
+ ref_colorspace colorspace;
+ /* Pattern is only relevant if the current color space */
+ /* is a pattern space. */
+ ref pattern; /* pattern (dictionary) */
+ struct {
+ ref dict; /* CIE color rendering dictionary */
+ ref_cie_render_procs procs; /* (see above) */
+ } colorrendering;
+ /* Halftone is only relevant if sethalftone was executed */
+ /* more recently than setscreen for this graphics context. */
+ /* setscreen sets it to null. */
+ ref halftone; /* halftone (dictionary) */
+ /* Pagedevice is only relevant if setpagedevice was */
+ /* executed more recently than nulldevice, setcachedevice, */
+ /* or setdevice with a non-page device (for this */
+ /* graphics context). If the current device is not a */
+ /* page device, pagedevice is an empty dictionary. */
+ ref pagedevice; /* page device (dictionary) */
+} int_gstate;
+extern ref i_null_pagedevice;
+#define clear_pagedevice(pigs) ((pigs)->pagedevice = i_null_pagedevice)
+/*
+ * Even though the interpreter's part of the graphics state actually
+ * consists of refs, allocating it as refs tends to create sandbars;
+ * since it is always allocated and freed as a unit, we can treat it
+ * as an ordinary structure.
+ */
+#define private_st_int_gstate() /* in zgstate.c */\
+ gs_private_st_ref_struct(st_int_gstate, int_gstate, "int_gstate")
+
+/* Enumerate the refs in an int_gstate. */
+/* Since all the elements of an int_gstate are refs, this is simple. */
+#define int_gstate_map_refs(p,m)\
+ { register ref *rp_ = (ref *)(p);\
+ register int i = sizeof(int_gstate) / sizeof(ref);\
+ do { m(rp_); ++rp_; } while ( --i );\
+ }
+
+/* Get the int_gstate from a gs_state. */
+#define gs_int_gstate(pgs) ((int_gstate *)gs_state_client_data(pgs))
+
+/* The current instances. */
+extern gs_state *igs;
+#define istate gs_int_gstate(igs)
diff --git a/pstoraster/iimage.h b/pstoraster/iimage.h
new file mode 100644
index 000000000..6d5b10e38
--- /dev/null
+++ b/pstoraster/iimage.h
@@ -0,0 +1,35 @@
+/* Copyright (C) 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* iimage.h */
+/* Internal interpreter interfaces to image painting */
+
+/* These procedures are exported by zpaint.c for other modules. */
+
+/* Exported for zimage2.c */
+int zimage_setup(P4(const gs_image_t *pim, bool multi, const ref *sources,
+ int npop));
+
+/* Exported for zcolor1.c */
+int zimage_opaque_setup(P4(os_ptr op, bool multi, const gs_color_space *pcs,
+ int npop));
diff --git a/pstoraster/iinit.c b/pstoraster/iinit.c
new file mode 100644
index 000000000..059c4783d
--- /dev/null
+++ b/pstoraster/iinit.c
@@ -0,0 +1,476 @@
+/* Copyright (C) 1989, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* iinit.c */
+/* Initialize internally known objects for Ghostscript interpreter */
+#include "string_.h"
+#include "ghost.h"
+#include "gscdefs.h"
+#include "gsexit.h"
+#include "gsstruct.h"
+#define INCLUDE_ERROR_NAMES /* see errors.h */
+#include "errors.h"
+#include "ialloc.h"
+#include "idict.h"
+#include "dstack.h"
+#include "ilevel.h"
+#include "iname.h"
+#include "interp.h"
+#include "ipacked.h"
+#include "iparray.h"
+#include "iutil.h"
+#include "ivmspace.h"
+#include "opdef.h"
+#include "store.h"
+
+/* Implementation parameters. */
+/* The size of systemdict can be set in the makefile. */
+/* We want the sizes to be prime numbers large enough to cover */
+/* all the operators, plus everything in the init files, */
+/* even if all the optional features are selected. */
+#ifndef SYSTEMDICT_SIZE
+# define SYSTEMDICT_SIZE 503
+#endif
+#ifndef SYSTEMDICT_LEVEL2_SIZE
+# define SYSTEMDICT_LEVEL2_SIZE 701
+#endif
+/* The size of level2dict, if applicable, can be set in the makefile. */
+#ifndef LEVEL2DICT_SIZE
+# define LEVEL2DICT_SIZE 191
+#endif
+/* Ditto the size of filterdict. */
+#ifndef FILTERDICT_SIZE
+# define FILTERDICT_SIZE 43
+#endif
+/* Ditto the size of internaldict. */
+#ifndef INTERNALDICT_SIZE
+# define INTERNALDICT_SIZE 3
+#endif
+/* Define an arbitrary size for the operator procedure tables. */
+#ifndef OP_ARRAY_TABLE_SIZE
+# define OP_ARRAY_TABLE_SIZE 120
+#endif
+#ifndef OP_ARRAY_TABLE_GLOBAL_SIZE
+# define OP_ARRAY_TABLE_GLOBAL_SIZE OP_ARRAY_TABLE_SIZE
+#endif
+#ifndef OP_ARRAY_TABLE_LOCAL_SIZE
+# define OP_ARRAY_TABLE_LOCAL_SIZE (OP_ARRAY_TABLE_SIZE / 2)
+#endif
+#define OP_ARRAY_TABLE_TOTAL_SIZE\
+ (OP_ARRAY_TABLE_GLOBAL_SIZE + OP_ARRAY_TABLE_LOCAL_SIZE)
+
+/* The operator tables */
+extern op_def_ptr (*(op_defs_all[]))(P0()); /* in iconfig.c */
+/* Because of a bug in Sun's SC1.0 compiler, */
+/* we have to spell out the typedef for op_def_ptr here: */
+const op_def **op_def_table;
+uint op_def_count;
+op_array_table op_array_table_global, op_array_table_local; /* definitions of `operator' procedures */
+uint op_array_count;
+/* GC roots for the same */
+private gs_gc_root_t
+ op_def_root, op_array_root_global, op_array_root_local,
+ op_array_nx_root_global, op_array_nx_root_local;
+
+/* Enter a name and value into a dictionary. */
+void
+initial_enter_name_in(const char *nstr, const ref *pref, ref *pdict)
+{ int code = dict_put_string(pdict, nstr, pref);
+ if ( code < 0 )
+ { lprintf4("initial_enter failed (%d), entering /%s in -dict:%u/%u-\n",
+ code, nstr, dict_length(pdict), dict_maxlength(pdict));
+ gs_exit(1);
+ }
+}
+void
+initial_enter_name(const char *nstr, const ref *pref)
+{ initial_enter_name_in(nstr, pref, systemdict);
+}
+
+/* Remove a name from systemdict. */
+void
+initial_remove_name(const char *nstr)
+{ ref nref;
+ if ( name_ref((const byte *)nstr, strlen(nstr), &nref, -1) >= 0 )
+ dict_undef(systemdict, &nref);
+}
+
+/* Create a name. Fatal error if it fails. */
+private void
+name_enter(const char *str, ref *pref)
+{ if ( name_enter_string(str, pref) != 0 )
+ { lprintf1("name_enter failed - %s\n", str);
+ gs_exit(1);
+ }
+}
+
+/* Define the names and sizes of the initial dictionaries. */
+/* The names are used to create references in systemdict. */
+const struct { const char *name; uint size; bool local; }
+ initial_dictionaries[] = {
+#ifdef INITIAL_DICTIONARIES
+ INITIAL_DICTIONARIES
+#else
+ /* systemdict is created and named automagically */
+ { "level2dict", LEVEL2DICT_SIZE, false },
+ { "globaldict", 0, false },
+ { "userdict", 0, true },
+ { "filterdict", FILTERDICT_SIZE, false },
+ { "internaldict", INTERNALDICT_SIZE, true }
+#endif
+};
+/* systemdict and globaldict are magically inserted at the bottom */
+const char *initial_dstack[] = {
+#ifdef INITIAL_DSTACK
+ INITIAL_DSTACK
+#else
+ "userdict"
+#endif
+};
+#define MIN_DSTACK_SIZE (countof(initial_dstack) + 1) /* +1 for systemdict */
+
+
+/* Detect whether we have any Level 2 operators. */
+/* We export this for gs_init1 in gsmain.c. */
+/* This is very slow, but we only call it a couple of times. */
+bool
+gs_have_level2(void)
+{ op_def_ptr (**tptr)(P0());
+
+ for ( tptr = op_defs_all; *tptr != 0; tptr++ )
+ { const op_def *def;
+ for ( def = (*tptr)(); def->oname != 0; def++ )
+ if ( op_def_is_begin_dict(def) &&
+ !strcmp(def->oname, "level2dict")
+ )
+ return true;
+ }
+ return false;
+}
+
+/* Create an initial dictionary if necessary. */
+private ref *
+make_initial_dict(const char *iname, uint namelen, ref idicts[])
+{ int i;
+ static const char sysname[] = "systemdict";
+
+ /*
+ * We wait to create systemdict until after everything else,
+ * so we can size it according to whether Level 2 is present.
+ */
+ if ( (namelen == countof(sysname) - 1) &&
+ !memcmp(iname, sysname, namelen)
+ )
+ return 0;
+
+ for ( i = 0; i < countof(initial_dictionaries); i++ )
+ { const char *dname = initial_dictionaries[i].name;
+ const int dsize = initial_dictionaries[i].size;
+
+ if ( (namelen == strlen(dname)) &&
+ !memcmp(iname, dname, namelen)
+ )
+ { ref *dref = &idicts[i];
+ if ( r_has_type(dref, t_null) )
+ { int code;
+ /* Perhaps dict_create should take */
+ /* the allocator as an argument.... */
+ uint space = ialloc_space(idmemory);
+ ialloc_set_space(idmemory,
+ (initial_dictionaries[i].local ?
+ avm_local : avm_global));
+ code = dict_create(dsize, dref);
+ ialloc_set_space(idmemory, space);
+ if ( code < 0 ) gs_abort();
+ }
+ return dref;
+ }
+ }
+
+ /*
+ * Name mentioned in some op_def,
+ * but not in initial_dictionaries.
+ */
+ gs_abort();
+ return (NULL);
+}
+
+/* Initialize objects other than operators. In particular, */
+/* initialize the dictionaries that hold operator definitions. */
+void
+obj_init(void)
+{ uint space = ialloc_space(idmemory);
+ bool level2 = gs_have_level2();
+
+ /* Initialize the language level. */
+ make_int(&ref_language_level, 1);
+
+ /* Initialize the interpreter. */
+ gs_interp_init();
+
+ {
+#define icount countof(initial_dictionaries)
+ ref idicts[icount];
+ int i;
+ op_def_ptr (**tptr)(P0());
+
+ min_dstack_size = MIN_DSTACK_SIZE;
+
+ refset_null(idicts, icount);
+
+ ialloc_set_space(idmemory, avm_global);
+ if ( level2 )
+ { dsp += 2;
+ dict_create(SYSTEMDICT_LEVEL2_SIZE, dsp);
+ /* For the moment, let globaldict be an alias */
+ /* for systemdict. */
+ dsp[-1] = *dsp;
+ min_dstack_size++;
+ }
+ else
+ { ++dsp;
+ dict_create(SYSTEMDICT_SIZE, dsp);
+ }
+ ref_systemdict = *dsp;
+ ialloc_set_space(idmemory, space);
+
+ /* Create dictionaries which are to be homes for operators. */
+ for ( tptr = op_defs_all; *tptr != 0; tptr++ )
+ { const op_def *def;
+ for ( def = (*tptr)(); def->oname != 0; def++ )
+ if ( op_def_is_begin_dict(def) )
+ make_initial_dict(def->oname, strlen(def->oname), idicts);
+ }
+
+ /* Set up the initial dstack. */
+ for ( i = 0; i < countof(initial_dstack); i++ )
+ { const char *dname = initial_dstack[i];
+ ++dsp;
+ ref_assign(dsp,
+ make_initial_dict(dname, strlen(dname), idicts));
+ }
+
+ /* Enter names of referenced initial dictionaries into systemdict. */
+ initial_enter_name("systemdict", &ref_systemdict);
+ for (i = 0; i < icount; i++)
+ { ref *idict = &idicts[i];
+ if ( !r_has_type(idict, t_null) )
+ { /*
+ * Note that we enter the dictionary in systemdict
+ * even if it is in local VM. There is a special
+ * provision in the garbage collector for this:
+ * see ivmspace.h for more information.
+ * In order to do this, we must temporarily
+ * identify systemdict as local, so that the
+ * store check in dict_put won't fail.
+ */
+ uint save_space = r_space(systemdict);
+ r_set_space(systemdict, avm_local);
+ initial_enter_name(initial_dictionaries[i].name,
+ idict);
+ r_set_space(systemdict, save_space);
+ }
+ }
+#undef icount
+ }
+
+ gs_interp_reset();
+
+ { ref vtemp;
+ make_null(&vtemp);
+ initial_enter_name("null", &vtemp);
+ make_true(&vtemp);
+ initial_enter_name("true", &vtemp);
+ make_false(&vtemp);
+ initial_enter_name("false", &vtemp);
+ }
+
+ /* Create the error name table */
+ { int n = countof(gs_error_names) - 1;
+ int i;
+ ref era;
+ ialloc_ref_array(&era, a_readonly, n, "ErrorNames");
+ for ( i = 0; i < n; i++ )
+ name_enter((const char *)gs_error_names[i],
+ era.value.refs + i);
+ initial_enter_name("ErrorNames", &era);
+ }
+}
+
+/* Run the initialization procedures of the individual operator files. */
+void
+zop_init(void)
+{ op_def_ptr (**tptr)(P0());
+ /* Because of a bug in Sun's SC1.0 compiler, */
+ /* we have to spell out the typedef for op_def_ptr here: */
+ const op_def *def;
+ for ( tptr = op_defs_all; *tptr != 0; tptr++ )
+ { for ( def = (*tptr)(); def->oname != 0; def++ )
+ DO_NOTHING;
+ if ( def->proc != 0 )
+ ((void (*)(P0()))(def->proc))();
+ }
+
+ /* Initialize the predefined names other than operators. */
+ /* Do this here in case op_init changed any of them. */
+ { ref vtemp;
+ make_const_string(&vtemp, a_readonly | avm_foreign,
+ strlen(gs_copyright),
+ (const byte *)gs_copyright);
+ initial_enter_name("copyright", &vtemp);
+ make_const_string(&vtemp, a_readonly | avm_foreign,
+ strlen(gs_product),
+ (const byte *)gs_product);
+ initial_enter_name("product", &vtemp);
+ make_int(&vtemp, gs_revision);
+ initial_enter_name("revision", &vtemp);
+ make_int(&vtemp, gs_revisiondate);
+ initial_enter_name("revisiondate", &vtemp);
+ }
+}
+
+/* Create an op_array table. */
+private int
+alloc_op_array_table(uint size, uint space, op_array_table *opt)
+{ uint save_space = ialloc_space(idmemory);
+ int code;
+ ialloc_set_space(idmemory, space);
+ code = ialloc_ref_array(&opt->table, a_readonly, size,
+ "op_array table");
+ ialloc_set_space(idmemory, save_space);
+ if ( code < 0 )
+ return code;
+ refset_null(opt->table.value.refs, size);
+ opt->nx_table =
+ (ushort *)ialloc_byte_array(size, sizeof(ushort),
+ "op_array nx_table");
+ if ( opt->nx_table == 0 )
+ return_error(e_VMerror);
+ opt->count = 0;
+ opt->root_p = &opt->table;
+ opt->attrs = space | a_executable;
+ return 0;
+}
+
+/* Initialize the operator table. */
+void
+op_init(void)
+{ int count = 1;
+ op_def_ptr (**tptr)(P0());
+ /* Because of a bug in Sun's SC1.0 compiler, */
+ /* we have to spell out the typedef for op_def_ptr here: */
+ const op_def *def;
+ const char _ds *nstr;
+
+ /* Do a first pass just to count the operators. */
+
+ for ( tptr = op_defs_all; *tptr != 0; tptr ++ )
+ { for ( def = (*tptr)(); def->oname != 0; def++ )
+ if ( !op_def_is_begin_dict(def) )
+ count++;
+ }
+
+ /* Do a second pass to construct the operator table, */
+ /* and enter the operators into the appropriate dictionary. */
+
+ /* Because of a bug in Sun's SC1.0 compiler, */
+ /* we have to spell out the typedef for op_def_ptr here: */
+ op_def_table =
+ (const op_def **)ialloc_byte_array(count, sizeof(op_def_ptr),
+ "op_init(op_def_table)");
+ op_def_count = count;
+ for (count = 0; count <= gs_interp_num_special_ops; count++)
+ op_def_table[count] = 0;
+ count = gs_interp_num_special_ops + 1; /* leave space for magic entries */
+ for ( tptr = op_defs_all; *tptr != 0; tptr ++ )
+ { ref *pdict = systemdict;
+ for ( def = (*tptr)(); (nstr = def->oname) != 0; def++ )
+ if ( op_def_is_begin_dict(def) )
+ { ref nref;
+ int code = name_ref((const byte *)nstr, strlen(nstr),
+ &nref, -1);
+
+ if ( code != 0 )
+ gs_abort();
+ if ( !dict_find(systemdict, &nref, &pdict) )
+ gs_abort();
+ if ( !r_has_type(pdict, t_dictionary) )
+ gs_abort();
+ }
+ else
+ { ref oper;
+ uint opidx;
+
+ gs_interp_make_oper(&oper, def->proc, count);
+ opidx = r_size(&oper);
+ /* The first character of the name is a digit */
+ /* giving the minimum acceptable number of operands. */
+ /* Check to make sure it's within bounds. */
+ if ( *nstr - '0' > gs_interp_max_op_num_args )
+ gs_abort();
+ nstr++;
+ /* Don't enter internal operators into */
+ /* the dictionary. */
+ if ( *nstr != '%' )
+ initial_enter_name_in(nstr, &oper, pdict);
+ op_def_table[opidx] = def;
+ if ( opidx == count )
+ count++;
+ }
+ }
+ /* All of the built-ins had better be defined somewhere, */
+ /* or things like op_find_index will choke. */
+ for ( count = 1; count <= gs_interp_num_special_ops; count++ )
+ if ( op_def_table[count] == 0 )
+ gs_abort();
+ gs_register_struct_root(imemory, &op_def_root,
+ (void **)&op_def_table, "op_def_table");
+
+ /* Allocate the tables for `operator' procedures. */
+ /* Make one of them local so we can have local operators. */
+
+ if ( alloc_op_array_table(OP_ARRAY_TABLE_GLOBAL_SIZE,
+ avm_global, &op_array_table_global) < 0 )
+ gs_abort();
+ op_array_table_global.base_index = op_def_count;
+ gs_register_ref_root(imemory, &op_array_root_global,
+ (void **)&op_array_table_global.root_p,
+ "op_array_table(global)");
+ gs_register_struct_root(imemory, &op_array_nx_root_global,
+ (void **)&op_array_table_global.nx_table,
+ "op_array nx_table(global)");
+
+ if ( alloc_op_array_table(OP_ARRAY_TABLE_LOCAL_SIZE,
+ avm_local, &op_array_table_local) < 0 )
+ gs_abort();
+ op_array_table_local.base_index =
+ op_array_table_global.base_index +
+ r_size(&op_array_table_global.table);
+ gs_register_ref_root(imemory, &op_array_root_local,
+ (void **)&op_array_table_local.root_p,
+ "op_array_table(local)");
+ gs_register_struct_root(imemory, &op_array_nx_root_local,
+ (void **)&op_array_table_local.nx_table,
+ "op_array nx_table(local)");
+
+}
diff --git a/pstoraster/ilevel.h b/pstoraster/ilevel.h
new file mode 100644
index 000000000..0af71eed9
--- /dev/null
+++ b/pstoraster/ilevel.h
@@ -0,0 +1,29 @@
+/* Copyright (C) 1992 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* ilevel.h */
+/* Interpreter language level interface for Ghostscript */
+
+/* The current interpreter language level (1 or 2) */
+extern ref ref_language_level;
+#define level2_enabled ((int)ref_language_level.value.intval == 2)
diff --git a/pstoraster/ilocate.c b/pstoraster/ilocate.c
new file mode 100644
index 000000000..8b2e2a7cc
--- /dev/null
+++ b/pstoraster/ilocate.c
@@ -0,0 +1,309 @@
+/* Copyright (C) 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* ilocate.c */
+/* Object locating and validating for Ghostscript memory manager */
+#include "ghost.h"
+#include "memory_.h"
+#include "errors.h"
+#include "gsexit.h"
+#include "gsstruct.h"
+#include "iastate.h"
+#include "igc.h" /* for gc_state_t */
+#include "igcstr.h" /* for prototype */
+#include "iname.h"
+#include "ipacked.h"
+#include "isstate.h"
+#include "ivmspace.h"
+#include "store.h"
+
+/* ================ Locating ================ */
+
+/* Locate a pointer in the chunks of a space being collected. */
+/* This is only used for string garbage collection and for debugging. */
+chunk_t *
+gc_locate(const void *ptr, gc_state_t *gcst)
+{ const gs_ref_memory_t *mem;
+ if ( chunk_locate(ptr, &gcst->loc) )
+ return gcst->loc.cp;
+ mem = gcst->loc.memory;
+ /* Try the other space, if there is one. */
+ if ( gcst->space_local != gcst->space_global )
+ { gcst->loc.memory =
+ (mem->space == avm_local ? gcst->space_global : gcst->space_local);
+ gcst->loc.cp = 0;
+ if ( chunk_locate(ptr, &gcst->loc) )
+ return gcst->loc.cp;
+ /* Try other save levels of this space. */
+ while ( gcst->loc.memory->saved != 0 )
+ { gcst->loc.memory = &gcst->loc.memory->saved->state;
+ gcst->loc.cp = 0;
+ if ( chunk_locate(ptr, &gcst->loc) )
+ return gcst->loc.cp;
+ }
+ }
+ /* Try the system space. This is simpler because it isn't */
+ /* subject to save/restore. */
+ if ( mem != gcst->space_system )
+ { gcst->loc.memory = gcst->space_system;
+ gcst->loc.cp = 0;
+ if ( chunk_locate(ptr, &gcst->loc) )
+ return gcst->loc.cp;
+ }
+ /* Try other save levels of the initial space, */
+ /* or of global space if the original space was system space. */
+ /* In the latter case, try all levels. */
+ gcst->loc.memory =
+ (mem == gcst->space_system || mem->space == avm_global ?
+ gcst->space_global : gcst->space_local);
+ for ( ; ; )
+ { if ( gcst->loc.memory != mem ) /* don't do twice */
+ { gcst->loc.cp = 0;
+ if ( chunk_locate(ptr, &gcst->loc) )
+ return gcst->loc.cp;
+ }
+ if ( gcst->loc.memory->saved == 0 )
+ break;
+ gcst->loc.memory = &gcst->loc.memory->saved->state;
+ }
+ /* Restore locator to a legal state. */
+ gcst->loc.memory = mem;
+ gcst->loc.cp = 0;
+ return 0;
+}
+
+/* ================ Debugging ================ */
+
+#ifdef DEBUG
+
+/* Validate the contents of an allocator. */
+void
+ialloc_validate_spaces(const gs_dual_memory_t *dmem)
+{ int i;
+ gc_state_t state;
+#define nspaces countof(dmem->spaces.indexed)
+ chunk_t cc[nspaces];
+ uint rsize[nspaces];
+ ref rlast[nspaces];
+
+ state.spaces = dmem->spaces;
+ state.loc.memory = state.spaces.named.local;
+ state.loc.cp = 0;
+
+ /* Save everything we need to reset temporarily. */
+ for ( i = 0; i < nspaces; i++ )
+ if ( dmem->spaces.indexed[i] != 0 )
+ { gs_ref_memory_t *mem = dmem->spaces.indexed[i];
+ chunk_t *pcc = mem->pcc;
+ obj_header_t *rcur = mem->cc.rcur;
+ if ( pcc != 0 )
+ { cc[i] = *pcc;
+ *pcc = mem->cc;
+ }
+ if ( rcur != 0 )
+ { rsize[i] = rcur[-1].o_size;
+ rcur[-1].o_size = mem->cc.rtop - (byte *)rcur;
+ /* Create the final ref, reserved for the GC. */
+ rlast[i] = ((ref *)mem->cc.rtop)[-1];
+ make_mark((ref *)mem->cc.rtop - 1);
+ }
+ }
+
+ /* Validate memory. */
+ for ( i = 0; i < nspaces; i++ )
+ if ( dmem->spaces.indexed[i] != 0 )
+ ialloc_validate_memory(dmem->spaces.indexed[i], &state);
+
+ /* Undo temporary changes. */
+ for ( i = 0; i < nspaces; i++ )
+ if ( dmem->spaces.indexed[i] != 0 )
+ { gs_ref_memory_t *mem = dmem->spaces.indexed[i];
+ chunk_t *pcc = mem->pcc;
+ obj_header_t *rcur = mem->cc.rcur;
+ if ( rcur != 0 )
+ { rcur[-1].o_size = rsize[i];
+ ((ref *)mem->cc.rtop)[-1] = rlast[i];
+ }
+ if ( pcc != 0 )
+ *pcc = cc[i];
+ }
+}
+void
+ialloc_validate_memory(const gs_ref_memory_t *mem, gc_state_t *gcst)
+{ const gs_ref_memory_t *smem;
+ for ( smem = mem; smem != 0; smem = &smem->saved->state )
+ { const chunk_t *cp;
+ for ( cp = mem->cfirst; cp != 0; cp = cp->cnext )
+ ialloc_validate_chunk(cp, gcst);
+ };
+}
+
+/* Validate all the objects in a chunk. */
+void
+ialloc_validate_chunk(const chunk_t *cp, gc_state_t *gcst)
+{ if_debug_chunk('6', "[6]validating chunk", cp);
+ SCAN_CHUNK_OBJECTS(cp);
+ DO_ALL
+ ialloc_validate_object(pre + 1, cp, gcst);
+ if_debug3('7', " [7]validating %s(%lu) 0x%lx\n",
+ struct_type_name_string(pre->o_type),
+ (ulong)size, (ulong)pre);
+ if ( pre->o_type == &st_refs )
+ { const ref_packed *rp = (const ref_packed *)(pre + 1);
+ const char *end = (const char *)rp + size;
+
+ while ( (const char *)rp < end )
+ if ( r_is_packed(rp) )
+ rp++;
+ else
+ { const void *optr = 0;
+#define pref ((const ref *)rp)
+ if ( r_space(pref) != avm_foreign )
+ switch ( r_type(pref) )
+ {
+ case t_file:
+ optr = pref->value.pfile;
+ goto cks;
+ case t_device:
+ optr = pref->value.pdevice;
+ goto cks;
+ case t_fontID:
+ case t_struct:
+ case t_astruct:
+ optr = pref->value.pstruct;
+cks: if ( optr != 0 )
+ ialloc_validate_object(optr, NULL, gcst);
+ break;
+ case t_name:
+ if ( name_index_ptr(r_size(pref)) !=
+ pref->value.pname
+ )
+ { lprintf3("At 0x%lx, bad name %u, pname = 0x%lx\n",
+ (ulong)pref,
+ (uint)r_size(pref),
+ (ulong)pref->value.pname);
+ break;
+ }
+ { ref sref;
+ name_string_ref(pref, &sref);
+ if ( r_space(&sref) != avm_foreign &&
+ !gc_locate(sref.value.const_bytes, gcst)
+ )
+ { lprintf4("At 0x%lx, bad name %u, pname = 0x%lx, string 0x%lx not in any chunk\n",
+ (ulong)pref,
+ (uint)r_size(pref),
+ (ulong)pref->value.pname,
+ (ulong)sref.value.const_bytes);
+ break;
+ }
+ }
+ break;
+ case t_string:
+ if ( r_size(pref) != 0 &&
+ !gc_locate(pref->value.bytes, gcst)
+ )
+ { lprintf3("At 0x%lx, string ptr 0x%lx[%u] not in any chunk\n",
+ (ulong)pref,
+ (ulong)pref->value.bytes,
+ (uint)r_size(pref));
+ }
+ break;
+ /****** SHOULD ALSO CHECK: ******/
+ /****** arrays, dict ******/
+ }
+#undef pref
+ rp += packed_per_ref;
+ }
+ }
+ else
+ { struct_proc_enum_ptrs((*proc)) =
+ pre->o_type->enum_ptrs;
+ uint index = 0;
+ void *ptr;
+ gs_ptr_type_t ptype;
+ if ( proc != 0 )
+ for ( ; (ptype = (*proc)(pre + 1, size, index, &ptr)) != 0; ++index )
+ if ( ptr != 0 && ptype == ptr_struct_type )
+ ialloc_validate_object(ptr, NULL, gcst);
+ }
+ END_OBJECTS_SCAN
+}
+
+/* Validate an object. */
+void
+ialloc_validate_object(const obj_header_t *ptr, const chunk_t *cp,
+ gc_state_t *gcst)
+{ const obj_header_t *pre = ptr - 1;
+ ulong size = pre_obj_contents_size(pre);
+ gs_memory_type_ptr_t otype = pre->o_type;
+ const char *oname;
+
+ if ( !gs_debug_c('?') )
+ return; /* no check */
+ if ( cp == 0 && gcst != 0 )
+ { gc_state_t st;
+ st = *gcst; /* no side effects! */
+ if ( !(cp = gc_locate(pre, &st)) )
+ { lprintf1("Object 0x%lx not in any chunk!\n",
+ (ulong)pre);
+ return;/*gs_abort();*/
+ }
+ }
+ if ( (cp != 0 &&
+ !(pre->o_large ? (const byte *)pre == cp->cbase :
+ size <= cp->ctop - (const byte *)(pre + 1))) ||
+ otype->ssize == 0 ||
+ size % otype->ssize != 0 ||
+ (oname = struct_type_name_string(otype),
+ *oname < 33 || *oname > 126)
+ )
+ { lprintf4("Bad object 0x%lx(%lu), ssize = %u, in chunk 0x%lx!\n",
+ (ulong)pre, (ulong)size, otype->ssize, (ulong)cp);
+ gs_abort();
+ }
+}
+
+#else /* !DEBUG */
+
+void
+ialloc_validate_spaces(const gs_dual_memory_t *dmem)
+{
+}
+
+void
+ialloc_validate_memory(const gs_ref_memory_t *mem, gc_state_t *gcst)
+{
+}
+
+void
+ialloc_validate_chunk(const chunk_t *cp, gc_state_t *gcst)
+{
+}
+
+void
+ialloc_validate_object(const obj_header_t *ptr, const chunk_t *cp,
+ gc_state_t *gcst)
+{
+}
+
+#endif /* (!)DEBUG */
diff --git a/pstoraster/imain.c b/pstoraster/imain.c
new file mode 100644
index 000000000..e6c910073
--- /dev/null
+++ b/pstoraster/imain.c
@@ -0,0 +1,569 @@
+/* Copyright (C) 1989, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* imain.c */
+/* Common support for interpreter front ends */
+#include "memory_.h"
+#include "string_.h"
+/* Capture stdin/out/err before gs.h redefines them. */
+#include <stdio.h>
+void
+gs_get_real_stdio(FILE *stdfiles[3])
+{ stdfiles[0] = stdin;
+ stdfiles[1] = stdout;
+ stdfiles[2] = stderr;
+}
+#include "ghost.h"
+#include "gp.h"
+#include "gslib.h"
+#include "gsmatrix.h" /* for gxdevice.h */
+#include "gsutil.h" /* for bytes_compare */
+#include "gxdevice.h"
+#include "errors.h"
+#include "oper.h"
+#include "idebug.h"
+#include "idict.h"
+#include "iname.h" /* for name_init */
+#include "dstack.h"
+#include "estack.h"
+#include "ostack.h" /* put here for files.h */
+#include "stream.h" /* for files.h */
+#include "files.h"
+#include "ialloc.h"
+#include "strimpl.h" /* for sfilter.h */
+#include "sfilter.h" /* for iscan.h */
+#include "iscan.h"
+#include "main.h"
+#include "store.h"
+#include "isave.h" /* for prototypes */
+#include "interp.h"
+#include "ivmspace.h"
+
+/* ------ Exported data ------ */
+
+/* Define the default instance of the interpreter. */
+/* Currently, this is the *only possible* instance, because most of */
+/* the places that need to take an explicit instance argument don't. */
+private gs_main_instance the_gs_main_instance =
+ { gs_main_instance_default_init_values };
+gs_main_instance *gs_main_instance_default()
+{ return &the_gs_main_instance;
+}
+
+/* The only reason we export gs_exit_status is so that window systems */
+/* with alert boxes can know whether to pause before exiting if */
+/* the program terminates with an error. There must be a better way .... */
+int gs_exit_status;
+
+/* ------ Imported data ------ */
+
+/* Configuration information imported from gconfig.c and iinit.c. */
+extern const char *gs_init_file;
+extern const byte far_data gs_init_string[];
+extern const uint far_data gs_init_string_sizeof;
+extern ref gs_init_file_array[];
+extern ref gs_emulator_name_array[];
+
+/* ------ Forward references ------ */
+
+private int gs_run_init_file(P3(gs_main_instance *, int *, ref *));
+
+/* ------ Initialization ------ */
+
+/* A handy way to declare and execute an initialization procedure: */
+#define call_init(proc)\
+{ extern void proc(P0()); proc(); }
+
+/* Initialization to be done before anything else. */
+void
+gs_main_init0(gs_main_instance *minst, FILE *in, FILE *out, FILE *err,
+ int max_lib_paths)
+{ /* Set our versions of stdin/out/err. */
+ gs_stdin = in;
+ gs_stdout = out;
+ gs_stderr = err;
+ /* Do platform-dependent initialization. */
+ /* We have to do this as the very first thing, */
+ /* because it detects attempts to run 80N86 executables (N>0) */
+ /* on incompatible processors. */
+ gp_init();
+ /* Initialize the imager. */
+ gs_lib_init0(gs_stdout);
+ /* Initialize the file search paths. */
+ make_array(&minst->lib_path.container, avm_foreign, max_lib_paths,
+ (ref *)gs_malloc(max_lib_paths, sizeof(ref),
+ "lib_path array"));
+ make_array(&minst->lib_path.list, avm_foreign | a_readonly, 0,
+ minst->lib_path.container.value.refs);
+ minst->lib_path.env = 0;
+ minst->lib_path.final = 0;
+ minst->lib_path.count = 0;
+ minst->user_errors = 1;
+ minst->init_done = 0;
+}
+
+/* Initialization to be done before constructing any objects. */
+void
+gs_main_init1(gs_main_instance *minst)
+{ if ( minst->init_done < 1 )
+ { { extern bool gs_have_level2(P0());
+ ialloc_init(&gs_memory_default,
+ minst->memory_chunk_size,
+ gs_have_level2());
+ gs_lib_init1((gs_memory_t *)imemory_system);
+ alloc_save_init(idmemory);
+ }
+ if ( name_init(minst->name_table_size, imemory_system) == 0 )
+ { puts("name_init failed");
+ gs_exit(1);
+ }
+ call_init(obj_init) /* requires name_init */
+ call_init(scan_init) /* ditto */
+ minst->init_done = 1;
+ }
+}
+
+/* Initialization to be done before running any files. */
+private void
+init2_make_string_array(ref *srefs, const char *aname)
+{ ref *ifp = srefs;
+ ref ifa;
+ for ( ; ifp->value.bytes != 0; ifp++ )
+ r_set_size(ifp, strlen((const char *)ifp->value.bytes));
+ make_tasv(&ifa, t_array, a_readonly | avm_foreign,
+ ifp - srefs, refs, srefs);
+ initial_enter_name(aname, &ifa);
+}
+void
+gs_main_init2(gs_main_instance *minst)
+{ gs_main_init1(minst);
+ if ( minst->init_done < 2 )
+ { int code, exit_code;
+ ref error_object;
+ call_init(igs_init)
+ call_init(zop_init)
+ { extern void gs_iodev_init(P1(gs_memory_t *));
+ gs_iodev_init(imemory);
+ }
+ call_init(op_init) /* requires obj_init, scan_init */
+
+ /* Set up the array of additional initialization files. */
+ init2_make_string_array(gs_init_file_array, "INITFILES");
+ /* Set up the array of emulator names. */
+ init2_make_string_array(gs_emulator_name_array, "EMULATORS");
+ /* Pass the search path. */
+ initial_enter_name("LIBPATH", &minst->lib_path.list);
+
+ /* Execute the standard initialization file. */
+ code = gs_run_init_file(minst, &exit_code, &error_object);
+ if ( code < 0 )
+ { if ( code != e_Fatal )
+ gs_debug_dump_stack(code, &error_object);
+ gs_exit_with_code((exit_code ? exit_code : 2), code);
+ }
+ minst->init_done = 2;
+ }
+}
+
+/* ------ Search paths ------ */
+
+/* Internal routine to add a set of directories to a search list. */
+/* Returns 0 or an error code. */
+private int
+file_path_add(gs_file_path *pfp, const char *dirs)
+{ uint len = r_size(&pfp->list);
+ const char *dpath = dirs;
+
+ if ( dirs == 0 )
+ return 0;
+ for ( ; ; )
+ { /* Find the end of the next directory name. */
+ const char *npath = dpath;
+
+ while ( *npath != 0 && *npath != gp_file_name_list_separator )
+ npath++;
+ if ( npath > dpath )
+ { if ( len == r_size(&pfp->container) )
+ return_error(e_limitcheck);
+ make_const_string(&pfp->container.value.refs[len],
+ avm_foreign | a_readonly,
+ npath - dpath, (const byte *)dpath);
+ ++len;
+ }
+ if ( !*npath )
+ break;
+ dpath = npath + 1;
+ }
+ r_set_size(&pfp->list, len);
+ return 0;
+}
+
+/* Add a library search path to the list. */
+void
+gs_main_add_lib_path(gs_main_instance *minst, const char *lpath)
+{ /* Account for the possibility that the first element */
+ /* is gp_current_directory name added by set_lib_paths. */
+ int first_is_here =
+ (r_size(&minst->lib_path.list) != 0 &&
+ minst->lib_path.container.value.refs[0].value.bytes ==
+ (const byte *)gp_current_directory_name ? 1 : 0);
+
+ r_set_size(&minst->lib_path.list, minst->lib_path.count +
+ first_is_here);
+ file_path_add(&minst->lib_path, lpath);
+ minst->lib_path.count = r_size(&minst->lib_path.list) - first_is_here;
+ gs_main_set_lib_paths(minst);
+}
+
+/* ------ Execution ------ */
+
+/* Complete the list of library search paths. */
+/* This may involve adding or removing the current directory */
+/* as the first element. */
+void
+gs_main_set_lib_paths(gs_main_instance *minst)
+{ ref *paths = minst->lib_path.container.value.refs;
+ int first_is_here =
+ (r_size(&minst->lib_path.list) != 0 &&
+ paths[0].value.bytes == (const byte *)gp_current_directory_name ? 1 : 0);
+ int count = minst->lib_path.count;
+
+ if ( minst->search_here_first )
+ { if ( !(first_is_here ||
+ (r_size(&minst->lib_path.list) != 0 &&
+ !bytes_compare((const byte *)gp_current_directory_name,
+ strlen(gp_current_directory_name),
+ paths[0].value.bytes,
+ r_size(&paths[0]))))
+ )
+ { memmove(paths + 1, paths, count * sizeof(*paths));
+ make_const_string(paths, avm_foreign | a_readonly,
+ strlen(gp_current_directory_name),
+ (const byte *)gp_current_directory_name);
+ }
+ }
+ else
+ { if ( first_is_here )
+ memmove(paths, paths + 1, count * sizeof(*paths));
+ }
+ r_set_size(&minst->lib_path.list,
+ count + (minst->search_here_first ? 1 : 0));
+ if ( minst->lib_path.env != 0 )
+ file_path_add(&minst->lib_path, minst->lib_path.env);
+ if ( minst->lib_path.final != 0 )
+ file_path_add(&minst->lib_path, minst->lib_path.final);
+}
+
+/* Open a file, using the search paths. */
+int
+gs_main_lib_open(gs_main_instance *minst, const char *file_name, ref *pfile)
+{ /* This is a separate procedure only to avoid tying up */
+ /* extra stack space while running the file. */
+#define maxfn 200
+ byte fn[maxfn];
+ uint len;
+ return lib_file_open(file_name, strlen(file_name), fn, maxfn,
+ &len, pfile);
+}
+
+/* Open and execute a file. */
+int
+gs_main_run_file(gs_main_instance *minst, const char *file_name, int user_errors, int *pexit_code, ref *perror_object)
+{ ref initial_file;
+ int code = gs_main_run_file_open(minst, file_name, &initial_file);
+ if ( code < 0 ) return code;
+ return gs_interpret(&initial_file, user_errors, pexit_code, perror_object);
+}
+int
+gs_main_run_file_open(gs_main_instance *minst, const char *file_name, ref *pfref)
+{ gs_main_set_lib_paths(minst);
+ if ( gs_main_lib_open(minst, file_name, pfref) < 0 )
+ { eprintf1("Can't find initialization file %s.\n", file_name);
+ return_error(e_Fatal);
+ }
+ r_set_attrs(pfref, a_execute + a_executable);
+ return 0;
+}
+
+/* Open and run the very first initialization file. */
+private int
+gs_run_init_file(gs_main_instance *minst, int *pexit_code, ref *perror_object)
+{ ref ifile;
+ ref first_token;
+ int code;
+ scanner_state state;
+
+ gs_main_set_lib_paths(minst);
+ if ( gs_init_string_sizeof == 0 )
+ { /* Read from gs_init_file. */
+ code = gs_main_run_file_open(minst, gs_init_file, &ifile);
+ }
+ else
+ { /* Read from gs_init_string. */
+ code = file_read_string(gs_init_string, gs_init_string_sizeof,
+ &ifile);
+ }
+ if ( code < 0 )
+ { *pexit_code = 255;
+ return code;
+ }
+ /* Check to make sure the first token is an integer */
+ /* (for the version number check.) */
+ scanner_state_init(&state, false);
+ code = scan_token(ifile.value.pfile, &first_token, &state);
+ if ( code != 0 || !r_has_type(&first_token, t_integer) )
+ { eprintf1("Initialization file %s does not begin with an integer.\n", gs_init_file);
+ *pexit_code = 255;
+ return_error(e_Fatal);
+ }
+ *++osp = first_token;
+ r_set_attrs(&ifile, a_executable);
+ return gs_interpret(&ifile, minst->user_errors,
+ pexit_code, perror_object);
+}
+
+/* Run a string. */
+int
+gs_main_run_string(gs_main_instance *minst, const char *str, int user_errors,
+ int *pexit_code, ref *perror_object)
+{ return gs_main_run_string_with_length(minst, str, (uint)strlen(str),
+ user_errors,
+ pexit_code, perror_object);
+}
+int
+gs_main_run_string_with_length(gs_main_instance *minst, const char *str,
+ uint length, int user_errors, int *pexit_code, ref *perror_object)
+{ int code;
+ code = gs_main_run_string_begin(minst, user_errors,
+ pexit_code, perror_object);
+ if ( code < 0 )
+ return code;
+ code = gs_main_run_string_continue(minst, str, length, user_errors,
+ pexit_code, perror_object);
+ if ( code != e_NeedInput )
+ return code;
+ return gs_main_run_string_end(minst, user_errors,
+ pexit_code, perror_object);
+}
+
+/* Set up for a suspendable run_string. */
+int
+gs_main_run_string_begin(gs_main_instance *minst, int user_errors,
+ int *pexit_code, ref *perror_object)
+{ const char *setup = "{.needinput}bind 0().subfiledecode cvx exec";
+ ref rstr;
+ int code;
+
+ gs_main_set_lib_paths(minst);
+ make_const_string(&rstr, avm_foreign | a_readonly | a_executable,
+ strlen(setup), (const byte *)setup);
+ code = gs_interpret(&rstr, user_errors, pexit_code, perror_object);
+ return (code == e_NeedInput ? 0 : code == 0 ? e_Fatal : code);
+}
+/* Continue running a string with the option of suspending. */
+int
+gs_main_run_string_continue(gs_main_instance *minst, const char *str,
+ uint length, int user_errors, int *pexit_code, ref *perror_object)
+{ ref rstr;
+
+ if ( length == 0 )
+ return 0; /* empty string signals EOF */
+ make_const_string(&rstr, avm_foreign | a_readonly, length,
+ (const byte *)str);
+ return gs_interpret(&rstr, user_errors, pexit_code, perror_object);
+}
+/* Signal EOF when suspended. */
+int
+gs_main_run_string_end(gs_main_instance *minst, int user_errors,
+ int *pexit_code, ref *perror_object)
+{ ref rstr;
+
+ make_empty_const_string(&rstr, avm_foreign | a_readonly);
+ return gs_interpret(&rstr, user_errors, pexit_code, perror_object);
+}
+
+/* ------ Operand stack access ------ */
+
+/* These are built for comfort, not for speed. */
+
+private int
+push_value(ref *pvalue)
+{ int code = ref_stack_push(&o_stack, 1);
+ if ( code < 0 )
+ return code;
+ *ref_stack_index(&o_stack, 0L) = *pvalue;
+ return 0;
+}
+
+int
+gs_push_boolean(gs_main_instance *minst, bool value)
+{ ref vref;
+ make_bool(&vref, value);
+ return push_value(&vref);
+}
+
+int
+gs_push_integer(gs_main_instance *minst, long value)
+{ ref vref;
+ make_int(&vref, value);
+ return push_value(&vref);
+}
+
+int
+gs_push_real(gs_main_instance *minst, floatp value)
+{ ref vref;
+ make_real(&vref, value);
+ return push_value(&vref);
+}
+
+int
+gs_push_string(gs_main_instance *minst, byte *chars, uint length,
+ bool read_only)
+{ ref vref;
+ make_string(&vref, avm_foreign | (read_only ? a_readonly : a_all),
+ length, (byte *)chars);
+ return push_value(&vref);
+}
+
+private int
+pop_value(ref *pvalue)
+{ if ( !ref_stack_count(&o_stack) )
+ return_error(e_stackunderflow);
+ *pvalue = *ref_stack_index(&o_stack, 0L);
+ return 0;
+}
+
+int
+gs_pop_boolean(gs_main_instance *minst, bool *result)
+{ ref vref;
+ int code = pop_value(&vref);
+ if ( code < 0 )
+ return code;
+ check_type_only(vref, t_boolean);
+ *result = vref.value.boolval;
+ ref_stack_pop(&o_stack, 1);
+ return 0;
+}
+
+int
+gs_pop_integer(gs_main_instance *minst, long *result)
+{ ref vref;
+ int code = pop_value(&vref);
+ if ( code < 0 )
+ return code;
+ check_type_only(vref, t_integer);
+ *result = vref.value.intval;
+ ref_stack_pop(&o_stack, 1);
+ return 0;
+}
+
+int
+gs_pop_real(gs_main_instance *minst, float *result)
+{ ref vref;
+ int code = pop_value(&vref);
+ if ( code < 0 )
+ return code;
+ switch ( r_type(&vref) )
+ {
+ case t_real:
+ *result = vref.value.realval;
+ break;
+ case t_integer:
+ *result = vref.value.intval;
+ break;
+ default:
+ return_error(e_typecheck);
+ }
+ ref_stack_pop(&o_stack, 1);
+ return 0;
+}
+
+int
+gs_pop_string(gs_main_instance *minst, gs_string *result)
+{ ref vref;
+ int code = pop_value(&vref);
+ if ( code < 0 )
+ return code;
+ switch ( r_type(&vref) )
+ {
+ case t_name:
+ name_string_ref(&vref, &vref);
+ code = 1;
+ goto rstr;
+ case t_string:
+ code = (r_has_attr(&vref, a_write) ? 0 : 1);
+rstr: result->data = vref.value.bytes;
+ result->size = r_size(&vref);
+ break;
+ default:
+ return_error(e_typecheck);
+ }
+ ref_stack_pop(&o_stack, 1);
+ return code;
+}
+
+/* ------ Termination ------ */
+
+/* Free all resources and exit. */
+void
+gs_main_finit(gs_main_instance *minst, int exit_status, int code)
+{ /*
+ * Previous versions of this code closed the devices in the
+ * device list here. Since these devices are now prototypes,
+ * they cannot be opened, so they do not need to be closed;
+ * alloc_restore_all will close dynamically allocated devices.
+ */
+ gs_exit_status = exit_status; /* see above */
+
+ /* Do the equivalent of a restore "past the bottom". */
+ /* This will release all memory, close all open files, etc. */
+ if ( minst->init_done >= 1 )
+ alloc_restore_all(idmemory);
+ gs_lib_finit(exit_status, code);
+}
+void
+gs_exit_with_code(int exit_status, int code)
+{ gs_finit(exit_status, code);
+ gp_do_exit(exit_status);
+}
+void
+gs_exit(int exit_status)
+{ gs_exit_with_code(exit_status, 0);
+}
+
+/* ------ Debugging ------ */
+
+/* Dump the stacks after interpretation */
+void
+gs_debug_dump_stack(int code, ref *perror_object)
+{ zflush(osp); /* force out buffered output */
+ dprintf1("\nUnexpected interpreter error %d.\n", code);
+ if ( perror_object != 0 )
+ { dputs("Error object: ");
+ debug_print_ref(perror_object);
+ dputc('\n');
+ }
+ debug_dump_stack(&o_stack, "Operand stack");
+ debug_dump_stack(&e_stack, "Execution stack");
+ debug_dump_stack(&d_stack, "Dictionary stack");
+}
diff --git a/pstoraster/imain.h b/pstoraster/imain.h
new file mode 100644
index 000000000..ac07cf5b0
--- /dev/null
+++ b/pstoraster/imain.h
@@ -0,0 +1,271 @@
+/* Copyright (C) 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* imain.h */
+/* Interface to imain.c */
+/* Requires <stdio.h>, stdpre.h, gsmemory.h, gstypes.h, iref.h */
+
+#ifndef imain_INCLUDED
+# define imain_INCLUDED
+
+#include "gsexit.h" /* exported by imain.c */
+
+/*
+ * This file defines the intended API between client front ends
+ * (such as imainarg.c, the command-line-driven front end)
+ * and imain.c, which provides top-level control of the interpreter.
+ */
+
+/* ================ Types ================ */
+
+/*
+ * Currently, the interpreter has a lot of static variables, but
+ * eventually it will have none, so that clients will be able to make
+ * multiple instances of it. In anticipation of this, many of the
+ * top-level API calls take an interpreter instance (gs_main_instance *)
+ * as their first argument.
+ */
+#ifndef gs_main_instance_DEFINED
+# define gs_main_instance_DEFINED
+typedef struct gs_main_instance_s gs_main_instance;
+#endif
+
+/* ================ Exported procedures from imain.c ================ */
+
+/* ---------------- Instance creation ---------------- */
+
+/*
+ * As noted above, multiple instances are not supported yet:
+ */
+/*gs_main_instance *gs_main_alloc_instance(P1(gs_memory_t *));*/
+/*
+ * Instead, we provide only a default instance:
+ */
+gs_main_instance *gs_main_instance_default(P0());
+
+/* ---------------- Initialization ---------------- */
+
+/*
+ * The interpreter requires three initialization steps, called init0,
+ * init1, and init2. These steps must be done in that order, but
+ * init1 may be omitted.
+ */
+
+/*
+ * Since gsio.h (which is included in many other header files)
+ * redefines stdin/out/err, callers need a way to get the "real"
+ * stdio files to pass to init0 if they wish to do so.
+ */
+void gs_get_real_stdio(P1(FILE *stdfiles[3]));
+
+/*
+ * init0 records the files to be used for stdio, and initializes the
+ * graphics library, the file search paths, and other instance data.
+ */
+void gs_main_init0(P5(gs_main_instance *minst, FILE *in, FILE *out, FILE *err,
+ int max_lib_paths));
+
+/*
+ * init1 initializes the memory manager and other internal data
+ * structures such as the name table, the token scanner tables,
+ * dictionaries such as systemdict, and the interpreter stacks.
+ */
+void gs_main_init1(P1(gs_main_instance *minst));
+
+/*
+ * init2 finishes preparing the interpreter for use by running
+ * initialization files with PostScript procedure definitions.
+ */
+void gs_main_init2(P1(gs_main_instance *minst));
+
+/*
+ * The runlibfile operator uses a search path, as described in
+ * use.doc, for looking up file names. Each interpreter instance has
+ * its own search path. The following call adds a directory or set of
+ * directories to the search path; it is equivalent to the -I command
+ * line switch. It may be called any time after init0.
+ */
+void gs_main_add_lib_path(P2(gs_main_instance *minst, const char *path));
+/*
+ * Under certain internal conditions, the search path may temporarily
+ * be in an inconsistent state; gs_main_set_lib_paths takes care of
+ * this. Clients should never need to call this procedure, and
+ * eventually it may be removed.
+ */
+void gs_main_set_lib_paths(P1(gs_main_instance *minst));
+
+/*
+ * Open a PostScript file using the search path. Clients should
+ * never need to call this procedure, since gs_main_run_file opens the
+ * file itself, and eventually the procedure may be removed.
+ */
+int gs_main_lib_open(P3(gs_main_instance *minst, const char *fname,
+ ref *pfile));
+
+/*
+ * Here we summarize the C API calls that correspond to some of the
+ * most common command line switches documented in use.doc, to help
+ * clients who are familiar with the command line and are starting to
+ * use the API.
+ *
+ * -d/D, -s/S (for setting device parameters like OutputFile)
+ * Use the C API for device parameters documented near the
+ * end of gsparam.h.
+ *
+ * -d/D (for setting Boolean parameters like NOPAUSE)
+ * { ref vtrue;
+ * make_true(&vtrue);
+ * dict_put_string(systemdict, "NOPAUSE", &vtrue);
+ * }
+ * -I
+ * Use gs_main_add_lib_path, documented above.
+ *
+ * -A, -A-
+ * Set gs_debug['@'] = 1 or 0 respectively.
+ * -E, -E-
+ * Set gs_debug['#'] = 1 or 0 respectively.
+ * -Z..., -Z-...
+ * Set gs_debug[c] = 1 or 0 respectively for each character
+ * c in the string.
+ */
+
+/* ---------------- Execution ---------------- */
+
+/*
+ * After initializing the interpreter, clients may pass it files or
+ * strings to be interpreted. There are four ways to do this:
+ * - Pass a file name (gs_main_run_file);
+ * - Pass a C string (gs_main_run_string);
+ * - Pass a string defined by pointer and length
+ * (gs_main_run_string_with_length);
+ * - Pass strings piece-by-piece
+ * (gs_main_run_string_begin/continue/end).
+ *
+ * The value returned by the first three of these calls is
+ * 0 if the interpreter ran to completion, e_Quit for a normal quit,
+ * or e_Fatal for a non-zero quit or a fatal error.
+ * e_Fatal stores the exit code in the third argument.
+ * The str argument of gs_main_run_string[_with_length] must be allocated
+ * in non-garbage-collectable space (e.g., by malloc or gs_malloc,
+ * or statically).
+ */
+int gs_main_run_file(P5(gs_main_instance *minst,
+ const char *fname,
+ int user_errors, int *pexit_code,
+ ref *perror_object));
+int gs_main_run_string(P5(gs_main_instance *minst,
+ const char *str,
+ int user_errors, int *pexit_code,
+ ref *perror_object));
+int gs_main_run_string_with_length(P6(gs_main_instance *minst,
+ const char *str, uint length,
+ int user_errors, int *pexit_code,
+ ref *perror_object));
+
+/*
+ * Open the file for gs_main_run_file. This is an internal routine
+ * that is only exported for some special clients.
+ */
+int gs_main_run_file_open(P3(gs_main_instance *minst,
+ const char *file_name, ref *pfref));
+
+/*
+ * The next 3 procedures provide for feeding input to the interpreter
+ * in arbitrary chunks, unlike run_string, which requires that each string
+ * be a properly formed PostScript program fragment. To use them:
+ * Call run_string_begin.
+ * Call run_string_continue as many times as desired,
+ * stopping if it returns anything other than e_NeedInput.
+ * If run_string_continue didn't indicate an error or a quit
+ * (i.e., a return value other than e_NeedInput), call run_string_end
+ * to provide an EOF indication.
+ * Note that run_string_continue takes a pointer and a length, like
+ * run_string_with_length.
+ */
+int gs_main_run_string_begin(P4(gs_main_instance *minst, int user_errors,
+ int *pexit_code, ref *perror_object));
+int gs_main_run_string_continue(P6(gs_main_instance *minst,
+ const char *str, uint length,
+ int user_errors, int *pexit_code,
+ ref *perror_object));
+int gs_main_run_string_end(P4(gs_main_instance *minst, int user_errors,
+ int *pexit_code, ref *perror_object));
+
+/* ---------------- Operand stack access ---------------- */
+
+/*
+ * The following procedures are not used in normal operation;
+ * they exist only to allow clients driving the interpreter through the
+ * gs_main_run_xxx procedures to push parameters quickly and to get results
+ * back. The push procedures return 0, e_stackoverflow, or e_VMerror;
+ * the pop procedures return 0, e_stackunderflow, or e_typecheck.
+ *
+ * Procedures to push values on the operand stack:
+ */
+int gs_push_boolean(P2(gs_main_instance *minst, bool value));
+int gs_push_integer(P2(gs_main_instance *minst, long value));
+int gs_push_real(P2(gs_main_instance *minst, floatp value));
+int gs_push_string(P4(gs_main_instance *minst, byte *chars, uint length,
+ bool read_only));
+/*
+ * Procedures to pop values from the operand stack:
+ */
+int gs_pop_boolean(P2(gs_main_instance *minst, bool *result));
+int gs_pop_integer(P2(gs_main_instance *minst, long *result));
+int gs_pop_real(P2(gs_main_instance *minst, float *result));
+/* gs_pop_string returns 1 if the string is read-only. */
+int gs_pop_string(P2(gs_main_instance *minst, gs_string *result));
+
+/* ---------------- Debugging ---------------- */
+
+/*
+ * Print an error mesage including the error code, error object (if any),
+ * and operand and execution stacks in hex. Clients will probably
+ * never call this.
+ */
+void gs_debug_dump_stack(P2(int code, ref *perror_object));
+
+/* ---------------- Termination ---------------- */
+
+/*
+ * Terminate the interpreter by closing all devices and releasing all
+ * allocated memory. Currently, because of some technical problems
+ * with statically initialized data, it is not possible to reinitialize
+ * the interpreter after terminating it; we plan to fix this as soon as
+ * possible.
+ *
+ * Note that calling gs_exit (defined in gsexit.h) automatically calls
+ * gs_main_finit for the default instance.
+ */
+void gs_main_finit(P3(gs_main_instance *minst, int exit_status, int code));
+
+/* ================ Other exported procedures ================ */
+
+/*
+ * Define an internal interface to the interpreter. Clients do not
+ * normally use this.
+ */
+int gs_interpret(P4(ref *pref, int user_errors, int *pexit_code,
+ ref *perror_object));
+
+#endif /* imain_INCLUDED */
diff --git a/pstoraster/imemory.h b/pstoraster/imemory.h
new file mode 100644
index 000000000..896081114
--- /dev/null
+++ b/pstoraster/imemory.h
@@ -0,0 +1,105 @@
+/* Copyright (C) 1993, 1994 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* imemory.h */
+/* Ghostscript memory allocator extensions for interpreter level */
+
+#ifndef imemory_INCLUDED
+# define imemory_INCLUDED
+
+#include "ivmspace.h"
+
+/*
+ * The interpreter level of Ghostscript defines a "subclass" extension
+ * of the allocator interface in gsmemory.h, by adding the ability to
+ * allocate and free arrays of refs, and by adding the distinction
+ * between local, global, and system allocation.
+ */
+
+#ifndef gs_ref_memory_DEFINED
+# define gs_ref_memory_DEFINED
+typedef struct gs_ref_memory_s gs_ref_memory_t;
+#endif
+
+#include "gsalloc.h"
+
+ /* Allocate a ref array. */
+
+int gs_alloc_ref_array(P5(gs_ref_memory_t *mem, ref *paref,
+ uint attrs, uint num_refs, client_name_t cname));
+
+ /* Resize a ref array. */
+ /* Currently this is only implemented for shrinking, */
+ /* not growing. */
+
+int gs_resize_ref_array(P4(gs_ref_memory_t *mem, ref *paref,
+ uint new_num_refs, client_name_t cname));
+
+ /* Free a ref array. */
+
+void gs_free_ref_array(P3(gs_ref_memory_t *mem, ref *paref,
+ client_name_t cname));
+
+ /* Allocate a string ref. */
+
+int gs_alloc_string_ref(P5(gs_ref_memory_t *mem, ref *psref,
+ uint attrs, uint nbytes, client_name_t cname));
+
+/*
+ * Define the pointer type for refs. Note that if a structure contains refs,
+ * both its clear_marks and its reloc_ptrs procedure must unmark them,
+ * since the GC will never see the refs during the unmarking sweep.
+ */
+extern const gs_ptr_procs_t ptr_ref_procs;
+#define ptr_ref_type (&ptr_ref_procs)
+
+/* Register a ref root. */
+/* Note that ref roots are a little peculiar: they assume that */
+/* the ref * that they point to points to a *statically* allocated ref. */
+#define gs_register_ref_root(mem, root, rp, cname)\
+ gs_register_root(mem, root, ptr_ref_type, rp, cname)
+
+/*
+ * The interpreter allocator can allocate in either local or global VM,
+ * and can switch between the two dynamically. In Level 1 configurations,
+ * global VM is the same as local; however, this is *not* currently true in
+ * a Level 2 system running in Level 1 mode. In addition, there is a third
+ * VM space, system VM, that exists in both modes and is used for objects
+ * that must not be affected by even the outermost save/restore (stack
+ * segments and names).
+ */
+typedef struct gs_dual_memory_s gs_dual_memory_t;
+struct gs_dual_memory_s {
+ gs_ref_memory_t *current; /* = ...global or ...local */
+ vm_spaces spaces; /* system, global, local */
+ uint current_space; /* = current->space */
+ /* Save/restore machinery */
+ int save_level;
+ /* Garbage collection hook */
+ int (*reclaim)(P2(gs_dual_memory_t *, int));
+ /* Masks for store checking, see isave.h. */
+ uint test_mask;
+ uint new_mask;
+};
+
+#endif /* imemory_INCLUDED */
diff --git a/pstoraster/iminst.h b/pstoraster/iminst.h
new file mode 100644
index 000000000..fb28b3b13
--- /dev/null
+++ b/pstoraster/iminst.h
@@ -0,0 +1,80 @@
+/* Copyright (C) 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* iminst.h */
+/* Definition of interpreter instance */
+#include "imain.h"
+
+/*
+ * Define whether or not file searching should always look in the
+ * current directory first. This leads to well-known problems,
+ * but users insist on it.
+ */
+#ifndef SEARCH_HERE_FIRST
+# define SEARCH_HERE_FIRST 1
+#endif
+
+/*
+ * Define the structure of a search path. Currently there is only one,
+ * but there might be more someday.
+ *
+ * container - an array large enough to hold the specified maximum
+ * number of directories. Both the array and all the strings in it are
+ * in the 'foreign' VM space.
+ * list - the initial interval of container that defines the actual
+ * search list.
+ * env - the contents of an environment variable, implicitly added
+ * at the end of the list; may be 0.
+ * final - the final set of directories specified in the makefile;
+ * may be 0.
+ * count - the number of elements in the list, excluding a possible
+ * initial '.', env, and final.
+ */
+typedef struct gs_file_path_s {
+ ref container;
+ ref list;
+ const char *env;
+ const char *final;
+ uint count;
+} gs_file_path;
+
+/*
+ * Here is where we actually define the structure of interpreter instances.
+ * Clients should not reference any of the members.
+ */
+struct gs_main_instance_s {
+ /* The following are set during initialization. */
+ FILE *fstdin;
+ FILE *fstdout;
+ FILE *fstderr;
+ uint memory_chunk_size; /* 'wholesale' allocation unit */
+ ulong name_table_size;
+ int init_done; /* highest init done so far */
+ int user_errors; /* define what to do with errors */
+ bool search_here_first; /* if true, make '.' first lib dir */
+ bool run_start; /* if true, run 'start' after */
+ /* processing command line */
+ gs_file_path lib_path; /* library search list (GS_LIB) */
+};
+#define gs_main_instance_default_init_values\
+ 0, 0, 0, 20000, 0, -1, 0, SEARCH_HERE_FIRST, 1
diff --git a/pstoraster/iname.c b/pstoraster/iname.c
new file mode 100644
index 000000000..9ad2c3a78
--- /dev/null
+++ b/pstoraster/iname.c
@@ -0,0 +1,575 @@
+/* Copyright (C) 1989, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* iname.c */
+/* Name lookup for Ghostscript interpreter */
+#include "memory_.h"
+#include "string_.h"
+#include "ghost.h"
+#include "gsstruct.h"
+#include "errors.h"
+#include "inamedef.h"
+#include "imemory.h" /* for isave.h */
+#include "isave.h"
+#include "store.h"
+
+/* Public values */
+const uint name_max_string = max_name_string;
+
+/* In the code below, we use the hashing method described in */
+/* "Fast Hashing of Variable-Length Text Strings" by Peter K. Pearson, */
+/* pp. 677-680, CACM 33(6), June 1990. */
+
+/* Define a pseudo-random permutation of the integers 0..255. */
+/* Pearson's article claims this permutation gave good results. */
+private const far_data byte hash_permutation[256] = {
+ 1, 87, 49, 12, 176, 178, 102, 166, 121, 193, 6, 84, 249, 230, 44, 163,
+ 14, 197, 213, 181, 161, 85, 218, 80, 64, 239, 24, 226, 236, 142, 38, 200,
+110, 177, 104, 103, 141, 253, 255, 50, 77, 101, 81, 18, 45, 96, 31, 222,
+ 25, 107, 190, 70, 86, 237, 240, 34, 72, 242, 20, 214, 244, 227, 149, 235,
+ 97, 234, 57, 22, 60, 250, 82, 175, 208, 5, 127, 199, 111, 62, 135, 248,
+174, 169, 211, 58, 66, 154, 106, 195, 245, 171, 17, 187, 182, 179, 0, 243,
+132, 56, 148, 75, 128, 133, 158, 100, 130, 126, 91, 13, 153, 246, 216, 219,
+119, 68, 223, 78, 83, 88, 201, 99, 122, 11, 92, 32, 136, 114, 52, 10,
+138, 30, 48, 183, 156, 35, 61, 26, 143, 74, 251, 94, 129, 162, 63, 152,
+170, 7, 115, 167, 241, 206, 3, 150, 55, 59, 151, 220, 90, 53, 23, 131,
+125, 173, 15, 238, 79, 95, 89, 16, 105, 137, 225, 224, 217, 160, 37, 123,
+118, 73, 2, 157, 46, 116, 9, 145, 134, 228, 207, 212, 202, 215, 69, 229,
+ 27, 188, 67, 124, 168, 252, 42, 4, 29, 108, 21, 247, 19, 205, 39, 203,
+233, 40, 186, 147, 198, 192, 155, 33, 164, 191, 98, 204, 165, 180, 117, 76,
+140, 36, 210, 172, 41, 54, 159, 8, 185, 232, 113, 196, 231, 47, 146, 120,
+ 51, 65, 28, 144, 254, 221, 93, 189, 194, 139, 112, 43, 71, 109, 184, 209
+};
+
+/*
+ * Definitions and structure for the name table.
+ * Entry 0 is left unused.
+ * The entry with count = 1 is the entry for the 0-length name.
+ * The next nt_1char_size entries (in count order) are 1-character names.
+ */
+#define nt_1char_size 128
+#define nt_1char_first 2
+private const far_data byte nt_1char_names[128] = {
+#define q8(n) n,n+1,n+2,n+3,n+4,n+5,n+6,n+7
+#define q32(n) q8(n),q8(n+8),q8(n+16),q8(n+24)
+ q32(0), q32(32), q32(64), q32(96)
+#undef q8
+};
+
+gs_private_st_simple(st_name_sub_table, name_sub_table, "name_sub_table");
+gs_private_st_composite(st_name_table, name_table,
+ "name_table", name_table_enum_ptrs, name_table_reloc_ptrs);
+
+/* The one and only name table (for now). */
+private name_table *the_nt;
+private gs_gc_root_t the_nt_root;
+
+/* Forward references */
+private int name_alloc_sub(P1(name_table *));
+private void name_scan_sub(P3(name_table *, uint, bool));
+
+/* Debugging printout */
+#ifdef DEBUG
+private void
+name_print(const char *msg, name *pname, uint nidx, const int *pflag)
+{ const byte *ptr = pname->string_bytes;
+ dprintf1("[n]%s", msg);
+ if ( pflag )
+ dprintf1("(%d)", *pflag);
+ dprintf2(" (0x%lx#%u)", (ulong)pname, nidx);
+ debug_print_string(ptr, pname->string_size);
+ dprintf2("(0x%lx,%u)\n", (ulong)ptr, pname->string_size);
+}
+# define if_debug_name(msg, pname, nidx, pflag)\
+ if ( gs_debug_c('n') ) name_print(msg, pname, nidx, pflag)
+#else
+# define if_debug_name(msg, pname, nidx, pflag) DO_NOTHING
+#endif
+
+/* Initialize the name table */
+name_table *
+name_init(ulong count, gs_memory_t *mem)
+{ register int i;
+ name_table *nt;
+
+ if ( count == 0 )
+ count = max_name_count + 1L;
+ else if ( count - 1 > max_name_count )
+ return 0;
+ nt =
+ gs_alloc_struct(mem, name_table, &st_name_table, "name_init(nt)");
+ the_nt = nt;
+ memset(nt, 0, sizeof(name_table));
+ nt->max_sub_count =
+ ((count - 1) | nt_sub_index_mask) >> nt_log2_sub_size;
+ nt->memory = mem;
+ /* Initialize the one-character names. */
+ /* Start by creating the necessary sub-tables. */
+ for ( i = 0; i < nt_1char_first + nt_1char_size; i += nt_sub_size )
+ name_alloc_sub(nt);
+ for ( i = -1; i < nt_1char_size; i++ )
+ { uint ncnt = nt_1char_first + i;
+ uint nidx = name_count_to_index(ncnt);
+ register name *pname = name_index_ptr_inline(nt, nidx);
+ if ( i < 0 )
+ pname->string_bytes = nt_1char_names,
+ pname->string_size = 0;
+ else
+ pname->string_bytes = nt_1char_names + i,
+ pname->string_size = 1;
+ pname->foreign_string = 1;
+ pname->mark = 1;
+ pname->pvalue = pv_no_defn;
+ }
+ /* Reconstruct the free list. */
+ nt->free = 0;
+ name_gc_cleanup(NULL);
+ /* Register the name table root. */
+ gs_register_struct_root(mem, &the_nt_root, (void **)&the_nt,
+ "name table");
+ return nt;
+}
+
+/* Return the one and only table. */
+const name_table *
+the_name_table(void)
+{ return the_nt;
+}
+
+/* Get the allocator for the name table. */
+gs_memory_t *
+name_memory(void)
+{ return the_nt->memory;
+}
+
+/* Look up or enter a name in the table. */
+/* Return 0 or an error code. */
+/* The return may overlap the characters of the string! */
+/* See iname.h for the meaning of enterflag. */
+int
+name_ref(const byte *ptr, uint size, ref *pref, int enterflag)
+{ name_table *nt = the_nt;
+ register name *pname;
+ uint nidx;
+ uint *phash;
+
+ /* Compute a hash for the string. */
+ { uint hash;
+ const byte *p = ptr;
+ uint n = size;
+ /* Make a special check for 1-character names. */
+ switch ( size )
+ {
+ case 0:
+ nidx = name_count_to_index(1);
+ pname = name_index_ptr_inline(nt, nidx);
+ goto mkn;
+ case 1:
+ if ( *p < nt_1char_size )
+ { hash = *p + nt_1char_first;
+ nidx = name_count_to_index(hash);
+ pname = name_index_ptr_inline(nt, nidx);
+ goto mkn;
+ }
+ /* falls through */
+ default:
+ hash = hash_permutation[*p++];
+ while ( --n > 0 )
+ hash = (hash << 8) |
+ hash_permutation[(byte)hash ^ *p++];
+ }
+ phash = nt->hash + (hash & (nt_hash_size - 1));
+ }
+
+ for ( nidx = *phash; nidx != 0;
+ nidx = name_next_index(nidx, pname)
+ )
+ { pname = name_index_ptr_inline(nt, nidx);
+ if ( pname->string_size == size &&
+ !memcmp_inline(ptr, pname->string_bytes, size)
+ )
+ goto mkn;
+ }
+ /* Name was not in the table. Make a new entry. */
+ if ( enterflag < 0 )
+ return_error(e_undefined);
+ if ( size > max_name_string )
+ return_error(e_limitcheck);
+ nidx = nt->free;
+ if ( nidx == 0 )
+ { int code = name_alloc_sub(nt);
+ if ( code < 0 ) return code;
+ nidx = nt->free;
+ }
+ pname = name_index_ptr_inline(nt, nidx);
+ if ( enterflag == 1 )
+ { byte *cptr = (byte *)gs_alloc_string(nt->memory, size,
+ "name_ref(string)");
+ if ( cptr == 0 )
+ return_error(e_VMerror);
+ memcpy(cptr, ptr, size);
+ pname->string_bytes = cptr;
+ pname->foreign_string = 0;
+ }
+ else
+ { pname->string_bytes = ptr;
+ pname->foreign_string = (enterflag == 0 ? 1 : 0);
+ }
+ pname->string_size = size;
+ pname->pvalue = pv_no_defn;
+ nt->free = name_next_index(nidx, pname);
+ set_name_next_index(nidx, pname, *phash);
+ *phash = nidx;
+ if_debug_name("new name", pname, nidx, &enterflag);
+mkn: make_name(pref, nidx, pname);
+ return 0;
+}
+
+/* Get the string for a name. */
+void
+name_string_ref(const ref *pnref /* t_name */,
+ ref *psref /* result, t_string */)
+{ name *pname = pnref->value.pname;
+ const name_table *nt = the_nt;
+ make_const_string(psref,
+ (pname->foreign_string ? avm_foreign :
+ imemory_space((gs_ref_memory_t *)nt->memory))
+ | a_readonly,
+ pname->string_size,
+ (const byte *)pname->string_bytes);
+}
+
+/* Convert a t_string object to a name. */
+/* Copy the executable attribute. */
+int
+name_from_string(const ref *psref, ref *pnref)
+{ int exec = r_has_attr(psref, a_executable);
+ int code = name_ref(psref->value.bytes, r_size(psref), pnref, 1);
+ if ( code < 0 )
+ return code;
+ if ( exec )
+ r_set_attrs(pnref, a_executable);
+ return code;
+}
+
+/* Enter a (permanently allocated) C string as a name. */
+int
+name_enter_string(const char *str, ref *pref)
+{ return name_ref((const byte *)str, strlen(str), pref, 0);
+}
+
+/* Invalidate the value cache for a name. */
+void
+name_invalidate_value_cache(const ref *pnref)
+{ pnref->value.pname->pvalue = pv_other;
+}
+
+/* Convert between names and indices. */
+#undef name_index
+uint
+name_index(const ref *pnref)
+{ return name_index_inline(pnref);
+}
+void
+name_index_ref(uint index, ref *pnref)
+{ name_index_ref_inline(the_nt, index, pnref);
+}
+name *
+name_index_ptr(uint index)
+{ return name_index_ptr_inline(the_nt, index);
+}
+
+/* Get the index of the next valid name. */
+/* The argument is 0 or a valid index. */
+/* Return 0 if there are no more. */
+uint
+name_next_valid_index(uint nidx)
+{ name_table *nt = the_nt;
+ name_sub_table *sub = nt->sub_tables[nidx >> nt_log2_sub_size];
+ name *pname;
+ do
+ { ++nidx;
+ if ( (nidx & nt_sub_index_mask) == 0 )
+ for ( ; ; nidx += nt_sub_size )
+ { if ( (nidx >> nt_log2_sub_size) >= nt->sub_count )
+ return 0;
+ sub = nt->sub_tables[nidx >> nt_log2_sub_size];
+ if ( sub != 0 )
+ break;
+ }
+ pname = &sub->names[nidx & nt_sub_index_mask];
+ }
+ while ( pname->string_bytes == 0 );
+ return nidx;
+}
+
+/* ------ Garbage collection ------ */
+
+/* Unmark all names, except for 1-character permanent names, */
+/* before a garbage collection. */
+void
+name_unmark_all(void)
+{ name_table *nt = the_nt;
+ uint si;
+ name_sub_table *sub;
+ for ( si = 0; si < nt->sub_count; ++si )
+ if ( (sub = nt->sub_tables[si]) != 0 )
+ { uint i;
+ for ( i = 0; i < nt_sub_size; ++i )
+ sub->names[i].mark = 0;
+ }
+ { uint ncnt;
+ for ( ncnt = 1; ncnt <= nt_1char_size; ++ncnt )
+ name_index_ptr(name_count_to_index(ncnt))->mark = 1;
+ }
+}
+
+/* Mark a name. Return true if new mark. We export this so we can mark */
+/* character names in the character cache. */
+bool
+name_mark_index(uint nidx)
+{ name *pname = name_index_ptr(nidx);
+ if ( pname->mark )
+ return false;
+ pname->mark = 1;
+ return true;
+}
+
+/* Get the object (sub-table) containing a name. */
+/* The garbage collector needs this so it can relocate pointers to names. */
+void/*obj_header_t*/ *
+name_ref_sub_table(const ref *pnref)
+{ /* When this procedure is called, the pointers from the name table */
+ /* to the sub-tables may or may not have been relocated already, */
+ /* so we can't use them. Instead, we have to work backwards from */
+ /* the name pointer itself. */
+ return pnref->value.pname - (name_index_inline(pnref) & nt_sub_index_mask);
+}
+void/*obj_header_t*/ *
+name_index_ptr_sub_table(uint index, name *pname)
+{ return pname - (index & nt_sub_index_mask);
+}
+
+/* Clean up the name table after a garbage collection, by removing */
+/* names that aren't marked, and relocating name string pointers */
+/* (the latter only if gcst != 0). */
+void
+name_gc_cleanup(gc_state_t *gcst)
+{ name_table *nt = the_nt;
+ uint *phash = &nt->hash[0];
+ register uint i;
+ for ( i = 0; i < nt_hash_size; phash++, i++ )
+ { uint prev = 0;
+ name *pnprev;
+ uint nidx = *phash;
+ while ( nidx != 0 )
+ { name *pname = name_index_ptr_inline(nt, nidx);
+ uint next = name_next_index(nidx, pname);
+ if ( pname->mark )
+ { if ( !pname->foreign_string && gcst != NULL)
+ { gs_const_string nstr;
+ nstr.data = pname->string_bytes;
+ nstr.size = pname->string_size;
+ gs_reloc_const_string(&nstr, gcst);
+ pname->string_bytes = nstr.data;
+ }
+ prev = nidx;
+ pnprev = pname;
+ }
+ else
+ { if_debug_name("GC remove name", pname, nidx,
+ NULL);
+ /* Zero out the string data for the GC. */
+ pname->string_bytes = 0;
+ pname->string_size = 0;
+ if ( prev == 0 )
+ *phash = next;
+ else
+ set_name_next_index(prev, pnprev, next);
+ }
+ nidx = next;
+ }
+ }
+ /* Reconstruct the free list. */
+ nt->free = 0;
+ for ( i = nt->sub_count - 1; ; --i )
+ { name_scan_sub(nt, i, true);
+ if ( i == 0 )
+ break;
+ }
+ nt->sub_next = 0;
+}
+
+/* ------ Save/restore ------ */
+
+/* Clean up the name table before a restore. */
+/* Currently, this is never called, because the name table is allocated */
+/* in system VM. However, for a Level 1 system, we might choose to */
+/* allocate the name table in global VM; in this case, this routine */
+/* would be called before doing the global part of a top-level restore. */
+/* Currently we don't make any attempt to optimize this. */
+void
+name_restore(alloc_save_t *save)
+{ name_table *nt = the_nt;
+ /* We simply mark all names older than the save, */
+ /* and let name_gc_cleanup sort everything out. */
+ uint si;
+ for ( si = 0; si < nt->sub_count; ++si )
+ if ( nt->sub_tables[si] != 0 )
+ { uint i;
+ for ( i = 0; i < nt_sub_size; ++i )
+ { name *pname =
+ name_index_ptr_inline(nt, (si << nt_log2_sub_size) + i);
+ if ( pname->string_bytes == 0 )
+ pname->mark = 0;
+ else if ( pname->foreign_string )
+ pname->mark = 1;
+ else
+ pname->mark =
+ !alloc_is_since_save(pname->string_bytes, save);
+ }
+ }
+ name_gc_cleanup(NULL);
+}
+
+/* ------ Internal procedures ------ */
+
+/* Allocate the next sub-table. */
+private int
+name_alloc_sub(name_table *nt)
+{ uint sub_index = nt->sub_next;
+ name_sub_table *sub;
+
+ for ( ; ; ++sub_index )
+ { if ( sub_index > nt->max_sub_count )
+ return_error(e_limitcheck);
+ if ( nt->sub_tables[sub_index] == 0 )
+ break;
+ }
+ nt->sub_next = sub_index + 1;
+ if ( nt->sub_next > nt->sub_count )
+ nt->sub_count = nt->sub_next;
+ sub = gs_alloc_struct(nt->memory, name_sub_table, &st_name_sub_table,
+ "name_alloc_sub");
+ if ( sub == 0 )
+ return_error(e_VMerror);
+ memset(sub, 0, sizeof(name_sub_table));
+ /* The following code is only used if EXTEND_NAMES is non-zero. */
+ if ( sub_index >= 0x10000L >> nt_log2_sub_size )
+ { /* Fill in my_extension in all the newly created names. */
+ uint extn = sub_index >> (16 - nt_log2_sub_size);
+ int i;
+ for ( i = 0; i < nt_sub_size; ++i )
+ set_name_extension(&sub->names[i], extn);
+ }
+ nt->sub_tables[sub_index] = sub;
+ /* Add the newly allocated entries to the free list. */
+ /* Note that the free list will only be properly sorted if */
+ /* it was empty initially. */
+ name_scan_sub(nt, sub_index, false);
+#ifdef DEBUG
+ if ( gs_debug_c('n') )
+ { /* Print the lengths of the hash chains. */
+ int i0;
+ for ( i0 = 0; i0 < nt_hash_size; i0 += 16 )
+ { int i;
+ dprintf1("[n]chain %d:", i0);
+ for ( i = i0; i < i0 + 16; i++ )
+ { int n = 0;
+ uint nidx;
+ for ( nidx = nt->hash[i]; nidx != 0;
+ nidx = name_next_index(nidx, name_index_ptr_inline(nt, nidx))
+ )
+ n++;
+ dprintf1(" %d", n);
+ }
+ dputc('\n');
+ }
+ }
+#endif
+ return 0;
+}
+
+/* Scan a sub-table and add unmarked entries to the free list. */
+/* We add the entries in decreasing count order, so the free list */
+/* will stay sorted. If all entries are unmarked and free_empty is true, */
+/* free the sub-table. */
+private void
+name_scan_sub(name_table *nt, uint sub_index, bool free_empty)
+{ name_sub_table *sub = nt->sub_tables[sub_index];
+ uint free = nt->free;
+ uint nbase = sub_index << nt_log2_sub_size;
+ uint ncnt = nbase + (nt_sub_size - 1);
+ bool keep = !free_empty;
+
+ if ( sub == 0 )
+ return;
+ if ( nbase == 0 )
+ nbase = 1, keep = true; /* don't free name 0 */
+ for ( ; ; --ncnt )
+ { uint nidx = name_count_to_index(ncnt);
+ name *pname = &sub->names[nidx & nt_sub_index_mask];
+ if ( pname->mark )
+ keep = true;
+ else
+ { set_name_next_index(nidx, pname, free);
+ free = nidx;
+ }
+ if ( ncnt == nbase )
+ break;
+ }
+ if ( keep )
+ nt->free = free;
+ else
+ { /* No marked entries, free the sub-table. */
+ gs_free_object(nt->memory, sub, "name_scan_sub");
+ nt->sub_tables[sub_index] = 0;
+ if ( sub_index == nt->sub_count - 1 )
+ { nt->sub_count = sub_index;
+ if ( nt->sub_next == sub_index )
+ nt->sub_next--;
+ }
+ }
+}
+
+/* Garbage collector enumeration and relocation procedures. */
+#define ntptr ((name_table *)vptr)
+private ENUM_PTRS_BEGIN_PROC(name_table_enum_ptrs) {
+ if ( index >= ntptr->sub_count )
+ return 0;
+ *pep = ntptr->sub_tables[index];
+ return ptr_struct_type;
+} ENUM_PTRS_END_PROC
+private RELOC_PTRS_BEGIN(name_table_reloc_ptrs) {
+ name_sub_table **sub = ntptr->sub_tables;
+ uint sub_count = ntptr->sub_count;
+ uint i;
+ /* Now we can relocate the sub-table pointers. */
+ for ( i = 0; i < sub_count; i++, sub++ )
+ *sub = gs_reloc_struct_ptr(*sub, gcst);
+ /*
+ * We also need to relocate the cached value pointers.
+ * We don't do this here, but in a separate scan over the
+ * permanent dictionaries, at the very end of garbage collection.
+ */
+} RELOC_PTRS_END
diff --git a/pstoraster/iname.h b/pstoraster/iname.h
new file mode 100644
index 000000000..995252ecf
--- /dev/null
+++ b/pstoraster/iname.h
@@ -0,0 +1,105 @@
+/* Copyright (C) 1989, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* iname.h */
+/* Name table interface */
+
+/*
+ * This file defines those parts of the name table API that do not depend
+ * at all on the implementation.
+ */
+
+/* ---------------- Abstract types ---------------- */
+
+typedef struct name_table_s name_table;
+
+/* ---------------- Constant values ---------------- */
+
+extern const uint name_max_string;
+
+/* ---------------- Procedural interface ---------------- */
+
+/* Allocate and initialize a name table. */
+name_table *name_init(P2(ulong, gs_memory_t *));
+
+/*
+ * The name table machinery is designed so that multiple name tables
+ * are possible, but the interpreter relies on there being only one,
+ * and many of the procedures below assume this (by virtue of
+ * not taking a name_table argument). Therefore, we provide a procedure
+ * to get our hands on that unique table (read-only, however).
+ */
+const name_table *the_name_table(P0());
+
+/* Get the allocator for the name table. */
+gs_memory_t *name_memory(P0());
+
+/*
+ * Look up and/or enter a name in the name table.
+ * The values of enterflag are:
+ * -1 -- don't enter (return an error) if missing;
+ * 0 -- enter if missing, don't copy the string, which was allocated
+ * statically;
+ * 1 -- enter if missing, copy the string;
+ * 2 -- enter if missing, don't copy the string, which was already
+ * allocated dynamically (using the name_memory allocator).
+ * Possible errors: VMerror, limitcheck (if string is too long or if
+ * we have assigned all possible name indices).
+ */
+int name_ref(P4(const byte *ptr, uint size, ref *pnref, int enterflag));
+void name_string_ref(P2(const ref *pnref, ref *psref));
+/*
+ * name_enter_string calls name_ref with a (permanent) C string.
+ */
+int name_enter_string(P2(const char *str, ref *pnref));
+/*
+ * name_from_string essentially implements cvn.
+ * It always enters the name, and copies the executable attribute.
+ */
+int name_from_string(P2(const ref *psref, ref *pnref));
+
+/* Compare two names for equality. */
+#define name_eq(pnref1, pnref2)\
+ ((pnref1)->value.pname == (pnref2)->value.pname)
+
+/* Invalidate the value cache for a name. */
+void name_invalidate_value_cache(P1(const ref *));
+
+/* Convert between names and indices. */
+uint name_index(P1(const ref *)); /* ref => index */
+name *name_index_ptr(P1(uint nidx)); /* index => name */
+void name_index_ref(P2(uint nidx, ref *pnref)); /* index => ref */
+
+/* Get the index of the next valid name. */
+/* The argument is 0 or a valid index. */
+/* Return 0 if there are no more. */
+uint name_next_valid_index(P1(uint));
+
+/* Mark a name for the garbage collector. */
+/* Return true if this is a new mark. */
+bool name_mark_index(P1(uint));
+
+/* Get the object (sub-table) containing a name. */
+/* The garbage collector needs this so it can relocate pointers to names. */
+void/*obj_header_t*/ *name_ref_sub_table(P1(const ref *));
+void/*obj_header_t*/ *name_index_ptr_sub_table(P2(uint, name *));
diff --git a/pstoraster/inamedef.h b/pstoraster/inamedef.h
new file mode 100644
index 000000000..b7714d93f
--- /dev/null
+++ b/pstoraster/inamedef.h
@@ -0,0 +1,212 @@
+/* Copyright (C) 1989, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* inamedef.h */
+/* Name table definition */
+#include "iname.h"
+#include "gconfigv.h" /* defines EXTEND_NAMES */
+
+/*
+ * The name table machinery has two slightly different configurations:
+ * a faster one that limits the total number of names to 64K and allows
+ * names up to 16K in size, and a slightly slower one that limits
+ * the total to 4M and restricts names to 256 characters.
+ */
+#if arch_sizeof_int < 4
+# undef EXTEND_NAMES /* no extended names if ints are short */
+#endif
+#ifndef EXTEND_NAMES /* # of bits beyond 16 */
+# define EXTEND_NAMES 0
+#endif
+#define max_name_extension_bits 6
+#if EXTEND_NAMES > max_name_extension_bits
+# undef EXTEND_NAMES
+# define EXTEND_NAMES max_name_extension_bits
+#endif
+/*
+ * We capture the small algorithmic differences between these two
+ * configurations entirely in this header file;
+ * the implementation doesn't need any conditionals on EXTEND_NAMES.
+ */
+#define max_name_index (uint)((0x10000 << EXTEND_NAMES) - 1)
+/* As explained below, we distinguish name indices from name counts. */
+#define max_name_count max_name_index
+
+/* ---------------- Structure definitions ---------------- */
+
+/*
+ * Define the structure of a name. The next_index "pointer" is used for
+ * the chained hash table in the name_table, and also for the list of
+ * free names. The pvalue member implements an important optimization
+ * to avoid lookup for operator and other global names.
+ */
+struct name_s {
+ ushort next_index; /* (low bits of) next name in chain or 0 */
+ unsigned foreign_string : 1; /* string is allocated statically */
+ unsigned mark : 1; /* GC mark bit */
+#if EXTEND_NAMES
+# define name_extension_bits 6
+ unsigned my_extension : name_extension_bits; /* high-order bits */
+# define set_name_extension(pname, xbits) ((pname)->my_extension = xbits)
+#else
+# define name_extension_bits 0
+# define set_name_extension(name, xbits) DO_NOTHING
+#endif
+ /* of index for this name */
+#define name_string_size_bits (14 - name_extension_bits)
+#define max_name_string ((1 << name_string_size_bits) - 1)
+ unsigned string_size : name_string_size_bits;
+ const byte *string_bytes;
+/* pvalue specifies the definition status of the name: */
+/* pvalue == pv_no_defn: no definitions */
+#define pv_no_defn ((ref *)0)
+/* pvalue == pv_other: other status */
+#define pv_other ((ref *)1)
+/* pvalue != pv_no_defn, pvalue != pv_other: pvalue is valid */
+#define pv_valid(pvalue) ((unsigned long)(pvalue) > 1)
+ ref *pvalue; /* if only defined in systemdict */
+ /* or userdict, this points to */
+ /* the value */
+};
+/*typedef struct name_s name;*/ /* in iref.h */
+
+/*
+ * Define the structure of a name table. Normally we would make this
+ * an opaque type, but we want to be able to in-line some of the
+ * access procedures.
+ *
+ * The name table is a two-level indexed table, consisting of
+ * sub-tables of size nt_sub_size each.
+ *
+ * First we define the name sub-table structure.
+ */
+#define nt_log2_sub_size (7 + (EXTEND_NAMES / 2))
+# define nt_sub_size (1 << nt_log2_sub_size)
+# define nt_sub_index_mask (nt_sub_size - 1)
+typedef struct name_sub_table_s {
+ name names[nt_sub_size]; /* must be first */
+#if EXTEND_NAMES
+ byte next_index_extension[nt_sub_size]; /* high-order bits */
+ /* of next_index */
+# define name_next_index(nidx, pname)\
+ ( (((name_sub_table *)((pname) - ((nidx) & nt_sub_index_mask)))->\
+ next_index_extension[(nidx) & nt_sub_index_mask] << 16) +\
+ ((pname)->next_index)\
+ )
+# define set_name_next_index(nidx, pname, next)\
+ ( ((name_sub_table *)((pname) - ((nidx) & nt_sub_index_mask)))->\
+ next_index_extension[(nidx) & nt_sub_index_mask] =\
+ (byte)((next) >> 16),\
+ (pname)->next_index = (ushort)(next)\
+ )
+#else /* !EXTEND_NAMES */
+# define name_next_index(nidx, pname)\
+ ((pname)->next_index)
+# define set_name_next_index(nidx, pname, next)\
+ ((pname)->next_index = (next))
+#endif /* (!)EXTEND_NAMES */
+} name_sub_table;
+/*
+ * Now define the name table itself.
+ * This must be made visible so that the interpreter can use the
+ * inline macros defined below.
+ */
+#define nt_hash_size (1024 << (EXTEND_NAMES / 2)) /* must be a power of 2 */
+struct name_table_s {
+ uint free; /* head of free list, which is sorted in */
+ /* increasing count (not index) order */
+ uint sub_next; /* index of next sub-table to allocate */
+ /* if not already allocated */
+ uint sub_count; /* index of highest allocated sub-table +1 */
+ uint max_sub_count; /* max allowable value of sub_count */
+ gs_memory_t *memory;
+ uint hash[nt_hash_size];
+ name_sub_table *sub_tables[max_name_index / nt_sub_size + 1];
+};
+/*typedef struct name_table_s name_table;*/ /* in iname.h */
+
+/* ---------------- Procedural interface ---------------- */
+
+/* Convert between names and indices. Note that the inline versions, */
+/* but not the procedure versions, take a name_table argument. */
+ /* ref => index */
+#if EXTEND_NAMES
+# define name_index_inline(pnref)\
+ ( ((uint)((pnref)->value.pname->my_extension) << 16) + r_size(pnref) )
+#else
+# define name_index_inline(pnref) r_size(pnref)
+#endif
+#define name_index(pnref) name_index_inline(pnref)
+ /* index => name */
+#define name_index_ptr_inline(nt, nidx)\
+ ((nt)->sub_tables[(nidx) >> nt_log2_sub_size]->names + ((nidx) & nt_sub_index_mask))
+ /* index => ref */
+#define name_index_ref_inline(nt, nidx, pnref)\
+ make_name(pnref, nidx, name_index_ptr_inline(nt, nidx));
+ /* name => ref */
+/* We have to set the space to system so that the garbage collector */
+/* won't think names are foreign and therefore untraceable. */
+#define make_name(pnref, nidx, pnm)\
+ make_tasv(pnref, t_name, avm_system, (ushort)(nidx), pname, pnm)
+
+/* ------ Garbage collection ------ */
+
+/* Unmark all names, except for 1-character permanent names, */
+/* before a garbage collection. */
+void name_unmark_all(P0());
+
+/* Clean up the name table after a garbage collection. */
+void name_gc_cleanup(P1(gc_state_t *));
+
+/* ------ Save/restore ------ */
+
+/* Clean up the name table before a restore, */
+/* by removing names whose count is less than old_count. */
+#ifndef alloc_save_t_DEFINED /* also in isave.h */
+typedef struct alloc_save_s alloc_save_t;
+# define alloc_save_t_DEFINED
+#endif
+void name_restore(P1(alloc_save_t *));
+
+/* ---------------- Name count/index maintenance ---------------- */
+
+/*
+ * We scramble the assignment order within a sub-table, so that
+ * dictionary lookup doesn't have to scramble the index.
+ * The scrambling algorithm must have three properties:
+ * - It must map 0 to 0;
+ * - It must only scramble the sub-table index;
+ * - It must be a permutation on the sub-table index.
+ * Something very simple works just fine.
+ */
+#define name_count_to_index(cnt)\
+ (((cnt) & (-nt_sub_size)) +\
+ (((cnt) * 59) & nt_sub_index_mask))
+/*
+ * The reverse permutation requires finding a number R such that
+ * 59*R = 1 mod nt_sub_size. For nt_sub_size any power of 2 up to 2048,
+ * R = 243 will work. Currently, this is only needed for debugging printout.
+ */
+#define name_index_to_count(nidx)\
+ (((nidx) & (-nt_sub_size)) +\
+ (((nidx) * 243) & nt_sub_index_mask))
diff --git a/pstoraster/interp.c b/pstoraster/interp.c
new file mode 100644
index 000000000..7925678fc
--- /dev/null
+++ b/pstoraster/interp.c
@@ -0,0 +1,1419 @@
+/* Copyright (C) 1989, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* interp.c */
+/* Ghostscript language interpreter */
+#include "memory_.h"
+#include "string_.h"
+#include "ghost.h"
+#include "gsstruct.h" /* for iastruct.h */
+#include "stream.h"
+#include "errors.h"
+#include "estack.h"
+#include "ialloc.h"
+#include "iastruct.h"
+#include "inamedef.h"
+#include "interp.h"
+#include "ipacked.h"
+#include "ostack.h" /* must precede iscan.h */
+#include "strimpl.h" /* for sfilter.h */
+#include "sfilter.h" /* for iscan.h */
+#include "iscan.h"
+#include "idict.h"
+#include "isave.h"
+#include "istack.h"
+#include "iutil.h" /* for array_get */
+#include "ivmspace.h"
+#include "dstack.h"
+#include "files.h" /* for file_check_read */
+#include "oper.h"
+#include "store.h"
+
+#define PACKED_SPECIAL_OPS 1
+
+/* Imported operator procedures */
+extern int zop_add(P1(os_ptr));
+extern int zop_def(P1(os_ptr));
+extern int zop_sub(P1(os_ptr));
+/* Other imported procedures */
+extern int in_stopped(P0());
+extern int ztokenexec_continue(P1(os_ptr));
+
+/*
+ * The procedure to call if an operator requests rescheduling.
+ * This causes an error unless the context machinery has been installed.
+ */
+private int
+no_reschedule(void)
+{ return_error(e_invalidcontext);
+}
+int (*gs_interp_reschedule_proc)(P0()) = no_reschedule;
+/*
+ * The procedure to call for time-slicing.
+ * This is a no-op unless the context machinery has been installed.
+ */
+int
+no_time_slice_proc(void)
+{ return 0;
+}
+int (*gs_interp_time_slice_proc)(P0()) = no_time_slice_proc;
+/*
+ * The number of interpreter "ticks" between calls on the time_slice_proc.
+ * Currently, the clock ticks before each operator, and at each
+ * procedure return.
+ */
+int gs_interp_time_slice_ticks = 0x7fff;
+
+/* Forward references */
+private int estack_underflow(P1(os_ptr));
+private int interp(P2(ref *, ref *));
+private int interp_exit(P1(os_ptr));
+private void set_gc_signal(P2(int *, int));
+private int copy_stack(P2(const ref_stack *, ref *));
+
+/* Stack sizes */
+
+/* The maximum stack sizes may all be set in the makefile. */
+
+/*
+ * Define the initial maximum size of the operand stack (MaxOpStack
+ * user parameter). Currently this is also the block size for extending
+ * the operand stack, but this isn't guaranteed in the future.
+ */
+#ifndef MAX_OSTACK
+# define MAX_OSTACK 800
+#endif
+/*
+ * The minimum block size for extending the operand stack is the larger of:
+ * - the maximum number of parameters to an operator
+ * (currently setcolorscreen, with 12 parameters);
+ * - the maximum number of values pushed by an operator
+ * (currently setcolortransfer, which calls zcolor_remap_one 4 times
+ * and therefore pushes 16 values).
+ */
+#define min_block_ostack 16
+const int gs_interp_max_op_num_args = min_block_ostack; /* for iinit.c */
+
+/*
+ * Define the initial maximum size of the execution stack (MaxExecStack
+ * user parameter).
+ */
+#ifndef MAX_ESTACK
+# define MAX_ESTACK 250
+#endif
+/*
+ * The minimum block size for extending the execution stack is the largest
+ * size of a contiguous block surrounding an e-stack mark, currently ???.
+ * At least, that's what the minimum value would be if we supported
+ * multi-block estacks, which we currently don't.
+ */
+#define min_block_estack MAX_ESTACK
+
+/*
+ * Define the initial maximum size of the dictionary stack (MaxDictStack
+ * user parameter). Again, this is also currently the block size for
+ * extending the d-stack.
+ */
+#ifndef MAX_DSTACK
+# define MAX_DSTACK 20
+#endif
+/*
+ * The minimum block size for extending the dictionary stack is the number
+ * of permanent entries on the dictionary stack, currently 3.
+ */
+#define min_block_dstack 3
+uint min_dstack_size; /* set by iinit.c */
+
+/* Interpreter state variables */
+ref ref_systemdict; /* set by iinit.c */
+ref ref_language_level; /* 1 or 2, set by iinit.c */
+
+/* See estack.h for a description of the execution stack. */
+
+/* The logic for managing icount and iref below assumes that */
+/* there are no control operators which pop and then push */
+/* information on the execution stack. */
+
+/* Stacks */
+/*
+ * We must create the stacks as legitimate 'objects' for the memory
+ * manager, so that the garbage collector can work with them.
+ */
+#define os_guard_under 10
+#define os_guard_over 10
+#define os_refs_size(body_size)\
+ (stack_block_refs + os_guard_under + (body_size) + os_guard_over)
+ref_stack o_stack;
+#define es_guard_under 1
+#define es_guard_over 10
+#define es_refs_size(body_size)\
+ (stack_block_refs + es_guard_under + (body_size) + es_guard_over)
+ref_stack e_stack;
+#define ds_refs_size(body_size)\
+ (stack_block_refs + (body_size))
+ref_stack d_stack;
+
+/* Allocate the top blocks statically iff we are dealing with */
+/* a segmented architecture where this is important. */
+#if stacks_are_segmented
+private struct stk_ {
+ chunk_head_t head;
+ obj_header_t prefix;
+ ref bodies[os_refs_size(MAX_OSTACK) +
+ es_refs_size(MAX_ESTACK) +
+ ds_refs_size(MAX_DSTACK)];
+ ref padding; /* for GC */
+} static_stacks;
+#endif
+extern_st(st_ref_stack);
+ref ref_static_stacks; /* exported for GC */
+ref ref_ref_stacks[3]; /* exported for GC */
+
+/* Stack pointers */
+ref *esfile; /* cache pointer to currentfile */
+/* Cached dstack values are in idict.c. */
+
+/* Extended types. The interpreter may replace the type of operators */
+/* in procedures with these, to speed up the interpretation loop. */
+/****** NOTE: If you add or change entries in this list, */
+/****** you must change the three dispatches in the interpreter loop. */
+/* The operator procedures are declared in opextern.h. */
+#define tx_op t_next_index
+private const op_proc_p special_ops[] = {
+ zadd, zdef, zdup, zexch, zif, zifelse, zindex, zpop, zroll, zsub
+};
+typedef enum {
+ tx_op_add = tx_op,
+ tx_op_def,
+ tx_op_dup,
+ tx_op_exch,
+ tx_op_if,
+ tx_op_ifelse,
+ tx_op_index,
+ tx_op_pop,
+ tx_op_roll,
+ tx_op_sub,
+ tx_next_op
+} special_op_types;
+#define num_special_ops ((int)tx_next_op - tx_op)
+const int gs_interp_num_special_ops = num_special_ops; /* for iinit.c */
+const int tx_next_index = tx_next_op;
+
+/* A null procedure. */
+#define make_null_proc(pref)\
+ make_empty_const_array(pref, a_executable + a_readonly)
+static ref null_proc;
+
+/* Initialize the interpreter */
+void
+gs_interp_init(void)
+{ /* Initialize the stacks. */
+ gs_ref_memory_t *smem = iimemory_system;
+ ref stk;
+ ref euop;
+ ref *next_body;
+ uint refs_size_ostack, refs_size_estack, refs_size_dstack;
+
+ if ( gs_debug_c('+') )
+ refs_size_ostack = os_refs_size(min_block_ostack),
+ refs_size_estack = es_refs_size(min_block_estack),
+ refs_size_dstack = ds_refs_size(min_block_dstack);
+ else
+ refs_size_ostack = os_refs_size(MAX_OSTACK),
+ refs_size_estack = es_refs_size(MAX_ESTACK),
+ refs_size_dstack = ds_refs_size(MAX_DSTACK);
+#if stacks_are_segmented
+ next_body = static_stacks.bodies;
+ make_array(&ref_static_stacks, avm_system,
+ countof(static_stacks.bodies), next_body);
+ refset_null(next_body, countof(static_stacks.bodies));
+#else
+ make_empty_array(&ref_static_stacks, 0);
+ { ref sdata;
+ gs_alloc_ref_array(smem, &sdata, 0,
+ refs_size_ostack + refs_size_estack +
+ refs_size_dstack, "interp_init");
+ next_body = sdata.value.refs;
+ }
+#endif
+#define alloc_init_stack(stk, i, n)\
+ make_array(&stk, avm_system, n, next_body),\
+ next_body += (n),\
+ make_struct(&ref_ref_stacks[i], avm_system,\
+ gs_alloc_struct((gs_memory_t *)smem, ref_stack, &st_ref_stack,\
+ "alloc_init_stack"))
+
+ alloc_init_stack(stk, 0, refs_size_ostack);
+ ref_stack_init(&o_stack, &stk, os_guard_under, os_guard_over, NULL,
+ smem);
+ o_stack.underflow_error = e_stackunderflow;
+ o_stack.overflow_error = e_stackoverflow;
+ ref_stack_set_max_count(&o_stack, MAX_OSTACK);
+
+ alloc_init_stack(stk, 1, refs_size_estack);
+ make_oper(&euop, 0, estack_underflow);
+ ref_stack_init(&e_stack, &stk, es_guard_under, es_guard_over, &euop,
+ smem);
+ e_stack.underflow_error = e_ExecStackUnderflow;
+ e_stack.overflow_error = e_execstackoverflow;
+ /**************** E-STACK EXPANSION IS NYI. ****************/
+ e_stack.allow_expansion = false;
+ esfile_clear_cache();
+ ref_stack_set_max_count(&e_stack, MAX_ESTACK);
+
+ alloc_init_stack(stk, 2, refs_size_dstack);
+ ref_stack_init(&d_stack, &stk, 0, 0, NULL, smem);
+ d_stack.underflow_error = e_dictstackunderflow;
+ d_stack.overflow_error = e_dictstackoverflow;
+ ref_stack_set_max_count(&d_stack, MAX_DSTACK);
+}
+void
+gs_interp_reset(void)
+{ /* Reset the stacks. */
+ ref_stack_clear(&o_stack);
+ ref_stack_clear(&e_stack);
+ esp++;
+ make_oper(esp, 0, interp_exit);
+ ref_stack_pop_to(&d_stack, min_dstack_size);
+ dict_set_top();
+}
+/* Report an e-stack block underflow. The bottom guard slots of */
+/* e-stack blocks contain a pointer to this procedure. */
+private int
+estack_underflow(os_ptr op)
+{ return e_ExecStackUnderflow;
+}
+
+/*
+ * Create an operator during initialization.
+ * If operator is hard-coded into the interpreter,
+ * assign it a special type and index.
+ */
+void
+gs_interp_make_oper(ref *opref, op_proc_p proc, int idx)
+{ register int i = num_special_ops;
+ while ( --i >= 0 && proc != special_ops[i] )
+ ;
+ if ( i >= 0 )
+ make_tasv(opref, tx_op + i, a_executable, i + 1, opproc, proc);
+ else
+ make_tasv(opref, t_operator, a_executable, idx, opproc, proc);
+}
+
+/*
+ * Invoke the interpreter. If execution completes normally, return 0.
+ * If an error occurs, the action depends on user_errors as follows:
+ * user_errors < 0: always return an error code.
+ * user_errors >= 0: let the PostScript machinery handle all errors.
+ * (This will eventually result in a fatal error if no 'stopped'
+ * is active.)
+ * In case of a quit or a fatal error, also store the exit code.
+ */
+private int gs_call_interp(P4(ref *, int, int *, ref *));
+int
+gs_interpret(ref *pref, int user_errors, int *pexit_code, ref *perror_object)
+{ gs_gc_root_t error_root;
+ int code;
+
+ gs_register_ref_root(imemory_system, &error_root,
+ (void **)&perror_object, "gs_interpret");
+ /* Initialize the error object in case of GC. */
+ make_null(perror_object);
+ code = gs_call_interp(pref, user_errors, pexit_code, perror_object);
+ gs_unregister_root(imemory_system, &error_root, "gs_interpret");
+ /* Avoid a dangling reference to a stack-allocated GC signal. */
+ set_gc_signal(NULL, 0);
+ return code;
+}
+private int
+gs_call_interp(ref *pref, int user_errors, int *pexit_code, ref *perror_object)
+{ ref *epref = pref;
+ ref doref;
+ ref *perrordict;
+ ref error_name;
+ int code, ccode;
+ ref saref;
+ int gc_signal = 0;
+
+ *pexit_code = 0;
+ ialloc_reset_requested(idmemory);
+again: o_stack.requested = e_stack.requested = d_stack.requested = 0;
+ while ( gc_signal )
+ { /* Some routine below triggered a GC. */
+ gc_signal = 0;
+ code = (*idmemory->reclaim)(idmemory, -1);
+ if ( code < 0 )
+ return code;
+ }
+ code = interp(epref, perror_object);
+ /* Prevent a dangling reference to the GC signal in ticks_left */
+ /* in the frame of interp, but be prepared to do a GC if */
+ /* an allocation in this routine asks for it. */
+ set_gc_signal(&gc_signal, 1);
+ if ( esp < esbot ) /* popped guard entry */
+ esp = esbot;
+ switch ( code )
+ {
+ case e_Fatal:
+ *pexit_code = 255;
+ return code;
+ case e_Quit:
+ *perror_object = osp[-1];
+ *pexit_code = code = osp->value.intval;
+ osp -= 2;
+ return (code == 0 ? e_Quit : code > -100 ? code : e_Fatal);
+ case e_InterpreterExit:
+ return 0;
+ case e_ExecStackUnderflow:
+ /****** WRONG -- must keep mark blocks intact ******/
+ ref_stack_pop_block(&e_stack);
+ doref = *perror_object;
+ epref = &doref;
+ goto again;
+ case e_VMreclaim:
+ /* Do the GC and continue. */
+ code = (*idmemory->reclaim)(idmemory,
+ (osp->value.intval == 2 ?
+ avm_global : avm_local));
+ /****** What if code < 0? ******/
+ make_oper(&doref, 0, zpop);
+ epref = &doref;
+ goto again;
+ case e_NeedInput:
+ return code;
+ }
+ /* Adjust osp in case of operand stack underflow */
+ if ( osp < osbot - 1 )
+ osp = osbot - 1;
+ /* We have to handle stack over/underflow specially, because */
+ /* we might be able to recover by adding or removing a block. */
+ switch ( code )
+ {
+ case e_dictstackoverflow:
+ if ( ref_stack_extend(&d_stack, d_stack.requested) >= 0 )
+ { dict_set_top();
+ doref = *perror_object;
+ epref = &doref;
+ goto again;
+ }
+ if ( osp >= ostop )
+ { if ( (ccode = ref_stack_extend(&o_stack, 1)) < 0 )
+ return ccode;
+ }
+ ccode = copy_stack(&d_stack, &saref);
+ if ( ccode < 0 )
+ return ccode;
+ ref_stack_pop_to(&d_stack, min_dstack_size);
+ dict_set_top();
+ *++osp = saref;
+ break;
+ case e_dictstackunderflow:
+ if ( ref_stack_pop_block(&d_stack) >= 0 )
+ { dict_set_top();
+ doref = *perror_object;
+ epref = &doref;
+ goto again;
+ }
+ break;
+ case e_execstackoverflow:
+ /* We don't have to handle this specially: */
+ /* The only places that could generate it */
+ /* use check_estack, which does a ref_stack_extend, */
+ /* so if we get this error, it's a real one. */
+ if ( osp >= ostop )
+ { if ( (ccode = ref_stack_extend(&o_stack, 1)) < 0 )
+ return ccode;
+ }
+ ccode = copy_stack(&e_stack, &saref);
+ if ( ccode < 0 )
+ return ccode;
+ { uint count = ref_stack_count(&e_stack);
+ long limit = ref_stack_max_count(&e_stack) - 10;
+ if ( count > limit )
+ pop_estack(count - limit);
+ }
+ *++osp = saref;
+ break;
+ case e_stackoverflow:
+ if ( ref_stack_extend(&o_stack, o_stack.requested) >= 0 )
+ { /* We can't just re-execute the object, because */
+ /* it might be a procedure being pushed as a */
+ /* literal. We check for this case specially. */
+ doref = *perror_object;
+ if ( r_is_proc(&doref) )
+ { *++osp = doref;
+ make_null_proc(&doref);
+ }
+ epref = &doref;
+ goto again;
+ }
+ ccode = copy_stack(&o_stack, &saref);
+ if ( ccode < 0 ) return ccode;
+ ref_stack_clear(&o_stack);
+ *++osp = saref;
+ break;
+ case e_stackunderflow:
+ if ( ref_stack_pop_block(&o_stack) >= 0 )
+ { doref = *perror_object;
+ epref = &doref;
+ goto again;
+ }
+ break;
+ }
+ if ( user_errors < 0 )
+ return code;
+ if ( gs_errorname(code, &error_name) < 0 )
+ return code; /* out-of-range error code! */
+ if ( dict_find_string(systemdict, "errordict", &perrordict) <= 0 ||
+ dict_find(perrordict, &error_name, &epref) <= 0
+ )
+ return code; /* error name not in errordict??? */
+ doref = *epref;
+ epref = &doref;
+ /* Push the error object on the operand stack if appropriate. */
+ if ( !error_is_interrupt(code) )
+ *++osp = *perror_object;
+ goto again;
+}
+private int
+interp_exit(os_ptr op)
+{ return e_InterpreterExit;
+}
+
+/* Set the GC signal for all VMs. */
+private void
+set_gc_signal(int *psignal, int value)
+{ gs_memory_gc_status_t stat;
+ int i;
+ for ( i = 0; i < countof(idmemory->spaces.indexed); i++ )
+ { gs_ref_memory_t *mem = idmemory->spaces.indexed[i];
+ if ( mem != 0 )
+ { gs_memory_gc_status(mem, &stat);
+ stat.psignal = psignal;
+ stat.signal_value = value;
+ gs_memory_set_gc_status(mem, &stat);
+ }
+ }
+}
+
+
+/* Copy the contents of an overflowed stack into a (local) array. */
+private int
+copy_stack(const ref_stack *pstack, ref *arr)
+{ uint size = ref_stack_count(pstack);
+ uint save_space = ialloc_space(idmemory);
+ int code;
+ ialloc_set_space(idmemory, avm_local);
+ code = ialloc_ref_array(arr, a_all, size, "copy_stack");
+ if ( code >= 0 )
+ code = ref_stack_store(pstack, arr, size, 0, 1, true, "copy_stack");
+ ialloc_set_space(idmemory, save_space);
+ return code;
+}
+
+/* Get the name corresponding to an error number. */
+int
+gs_errorname(int code, ref *perror_name)
+{ ref *perrordict, *pErrorNames;
+ if ( dict_find_string(systemdict, "errordict", &perrordict) <= 0 ||
+ dict_find_string(systemdict, "ErrorNames", &pErrorNames) <= 0
+ )
+ return_error(e_undefined); /* errordict or ErrorNames not found?! */
+ return array_get(pErrorNames, (long)(-code - 1), perror_name);
+}
+
+/* Store an error string in $error.errorinfo. */
+/* This routine is here because of the proximity to the error handler. */
+int
+gs_errorinfo_put_string(const char *str)
+{ ref rstr;
+ ref *pderror;
+ int code = string_to_ref(str, &rstr, iimemory, "gs_errorinfo_put_string");
+ if ( code < 0 )
+ return code;
+ if ( dict_find_string(systemdict, "$error", &pderror) <= 0 ||
+ !r_has_type(pderror, t_dictionary) ||
+ dict_put_string(pderror, "errorinfo", &rstr) < 0
+ )
+ return_error(e_Fatal);
+ return 0;
+}
+
+/* Main interpreter. */
+/* If execution terminates normally, return e_InterpreterExit. */
+/* If an error occurs, leave the current object in *perror_object */
+/* and return a (negative) error code. */
+private int
+interp(ref *pref /* object to interpret */, ref *perror_object)
+{ /*
+ * Note that iref is declared as a ref *, but it may actually be
+ * a ref_packed *.
+ */
+ register const ref *iref = pref;
+ register int icount = 0; /* # of consecutive tokens at iref */
+ register os_ptr iosp = osp; /* private copy of osp */
+ register es_ptr iesp = esp; /* private copy of esp */
+ int code;
+ ref token; /* token read from file or string, */
+ /* must be declared in this scope */
+ register const ref *pvalue;
+ os_ptr whichp;
+ /*
+ * We have to make the error information into a struct;
+ * otherwise, the Watcom compiler will assign it to registers
+ * strictly on the basis of textual frequency.
+ * We also have to use ref_assign_inline everywhere, and
+ * avoid direct assignments of refs, so that esi and edi
+ * will remain available on Intel processors.
+ */
+ struct interp_error_s {
+ int code;
+ int line;
+ const ref *obj;
+ ref full;
+ } ierror;
+ /*
+ * Get a pointer to the name table so that we can use the
+ * inline version of name_index_ref.
+ */
+ const name_table *int_nt = the_name_table();
+#define set_error(ecode)\
+ { ierror.code = ecode; ierror.line = __LINE__; }
+#define return_with_error(ecode, objp)\
+ { set_error(ecode); ierror.obj = objp; goto rwe; }
+#define return_with_error_iref(ecode)\
+ { set_error(ecode); goto rwei; }
+#define return_with_code_iref()\
+ { ierror.line = __LINE__; goto rweci; }
+#define return_with_error_code_op(nargs)\
+ return_with_code_iref()
+#define return_with_stackoverflow(objp)\
+ { o_stack.requested = 1; return_with_error(e_stackoverflow, objp); }
+#define return_with_stackoverflow_iref()\
+ { o_stack.requested = 1; return_with_error_iref(e_stackoverflow); }
+ int ticks_left = gs_interp_time_slice_ticks;
+
+ /*
+ * If we exceed the VMThreshold, set ticks_left to -1
+ * to alert the interpreter that we need to garbage collect.
+ */
+ set_gc_signal(&ticks_left, -100);
+
+ esfile_clear_cache();
+ /*
+ * From here on, if icount > 0, iref and icount correspond
+ * to the top entry on the execution stack: icount is the count
+ * of sequential entries remaining AFTER the current one.
+ */
+#define add1_short(pref) (const ref *)((const ushort *)(pref) + 1)
+#define add1_either(pref) (r_is_packed(pref) ? add1_short(pref) : (pref) + 1)
+#define store_state(ep)\
+ ( icount > 0 ? (ep->value.const_refs = iref + 1, r_set_size(ep, icount)) : 0 )
+#define store_state_short(ep)\
+ ( icount > 0 ? (ep->value.const_refs = add1_short(iref), r_set_size(ep, icount)) : 0 )
+#define store_state_either(ep)\
+ ( icount > 0 ? (ep->value.const_refs = add1_either(iref), r_set_size(ep, icount)) : 0 )
+#define next()\
+ if ( --icount > 0 ) { iref++; goto top; } else goto out
+#define next_short()\
+ if ( --icount <= 0 ) { if ( icount < 0 ) goto up; iesp--; }\
+ iref = add1_short(iref); goto top
+#define next_either()\
+ if ( --icount <= 0 ) { if ( icount < 0 ) goto up; iesp--; }\
+ iref = add1_either(iref); goto top
+
+#if !PACKED_SPECIAL_OPS
+# undef next_either
+# define next_either() next()
+# undef store_state_either
+# define store_state_either(ep) store_state(ep)
+#endif
+
+ /* We want to recognize executable arrays here, */
+ /* so we push the argument on the estack and enter */
+ /* the loop at the bottom. */
+ if ( iesp >= estop ) return_with_error(e_execstackoverflow, pref);
+ ++iesp;
+ ref_assign_inline(iesp, pref);
+ goto bot;
+top: /*
+ * This is the top of the interpreter loop.
+ * iref points to the ref being interpreted.
+ * Note that this might be an element of a packed array,
+ * not a real ref: we carefully arranged the first 16 bits of
+ * a ref and of a packed array element so they could be distinguished
+ * from each other. (See ghost.h and packed.h for more detail.)
+ */
+#ifdef DEBUG
+ /* Do a little validation on the top o-stack entry. */
+ if ( iosp >= osbot && r_type(iosp) == t__invalid )
+ { lprintf("Invalid value on o-stack!\n");
+ return_with_error_iref(e_Fatal);
+ }
+if ( gs_debug['I'] ||
+ (gs_debug['i'] &&
+ (r_is_packed(iref) ?
+ r_packed_is_name((const ref_packed *)iref) :
+ r_has_type(iref, t_name)))
+ )
+ { void debug_print_ref(P1(const ref *));
+ os_ptr save_osp = osp; /* avoid side-effects */
+ es_ptr save_esp = esp;
+ int edepth;
+ char depth[10];
+ osp = iosp;
+ esp = iesp;
+ edepth = ref_stack_count(&e_stack);
+ sprintf(depth, "%2d", edepth);
+ dputs(depth);
+ for ( edepth -= strlen(depth); edepth >= 5; edepth -= 5 )
+ dputc('*'); /* indent */
+ for ( ; edepth > 0; --edepth )
+ dputc('.');
+ dprintf3("0x%lx(%d)<%d>: ",
+ (ulong)iref, icount, ref_stack_count(&o_stack));
+ debug_print_ref(iref);
+ if ( iosp >= osbot )
+ { dputs(" // ");
+ debug_print_ref(iosp);
+ }
+ dputc('\n');
+ osp = save_osp;
+ esp = save_esp;
+ fflush(dstderr);
+ }
+#endif
+/* Objects that have attributes (arrays, dictionaries, files, and strings) */
+/* use lit and exec; other objects use plain and plain_exec. */
+#define lit(t) type_xe_value(t, a_execute)
+#define exec(t) type_xe_value(t, a_execute + a_executable)
+#define nox(t) type_xe_value(t, 0)
+#define nox_exec(t) type_xe_value(t, a_executable)
+#define plain(t) type_xe_value(t, 0)
+#define plain_exec(t) type_xe_value(t, a_executable)
+ /*
+ * We have to populate enough cases of the switch statement to force
+ * some compilers to use a dispatch rather than a testing loop.
+ * What a nuisance!
+ */
+ switch ( r_type_xe(iref) )
+ {
+ /* Access errors. */
+#define cases_invalid()\
+ case plain(t__invalid): case plain_exec(t__invalid)
+ cases_invalid():
+ return_with_error_iref(e_Fatal);
+#define cases_nox()\
+ case nox_exec(t_array): case nox_exec(t_dictionary):\
+ case nox_exec(t_file): case nox_exec(t_string):\
+ case nox_exec(t_mixedarray): case nox_exec(t_shortarray)
+ cases_nox():
+ return_with_error_iref(e_invalidaccess);
+ /*
+ * Literal objects. We have to enumerate all the types.
+ * In fact, we have to include some extra plain_exec entries
+ * just to populate the switch. We break them up into groups
+ * to avoid overflowing some preprocessors.
+ */
+#define cases_lit_1()\
+ case lit(t_array): case nox(t_array):\
+ case plain(t_boolean): case plain_exec(t_boolean):\
+ case lit(t_dictionary): case nox(t_dictionary)
+#define cases_lit_2()\
+ case lit(t_file): case nox(t_file):\
+ case plain(t_fontID): case plain_exec(t_fontID):\
+ case plain(t_integer): case plain_exec(t_integer):\
+ case plain(t_mark): case plain_exec(t_mark)
+#define cases_lit_3()\
+ case plain(t_name):\
+ case plain(t_null):\
+ case plain(t_oparray):\
+ case plain(t_operator)
+#define cases_lit_4()\
+ case plain(t_real): case plain_exec(t_real):\
+ case plain(t_save): case plain_exec(t_save):\
+ case lit(t_string): case nox(t_string)
+#define cases_lit_5()\
+ case lit(t_mixedarray): case nox(t_mixedarray):\
+ case lit(t_shortarray): case nox(t_shortarray):\
+ case plain(t_device): case plain_exec(t_device):\
+ case plain(t_struct): case plain_exec(t_struct):\
+ case plain(t_astruct): case plain_exec(t_astruct)
+ /* Executable arrays are treated as literals in direct execution. */
+#define cases_lit_array()\
+ case exec(t_array): case exec(t_mixedarray): case exec(t_shortarray)
+ cases_lit_1():
+ cases_lit_2():
+ cases_lit_3():
+ cases_lit_4():
+ cases_lit_5():
+ cases_lit_array():
+ break;
+ /* Special operators. */
+ case plain_exec(tx_op_add):
+x_add: if ( (code = zop_add(iosp)) < 0 )
+ return_with_error_code_op(2);
+ iosp--;
+ next_either();
+ case plain_exec(tx_op_def):
+x_def: if ( (code = zop_def(iosp)) < 0 )
+ return_with_error_code_op(2);
+ iosp -= 2;
+ next_either();
+ case plain_exec(tx_op_dup):
+x_dup: if ( iosp < osbot )
+ return_with_error_iref(e_stackunderflow);
+ if ( iosp >= ostop )
+ return_with_stackoverflow_iref();
+ iosp++;
+ ref_assign_inline(iosp, iosp - 1);
+ next_either();
+ case plain_exec(tx_op_exch):
+x_exch: if ( iosp <= osbot )
+ return_with_error_iref(e_stackunderflow);
+ ref_assign_inline(&token, iosp);
+ ref_assign_inline(iosp, iosp - 1);
+ ref_assign_inline(iosp - 1, &token);
+ next_either();
+ case plain_exec(tx_op_if):
+x_if: if ( !r_has_type(iosp - 1, t_boolean) )
+ return_with_error_iref((iosp <= osbot ?
+ e_stackunderflow : e_typecheck));
+ if ( !r_is_proc(iosp) )
+ return_with_error_iref(check_proc_failed(iosp));
+ if ( !iosp[-1].value.boolval )
+ { iosp -= 2;
+ next_either();
+ }
+ if ( iesp >= estop )
+ return_with_error_iref(e_execstackoverflow);
+ store_state_either(iesp);
+ whichp = iosp;
+ iosp -= 2;
+ goto ifup;
+ case plain_exec(tx_op_ifelse):
+x_ifelse: if ( !r_has_type(iosp - 2, t_boolean) )
+ return_with_error_iref((iosp < osbot + 2 ?
+ e_stackunderflow : e_typecheck));
+ if ( !r_is_proc(iosp - 1) )
+ return_with_error_iref(check_proc_failed(iosp - 1));
+ if ( !r_is_proc(iosp) )
+ return_with_error_iref(check_proc_failed(iosp));
+ if ( iesp >= estop )
+ return_with_error_iref(e_execstackoverflow);
+ store_state_either(iesp);
+ whichp = (iosp[-2].value.boolval ? iosp - 1 : iosp);
+ iosp -= 3;
+ /* Open code "up" for the array case(s) */
+ifup:
+ /* The following check is no longer needed, since */
+ /* the conditional operators now require real procedures. */
+#if 0
+ if ( !r_is_proc(whichp) )
+ { /* This is an unusual enough case that we go ahead */
+ /* and clear the currentfile cache without */
+ /* checking whether we have an exec(t_file), */
+ /* just to avoid another case test. */
+ esfile_clear_cache();
+ ref_assign_inline(iesp + 1, whichp);
+ iref = iesp + 1;
+ icount = 0;
+ goto top;
+ }
+#endif
+ if ( (icount = r_size(whichp) - 1) <= 0 )
+ { if ( icount < 0 ) goto up; /* 0-element proc */
+ iref = whichp->value.refs; /* 1-element proc */
+ if ( --ticks_left > 0 )
+ goto top;
+ }
+ ++iesp;
+ /* Do a ref_assign, but also set iref. */
+ iesp->tas = whichp->tas;
+ iref = iesp->value.refs = whichp->value.refs;
+ if ( --ticks_left > 0 )
+ goto top;
+ goto slice;
+ case plain_exec(tx_op_index):
+x_index: osp = iosp; /* zindex references o_stack */
+ if ( (code = zindex(iosp)) < 0 )
+ return_with_error_code_op(1);
+ next_either();
+ case plain_exec(tx_op_pop):
+x_pop: if ( iosp < osbot )
+ return_with_error_iref(e_stackunderflow);
+ iosp--;
+ next_either();
+ case plain_exec(tx_op_roll):
+x_roll: osp = iosp; /* zroll references o_stack */
+ if ( (code = zroll(iosp)) < 0 )
+ return_with_error_code_op(2);
+ iosp -= 2;
+ next_either();
+ case plain_exec(tx_op_sub):
+x_sub: if ( (code = zop_sub(iosp)) < 0 )
+ return_with_error_code_op(2);
+ iosp--;
+ next_either();
+ /* Executable types. */
+ case plain_exec(t_null):
+ goto bot;
+ case plain_exec(t_oparray):
+ /* Replace with the definition and go again. */
+ pvalue = (const ref *)iref->value.const_refs;
+prst: /* Prepare to call the procedure (array) in *pvalue. */
+ store_state(iesp);
+pr: /* Call the array in *pvalue. State has been stored. */
+ if ( (icount = r_size(pvalue) - 1) <= 0 )
+ { if ( icount < 0 ) goto up; /* 0-element proc */
+ iref = pvalue->value.refs; /* 1-element proc */
+ if ( --ticks_left > 0 )
+ goto top;
+ }
+ if ( iesp >= estop )
+ return_with_error_iref(e_execstackoverflow);
+ ++iesp;
+ /* Do a ref_assign, but also set iref. */
+ iesp->tas = pvalue->tas;
+ iref = iesp->value.refs = pvalue->value.refs;
+ if ( --ticks_left > 0 )
+ goto top;
+ goto slice;
+ case plain_exec(t_operator):
+ if ( --ticks_left <= 0 )
+ { /* The following doesn't work, */
+ /* and I can't figure out why. */
+ /****** goto sst; ******/
+ }
+ esp = iesp; /* save for operator */
+ osp = iosp; /* ditto */
+ /* Operator routines take osp as an argument. */
+ /* This is just a convenience, since they adjust */
+ /* osp themselves to reflect the results. */
+ /* Operators that (net) push information on the */
+ /* operand stack must check for overflow: */
+ /* this normally happens automatically through */
+ /* the push macro (in oper.h). */
+ /* Operators that do not typecheck their operands, */
+ /* or take a variable number of arguments, */
+ /* must check explicitly for stack underflow. */
+ /* (See oper.h for more detail.) */
+ /* Note that each case must set iosp = osp: */
+ /* this is so we can switch on code without having to */
+ /* store it and reload it (for dumb compilers). */
+ switch ( code = (*real_opproc(iref))(iosp) )
+ {
+ case 0: /* normal case */
+ case 1: /* alternative success case */
+ iosp = osp;
+ next();
+ case o_push_estack: /* store the state and go to up */
+ store_state(iesp);
+opush: iosp = osp;
+ iesp = esp;
+ if ( --ticks_left > 0 )
+ goto up;
+ goto slice;
+ case o_pop_estack: /* just go to up */
+opop: iosp = osp;
+ if ( esp == iesp ) goto bot;
+ iesp = esp;
+ goto up;
+ case o_reschedule:
+ store_state(iesp);
+ goto res;
+ case e_InsertProc:
+ store_state(iesp);
+oeinsert: ref_assign_inline(iesp + 1, iref);
+ /* esp = iesp + 2; *esp = the procedure */
+ iesp = esp;
+ goto up;
+ }
+ iosp = osp;
+ iesp = esp;
+ return_with_code_iref();
+ case plain_exec(t_name):
+ pvalue = iref->value.pname->pvalue;
+ if ( !pv_valid(pvalue) )
+ { uint nidx = name_index(iref);
+ uint htemp;
+ if ( (pvalue = dict_find_name_by_index_inline(nidx, htemp)) == 0 )
+ return_with_error_iref(e_undefined);
+ }
+ /* Dispatch on the type of the value. */
+ /* Again, we have to over-populate the switch. */
+ switch ( r_type_xe(pvalue) )
+ {
+ cases_invalid():
+ return_with_error_iref(e_Fatal);
+ cases_nox(): /* access errors */
+ return_with_error_iref(e_invalidaccess);
+ cases_lit_1():
+ cases_lit_2():
+ cases_lit_3():
+ cases_lit_4():
+ cases_lit_5():
+ /* Just push the value */
+ if ( iosp >= ostop )
+ return_with_stackoverflow(pvalue);
+ ++iosp;
+ ref_assign_inline(iosp, pvalue);
+ next();
+ case exec(t_array):
+ case exec(t_mixedarray):
+ case exec(t_shortarray):
+ /* This is an executable procedure, execute it. */
+ goto prst;
+ case plain_exec(tx_op_add): goto x_add;
+ case plain_exec(tx_op_def): goto x_def;
+ case plain_exec(tx_op_dup): goto x_dup;
+ case plain_exec(tx_op_exch): goto x_exch;
+ case plain_exec(tx_op_if): goto x_if;
+ case plain_exec(tx_op_ifelse): goto x_ifelse;
+ case plain_exec(tx_op_index): goto x_index;
+ case plain_exec(tx_op_pop): goto x_pop;
+ case plain_exec(tx_op_roll): goto x_roll;
+ case plain_exec(tx_op_sub): goto x_sub;
+ case plain_exec(t_null):
+ goto bot;
+ case plain_exec(t_oparray):
+ pvalue = (const ref *)pvalue->value.const_refs;
+ goto prst;
+ case plain_exec(t_operator):
+ { /* Shortcut for operators. */
+ /* See above for the logic. */
+ if ( --ticks_left <= 0 )
+ { /* The following doesn't work, */
+ /* and I can't figure out why. */
+ /****** goto sst; ******/
+ }
+ esp = iesp;
+ osp = iosp;
+ switch ( code = (*real_opproc(pvalue))(iosp) )
+ {
+ case 0: /* normal case */
+ case 1: /* alternative success case */
+ iosp = osp;
+ next();
+ case o_push_estack:
+ store_state(iesp);
+ goto opush;
+ case o_pop_estack:
+ goto opop;
+ case o_reschedule:
+ store_state(iesp);
+ goto res;
+ case e_InsertProc:
+ store_state(iesp);
+ goto oeinsert;
+ }
+ iosp = osp;
+ iesp = esp;
+ return_with_error(code, pvalue);
+ }
+ case plain_exec(t_name):
+ case exec(t_file):
+ case exec(t_string):
+ default:
+ /* Not a procedure, reinterpret it. */
+ store_state(iesp);
+ icount = 0;
+ iref = pvalue;
+ goto top;
+ }
+ case exec(t_file):
+ { /* Executable file. Read the next token and interpret it. */
+ stream *s;
+ scanner_state sstate;
+ check_read_known_file(s, iref, return_with_error_iref);
+rt: if ( iosp >= ostop ) /* check early */
+ return_with_stackoverflow_iref();
+ osp = iosp; /* scan_token uses ostack */
+ scanner_state_init(&sstate, false);
+again: code = scan_token(s, &token, &sstate);
+ iosp = osp; /* ditto */
+ switch ( code )
+ {
+ case 0: /* read a token */
+ /* It's worth checking for literals, which make up */
+ /* the majority of input tokens, before storing the */
+ /* state on the e-stack. Note that because of //, */
+ /* the token may have *any* type and attributes. */
+ /* Note also that executable arrays aren't executed */
+ /* at the top level -- they're treated as literals. */
+ if ( !r_has_attr(&token, a_executable) ||
+ r_is_array(&token)
+ )
+ { /* If scan_token used the o-stack, */
+ /* we know we can do a push now; if not, */
+ /* the pre-check is still valid. */
+ iosp++;
+ ref_assign_inline(iosp, &token);
+ goto rt;
+ }
+ store_state(iesp);
+ /* Push the file on the e-stack */
+ if ( iesp >= estop )
+ return_with_error_iref(e_execstackoverflow);
+ esfile_set_cache(++iesp);
+ ref_assign_inline(iesp, iref);
+ iref = &token;
+ icount = 0;
+ goto top;
+ case scan_EOF: /* end of file */
+ esfile_clear_cache();
+ goto bot;
+ case scan_BOS:
+ /* Binary object sequences */
+ /* ARE executed at the top level. */
+ store_state(iesp);
+ /* Push the file on the e-stack */
+ if ( iesp >= estop )
+ return_with_error_iref(e_execstackoverflow);
+ esfile_set_cache(++iesp);
+ ref_assign_inline(iesp, iref);
+ pvalue = &token;
+ goto pr;
+ case scan_Refill:
+ store_state(iesp);
+ /* iref may point into the exec stack; */
+ /* save its referent now. */
+ ref_assign_inline(&token, iref);
+ /* Push the file on the e-stack */
+ if ( iesp >= estop )
+ return_with_error_iref(e_execstackoverflow);
+ ++iesp;
+ ref_assign_inline(iesp, &token);
+ esp = iesp;
+ osp = iosp;
+ code = scan_handle_refill(&token, &sstate, true, true,
+ ztokenexec_continue);
+ iosp = osp;
+ iesp = esp;
+ switch ( code )
+ {
+ case 0:
+ iesp--; /* don't push the file */
+ goto again; /* stacks are unchanged */
+ case o_push_estack:
+ esfile_clear_cache();
+ if ( --ticks_left > 0 )
+ goto up;
+ goto slice;
+ }
+ /* must be an error */
+ iesp--; /* don't push the file */
+ default: /* error */
+ return_with_code_iref();
+ }
+ }
+ case exec(t_string):
+ { /* Executable string. Read a token and interpret it. */
+ stream ss;
+ scanner_state sstate;
+ scanner_state_init(&sstate, true);
+ sread_string(&ss, iref->value.bytes, r_size(iref));
+ osp = iosp; /* scan_token uses ostack */
+ code = scan_token(&ss, &token, &sstate);
+ iosp = osp; /* ditto */
+ switch ( code )
+ {
+ case 0: /* read a token */
+ case scan_BOS: /* binary object sequence */
+ store_state(iesp);
+ /* Push the updated string back on the e-stack */
+ if ( iesp >= estop )
+ return_with_error_iref(e_execstackoverflow);
+ ++iesp;
+ iesp->tas.type_attrs = iref->tas.type_attrs;
+ iesp->value.const_bytes = sbufptr(&ss);
+ r_set_size(iesp, sbufavailable(&ss));
+ if ( code == 0 )
+ { iref = &token;
+ icount = 0;
+ goto top;
+ }
+ /* Handle BOS specially */
+ pvalue = &token;
+ goto pr;
+ case scan_EOF: /* end of string */
+ goto bot;
+ case scan_Refill: /* error */
+ code = gs_note_error(e_syntaxerror);
+ default: /* error */
+ return_with_code_iref();
+ }
+ }
+ /* Handle packed arrays here by re-dispatching. */
+ /* This also picks up some anomalous cases of non-packed arrays. */
+ default:
+ { uint index;
+ switch ( *(const ushort *)iref >> r_packed_type_shift )
+ {
+ case pt_full_ref:
+ case pt_full_ref+1:
+ if ( iosp >= ostop )
+ return_with_stackoverflow_iref();
+ /* We know this can't be an executable object */
+ /* requiring special handling, so we just push it. */
+ ++iosp;
+ /* We know that refs are properly aligned: */
+ /* see packed.h for details. */
+ ref_assign_inline(iosp, iref);
+ next();
+ case pt_executable_operator:
+ index = *(const ushort *)iref & packed_value_mask;
+ if ( --ticks_left <= 0 )
+ { /* The following doesn't work, */
+ /* and I can't figure out why. */
+ /****** goto sst_short; ******/
+ }
+ if ( !op_index_is_operator(index) )
+ { store_state_short(iesp);
+ /* Call the operator procedure. */
+ index -= op_def_count;
+ pvalue = (const ref *)
+ (index < r_size(&op_array_table_global.table) ?
+ op_array_table_global.table.value.const_refs +
+ index :
+ op_array_table_local.table.value.const_refs +
+ (index - r_size(&op_array_table_global.table)));
+ goto pr;
+ }
+ /* See the main plain_exec(t_operator) case */
+ /* for details of what happens here. */
+#if PACKED_SPECIAL_OPS
+ /* We arranged in iinit.c that the special ops */
+ /* have operator indices starting at 1. */
+# define case_xop(xop) case xop - tx_op + 1
+ switch ( index )
+ {
+ case_xop(tx_op_add): goto x_add;
+ case_xop(tx_op_def): goto x_def;
+ case_xop(tx_op_dup): goto x_dup;
+ case_xop(tx_op_exch): goto x_exch;
+ case_xop(tx_op_if): goto x_if;
+ case_xop(tx_op_ifelse): goto x_ifelse;
+ case_xop(tx_op_index): goto x_index;
+ case_xop(tx_op_pop): goto x_pop;
+ case_xop(tx_op_roll): goto x_roll;
+ case_xop(tx_op_sub): goto x_sub;
+ case 0: /* for dumb compilers */
+ default:
+ ;
+ }
+# undef case_xop
+#endif
+ esp = iesp;
+ osp = iosp;
+ switch ( code = (*op_index_proc(index))(iosp) )
+ {
+ case 0:
+ case 1:
+ iosp = osp;
+ next_short();
+ case o_push_estack:
+ store_state_short(iesp);
+ goto opush;
+ case o_pop_estack:
+ iosp = osp;
+ if ( esp == iesp )
+ { next_short();
+ }
+ iesp = esp;
+ goto up;
+ case o_reschedule:
+ store_state_short(iesp);
+ goto res;
+ case e_InsertProc:
+ store_state_short(iesp);
+ packed_get((const ref_packed *)iref, iesp + 1);
+ /* esp = iesp + 2; *esp = the procedure */
+ iesp = esp;
+ goto up;
+ }
+ iosp = osp;
+ iesp = esp;
+ return_with_code_iref();
+ case pt_integer:
+ if ( iosp >= ostop )
+ return_with_stackoverflow_iref();
+ ++iosp;
+ make_int(iosp,
+ (*(const short *)iref & packed_int_mask) +
+ packed_min_intval);
+ next_short();
+ case pt_literal_name:
+ { uint nidx = *(const ushort *)iref & packed_value_mask;
+ if ( iosp >= ostop )
+ return_with_stackoverflow_iref();
+ ++iosp;
+ name_index_ref_inline(int_nt, nidx, iosp);
+ next_short();
+ }
+ case pt_executable_name:
+ { uint nidx =
+ (uint)*(const ushort *)iref & packed_value_mask;
+ pvalue = name_index_ptr_inline(int_nt, nidx)->pvalue;
+ if ( !pv_valid(pvalue) )
+ { uint htemp;
+ if ( (pvalue = dict_find_name_by_index_inline(nidx, htemp)) == 0 )
+ { name_index_ref(nidx, &token);
+ return_with_error(e_undefined, &token);
+ }
+ }
+ if ( r_has_masked_attrs(pvalue, a_execute, a_execute + a_executable) )
+ { /* Literal, push it. */
+ if ( iosp >= ostop )
+ return_with_stackoverflow_iref();
+ ++iosp;
+ ref_assign_inline(iosp, pvalue);
+ next_short();
+ }
+ if ( r_is_proc(pvalue) )
+ { /* This is an executable procedure, */
+ /* execute it. */
+ store_state_short(iesp);
+ goto pr;
+ }
+ /* Not a literal or procedure, reinterpret it. */
+ store_state_short(iesp);
+ icount = 0;
+ iref = pvalue;
+ goto top;
+ }
+ /* default can't happen here */
+ }
+ }
+ }
+ /* Literal type, just push it. */
+ if ( iosp >= ostop )
+ return_with_stackoverflow_iref();
+ ++iosp;
+ ref_assign_inline(iosp, iref);
+bot: next();
+out: /* At most 1 more token in the current procedure. */
+ /* (We already decremented icount.) */
+ if ( !icount )
+ { /* Pop the execution stack for tail recursion. */
+ iesp--;
+ iref++;
+ goto top;
+ }
+up: if ( --ticks_left < 0 )
+ goto slice;
+ /* See if there is anything left on the execution stack. */
+ if ( !r_is_proc(iesp) )
+ { iref = iesp--;
+ icount = 0;
+ goto top;
+ }
+ iref = iesp->value.refs; /* next element of array */
+ icount = r_size(iesp) - 1;
+ if ( icount <= 0 ) /* <= 1 more elements */
+ { iesp--; /* pop, or tail recursion */
+ if ( icount < 0 ) goto up;
+ }
+ goto top;
+res: /* Some operator has asked for context rescheduling. */
+ /* We've done a store_state. */
+ code = (*gs_interp_reschedule_proc)();
+sched: /* We've just called a scheduling procedure. */
+ /* The interpreter state is in memory; iref is not current. */
+ if ( code < 0 )
+ { set_error(code);
+ make_null_proc(&null_proc);
+ ierror.obj = iref = &null_proc;
+ goto error_exit;
+ }
+ /* Reload state information from memory. */
+ iosp = osp;
+ iesp = esp;
+ goto up;
+#if 0 /****** ****** ******/
+sst: /* Time-slice, but push the current object first. */
+ store_state(iesp);
+ if ( iesp >= estop )
+ return_with_error_iref(e_execstackoverflow);
+ iesp++;
+ ref_assign_inline(iesp, iref);
+#endif /****** ****** ******/
+slice: /* It's time to time-slice or garbage collect. */
+ /* iref is not live, so we don't need to do a store_state. */
+ osp = iosp;
+ esp = iesp;
+ /* If ticks_left <= -100, we need to GC now. */
+ if ( ticks_left <= -100 )
+ { /* We need to garbage collect now. */
+ code = (*idmemory->reclaim)(idmemory, -1);
+ }
+ else
+ code = (*gs_interp_time_slice_proc)();
+ ticks_left = gs_interp_time_slice_ticks;
+ goto sched;
+
+ /* Error exits. */
+
+rweci:
+ ierror.code = code;
+rwei:
+ ierror.obj = iref;
+rwe:
+ if ( !r_is_packed(iref) )
+ store_state(iesp);
+ else
+ { /* We need a real object to return as the error object. */
+ /* (It only has to last long enough to store in *perror_object.) */
+ packed_get((const ref_packed *)ierror.obj, &ierror.full);
+ store_state_short(iesp);
+ if ( iref == ierror.obj )
+ iref = &ierror.full;
+ ierror.obj = &ierror.full;
+ }
+error_exit:
+ if ( error_is_interrupt(ierror.code) )
+ { /* We must push the current object being interpreted */
+ /* back on the e-stack so it will be re-executed. */
+ /* Currently, this is always an executable operator, */
+ /* but it might be something else someday if we check */
+ /* for interrupts in the interpreter loop itself. */
+ if ( iesp >= estop )
+ code = e_execstackoverflow;
+ else
+ { iesp++;
+ ref_assign_inline(iesp, iref);
+ }
+ }
+ esp = iesp;
+ osp = iosp;
+ ref_assign_inline(perror_object, ierror.obj);
+ return gs_log_error(ierror.code, __FILE__, ierror.line);
+
+}
+
+/* ------ Initialization procedure ------ */
+
+BEGIN_OP_DEFS(interp_op_defs) {
+ /* Internal operators */
+ {"0%interp_exit", interp_exit},
+END_OP_DEFS(0) }
diff --git a/pstoraster/interp.h b/pstoraster/interp.h
new file mode 100644
index 000000000..ee13af03f
--- /dev/null
+++ b/pstoraster/interp.h
@@ -0,0 +1,66 @@
+/* Copyright (C) 1994 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* interp.h */
+/* Internal interfaces to interp.c and iinit.c */
+
+/* ------ iinit.c ------ */
+
+/* Enter a name and value into systemdict. */
+void initial_enter_name(P2(const char *, const ref *));
+
+/* Remove a name from systemdict. */
+void initial_remove_name(P1(const char *));
+
+/* ------ interp.c ------ */
+
+/*
+ * Maximum number of arguments (and results) for an operator,
+ * determined by operand stack block size.
+ */
+extern const int gs_interp_max_op_num_args;
+
+/*
+ * Number of slots to reserve at the start of op_def_table for
+ * operators which are hard-coded into the interpreter loop.
+ */
+extern const int gs_interp_num_special_ops;
+
+/*
+ * Create an operator during initialization.
+ * If operator is hard-coded into the interpreter,
+ * assign it a special type and index.
+ */
+void gs_interp_make_oper(P3(ref *opref, op_proc_p, int index));
+
+/* Get the name corresponding to an error number. */
+int gs_errorname(P2(int, ref *));
+
+/* Put a string in $error /errorinfo. */
+int gs_errorinfo_put_string(P1(const char *));
+
+/* Initialize the interpreter. */
+void gs_interp_init(P0());
+
+/* Reset the interpreter. */
+void gs_interp_reset(P0());
diff --git a/pstoraster/ipacked.h b/pstoraster/ipacked.h
new file mode 100644
index 000000000..be1cc8e92
--- /dev/null
+++ b/pstoraster/ipacked.h
@@ -0,0 +1,128 @@
+/* Copyright (C) 1991, 1992, 1993, 1994 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* ipacked.h */
+/* Packed array format for Ghostscript */
+
+/*
+
+In a packed array, an element may either be a 2-byte ref_packed or a
+full-size ref (8 or 16 bytes). We carefully arrange the first two bytes,
+which are either an entire ref_packed or the type_attrs member of a ref,
+so that we can distinguish the 2 forms. The encoding:
+
+ 00tttttt exrwsfnm full-size ref
+ 010mjjjj jjjjjjjj executable operator (so bind can work)
+ 011mvvvv vvvvvvvv integer (biased by packed_min_intval)
+ 100m---- -------- (not used)
+ 101m---- -------- (not used)
+ 110miiii iiiiiiii literal name
+ 111miiii iiiiiiii executable name
+
+The m bit is the mark bit for the garbage collector.
+
+****** Note for the future: We could get packed tokens into the first-level
+****** interpreter dispatch by changing to the following representation:
+
+ 000ttttt exrwsfnm full-size ref
+ m0100jjj jjjjjjjj executable operator (so bind can work)
+ m0101vvv vvvvvvvv integer (biased by packed_min_intval)
+ m011iiii iiiiiiii literal name
+ m100iiii iiiiiiii executable name
+ m101---- -------- (not used)
+ m11----- -------- (not used)
+
+****** We aren't going to do this for a while.
+
+The jjj index of executable operators is either the index of the operator
+in the op_def_table, if the index is less than op_def_count, or the index
+of the definition in the op_array_table (subtracting op_def_count first).
+
+The iii index of names is the one that the name machinery already
+maintains. A name whose index is larger than will fit in the packed
+representation must be represented as a full-size ref.
+
+There are two packed array types, t_mixedarray and t_shortarray. A
+t_mixedarray can have a mix of packed and full-size elements; a
+t_shortarray has all packed elements. The 'size' of a packed array is the
+number of elements, not the number of bytes it occupies.
+
+Packed array elements can be distinguished from full-size elements, so we
+allow the interpreter to simply execute all the different kinds of arrays
+directly. However, if we really allowed free mixing of packed and
+full-size elements, this could lead to unaligned placement of full-size
+refs; some machines can't handle unaligned accesses of this kind. To
+guarantee that full-size elements in mixed arrays are always properly
+aligned, if a full-size ref must be aligned at an address which is 0 mod
+N, we convert up to N/2-1 preceding packed elements into full-size
+elements, when creating the array, so that the alignment is preserved.
+The only code this actually affects is in make_packed_array and in the
+code for compacting refs in the garbage collector.
+
+Note that code in zpacked.c and interp.c knows more about the
+representation of packed elements than the definitions in this file would
+imply. Read the code carefully if you change the representation.
+
+ */
+
+#define r_packed_type_shift 13
+#define r_packed_value_bits 12
+typedef enum {
+ pt_full_ref = 0,
+#define pt_min_packed 2
+ pt_executable_operator = 2,
+ pt_integer = 3,
+ pt_unused1 = 4,
+ pt_unused2 = 5,
+#define pt_min_name 6
+ pt_literal_name = 6,
+#define pt_min_exec_name 7
+ pt_executable_name = 7
+} packed_type;
+#define packed_per_ref (sizeof(ref) / sizeof(ref_packed))
+#define align_packed_per_ref\
+ (arch_align_ref_mod / arch_align_short_mod)
+#define pt_tag(pt) ((ref_packed)(pt) << r_packed_type_shift)
+#define packed_value_mask ((1 << r_packed_value_bits) - 1)
+#define packed_max_value packed_value_mask
+#define r_is_packed(rp) (*(const ref_packed *)(rp) >= pt_tag(pt_min_packed))
+/* Names */
+#define r_packed_is_name(prp) (*(prp) >= pt_tag(pt_min_name))
+#define r_packed_is_exec_name(prp) (*(prp) >= pt_tag(pt_min_exec_name))
+#define packed_name_max_index packed_max_value
+#define packed_name_index(prp) (*(prp) & packed_value_mask)
+/* Integers */
+#define packed_min_intval (-(1 << (r_packed_value_bits - 1)))
+#define packed_max_intval ((1 << (r_packed_value_bits - 1)) - 1)
+#define packed_int_mask packed_value_mask
+
+/* Packed ref marking */
+#define lp_mark 0x1000
+#define r_has_pmark(rp) (*(rp) & lp_mark)
+#define r_set_pmark(rp) (*(rp) |= lp_mark)
+#define r_clear_pmark(rp) (*(rp) &= ~lp_mark)
+#define r_store_pmark(rp,pm) (*(rp) = (*(rp) & ~lp_mark) | (pm))
+
+/* Advance to the next element in a packed array. */
+#define packed_next(prp)\
+ (r_is_packed(prp) ? prp + 1 : prp + packed_per_ref)
diff --git a/pstoraster/iparam.c b/pstoraster/iparam.c
new file mode 100644
index 000000000..5cbfd57a2
--- /dev/null
+++ b/pstoraster/iparam.c
@@ -0,0 +1,822 @@
+/* Copyright (C) 1993, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* iparam.c */
+/* Interpreter implementations of parameter dictionaries */
+#include "memory_.h"
+#include "string_.h"
+#include "ghost.h"
+#include "errors.h"
+#include "opcheck.h"
+#include "ialloc.h"
+#include "idict.h"
+#include "imemory.h" /* for iutil.h */
+#include "iname.h"
+#include "istack.h"
+#include "iparam.h"
+#include "iutil.h" /* for num_params */
+#include "ivmspace.h"
+#include "store.h"
+
+/* ================ Utilities ================ */
+
+#define iplist ((iparam_list *)plist)
+#define ciplist ((const iparam_list *)plist)
+
+/* Convert a key to a ref. */
+private int near
+ref_param_key(const iparam_list *plist, gs_param_name pkey, ref *pkref)
+{ if ( plist->int_keys )
+ { long key;
+ if ( sscanf(pkey, "%ld", &key) != 1 )
+ return_error(e_rangecheck);
+ make_int(pkref, key);
+ return 0;
+ }
+ else
+ { return name_ref((const byte *)pkey, strlen(pkey), pkref, 0);
+ }
+}
+
+/* ================ Writing parameters to refs ================ */
+
+/* ---------------- Generic writing procedures ---------------- */
+
+private param_proc_xmit_null(ref_param_write_null);
+private param_proc_xmit_bool(ref_param_write_bool);
+private param_proc_xmit_int(ref_param_write_int);
+private param_proc_xmit_long(ref_param_write_long);
+private param_proc_xmit_float(ref_param_write_float);
+private param_proc_xmit_string(ref_param_write_string);
+private param_proc_xmit_name(ref_param_write_name);
+private param_proc_xmit_int_array(ref_param_write_int_array);
+private param_proc_xmit_float_array(ref_param_write_float_array);
+private param_proc_xmit_string_array(ref_param_write_string_array);
+private param_proc_xmit_name_array(ref_param_write_name_array);
+private param_proc_begin_xmit_dict(ref_param_begin_write_dict);
+private param_proc_end_xmit_dict(ref_param_end_write_dict);
+private param_proc_requested(ref_param_requested);
+private const gs_param_list_procs ref_write_procs = {
+ ref_param_write_null,
+ ref_param_write_bool,
+ ref_param_write_int,
+ ref_param_write_long,
+ ref_param_write_float,
+ ref_param_write_string,
+ ref_param_write_name,
+ ref_param_write_int_array,
+ ref_param_write_float_array,
+ ref_param_write_string_array,
+ ref_param_write_name_array,
+ ref_param_begin_write_dict,
+ ref_param_end_write_dict,
+ ref_param_requested
+};
+private int near ref_array_param_requested(P5(const gs_param_list *, gs_param_name, ref *, uint, client_name_t));
+private int near ref_param_write(P3(iparam_list *, gs_param_name, const ref *));
+private int near ref_param_write_string_value(P2(ref *,
+ const gs_param_string *));
+#define ref_param_write_name_value(pref, pvalue)\
+ name_ref((pvalue)->data, (pvalue)->size, pref,\
+ ((pvalue)->persistent ? 0 : 1))
+
+private int
+ref_param_write_null(gs_param_list *plist, gs_param_name pkey)
+{ ref value;
+ make_null(&value);
+ return ref_param_write(iplist, pkey, &value);
+}
+private int
+ref_param_write_bool(gs_param_list *plist, gs_param_name pkey, bool *pvalue)
+{ ref value;
+ make_bool(&value, *pvalue);
+ return ref_param_write(iplist, pkey, &value);
+}
+private int
+ref_param_write_int(gs_param_list *plist, gs_param_name pkey, int *pvalue)
+{ ref value;
+ make_int(&value, *pvalue);
+ return ref_param_write(iplist, pkey, &value);
+}
+private int
+ref_param_write_long(gs_param_list *plist, gs_param_name pkey, long *pvalue)
+{ ref value;
+ make_int(&value, *pvalue);
+ return ref_param_write(iplist, pkey, &value);
+}
+private int
+ref_param_write_float(gs_param_list *plist, gs_param_name pkey, float *pvalue)
+{ ref value;
+ make_real(&value, *pvalue);
+ return ref_param_write(iplist, pkey, &value);
+}
+private int
+ref_param_write_string(gs_param_list *plist, gs_param_name pkey,
+ gs_param_string *pvalue)
+{ ref sref;
+ int code;
+
+ if ( !ref_param_requested(plist, pkey) )
+ return 0;
+ code = ref_param_write_string_value(&sref, pvalue);
+ if ( code < 0 )
+ return code;
+ return ref_param_write(iplist, pkey, &sref);
+}
+private int
+ref_param_write_name(gs_param_list *plist, gs_param_name pkey,
+ gs_param_string *pvalue)
+{ ref nref;
+ int code;
+
+ if ( !ref_param_requested(plist, pkey) )
+ return 0;
+ code = ref_param_write_name_value(&nref, pvalue);
+ if ( code < 0 )
+ return code;
+ return ref_param_write(iplist, pkey, &nref);
+}
+private int
+ref_param_write_int_array(gs_param_list *plist, gs_param_name pkey,
+ gs_param_int_array *pvalue)
+{ ref value;
+ const int *pdata = pvalue->data;
+ uint n = pvalue->size;
+ ref *pe;
+ int code;
+
+ if ( (code = ref_array_param_requested(plist, pkey, &value, n,
+ "ref_param_write_int_array")) <= 0 )
+ return code;
+ for ( pe = value.value.refs; n > 0; n--, pe++, pdata++ )
+ make_int_new(pe, *pdata);
+ return ref_param_write(iplist, pkey, &value);
+}
+private int
+ref_param_write_float_array(gs_param_list *plist, gs_param_name pkey,
+ gs_param_float_array *pvalue)
+{ ref value;
+ const float *pdata = pvalue->data;
+ uint n = pvalue->size;
+ int code;
+ ref *pe;
+
+ if ( (code = ref_array_param_requested(plist, pkey, &value, n,
+ "ref_param_write_float_array")) <= 0 )
+ return code;
+ for ( pe = value.value.refs; n > 0; n--, pe++, pdata++ )
+ make_real_new(pe, *pdata);
+ return ref_param_write(iplist, pkey, &value);
+}
+private int
+ref_param_write_string_array(gs_param_list *plist, gs_param_name pkey,
+ gs_param_string_array *pvalue)
+{ ref value;
+ const gs_param_string *pdata = pvalue->data;
+ uint n = pvalue->size;
+ int code;
+ ref *pe;
+
+ if ( (code = ref_array_param_requested(plist, pkey, &value, n,
+ "ref_param_write_string_array")) <= 0 )
+ return code;
+ for ( pe = value.value.refs; n > 0; n--, pe++, pdata++ )
+ { code = ref_param_write_string_value(pe, pdata);
+ if ( code < 0 )
+ { /* Don't bother trying to release memory. */
+ return code;
+ }
+ }
+ return ref_param_write(iplist, pkey, &value);
+}
+private int
+ref_param_write_name_array(gs_param_list *plist, gs_param_name pkey,
+ gs_param_string_array *pvalue)
+{ ref value;
+ const gs_param_string *pdata = pvalue->data;
+ uint n = pvalue->size;
+ int code;
+ ref *pe;
+
+ if ( (code = ref_array_param_requested(plist, pkey, &value, n,
+ "ref_param_write_name_array")) <= 0 )
+ return code;
+ for ( pe = value.value.refs; n > 0; n--, pe++, pdata++ )
+ { code = ref_param_write_name_value(pe, pdata);
+ if ( code < 0 )
+ { /* Don't bother trying to release memory. */
+ return code;
+ }
+ }
+ return ref_param_write(iplist, pkey, &value);
+}
+private int
+ref_param_begin_write_dict(gs_param_list *plist, gs_param_name pkey,
+ gs_param_dict *pvalue, bool int_keys)
+{ dict_param_list *dlist =
+ (dict_param_list *)ialloc_bytes(size_of(dict_param_list),
+ "ref_param_begin_write_dict");
+ ref dref;
+ int code;
+
+ if ( dlist == 0 )
+ return_error(e_VMerror);
+ code = dict_create(pvalue->size, &dref);
+ if ( code < 0 )
+ { ifree_object(dlist, "ref_param_begin_write_dict");
+ return code;
+ }
+ pvalue->list = (gs_param_list *)dlist;
+ code = dict_param_list_write(dlist, &dref, NULL);
+ if ( code < 0 )
+ return code;
+ dlist->int_keys = int_keys;
+ return 0;
+}
+private int
+ref_param_end_write_dict(gs_param_list *plist, gs_param_name pkey,
+ gs_param_dict *pvalue)
+{ int code = ref_param_write(iplist, pkey,
+ &((dict_param_list *)pvalue->list)->dict);
+ ifree_object(pvalue->list, "ref_param_end_write_dict");
+ return code;
+}
+
+/* Check whether a given parameter was requested. */
+private bool
+ref_param_requested(const gs_param_list *plist, gs_param_name pkey)
+{ ref kref;
+ ref *ignore_value;
+ if ( !r_has_type(&ciplist->u.w.wanted, t_dictionary) )
+ return true;
+ if ( ref_param_key(ciplist, pkey, &kref) < 0 )
+ return true; /* catch it later */
+ return (dict_find(&ciplist->u.w.wanted, &kref, &ignore_value) > 0);
+}
+/* Check whether an array parameter is wanted, and allocate it if so. */
+/* Return <0 on error, 0 if not wanted, 1 if wanted. */
+private int near
+ref_array_param_requested(const gs_param_list *plist, gs_param_name pkey,
+ ref *pvalue, uint size, client_name_t cname)
+{ int code;
+ if ( !ref_param_requested(plist, pkey) )
+ return 0;
+ code = ialloc_ref_array(pvalue, a_all, size, cname);
+ return (code < 0 ? code : 1);
+}
+
+/* ---------------- Internal routines ---------------- */
+
+/* Prepare to write a string value. */
+private int near
+ref_param_write_string_value(ref *pref, const gs_param_string *pvalue)
+{ const byte *pdata = pvalue->data;
+ uint n = pvalue->size;
+
+ if ( pvalue->persistent )
+ make_const_string(pref, a_readonly | avm_foreign, n, pdata);
+ else
+ { byte *pstr = ialloc_string(n, "ref_param_write_string");
+ if ( pstr == 0 )
+ return_error(e_VMerror);
+ memcpy(pstr, pdata, n);
+ make_string(pref, a_readonly | icurrent_space, n, pstr);
+ }
+ return 0;
+}
+
+/* Generic routine for writing a ref parameter. */
+private int near
+ref_param_write(iparam_list *plist, gs_param_name pkey, const ref *pvalue)
+{ ref kref;
+ int code;
+ if ( !ref_param_requested((gs_param_list *)plist, pkey) )
+ return 0;
+ code = ref_param_key(plist, pkey, &kref);
+ if ( code < 0 )
+ return code;
+ return (*plist->u.w.write)(plist, &kref, pvalue);
+}
+
+/* ---------------- Implementations ---------------- */
+
+/* Initialize for writing parameters. */
+private void near
+ref_param_write_init(iparam_list *plist, const ref *pwanted)
+{ if ( pwanted == 0 )
+ make_null(&plist->u.w.wanted);
+ else
+ plist->u.w.wanted = *pwanted;
+ plist->results = 0;
+ plist->int_keys = false;
+}
+
+/* Implementation for getting parameters to a stack. */
+private int
+stack_param_write(iparam_list *plist, const ref *pkey, const ref *pvalue)
+{
+#define splist ((stack_param_list *)plist)
+ ref_stack *pstack = splist->pstack;
+ s_ptr p = pstack->p;
+ if ( pstack->top - p < 2 )
+ { int code = ref_stack_push(pstack, 2);
+ if ( code < 0 )
+ return code;
+ *ref_stack_index(pstack, 1) = *pkey;
+ p = pstack->p;
+ }
+ else
+ { pstack->p = p += 2;
+ p[-1] = *pkey;
+ }
+ *p = *pvalue;
+ splist->count++;
+#undef splist
+ return 0;
+}
+int
+stack_param_list_write(stack_param_list *plist, ref_stack *pstack,
+ const ref *pwanted)
+{ plist->procs = &ref_write_procs;
+ plist->u.w.write = stack_param_write;
+ ref_param_write_init((iparam_list *)plist, pwanted);
+ plist->pstack = pstack;
+ /* plist->skip not used */
+ plist->count = 0;
+ return 0;
+}
+
+/* Implementation for getting parameters to a dictionary. */
+private int
+dict_param_write(iparam_list *plist, const ref *pkey, const ref *pvalue)
+{ int code = dict_put(&((dict_param_list *)plist)->dict, pkey, pvalue);
+ return min(code, 0);
+}
+int
+dict_param_list_write(dict_param_list *plist, ref *pdict,
+ const ref *pwanted)
+{ check_dict_write(*pdict);
+ plist->procs = &ref_write_procs;
+ plist->u.w.write = dict_param_write;
+ ref_param_write_init((iparam_list *)plist, pwanted);
+ plist->dict = *pdict;
+ return 0;
+}
+
+/* ================ Reading refs to parameters ================ */
+
+/* ---------------- Generic reading procedures ---------------- */
+
+private param_proc_xmit_null(ref_param_read_null);
+private param_proc_xmit_bool(ref_param_read_bool);
+private param_proc_xmit_int(ref_param_read_int);
+private param_proc_xmit_long(ref_param_read_long);
+private param_proc_xmit_float(ref_param_read_float);
+private param_proc_xmit_string(ref_param_read_string);
+private param_proc_xmit_int_array(ref_param_read_int_array);
+private param_proc_xmit_float_array(ref_param_read_float_array);
+private param_proc_xmit_string_array(ref_param_read_string_array);
+private param_proc_begin_xmit_dict(ref_param_begin_read_dict);
+private param_proc_end_xmit_dict(ref_param_end_read_dict);
+private param_proc_get_policy(ref_param_read_get_policy);
+private param_proc_signal_error(ref_param_read_signal_error);
+private param_proc_commit(ref_param_read_commit);
+private const gs_param_list_procs ref_read_procs = {
+ ref_param_read_null,
+ ref_param_read_bool,
+ ref_param_read_int,
+ ref_param_read_long,
+ ref_param_read_float,
+ ref_param_read_string,
+ ref_param_read_string, /* name = string */
+ ref_param_read_int_array,
+ ref_param_read_float_array,
+ ref_param_read_string_array,
+ ref_param_read_string_array, /* name = string */
+ ref_param_begin_read_dict,
+ ref_param_end_read_dict,
+ NULL, /* requested */
+ ref_param_read_get_policy,
+ ref_param_read_signal_error,
+ ref_param_read_commit
+};
+private int near ref_param_read(P4(iparam_list *, gs_param_name,
+ iparam_loc *, int));
+private int near ref_param_read_string_value(P2(const iparam_loc *,
+ gs_param_string *));
+private int near ref_param_read_array(P3(iparam_list *, gs_param_name,
+ iparam_loc *));
+#define iparam_return_error(loc, code)\
+ return_error(*(loc).presult = code)
+#define iparam_check_type(loc, typ)\
+ if ( !r_has_type((loc).pvalue, typ) )\
+ iparam_return_error(loc, e_typecheck)
+#define iparam_check_read(loc)\
+ if ( !r_has_attr((loc).pvalue, a_read) )\
+ iparam_return_error(loc, e_invalidaccess)
+
+private int
+ref_param_read_null(gs_param_list *plist, gs_param_name pkey)
+{ iparam_loc loc;
+ return ref_param_read(iplist, pkey, &loc, t_null);
+}
+private int
+ref_param_read_bool(gs_param_list *plist, gs_param_name pkey, bool *pvalue)
+{ iparam_loc loc;
+ int code = ref_param_read(iplist, pkey, &loc, t_boolean);
+ if ( code != 0 )
+ return code;
+ *pvalue = loc.pvalue->value.boolval;
+ return 0;
+}
+private int
+ref_param_read_int(gs_param_list *plist, gs_param_name pkey, int *pvalue)
+{ iparam_loc loc;
+ int code = ref_param_read(iplist, pkey, &loc, t_integer);
+ if ( code != 0 )
+ return code;
+#if arch_sizeof_int < arch_sizeof_long
+ if ( loc.pvalue->value.intval != (int)loc.pvalue->value.intval )
+ return_error(e_rangecheck);
+#endif
+ *pvalue = (int)loc.pvalue->value.intval;
+ return 0;
+}
+private int
+ref_param_read_long(gs_param_list *plist, gs_param_name pkey, long *pvalue)
+{ iparam_loc loc;
+ int code = ref_param_read(iplist, pkey, &loc, t_integer);
+ if ( code != 0 )
+ return code;
+ *pvalue = loc.pvalue->value.intval;
+ return 0;
+}
+private int
+ref_param_read_float(gs_param_list *plist, gs_param_name pkey, float *pvalue)
+{ iparam_loc loc;
+ int code = ref_param_read(iplist, pkey, &loc, -1);
+ if ( code != 0 )
+ return code;
+ switch ( r_type(loc.pvalue) )
+ {
+ case t_integer:
+ *pvalue = loc.pvalue->value.intval;
+ break;
+ case t_real:
+ *pvalue = loc.pvalue->value.realval;
+ break;
+ default:
+ iparam_return_error(loc, e_typecheck);
+ }
+ return 0;
+}
+private int
+ref_param_read_string(gs_param_list *plist, gs_param_name pkey,
+ gs_param_string *pvalue)
+{ iparam_loc loc;
+ int code = ref_param_read(iplist, pkey, &loc, -1);
+ if ( code != 0 )
+ return code;
+ return ref_param_read_string_value(&loc, pvalue);
+}
+private int
+ref_param_read_int_array(gs_param_list *plist, gs_param_name pkey,
+ gs_param_int_array *pvalue)
+{ iparam_loc loc;
+ int code = ref_param_read_array(iplist, pkey, &loc);
+ int *piv;
+ uint size;
+ uint i;
+
+ if ( code != 0 )
+ return code;
+ size = r_size(loc.pvalue);
+ piv = (int *)ialloc_byte_array(size, sizeof(int),
+ "ref_param_read_int_array");
+ if ( piv == 0 )
+ return_error(e_VMerror);
+ for ( i = 0; i < size; i++ )
+ { const ref *pe = loc.pvalue->value.const_refs + i;
+ if ( !r_has_type(pe, t_integer) )
+ { code = gs_note_error(e_typecheck); break; }
+#if arch_sizeof_int < arch_sizeof_long
+ if ( pe->value.intval != (int)pe->value.intval )
+ { code = gs_note_error(e_rangecheck); break; }
+#endif
+ piv[i] = (int)pe->value.intval;
+ }
+ if ( code < 0 )
+ { ifree_object(piv, "ref_param_read_int_array");
+ return (*loc.presult = code);
+ }
+ pvalue->data = piv;
+ pvalue->size = size;
+ pvalue->persistent = true;
+ return 0;
+}
+private int
+ref_param_read_float_array(gs_param_list *plist, gs_param_name pkey,
+ gs_param_float_array *pvalue)
+{ iparam_loc loc;
+ int code = ref_param_read_array(iplist, pkey, &loc);
+ float *pfv;
+ uint size;
+
+ if ( code != 0 )
+ return code;
+ size = r_size(loc.pvalue);
+ pfv = (float *)ialloc_byte_array(size, sizeof(float),
+ "ref_param_read_float_array");
+ if ( pfv == 0 )
+ return_error(e_VMerror);
+ code = num_params(loc.pvalue->value.const_refs + size - 1, size, pfv);
+ if ( code < 0 )
+ { ifree_object(pfv, "ref_read_float_array_param");
+ return (*loc.presult = code);
+ }
+ pvalue->data = pfv;
+ pvalue->size = size;
+ pvalue->persistent = true;
+ return 0;
+}
+private int
+ref_param_read_string_array(gs_param_list *plist, gs_param_name pkey,
+ gs_param_string_array *pvalue)
+{ iparam_loc loc;
+ int code = ref_param_read_array(iplist, pkey, &loc);
+ gs_param_string *psv;
+ ref *prefs;
+ uint size;
+ uint i;
+
+ if ( code != 0 )
+ return code;
+ prefs = loc.pvalue->value.refs;
+ size = r_size(loc.pvalue);
+ psv = (gs_param_string *)ialloc_byte_array(size, sizeof(gs_param_string),
+ "ref_param_read_string_array");
+ if ( psv == 0 )
+ return_error(e_VMerror);
+ for ( i = 0; code >= 0 && i < size; i++ )
+ { loc.pvalue = prefs + i;
+ code = ref_param_read_string_value(&loc, psv + i);
+ }
+ if ( code < 0 )
+ { ifree_object(psv, "ref_param_read_string_array");
+ return (*loc.presult = code);
+ }
+ pvalue->data = psv;
+ pvalue->size = size;
+ pvalue->persistent = true;
+ return 0;
+}
+private int
+ref_param_begin_read_dict(gs_param_list *plist, gs_param_name pkey,
+ gs_param_dict *pvalue, bool int_keys)
+{ iparam_loc loc;
+ dict_param_list *dlist =
+ (dict_param_list *)ialloc_bytes(size_of(dict_param_list),
+ "ref_param_begin_write_dict");
+ int code = ref_param_read(iplist, pkey, &loc, t_dictionary);
+
+ if ( code != 0 )
+ return code;
+ code = dict_param_list_read(dlist, loc.pvalue, NULL, false);
+ if ( code < 0 )
+ iparam_return_error(loc, code);
+ dlist->int_keys = int_keys;
+ pvalue->list = (gs_param_list *)dlist;
+ pvalue->size = dict_length(loc.pvalue);
+ return 0;
+}
+private int
+ref_param_end_read_dict(gs_param_list *plist, gs_param_name pkey,
+ gs_param_dict *pvalue)
+{ iparam_list_release((dict_param_list *)pvalue->list);
+ ifree_object(pvalue->list, "ref_param_end_read_dict");
+ return 0;
+}
+private int
+ref_param_read_get_policy(gs_param_list *plist, gs_param_name pkey)
+{ ref kname;
+ int code;
+ ref *pvalue;
+ /* We can't use dict_find_string directly here, because */
+ /* pkey might not be a _ds string. */
+ if ( !(r_has_type(&iplist->u.r.policies, t_dictionary) &&
+ (code = name_ref((const byte *)pkey, strlen(pkey), &kname, -1)) >= 0 &&
+ dict_find(&iplist->u.r.policies, &kname, &pvalue) > 0 &&
+ r_has_type(pvalue, t_integer))
+ )
+ return gs_param_policy_ignore;
+ return (int)pvalue->value.intval;
+}
+private int
+ref_param_read_signal_error(gs_param_list *plist, gs_param_name pkey, int code)
+{ iparam_loc loc;
+ ref_param_read(iplist, pkey, &loc, -1); /* can't fail */
+ *loc.presult = code;
+ switch ( ref_param_read_get_policy(plist, pkey) )
+ {
+ case gs_param_policy_ignore:
+ return 0;
+ case gs_param_policy_consult_user:
+ return_error(e_configurationerror);
+ default:
+ return code;
+ }
+}
+private int
+ref_param_read_commit(gs_param_list *plist)
+{ int i;
+ int ecode = 0;
+ if ( !iplist->u.r.require_all )
+ return 0;
+ /* Check to make sure that all parameters were actually read. */
+ for ( i = 0; i < iplist->count; ++i )
+ if ( iplist->results[i] == 0 )
+ iplist->results[i] = ecode = gs_note_error(e_undefined);
+ return ecode;
+}
+
+/* ---------------- Internal routines ---------------- */
+
+/* Read a string value. */
+private int near
+ref_param_read_string_value(const iparam_loc *ploc, gs_param_string *pvalue)
+{ const ref *pref = ploc->pvalue;
+ ref nref;
+ switch ( r_type(pref) )
+ {
+ case t_name:
+ name_string_ref(pref, &nref);
+ pref = &nref;
+ pvalue->persistent = true;
+ goto s;
+ case t_string:
+ iparam_check_read(*ploc);
+ pvalue->persistent = false;
+s: pvalue->data = pref->value.const_bytes;
+ pvalue->size = r_size(pref);
+ break;
+ default:
+ iparam_return_error(*ploc, e_typecheck);
+ }
+ return 0;
+}
+
+/* Read an array parameter. */
+private int near
+ref_param_read_array(iparam_list *plist, gs_param_name pkey, iparam_loc *ploc)
+{ int code = ref_param_read(plist, pkey, ploc, t_array);
+ if ( code != 0 )
+ return code;
+ iparam_check_read(*ploc);
+ return 0;
+}
+
+/* Generic routine for reading a ref parameter. */
+private int near
+ref_param_read(iparam_list *plist, gs_param_name pkey, iparam_loc *ploc,
+ int type)
+{ ref kref;
+ int code = ref_param_key(plist, pkey, &kref);
+ if ( code < 0 )
+ return code;
+ code = (*plist->u.r.read)(iplist, &kref, ploc);
+ if ( code != 0 )
+ return code;
+ if ( type >= 0 )
+ iparam_check_type(*ploc, type);
+ return 0;
+}
+
+/* ---------------- Implementations ---------------- */
+
+/* Initialize for reading parameters. */
+private int
+ref_param_read_init(iparam_list *plist, uint count, const ref *ppolicies,
+ bool require_all)
+{ if ( ppolicies == 0 )
+ make_null(&plist->u.r.policies);
+ else
+ plist->u.r.policies = *ppolicies;
+ plist->u.r.require_all = require_all;
+ plist->count = count;
+ plist->results =
+ (int *)ialloc_byte_array(count, sizeof(int), "ref_param_read_init");
+ if ( plist->results == 0 )
+ return_error(e_VMerror);
+ memset(plist->results, 0, count * sizeof(int));
+ plist->int_keys = false;
+ return 0;
+}
+
+/* Implementation for putting parameters from an array. */
+private int
+array_param_read(iparam_list *plist, const ref *pkey, iparam_loc *ploc)
+{ ref *bot = ((array_param_list *)plist)->bot;
+ ref *ptr = bot;
+ ref *top = ((array_param_list *)plist)->top;
+ for ( ; ptr < top; ptr += 2 )
+ { if ( r_has_type(ptr, t_name) && name_eq(ptr, pkey) )
+ { ploc->pvalue = ptr + 1;
+ ploc->presult = &plist->results[ptr - bot];
+ *ploc->presult = 1;
+ return 0;
+ }
+ }
+ return 1;
+}
+int
+array_param_list_read(array_param_list *plist, ref *bot, uint count,
+ const ref *ppolicies, bool require_all)
+{ if ( count & 1 )
+ return_error(e_rangecheck);
+ plist->procs = &ref_read_procs;
+ plist->u.r.read = array_param_read;
+ plist->bot = bot;
+ plist->top = bot + count;
+ return ref_param_read_init(iplist, count, ppolicies, require_all);
+}
+
+/* Implementation for putting parameters from a stack. */
+private int
+stack_param_read(iparam_list *plist, const ref *pkey, iparam_loc *ploc)
+{
+#define splist ((stack_param_list *)plist)
+ ref_stack *pstack = splist->pstack;
+ /* This implementation is slow, but it probably doesn't matter. */
+ uint index = splist->skip + 1;
+ uint count = splist->count;
+ for ( ; count; count--, index += 2 )
+ { const ref *p = ref_stack_index(pstack, index);
+ if ( r_has_type(p, t_name) && name_eq(p, pkey) )
+ { ploc->pvalue = ref_stack_index(pstack, index - 1);
+ ploc->presult = &plist->results[count - 1];
+ *ploc->presult = 1;
+ return 0;
+ }
+ }
+#undef splist
+ return 1;
+}
+int
+stack_param_list_read(stack_param_list *plist, ref_stack *pstack, uint skip,
+ const ref *ppolicies, bool require_all)
+{ uint count = ref_stack_counttomark(pstack);
+ if ( count == 0 )
+ return_error(e_unmatchedmark);
+ count -= skip + 1;
+ if ( count & 1 )
+ return_error(e_rangecheck);
+ plist->procs = &ref_read_procs;
+ plist->u.r.read = stack_param_read;
+ plist->pstack = pstack;
+ plist->skip = skip;
+ return ref_param_read_init(iplist, count >> 1, ppolicies, require_all);
+}
+
+/* Implementation for putting parameters from a dictionary. */
+private int
+dict_param_read(iparam_list *plist, const ref *pkey, iparam_loc *ploc)
+{
+#define spdict (&((dict_param_list *)plist)->dict)
+ int code = dict_find(spdict, pkey, &ploc->pvalue);
+ if ( code != 1 )
+ return 1;
+ ploc->presult = &plist->results[dict_value_index(spdict, ploc->pvalue)];
+#undef spdict
+ *ploc->presult = 1;
+ return 0;
+}
+int
+dict_param_list_read(dict_param_list *plist, const ref *pdict,
+ const ref *ppolicies, bool require_all)
+{ check_dict_read(*pdict);
+ plist->procs = &ref_read_procs;
+ plist->u.r.read = dict_param_read;
+ plist->dict = *pdict;
+ return ref_param_read_init(iplist, dict_maxlength(pdict), ppolicies,
+ require_all);
+}
diff --git a/pstoraster/iparam.h b/pstoraster/iparam.h
new file mode 100644
index 000000000..773bda088
--- /dev/null
+++ b/pstoraster/iparam.h
@@ -0,0 +1,103 @@
+/* Copyright (C) 1993, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* iparam.h */
+/* Interpreter-level implementations of parameter dictionaries */
+/* Requires istack.h */
+#include "gsparam.h"
+
+/*
+ * This file defines the interface to iparam.c, which provides
+ * several implementations of the parameter dictionary interface
+ * defined in gsparam.h:
+ * - an implementation using dictionary objects;
+ * - an implementation using name/value pairs in an array;
+ * - an implementation using name/value pairs on a stack.
+ *
+ * When reading ('putting'), these implementations keep track of
+ * which parameters have been referenced and which have caused errors.
+ * The results array contains 0 for a parameter that has not been accessed,
+ * 1 for a parameter accessed without error, or <0 for an error.
+ */
+
+typedef struct iparam_loc_s {
+ ref *pvalue; /* (actually const) */
+ int *presult;
+} iparam_loc;
+
+#define iparam_list_common\
+ gs_param_list_common;\
+ union {\
+ struct { /* reading */\
+ int (*read)(P3(iparam_list *, const ref *, iparam_loc *));\
+ ref policies; /* policy dictionary or null */\
+ bool require_all; /* if true, require all params to be known */\
+ } r;\
+ struct { /* writing */\
+ int (*write)(P3(iparam_list *, const ref *, const ref *));\
+ ref wanted; /* desired keys or null */\
+ } w;\
+ } u;\
+ int *results; /* (only used when reading, 0 when writing) */\
+ uint count; /* # of key/value pairs */\
+ bool int_keys /* if true, keys are integers */
+typedef struct iparam_list_s iparam_list;
+struct iparam_list_s {
+ iparam_list_common;
+};
+
+typedef struct dict_param_list_s {
+ iparam_list_common;
+ ref dict;
+} dict_param_list;
+typedef struct array_param_list_s {
+ iparam_list_common;
+ ref *bot;
+ ref *top;
+} array_param_list;
+/* For stack lists, the bottom of the list is just above a mark. */
+typedef struct stack_param_list_s {
+ iparam_list_common;
+ ref_stack *pstack;
+ uint skip; /* # of top items to skip (reading only) */
+} stack_param_list;
+
+/* Procedural interface */
+/*
+ * The const ref * parameter is the policies dictionary when reading,
+ * or the key selection dictionary when writing; It may be NULL in either case.
+ * If the bool parameter is true, if there are any unqueried parameters,
+ * the commit procedure will return an e_undefined error.
+ */
+int dict_param_list_read(P4(dict_param_list *, const ref * /*t_dictionary*/,
+ const ref *, bool));
+int dict_param_list_write(P3(dict_param_list *, ref * /*t_dictionary*/,
+ const ref *));
+int array_param_list_read(P5(array_param_list *, ref *, uint,
+ const ref *, bool));
+int stack_param_list_read(P5(stack_param_list *, ref_stack *, uint,
+ const ref *, bool));
+int stack_param_list_write(P3(stack_param_list *, ref_stack *,
+ const ref *));
+#define iparam_list_release(plist)\
+ ifree_object((plist)->results, "iparam_list_release")
diff --git a/pstoraster/iparray.h b/pstoraster/iparray.h
new file mode 100644
index 000000000..6cfd751e8
--- /dev/null
+++ b/pstoraster/iparray.h
@@ -0,0 +1,36 @@
+/* Copyright (C) 1994 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* iparray.h */
+/* Packed array constructor for Ghostscript */
+
+/*
+ * The only reason to put this in a separate header is that it requires
+ * both ipacked.h and istack.h; putting it in either one would make it
+ * depend on the other one. There must be a better way....
+ */
+
+/* Procedures implemented in zpacked.c */
+
+/* Make a packed array from the top N elements of a stack. */
+int make_packed_array(P4(ref *, ref_stack *, uint, client_name_t));
diff --git a/pstoraster/ireclaim.c b/pstoraster/ireclaim.c
new file mode 100644
index 000000000..9a292fc88
--- /dev/null
+++ b/pstoraster/ireclaim.c
@@ -0,0 +1,206 @@
+/* Copyright (C) 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* ireclaim.c */
+/* Interpreter's interface to garbage collector */
+#include "ghost.h"
+#include "errors.h"
+#include "gsstruct.h"
+#include "iastate.h"
+#include "dstack.h" /* for dsbot, dsp, dict_set_top */
+#include "estack.h" /* for esbot, esp */
+#include "ostack.h" /* for osbot, osp */
+#include "opdef.h" /* for defining init procedure */
+#include "store.h" /* for make_array */
+
+/* Import the static interpreter refs from interp.c. */
+extern ref ref_static_stacks;
+extern ref ref_ref_stacks[3];
+
+/* Import preparation and cleanup routines. */
+extern void file_gc_prepare(P0());
+extern void dstack_gc_cleanup(P0());
+
+/* Import the top-level GC entry. */
+extern void gc_top_level(P2(gs_dual_memory_t *, bool));
+
+/* Forward references */
+private void gs_vmreclaim(P2(gs_dual_memory_t *, bool));
+
+/* Initialize the GC hook in the allocator. */
+private int ireclaim(P2(gs_dual_memory_t *, int));
+private void
+ireclaim_init(void)
+{ gs_imemory.reclaim = ireclaim;
+}
+
+/* GC hook called when the allocator returns a VMerror (space = -1), */
+/* or for vmreclaim (space = the space to collect). */
+private int
+ireclaim(gs_dual_memory_t *dmem, int space)
+{ bool global;
+ gs_ref_memory_t *mem;
+ if ( space < 0 )
+ { /* Determine which allocator got the VMerror. */
+ gs_memory_status_t stats;
+ int i;
+ mem = dmem->space_global; /* just in case */
+ for ( i = 0; i < countof(dmem->spaces.indexed); ++i )
+ { mem = dmem->spaces.indexed[i];
+ if ( mem == 0 )
+ continue;
+ if ( mem->gc_status.requested > 0 )
+ break;
+ }
+ gs_memory_status((gs_memory_t *)mem, &stats);
+ if ( stats.allocated >= mem->gc_status.max_vm )
+ { /* We can't satisfy this request within max_vm. */
+ return_error(e_VMerror);
+ }
+ }
+ else
+ { mem = dmem->spaces.indexed[space >> r_space_shift];
+ }
+ if_debug3('0', "[0]GC called, space=%d, requestor=%d, requested=%ld\n",
+ space, mem->space, (long)mem->gc_status.requested);
+ global = mem->space != avm_local;
+/****************/
+global = true;
+/****************/
+ gs_vmreclaim(dmem, global);
+ ialloc_set_limit(mem);
+ ialloc_reset_requested(dmem);
+ return 0;
+}
+
+/* Interpreter entry to garbage collector. */
+/* This registers the stacks before calling the main GC. */
+private void near set_ref_chunk(P4(chunk_t *, ref *, ref *, gs_ref_memory_t *));
+private void
+gs_vmreclaim(gs_dual_memory_t *dmem, bool global)
+{ /*
+ * Create pseudo-chunks to hold the interpreter roots:
+ * copies of the ref_stacks, and, if necessary,
+ * the statically allocated stack bodies.
+ */
+ gs_ref_memory_t *mem = dmem->space_local;
+ gs_ref_memory_t *gmem = dmem->space_global;
+ gs_ref_memory_t *smem = dmem->space_system;
+ struct ir_ {
+ chunk_head_t head;
+ obj_header_t prefix;
+ ref refs[5+1]; /* +1 for extra relocation ref */
+ } iroot_refs;
+ chunk_t cir, css;
+ void *piroot = &iroot_refs.refs[0];
+ gs_gc_root_t iroot;
+
+ alloc_close_chunk(mem);
+ if ( gmem != mem )
+ alloc_close_chunk(gmem);
+ alloc_close_chunk(smem);
+
+ /* Copy the ref_stacks into the heap, so we can trace and */
+ /* relocate them. */
+#define get_stack(i, stk)\
+ ref_stack_cleanup(&stk);\
+ iroot_refs.refs[i+2] = ref_ref_stacks[i],\
+ *r_ptr(&iroot_refs.refs[i+2], ref_stack) = stk
+ get_stack(0, d_stack);
+ get_stack(1, e_stack);
+ get_stack(2, o_stack);
+#undef get_stack
+
+ /* Make the root chunk. */
+ iroot_refs.refs[1] = ref_static_stacks;
+ make_array(&iroot_refs.refs[0], avm_system, 4, &iroot_refs.refs[1]);
+ set_ref_chunk(&cir, &iroot_refs.refs[0], &iroot_refs.refs[5], mem);
+ gs_register_ref_root((gs_memory_t *)mem, &iroot, &piroot, "gs_gc_main");
+
+ /* If necessary, make the static stack chunk. */
+#define css_array iroot_refs.refs[1]
+#define css_base css_array.value.refs
+ if ( css_base != NULL )
+ set_ref_chunk(&css, css_base, css_base + r_size(&css_array), mem);
+
+ /* Prune the file list so it won't retain potentially collectible */
+ /* files. */
+ file_gc_prepare();
+
+ /* Do the actual collection. */
+ gc_top_level(dmem, global);
+
+ /* Remove the temporary chunks. */
+ if ( css_base != NULL )
+ alloc_unlink_chunk(&css, mem);
+ gs_unregister_root((gs_memory_t *)mem, &iroot, "gs_gc_main");
+ alloc_unlink_chunk(&cir, mem);
+#undef css_array
+#undef css_base
+
+ /* Update the static copies of the ref_stacks. */
+#define put_stack(i, stk)\
+ ref_ref_stacks[i].value.pstruct = iroot_refs.refs[i+2].value.pstruct,\
+ stk = *r_ptr(&iroot_refs.refs[i+2], ref_stack)
+ put_stack(0, d_stack);
+ put_stack(1, e_stack);
+ put_stack(2, o_stack);
+#undef put_stack
+
+ /* Update the cached value pointers in names. */
+
+ dstack_gc_cleanup();
+
+ /* Reopen the active chunks. */
+
+ alloc_open_chunk(smem);
+ if ( gmem != mem )
+ alloc_open_chunk(gmem);
+ alloc_open_chunk(mem);
+
+ /* Update caches */
+
+ { uint dcount = ref_stack_count(&d_stack);
+ ref_systemdict = *ref_stack_index(&d_stack, dcount - 1);
+ }
+ dict_set_top();
+}
+private void near
+set_ref_chunk(chunk_t *cp, ref *bot, ref *top, gs_ref_memory_t *mem)
+{ obj_header_t *pre = (obj_header_t *)bot - 1;
+ chunk_head_t *head = (chunk_head_t *)pre - 1;
+ pre->o_large = 1; /* not relocatable */
+ pre->o_lsize = 0;
+ pre->o_lmark = o_l_unmarked;
+ pre->o_size = (byte *)(top + 1) - (byte *)bot;
+ pre->o_type = &st_refs;
+ alloc_init_chunk(cp, (byte *)head, (byte *)(top + 1), false, NULL); /* +1 for extra reloc ref */
+ cp->cbot = cp->ctop;
+ alloc_link_chunk(cp, mem);
+ make_int(top, 0); /* relocation ref */
+}
+
+/* ------ Initialization procedure ------ */
+
+BEGIN_OP_DEFS(ireclaim_l2_op_defs) {
+END_OP_DEFS(ireclaim_init) }
diff --git a/pstoraster/iref.h b/pstoraster/iref.h
new file mode 100644
index 000000000..126d506e9
--- /dev/null
+++ b/pstoraster/iref.h
@@ -0,0 +1,367 @@
+/* Copyright (C) 1989, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* iref.h */
+/* Object structure and type definitions for Ghostscript */
+
+#ifndef iref_INCLUDED
+# define iref_INCLUDED
+
+/* The typedef for object references */
+typedef struct ref_s ref;
+
+/* The typedef for packed object references. This is opaque here: */
+/* the details are in packed.h. */
+typedef ushort ref_packed;
+
+/*
+ * Define the object types.
+ * The types marked with @ are composite and hence use the a_space field;
+ * objects of all other types must have a_space cleared.
+ * The types marked with ! behave differently in the interpreter
+ * depending on whether they are executable or literal.
+ * The types marked with + use the read/write/execute
+ * attributes; the rest only use the executable attribute.
+ * The types marked with # use the size field.
+ */
+typedef enum {
+
+/*
+ * Type 0 must be left unassigned, so that the type (and type_attrs)
+ * of a valid ref will never be zero. This speeds up simultaneous
+ * type/space checking in def (see dstack.h for details) and a similar
+ * check in ref_save (see store.h for details). We may as well use
+ * type 0 for t__invalid, which will never appear in a real ref.
+ *
+ * The "invalid" type is only used in a few special places: the guard
+ * entries at the bottom of the o-stack that detect stack underflow,
+ * and (eventually) the ref that the cached value pointer in names points to
+ * if the binding isn't known. It never appears on a stack or in a
+ * program-visible data structure.
+ */
+
+ t__invalid, /* (no value) */
+ t_boolean, /* value.boolval */
+ t_dictionary, /* @ + value.pdict */
+ t_file, /* @!+# value.pfile, uses size for id */
+
+/*
+ * The 4 array types must be kept together, and must start at
+ * a multiple of 4, for the sake of r_is_array and r_is_proc (see below).
+ */
+
+#define _t_array_span 4
+ t_array, /* @!+# value.refs */
+ /* The following are the two implementations of */
+ /* packed arrays. */
+ t_mixedarray, /* @!+# value.packed */
+ t_shortarray, /* @!+# value.packed */
+ t_unused_array_, /* (an unused array type) */
+
+/*
+ * t_[a]struct is an "umbrella" for other types that are represented by
+ * allocated objects (structures). Objects of these types are composite
+ * and hence use the a_local attribute. The type name is taken from
+ * the allocator template for the structure. t_astruct objects use the
+ * access attributes; t_struct objects do not. Neither t_struct nor
+ * t_astruct objects use the size.
+ *
+ * t_struct is currently used for the following PostScript types:
+ * condition, lock.
+ * We could use it for fontIDs, except that they may have subclasses.
+ * Eventually it will also be used for the new 'device' type.
+ * t_astruct is currently used for the following PostScript types:
+ * gstate.
+ *
+ * The 2 structure types must be kept together, and must start at
+ * a multiple of 2, for the sake of r_has_stype (see below).
+ */
+
+#define _t_struct_span 2
+ t_struct, /* @ value.pstruct */
+ t_astruct, /* @ + value.pstruct */
+
+/*
+ * We now continue with individual types.
+ */
+ t_fontID, /* @ value.pstruct */
+ t_integer, /* value.intval */
+ t_mark, /* (no value) */
+/*
+ * Name objects use the a_space field because they really are composite
+ * objects internally.
+ */
+ t_name, /* @! # value.pname, uses size for index */
+ t_null, /* ! # (value.opproc, uses size for mark */
+ /* type, on e-stack only) */
+/*
+ * Operator objects use the a_space field because they may actually be
+ * disguised procedures. (Real operators always have a_space = 0.)
+ */
+ t_operator, /* @! # value.opproc, uses size for index */
+ t_real, /* value.realval */
+ t_save, /* value.saveid, see isave.h for why */
+ /* this isn't a t_struct */
+ t_string, /* @!+# value.bytes */
+/*
+ * The following are extensions to the PostScript type set.
+ * When adding new types, be sure to edit the table in gs_init.ps
+ * (==only operator), the printing routine in idebug.c, the dispatches
+ * in igc.c, igcref.c, and interp.c, obj_eq in iutil.c, restore_check_stack
+ * in zvmem.c, and also type_name_strings and type_print_strings below.
+ */
+ t_device, /* @ + value.pdevice */
+ t_oparray, /* @! # value.const_refs, uses size */
+ /* for index */
+ t_next_index /*** first available index ***/
+} ref_type;
+/*
+ * The interpreter uses types starting at t_next_index for representing
+ * a few high-frequency operators.
+ * Since there are no operations specifically on operators,
+ * there is no need for any operators to check specifically for these
+ * types. The r_btype macro takes care of the conversion when required.
+ */
+/*extern const int tx_next_index;*/ /* in interp.c */
+/*
+ * Define the types that use the size field.
+ * (The extended types used by the interpreter also use the size field.)
+ * We don't include t_null here, because it only uses the size field
+ * on the e-stack.
+ */
+#define case_types_with_size\
+ case t_array: case t_mixedarray: case t_shortarray:\
+ case t_file: case t_name: case t_operator: case t_string:\
+ case t_oparray
+/*
+ * Define the type names for debugging printout.
+ * All names must be the same length, so that columns will line up.
+ */
+#define type_print_strings\
+ "INVL","bool","dict","file",\
+ "arry","mpry","spry","u?ry",\
+ "STRC","ASTR",\
+ "font","int ","mark","name","null",\
+ "oper","real","save","str ",\
+ "devc","opry"
+/*
+ * Define the type names for the type operator.
+ */
+#define type_name_strings\
+ 0,"booleantype","dicttype","filetype",\
+ "arraytype","packedarraytype","packedarraytype","arraytype",\
+ 0,0,\
+ "fonttype","integertype","marktype","nametype","nulltype",\
+ "operatortype","realtype","savetype","stringtype",\
+ "devicetype","operatortype"
+
+/*
+ * The following factors affect the encoding of attributes:
+ *
+ * - The packed array format requires the high-order bits of the
+ * type/attributes field to be 0. (see packed.h)
+ *
+ * - The interpreter wants the type, executable bit, and execute
+ * permission to be adjacent, and in that order from high to low.
+ *
+ * - Type testing is most efficient if the type is in a byte by itself.
+ *
+ * The layout given below results in the most efficient code overall.
+ */
+
+/* Location attributes. */
+/* Note that these are associated with the *location*, not with the */
+/* ref that is *stored* in that location. */
+#define l_mark 1 /* mark for garbage collector */
+#define l_new 2 /* stored into since last save */
+/* Attributes visible at the PostScript language level. */
+/* Reserve bits for VM space information (defined in ivmspace.h). */
+#define r_space_bits 2
+#define r_space_shift 2
+#define a_write 0x10
+#define a_read 0x20
+#define a_execute 0x40
+#define a_executable 0x80
+#define a_readonly (a_read+a_execute)
+#define a_all (a_write+a_read+a_execute)
+#define r_type_shift 8
+#define r_type_bits 6
+
+/* Define the attribute names for debugging printout. */
+/* Each entry has the form <mask, value, character>. */
+typedef struct attr_print_mask_s {
+ ushort mask;
+ ushort value;
+ char print;
+} attr_print_mask;
+#define attr_print_flag(m,c)\
+ {m,m,c},{m,0,'-'}
+#define attr_print_space(v,c)\
+ {((1<<r_space_bits)-1)<<r_space_shift,v,c}
+#define attr_print_masks\
+ attr_print_flag(l_mark,'m'),\
+ attr_print_flag(l_new,'n'),\
+ attr_print_space(avm_foreign,'F'),\
+ attr_print_space(avm_system,'S'),\
+ attr_print_space(avm_global,'G'),\
+ attr_print_space(avm_local,'L'),\
+ attr_print_flag(a_write,'w'),\
+ attr_print_flag(a_read,'r'),\
+ attr_print_flag(a_execute,'x'),\
+ attr_print_flag(a_executable,'e'),\
+ attr_print_flag(0x4000,'?'),\
+ attr_print_flag(0x8000,'?')
+
+/* Abstract types */
+typedef struct dict_s dict;
+typedef struct name_s name;
+#ifndef stream_DEFINED
+# define stream_DEFINED
+typedef struct stream_s stream;
+#endif
+#ifndef gx_device_DEFINED
+# define gx_device_DEFINED
+typedef struct gx_device_s gx_device;
+#endif
+#ifndef obj_header_DEFINED
+# define obj_header_DEFINED
+typedef struct obj_header_s obj_header_t;
+#endif
+/* We duplicate the definition of os_ptr (a.k.a. s_ptr) here */
+/* so that we can have an accurate typedef for op_proc */
+/* without having to drag in istack.h and ostack.h. */
+typedef int (*op_proc_p)(P1(ref _ds *));
+/* real_opproc is a holdover.... */
+#define real_opproc(pref) ((pref)->value.opproc)
+
+/* Object reference */
+/*
+ * Note that because of the way packed arrays are represented,
+ * the type_attrs member must be the first one in the ref structure.
+ */
+struct tas_s {
+ ushort type_attrs;
+ ushort rsize;
+};
+struct ref_s {
+
+ struct tas_s tas;
+
+#define r_size(rp) ((rp)->tas.rsize)
+#define r_inc_size(rp,inc) ((rp)->tas.rsize += (inc))
+#define r_dec_size(rp,dec) ((rp)->tas.rsize -= (dec))
+#define r_set_size(rp,siz) ((rp)->tas.rsize = (siz))
+/* type_attrs is a single element for fast dispatching in the interpreter */
+#if r_type_shift == 8
+# if arch_is_big_endian
+# define r_type(rp) (((const byte *)&((rp)->tas.type_attrs))[sizeof(ushort)-2])
+# else
+# define r_type(rp) (((const byte *)&((rp)->tas.type_attrs))[1])
+# endif
+# define r_has_type(rp,typ) (r_type(rp) == (typ))
+#else
+# define r_type(rp) ((rp)->tas.type_attrs >> r_type_shift)
+# define r_has_type(rp,typ) r_has_type_attrs(rp,typ,0) /* see below */
+#endif
+/* A special macro for testing arrayhood. */
+#define r_is_array(rp) _r_has_masked_type_attrs(rp,t_array,_t_array_span,0)
+#define r_set_type(rp,typ) ((rp)->tas.type_attrs = (typ) << r_type_shift)
+#define r_btype(rp)\
+ ((rp)->tas.type_attrs >= (t_next_index << r_type_shift) ?\
+ t_operator : r_type(rp))
+#define r_type_xe_shift (r_type_shift - 2)
+#define type_xe_(tas) ((tas) >> r_type_xe_shift) /* internal use only */
+/*
+ * The r_type_xe macro is used in (and only in) the main interpreter loop,
+ * where its rp operand may be a ref_packed, not necessarily aligned as
+ * strictly as a full-size ref. The DEC C compiler, and possibly others,
+ * may compile code assuming that rp is ref-aligned. Therefore, we
+ * explicitly cast the pointer to a less-strictly-aligned type.
+ */
+#define r_type_xe(rp) type_xe_(*(const ushort *)&(rp)->tas.type_attrs)
+#define type_xe_value(t,xe) type_xe_(((t) << r_type_shift) + (xe))
+#define r_type_attrs(rp) ((rp)->tas.type_attrs) /* reading only */
+#define r_has_attrs(rp,mask) !(~r_type_attrs(rp) & (mask))
+#define r_has_masked_attrs(rp,attrs,mask)\
+ ((r_type_attrs(rp) & (mask)) == (attrs))
+#define r_has_attr(rp,mask1) /* optimize 1-bit case */\
+ (r_type_attrs(rp) & (mask1))
+/* The following macro is not for external use. */
+#define _r_has_masked_type_attrs(rp,typ,tspan,mask)\
+ (((rp)->tas.type_attrs &\
+ ((((1 << r_type_bits) - (tspan)) << r_type_shift) + (mask))) ==\
+ (((typ) << r_type_shift) + (mask)))
+#define r_has_type_attrs(rp,typ,mask)\
+ _r_has_masked_type_attrs(rp,typ,1,mask)
+/* A special macro for testing procedurehood. */
+#define r_is_proc(rp)\
+ _r_has_masked_type_attrs(rp,t_array,_t_array_span,a_execute+a_executable)
+#define r_set_attrs(rp,mask) ((rp)->tas.type_attrs |= (mask))
+#define r_clear_attrs(rp,mask) ((rp)->tas.type_attrs &= ~(mask))
+#define r_store_attrs(rp,mask,attrs)\
+ ((rp)->tas.type_attrs = ((rp)->tas.type_attrs & ~(mask)) | (attrs))
+#define r_copy_attrs(rp,mask,sp)\
+ r_store_attrs(rp,mask,(sp)->tas.type_attrs & (mask))
+#define r_set_type_attrs(rp,typ,mask)\
+ ((rp)->tas.type_attrs = ((typ) << r_type_shift) + (mask))
+/* Macros for t_[a]struct objects. */
+#define r_is_struct(rp) _r_has_masked_type_attrs(rp,t_struct,_t_struct_span,0)
+#define r_has_stype(rp,mem,styp)\
+ (r_is_struct(rp) && gs_object_type(mem, (rp)->value.pstruct) == &styp)
+#define r_ptr(rp,typ) ((typ *)((rp)->value.pstruct))
+#define r_set_ptr(rp,ptr) ((rp)->value.pstruct = (obj_header_t *)(ptr))
+
+ union v { /* name the union to keep gdb happy */
+ long intval;
+ ushort boolval;
+ float realval;
+ ulong saveid;
+ byte *bytes;
+ const byte *const_bytes;
+ ref *refs;
+ const ref *const_refs;
+ name *pname;
+ const name *const_pname;
+ dict *pdict;
+ const dict *const_pdict;
+ const ref_packed *packed;
+ op_proc_p opproc;
+ struct stream_s *pfile;
+ struct gx_device_s *pdevice;
+ obj_header_t *pstruct;
+ } value;
+};
+
+/* Define the required alignment for refs. */
+/* We assume all alignment values are powers of 2. */
+#define align_ref_mod\
+ (((arch_align_long_mod - 1) | (arch_align_float_mod - 1) |\
+ (arch_align_ptr_mod - 1)) + 1)
+
+/* Define the maximum size of an array or a string. */
+/* The maximum array size is determined by the fact that */
+/* the allocator cannot allocate a block larger than max_uint. */
+#define max_array_size (max_ushort & (max_uint / (uint)arch_sizeof_ref))
+#define max_string_size max_ushort
+
+#endif /* iref_INCLUDED */
diff --git a/pstoraster/isave.c b/pstoraster/isave.c
new file mode 100644
index 000000000..66359a68f
--- /dev/null
+++ b/pstoraster/isave.c
@@ -0,0 +1,989 @@
+/* Copyright (C) 1993, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* isave.c */
+/* Save/restore manager for Ghostscript interpreter */
+#include "ghost.h"
+#include "memory_.h"
+#include "errors.h"
+#include "gsexit.h"
+#include "gsstruct.h"
+#include "iastate.h"
+#include "inamedef.h"
+#include "ipacked.h"
+#include "isave.h"
+#include "isstate.h"
+#include "store.h" /* for ref_assign */
+#include "ivmspace.h"
+#include "gsutil.h" /* gs_next_ids prototype */
+
+/* Imported restore routines */
+extern void file_save(P0());
+extern void file_restore(P2(const alloc_save_t *, const gs_memory_t *));
+extern void file_forget_save(P1(const alloc_save_t *));
+extern void font_restore(P1(const alloc_save_t *));
+
+/* Structure descriptor */
+private_st_alloc_save();
+
+/* Define the maximum amount of data we are willing to scan repeatedly -- */
+/* see below for details. */
+private const long max_repeated_scan = 100000;
+
+#define print_save(str, space, sav)\
+ if_debug5('u', "[u]%s space %u 0x%lx: cdata = 0x%lx, id = %lu\n",\
+ str, space, (ulong)(sav), (ulong)(sav)->client_data, (ulong)(sav)->id);
+
+/*
+ * The logic for saving and restoring the state is complex.
+ * Both the changes to individual objects, and the overall state
+ * of the memory manager, must be saved and restored.
+ */
+
+/*
+ * To save the state of the memory manager:
+ * Save the state of the current chunk in which we are allocating.
+ * Shrink the current chunk to its inner unallocated region.
+ * Save and reset the free block chains.
+ * By doing this, we guarantee that no object older than the save
+ * can be freed.
+ *
+ * To restore the state of the memory manager:
+ * Free all chunks newer than the save, and the descriptor for
+ * the inner chunk created by the save.
+ * Make current the chunk that was current at the time of the save.
+ * Restore the state of the current chunk.
+ *
+ * In addition to save ("start transaction") and restore ("abort transaction"),
+ * we support forgetting a save ("commit transation"). To forget a save:
+ * Reassign to the next outer save all chunks newer than the save.
+ * Free the descriptor for the inner chunk, updating its outer chunk
+ * to reflect additional allocations in the inner chunk.
+ * Concatenate the free block chains with those of the outer save.
+ */
+
+/*
+ * For saving changes to individual objects, we add an "attribute" bit
+ * (l_new) that logically belongs to the slot where the ref is stored,
+ * not to the ref itself. The bit means "the contents of this slot
+ * have been changed, or the slot was allocated, since the last save."
+ * To keep track of changes since the save, we associate a chain of
+ * <slot, old_contents> pairs that remembers the old contents of slots.
+ *
+ * When creating an object, if the save level is non-zero:
+ * Set l_new in all slots.
+ *
+ * When storing into a slot, if the save level is non-zero:
+ * If l_new isn't set, save the address and contents of the slot
+ * on the current contents chain.
+ * Set l_new after storing the new value.
+ *
+ * To do a save:
+ * If the save level is non-zero:
+ * Reset l_new in all slots on the contents chain, and in all
+ * objects created since the previous save.
+ * Push the head of the contents chain, and reset the chain to empty.
+ *
+ * To do a restore:
+ * Check all the stacks to make sure they don't contain references
+ * to objects created since the save.
+ * Restore all the slots on the contents chain.
+ * Pop the contents chain head.
+ * If the save level is now non-zero:
+ * Scan the newly restored contents chain, and set l_new in all
+ * the slots it references.
+ * Scan all objects created since the previous save, and set
+ * l_new in all the slots of each object.
+ *
+ * To forget a save:
+ * If the save level is greater than 1:
+ * Set l_new as for a restore, per the next outer save.
+ * Concatenate the next outer contents chain to the end of
+ * the current one.
+ * If the save level is 1:
+ * Reset l_new as for a save.
+ * Free the contents chain.
+ */
+
+/*
+ * A consequence of the foregoing algorithms is that the cost of a save
+ * is proportional to the total amount of data allocated since the previous
+ * save. If a PostScript program reads in a large amount of setup code
+ * and then uses save/restore heavily, each save/restore will be expensive.
+ * To mitigate this, we check to see how much data we are scanning at a save;
+ * if it is large, we do a second, invisible save. This greatly reduces
+ * the cost of inner saves, at the expense of possibly saving some changes
+ * twice that otherwise would only have to be saved once.
+ */
+
+/*
+ * The presence of global and local VM complicates the situation further.
+ * There is a separate save chain and contents chain for each VM space.
+ * When multiple contexts are fully implemented, save and restore will have
+ * the following effects, according to the privacy status of the current
+ * context's global and local VM:
+ * Private global, private local:
+ * The outermost save saves both global and local VM;
+ * otherwise, save only saves local VM.
+ * Shared global, private local:
+ * Save only saves local VM.
+ * Shared global, shared local:
+ * Save only saves local VM, and suspends all other contexts
+ * sharing the same local VM until the matching restore.
+ * Since we do not currently implement multiple contexts, only the first
+ * case is relevant.
+ *
+ * Note that when saving the contents of a slot, the choice of chain
+ * is determined by the VM space in which the slot is allocated,
+ * not by the current allocation mode.
+ */
+
+#define set_in_save(dmem)\
+ ((dmem)->test_mask = (dmem)->new_mask = l_new)
+#define set_not_in_save(dmem)\
+ ((dmem)->test_mask = ~0, (dmem)->new_mask = 0)
+
+/*
+ * Structure for saved change chain for save/restore. Because of the
+ * garbage collector, we need to distinguish the cases where the change
+ * is in a static object, a dynamic ref, or a dynamic struct.
+ */
+typedef struct alloc_change_s alloc_change_t;
+struct alloc_change_s {
+ alloc_change_t *next;
+ ref_packed *where;
+ ref contents;
+#define ac_offset_static (-2) /* static object */
+#define ac_offset_ref (-1) /* dynamic ref */
+ short offset; /* if >= 0, offset within struct */
+};
+#define ptr ((alloc_change_t *)vptr)
+private CLEAR_MARKS_PROC(change_clear_marks) {
+ if ( r_is_packed(&ptr->contents) )
+ r_clear_pmark((ref_packed *)&ptr->contents);
+ else
+ r_clear_attrs(&ptr->contents, l_mark);
+}
+private ENUM_PTRS_BEGIN(change_enum_ptrs) return 0;
+ ENUM_PTR(0, alloc_change_t, next);
+ case 1:
+ if ( ptr->offset >= 0 )
+ { *pep = (byte *)ptr->where - ptr->offset;
+ return ptr_struct_type;
+ }
+ else
+ { *pep = ptr->where;
+ return ptr_ref_type;
+ }
+ case 2:
+ *pep = &ptr->contents;
+ return ptr_ref_type;
+ENUM_PTRS_END
+private RELOC_PTRS_BEGIN(change_reloc_ptrs) {
+ RELOC_PTR(alloc_change_t, next);
+ switch ( ptr->offset )
+ {
+ case ac_offset_static:
+ break;
+ case ac_offset_ref:
+ ptr->where = gs_reloc_ref_ptr(ptr->where, gcst);
+ break;
+ default:
+ { byte *obj = (byte *)ptr->where - ptr->offset;
+ byte *robj = gs_reloc_struct_ptr(obj, gcst);
+ ptr->where = (ref_packed *)(robj + ptr->offset);
+ } break;
+ }
+ if ( r_is_packed(&ptr->contents) )
+ r_clear_pmark((ref_packed *)&ptr->contents);
+ else
+ { gs_reloc_refs((ref_packed *)&ptr->contents,
+ (ref_packed *)(&ptr->contents + 1),
+ gcst);
+ r_clear_attrs(&ptr->contents, l_mark);
+ }
+} RELOC_PTRS_END
+#undef ptr
+gs_private_st_complex_only(st_alloc_change, alloc_change_t, "alloc_change",
+ change_clear_marks, change_enum_ptrs, change_reloc_ptrs, 0);
+
+/* Debugging printout */
+#ifdef DEBUG
+private void
+alloc_save_print(alloc_change_t *cp, bool print_current)
+{ dprintf2(" 0x%lx: 0x%lx: ", (ulong)cp, (ulong)cp->where);
+ if ( r_is_packed(&cp->contents) )
+ { if ( print_current )
+ dprintf2("saved=%x cur=%x\n", *(ref_packed *)&cp->contents,
+ *cp->where);
+ else
+ dprintf1("%x\n", *(ref_packed *)&cp->contents);
+ }
+ else
+ { if ( print_current )
+ dprintf6("saved=%x %x %lx cur=%x %x %lx\n",
+ r_type_attrs(&cp->contents), r_size(&cp->contents),
+ (ulong)cp->contents.value.intval,
+ r_type_attrs((ref *)cp->where),
+ r_size((ref *)cp->where),
+ (ulong)((ref *)cp->where)->value.intval);
+ else
+ dprintf3("%x %x %lx\n",
+ r_type_attrs(&cp->contents), r_size(&cp->contents),
+ (ulong)cp->contents.value.intval);
+ }
+}
+#endif
+
+/* Forward references */
+private void restore_resources(P2(alloc_save_t *, gs_ref_memory_t *));
+private void restore_free(P1(gs_ref_memory_t *));
+private long save_set_new(P2(gs_ref_memory_t *, bool));
+private void save_set_new_changes(P2(gs_ref_memory_t *, bool));
+
+/* Initialize the save/restore machinery. */
+void
+alloc_save_init(gs_dual_memory_t *dmem)
+{ dmem->save_level = 0;
+ set_not_in_save(dmem);
+}
+
+/* Save the state. */
+private alloc_save_t *alloc_save_space(P2(gs_ref_memory_t *,
+ gs_dual_memory_t *));
+ulong
+alloc_save_state(gs_dual_memory_t *dmem, void *cdata)
+{ gs_ref_memory_t *lmem = dmem->space_local;
+ gs_ref_memory_t *gmem = dmem->space_global;
+ ulong sid = gs_next_ids(2);
+#define alloc_free_save(mem, s, scn, icn)\
+ { chunk_t *inner = (mem)->pcc;\
+ gs_free_object((gs_memory_t *)(mem), s, scn);\
+ gs_free_object((mem)->parent, inner, icn);\
+ }
+ bool global =
+ dmem->save_level == 0 && gmem != lmem &&
+ gmem->num_contexts == 1;
+ alloc_save_t *gsave =
+ (global ? alloc_save_space(gmem, dmem) : (alloc_save_t *)0);
+ alloc_save_t *lsave = alloc_save_space(lmem, dmem);
+
+ if ( lsave == 0 || (global && gsave == 0) )
+ { if ( lsave != 0 )
+ alloc_free_save(lmem, lsave, "alloc_save_state(local save)",
+ "alloc_save_state(local inner)");
+ if ( gsave != 0 )
+ alloc_free_save(gmem, gsave, "alloc_save_state(global save)",
+ "alloc_save_state(global inner)");
+ return 0;
+ }
+#undef alloc_free_save
+ if ( gsave != 0 )
+ { gsave->id = sid + 1;
+ gsave->client_data = 0;
+ print_save("save", gmem->space, gsave);
+ /* Restore names when we do the local restore. */
+ lsave->restore_names = gsave->restore_names;
+ gsave->restore_names = false;
+ }
+ lsave->id = sid;
+ lsave->client_data = cdata;
+ print_save("save", lmem->space, lsave);
+ /* Reset the l_new attribute in all slots. The only slots that */
+ /* can have the attribute set are the ones on the changes chain, */
+ /* and ones in objects allocated since the last save. */
+ if ( dmem->save_level != 0 )
+ { long scanned = save_set_new(&lsave->state, false);
+ if ( scanned > max_repeated_scan )
+ { /* Do a second, invisible save. */
+ alloc_save_t *rsave;
+ /* Notify the file machinery of the first save. */
+ file_save();
+ rsave = alloc_save_space(lmem, dmem);
+ if ( rsave != 0 )
+ { rsave->id = sid;
+ rsave->client_data = cdata;
+ print_save("save", lmem->space, rsave);
+ lsave->id = 0; /* mark as invisible */
+ lsave->client_data = 0;
+ /* Inherit the allocated space count -- */
+ /* we need this for triggering a GC. */
+ rsave->state.inherited =
+ lsave->state.allocated +
+ lsave->state.inherited;
+ lmem->inherited = rsave->state.inherited;
+ print_save("save", lmem->space, lsave);
+ }
+ }
+ }
+ dmem->save_level++;
+ set_in_save(dmem);
+ /* Notify the file machinery we just did a save. */
+ file_save();
+ return sid;
+}
+/* Save the state of one space (global or local). */
+private alloc_save_t *
+alloc_save_space(gs_ref_memory_t *mem, gs_dual_memory_t *dmem)
+{ gs_ref_memory_t save_mem;
+ alloc_save_t *save;
+ chunk_t *inner = 0;
+
+ if ( mem->cc.ctop - mem->cc.cbot > sizeof(chunk_head_t) )
+ { inner = gs_alloc_struct(mem->parent, chunk_t, &st_chunk,
+ "alloc_save_space(inner)");
+ if ( inner == 0 )
+ return 0;
+ }
+ save_mem = *mem;
+ alloc_close_chunk(mem);
+ gs_memory_status((gs_memory_t *)mem, &mem->previous_status);
+ ialloc_reset(mem);
+ mem->cc.cnext = mem->cc.cprev = 0;
+ if ( inner != 0 )
+ { /* Create an inner chunk to cover only the unallocated part. */
+ alloc_init_chunk(&mem->cc, mem->cc.cbot, mem->cc.ctop,
+ true, mem->pcc);
+ *inner = mem->cc;
+ mem->pcc = inner;
+ mem->cfirst = mem->clast = inner;
+ }
+ else
+ { /* Not enough room to create an inner chunk. */
+ mem->pcc = 0;
+ mem->cfirst = mem->clast = 0;
+ mem->cc.cbot = mem->cc.ctop = 0;
+ }
+ save = gs_alloc_struct((gs_memory_t *)mem, alloc_save_t,
+ &st_alloc_save, "alloc_save_space(save)");
+#ifdef DEBUG
+ if ( inner != 0 )
+ { if_debug4('u',
+ "[u]save space %u at 0x%lx: cbot=0x%lx ctop=0x%lx\n",
+ mem->space, (ulong)save,
+ (ulong)inner->cbot, (ulong)inner->ctop);
+ }
+ else
+ { if_debug2('u',
+ "[u]save space %u at 0x%lx (no inner)\n",
+ mem->space, (ulong)save);
+ }
+#endif
+ if ( save == 0 )
+ { gs_free_object(mem->parent, inner, "alloc_save_space(inner)");
+ *mem = save_mem;
+ return 0;
+ }
+ save->state = save_mem;
+ save->dmem = dmem;
+ save->restore_names = (name_memory() == (gs_memory_t *)mem);
+ save->is_current = (dmem->current == mem);
+ mem->saved = save;
+ return save;
+}
+
+/* Record a state change that must be undone for restore, */
+/* and mark it as having been saved. */
+int
+alloc_save_change(gs_dual_memory_t *dmem, const ref *pcont,
+ ref_packed *where, client_name_t cname)
+{ gs_ref_memory_t *mem;
+ register alloc_change_t *cp;
+ if ( dmem->save_level == 0 )
+ return 0; /* no saving */
+ mem = (pcont == NULL ? dmem->space_local :
+ dmem->spaces.indexed[r_space(pcont) >> r_space_shift]);
+ cp = gs_alloc_struct((gs_memory_t *)mem, alloc_change_t,
+ &st_alloc_change, "alloc_save_change");
+ if ( cp == 0 )
+ return -1;
+ cp->next = mem->changes;
+ cp->where = where;
+ if ( pcont == NULL )
+ cp->offset = ac_offset_static;
+ else if ( r_is_array(pcont) || r_has_type(pcont, t_dictionary) )
+ cp->offset = ac_offset_ref;
+ else if ( r_is_struct(pcont) )
+ cp->offset = (byte *)where - (byte *)pcont->value.pstruct;
+ else
+ { lprintf3("Bad type %u for save! pcont = 0x%lx, where = 0x%lx\n",
+ r_type(pcont), (ulong)pcont, (ulong)where);
+ gs_abort();
+ }
+ if ( r_is_packed(where) )
+ *(ref_packed *)&cp->contents = *where;
+ else
+ { ref_assign_inline(&cp->contents, (ref *)where);
+ r_set_attrs((ref *)where, l_new);
+ }
+ mem->changes = cp;
+#ifdef DEBUG
+if ( gs_debug_c('U') )
+ { dprintf1("[u]save(%s)", client_name_string(cname));
+ alloc_save_print(cp, false);
+ }
+#endif
+ return 0;
+}
+
+/* Return the current save level */
+int
+alloc_save_level(const gs_dual_memory_t *dmem)
+{ return dmem->save_level;
+}
+
+/* Return the innermost externally visible save object, i.e., */
+/* the innermost save with a non-zero ID. */
+alloc_save_t *
+alloc_save_current(const gs_dual_memory_t *dmem)
+{ alloc_save_t *save = dmem->space_local->saved;
+ while ( save != 0 && save->id == 0 )
+ save = save->state.saved;
+ return save;
+}
+
+/* Test whether a reference would be invalidated by a restore. */
+bool
+alloc_is_since_save(const void *vptr, const alloc_save_t *save)
+{
+#define ptr ((const char *)vptr)
+
+ /* A reference postdates a save iff it is in a chunk allocated */
+ /* since the save (including the carried-over inner chunk). */
+
+ const gs_dual_memory_t *dmem = save->dmem;
+ register const gs_ref_memory_t *mem = dmem->space_local;
+
+ if_debug2('U', "[U]is_since_save 0x%lx, 0x%lx:\n",
+ (ulong)ptr, (ulong)save);
+ if ( mem->saved == 0 )
+ { /* This is a special case, the final 'restore' from */
+ /* alloc_restore_all. */
+ return true;
+ }
+
+ /* Check against chunks allocated since the save. */
+ /* (There may have been intermediate saves as well.) */
+ for ( ; ; mem = &mem->saved->state )
+ { const chunk_t *cp;
+ if_debug1('U', "[U]checking mem=0x%lx\n", (ulong)mem);
+ for ( cp = mem->cfirst; cp != 0; cp = cp->cnext )
+ { if ( ptr_is_within_chunk(ptr, cp) )
+ { if_debug3('U', "[U+]in new chunk 0x%lx: 0x%lx, 0x%lx\n",
+ (ulong)cp,
+ (ulong)cp->cbase, (ulong)cp->cend);
+ return true;
+ }
+ if_debug1('U', "[U-]not in 0x%lx\n", (ulong)cp);
+ }
+ if ( mem->saved == save )
+ { /* We've checked all the more recent saves, */
+ /* must be OK. */
+ break;
+ }
+ }
+
+ /* If we're about to do a global restore (save level = 1), */
+ /* we also have to check the global save. */
+ /* Global saves can't be nested, which makes things easy. */
+ if ( dmem->save_level == 1 &&
+ (mem = dmem->space_global) != dmem->space_local
+ )
+ { const chunk_t *cp;
+ if_debug1('U', "[U]checking global mem=0x%lx\n", (ulong)mem);
+ for ( cp = mem->cfirst; cp != 0; cp = cp->cnext )
+ if ( ptr_is_within_chunk(ptr, cp) )
+ { if_debug3('U', "[U+] new chunk 0x%lx: 0x%lx, 0x%lx\n",
+ (ulong)cp, (ulong)cp->cbase, (ulong)cp->cend);
+ return true;
+ }
+ }
+
+ return false;
+
+#undef ptr
+}
+
+/* Test whether a name would be invalidated by a restore. */
+bool
+alloc_name_is_since_save(const ref *pnref, const alloc_save_t *save)
+{ const name *pname;
+ if ( !save->restore_names )
+ return false;
+ pname = pnref->value.pname;
+ if ( pname->foreign_string )
+ return false;
+ return alloc_is_since_save(pname->string_bytes, save);
+}
+bool
+alloc_name_index_is_since_save(uint nidx, const alloc_save_t *save)
+{ ref nref;
+ nref.value.pname = name_index_ptr(nidx);
+ return alloc_name_is_since_save(&nref, save);
+}
+
+/* Check whether any names have been created since a given save */
+/* that might be released by the restore. */
+bool
+alloc_any_names_since_save(const alloc_save_t *save)
+{ return save->restore_names;
+}
+
+/* Get the saved state with a given ID. */
+alloc_save_t *
+alloc_find_save(const gs_dual_memory_t *dmem, ulong sid)
+{ alloc_save_t *sprev = dmem->space_local->saved;
+ if ( sid == 0 )
+ return 0; /* invalid id */
+ while ( sprev != 0 )
+ { if ( sprev->id == sid )
+ return sprev;
+ sprev = sprev->state.saved;
+ }
+ return 0;
+}
+
+/* Get the client data from a saved state. */
+void *
+alloc_save_client_data(const alloc_save_t *save)
+{ return save->client_data;
+}
+
+/*
+ * Do one step of restoring the state. The client is responsible for
+ * calling alloc_find_save to get the save object, and for ensuring that
+ * there are no surviving pointers for which alloc_is_since_save is true.
+ * Return true if the argument was the innermost save, in which case
+ * this is the last (or only) step.
+ * Note that "one step" may involve multiple internal steps,
+ * if this is the outermost restore (which requires restoring both local
+ * and global VM) or if we created extra save levels to reduce scanning.
+ */
+private void restore_finalize(P1(gs_ref_memory_t *));
+private void restore_space(P1(gs_ref_memory_t *));
+bool
+alloc_restore_state_step(alloc_save_t *save)
+{ gs_dual_memory_t *dmem = save->dmem;
+ gs_ref_memory_t *mem = dmem->space_local;
+ alloc_save_t *sprev;
+
+ /* Do one (externally visible) step of restoring the state. */
+ do
+ { ulong sid;
+ sprev = mem->saved;
+ sid = sprev->id;
+ restore_finalize(mem); /* finalize objects */
+ restore_resources(sprev, mem); /* release other resources */
+ restore_space(mem); /* release memory */
+ if ( sid != 0 )
+ { dmem->save_level--;
+ break;
+ }
+ }
+ while ( sprev != save );
+
+ if ( dmem->save_level == 0 )
+ { /* This is the outermost save, which might also */
+ /* need to restore global VM. */
+ mem = dmem->space_global;
+ if ( mem != dmem->space_local && mem->saved != 0 )
+ { restore_finalize(mem);
+ restore_resources(mem->saved, mem);
+ restore_space(mem);
+ }
+ set_not_in_save(dmem);
+ }
+ else
+ { /* Set the l_new attribute in all slots that are now new. */
+ save_set_new(mem, true);
+ }
+
+ return sprev == save;
+}
+/* Restore the memory of one space, by undoing changes and freeing */
+/* memory allocated since the save. */
+private void
+restore_space(gs_ref_memory_t *mem)
+{ alloc_save_t *save = mem->saved;
+ alloc_save_t saved;
+
+ print_save("restore", mem->space, save);
+
+ /* Undo changes since the save. */
+ { register alloc_change_t *cp = mem->changes;
+ while ( cp )
+ {
+#ifdef DEBUG
+if ( gs_debug_c('U') )
+ { dprintf("[U]restore");
+ alloc_save_print(cp, true);
+ }
+#endif
+ if ( r_is_packed(&cp->contents) )
+ *cp->where = *(ref_packed *)&cp->contents;
+ else
+ ref_assign_inline((ref *)cp->where, &cp->contents);
+ cp = cp->next;
+ }
+ }
+
+ /* Free memory allocated since the save. */
+ /* Note that this frees all chunks except the inner one */
+ /* belonging to this level. */
+ saved = *save;
+ restore_free(mem);
+
+ /* Restore the allocator state. */
+ { int num_contexts = mem->num_contexts; /* don't restore */
+ *mem = saved.state;
+ mem->num_contexts = num_contexts;
+ }
+ alloc_open_chunk(mem);
+
+ /* Make the allocator current if it was current before the save. */
+ if ( saved.is_current )
+ { gs_dual_memory_t *dmem = saved.dmem;
+ dmem->current = mem;
+ dmem->current_space = mem->space;
+ }
+}
+
+/* Restore to the initial state, releasing all resources. */
+/* The allocator is no longer usable after calling this routine! */
+void
+alloc_restore_all(gs_dual_memory_t *dmem)
+{
+ /* Restore to a state outside any saves. */
+ while ( dmem->save_level != 0 )
+ discard(alloc_restore_state_step(dmem->space_local->saved));
+
+ /* Finalize memory. */
+ restore_finalize(dmem->space_local);
+ { gs_ref_memory_t *mem = dmem->space_global;
+ if ( mem != dmem->space_local && mem->num_contexts == 1 )
+ restore_finalize(mem);
+ }
+ restore_finalize(dmem->space_system);
+
+ /* Release resources other than memory, using fake */
+ /* save and memory objects. */
+ { alloc_save_t empty_save;
+ empty_save.dmem = dmem;
+ empty_save.restore_names = false; /* don't bother to release */
+ restore_resources(&empty_save, NULL);
+ }
+
+ /* Finally, release memory. */
+ restore_free(dmem->space_local);
+ { gs_ref_memory_t *mem = dmem->space_global;
+ if ( mem != dmem->space_local )
+ { if ( !--(mem->num_contexts) )
+ restore_free(mem);
+ }
+ }
+ restore_free(dmem->space_system);
+
+}
+
+/*
+ * Finalize objects that will be freed by a restore.
+ * Note that we must temporarily disable the freeing operations
+ * of the allocator while doing this.
+ */
+private void
+restore_finalize(gs_ref_memory_t *mem)
+{ chunk_t *cp;
+
+ alloc_close_chunk(mem);
+ gs_enable_free((gs_memory_t *)mem, false);
+ for ( cp = mem->clast; cp != 0; cp = cp->cprev )
+ { SCAN_CHUNK_OBJECTS(cp)
+ DO_ALL
+ struct_proc_finalize((*finalize)) =
+ pre->o_type->finalize;
+ if ( finalize != 0 )
+ { if_debug2('u', "[u]restore finalizing %s 0x%lx\n",
+ struct_type_name_string(pre->o_type),
+ (ulong)(pre + 1));
+ (*finalize)(pre + 1);
+ }
+ END_OBJECTS_SCAN
+ }
+ gs_enable_free((gs_memory_t *)mem, true);
+}
+
+/* Release resources for a restore */
+private void
+restore_resources(alloc_save_t *sprev, gs_ref_memory_t *mem)
+{
+ /* Close inaccessible files. */
+ file_restore(sprev, (const gs_memory_t *)mem);
+
+ /* Remove entries from font and character caches. */
+ font_restore(sprev);
+
+ /* Adjust the name table. */
+ if ( sprev->restore_names )
+ name_restore(sprev);
+}
+
+/* Release memory for a restore. */
+private void
+restore_free(gs_ref_memory_t *mem)
+{ /* Free chunks allocated since the save. */
+ chunk_t *cp;
+ chunk_t *csucc;
+
+ /* Free the chunks in reverse order, to encourage LIFO behavior. */
+ /* Don't free the chunk holding the allocator itself! */
+ for ( cp = mem->clast; cp != 0; cp = csucc )
+ { csucc = cp->cprev; /* save before freeing */
+ if ( cp->cbase + sizeof(obj_header_t) != (byte *)mem )
+ alloc_free_chunk(cp, mem);
+ }
+}
+
+/* Forget a save, by merging this level with the next outer one. */
+private void combine_space(P1(gs_ref_memory_t *));
+private void forget_changes(P1(gs_ref_memory_t *));
+void
+alloc_forget_save(alloc_save_t *save)
+{ gs_dual_memory_t *dmem = save->dmem;
+ gs_ref_memory_t *mem = dmem->space_local;
+ alloc_save_t *sprev;
+
+ print_save("forget_save", mem->space, save);
+
+ /* Iteratively combine the current level with the previous one. */
+ do
+ { sprev = mem->saved;
+ if ( sprev->id != 0 )
+ dmem->save_level--;
+ if ( dmem->save_level != 0 )
+ { alloc_change_t *chp = mem->changes;
+ save_set_new(&sprev->state, true);
+ /* Concatenate the changes chains. */
+ if ( chp == 0 )
+ mem->changes = sprev->state.changes;
+ else
+ { while ( chp->next != 0 )
+ chp = chp->next;
+ chp->next = sprev->state.changes;
+ }
+ file_forget_save(sprev);
+ combine_space(mem); /* combine memory */
+ }
+ else
+ { forget_changes(mem);
+ save_set_new(mem, false);
+ file_forget_save(sprev);
+ combine_space(mem); /* combine memory */
+ /* This is the outermost save, which might also */
+ /* need to combine global VM. */
+ mem = dmem->space_global;
+ if ( mem != dmem->space_local && mem->saved != 0 )
+ { forget_changes(mem);
+ save_set_new(mem, false);
+ combine_space(mem);
+ }
+ set_not_in_save(dmem);
+ break; /* must be outermost */
+ }
+ }
+ while ( sprev != save );
+}
+/* Combine the chunks of the next outer level with those of the current one, */
+/* and free the bookkeeping structures. */
+private void
+combine_space(gs_ref_memory_t *mem)
+{ alloc_save_t *saved = mem->saved;
+ gs_ref_memory_t *omem = &saved->state;
+ chunk_t *cp;
+ chunk_t *csucc;
+
+ alloc_close_chunk(mem);
+ for ( cp = mem->cfirst; cp != 0; cp = csucc )
+ { csucc = cp->cnext; /* save before relinking */
+ if ( cp->outer == 0 )
+ alloc_link_chunk(cp, omem);
+ else
+ { chunk_t *outer = cp->outer;
+ outer->inner_count--;
+ if ( mem->pcc == cp )
+ mem->pcc = outer;
+ if ( mem->cfreed.cp == cp )
+ mem->cfreed.cp = outer;
+ /* "Free" the header of the inner chunk, */
+ /* and any immediately preceding gap left by */
+ /* the GC having compacted the outer chunk. */
+ { obj_header_t *hp = (obj_header_t *)outer->cbot;
+ hp->o_large = 0;
+ hp->o_size = (char *)(cp->chead + 1)
+ - (char *)(hp + 1);
+ hp->o_type = &st_bytes;
+ /* The following call is probably not safe. */
+#if 0 /* **************** */
+ gs_free_object((gs_memory_t *)mem,
+ hp + 1, "combine_space(header)");
+#endif /* **************** */
+ }
+ /* Update the outer chunk's allocation pointers. */
+ outer->cbot = cp->cbot;
+ outer->rcur = cp->rcur;
+ outer->rtop = cp->rtop;
+ outer->ctop = cp->ctop;
+ outer->has_refs |= cp->has_refs;
+ gs_free_object(mem->parent, cp,
+ "combine_space(inner)");
+ }
+ }
+ /* Update relevant parts of allocator state. */
+ mem->cfirst = omem->cfirst;
+ mem->clast = omem->clast;
+ mem->allocated += omem->allocated;
+ mem->gc_allocated += omem->allocated;
+ mem->freed_lost += omem->freed_lost;
+ mem->saved = omem->saved;
+ mem->previous_status = omem->previous_status;
+ { /* Concatenate free lists. */
+ int i;
+ for ( i = 0; i < num_freelists; i++ )
+ { obj_header_t *olist = omem->freelists[i];
+ obj_header_t *list = mem->freelists[i];
+ if ( olist == 0 )
+ ;
+ else if ( list == 0 )
+ mem->freelists[i] = olist;
+ else
+ { while ( *(obj_header_t **)list != 0 )
+ list = *(obj_header_t **)list;
+ *(obj_header_t **)list = olist;
+ }
+ }
+ }
+ gs_free_object((gs_memory_t *)mem, saved, "combine_space(saved)");
+ alloc_open_chunk(mem);
+}
+/* Free the changes chain for a level 0 .forgetsave, */
+/* resetting the l_new flag in the changed refs. */
+private void
+forget_changes(gs_ref_memory_t *mem)
+{ register alloc_change_t *chp = mem->changes;
+ alloc_change_t *next;
+ for ( ; chp; chp = next )
+ { ref_packed *prp = chp->where;
+ if_debug1('U', "[U]forgetting change 0x%lx\n", (ulong)chp);
+ if ( !r_is_packed(prp) )
+ r_clear_attrs((ref *)prp, l_new);
+ next = chp->next;
+ gs_free_object((gs_memory_t *)mem, chp, "forget_changes");
+ }
+ mem->changes = 0;
+}
+
+/* ------ Internal routines ------ */
+
+/* Set or reset the l_new attribute in every relevant slot. */
+/* This includes every slot on the current change chain, */
+/* and every (ref) slot allocated at this save level. */
+/* Return the number of bytes of data scanned. */
+private long
+save_set_new(gs_ref_memory_t *mem, bool to_new)
+{
+ long scanned = 0;
+
+ /* Handle the change chain. */
+ save_set_new_changes(mem, to_new);
+
+ /* Handle newly allocated ref objects. */
+ SCAN_MEM_CHUNKS(mem, cp)
+ { if ( cp->has_refs )
+ { bool has_refs = false;
+ SCAN_CHUNK_OBJECTS(cp)
+ DO_ALL
+ if_debug3('U', "[U]set_new scan(0x%lx(%lu), %d)\n",
+ (ulong)pre, size, to_new);
+ if ( pre->o_type == &st_refs )
+ { /* These are refs, scan them. */
+ ref_packed *prp = (ref_packed *)(pre + 1);
+ ref_packed *next = (ref_packed *)((char *)prp + size);
+ if_debug2('U', "[U]refs 0x%lx to 0x%lx\n",
+ (ulong)prp, (ulong)next);
+ has_refs = true;
+ scanned += size;
+ /* We know that every block of refs ends with */
+ /* a full-size ref, so we only need the end check */
+ /* when we encounter one of those. */
+#define rp ((ref *)prp)
+ if ( to_new )
+ while ( 1 )
+ { if ( r_is_packed(prp) )
+ prp++;
+ else
+ { rp->tas.type_attrs |= l_new;
+ prp += packed_per_ref;
+ if ( prp >= next )
+ break;
+ }
+ }
+ else
+ while ( 1 )
+ { if ( r_is_packed(prp) )
+ prp++;
+ else
+ { rp->tas.type_attrs &= ~l_new;
+ prp += packed_per_ref;
+ if ( prp >= next )
+ break;
+ }
+ }
+#undef rp
+ }
+ else
+ scanned += sizeof(obj_header_t);
+ END_OBJECTS_SCAN
+ cp->has_refs = has_refs;
+ }
+ }
+ END_CHUNKS_SCAN
+ if_debug2('u', "[u]set_new (%s) scanned %ld\n",
+ (to_new ? "restore" : "save"), scanned);
+ return scanned;
+}
+
+/* Set or reset the l_new attribute on the changes chain. */
+private void
+save_set_new_changes(gs_ref_memory_t *mem, bool to_new)
+{ register alloc_change_t *chp = mem->changes;
+ register uint new = (to_new ? l_new : 0);
+ for ( ; chp; chp = chp->next )
+ { ref_packed *prp = chp->where;
+ if_debug2('U', "[U]set_new(0x%lx, %d)\n",
+ (ulong)prp, new);
+ if ( !r_is_packed(prp) )
+#define rp ((ref *)prp)
+ rp->tas.type_attrs =
+ (rp->tas.type_attrs & ~l_new) + new;
+#undef rp
+ }
+}
diff --git a/pstoraster/isave.h b/pstoraster/isave.h
new file mode 100644
index 000000000..8121f238c
--- /dev/null
+++ b/pstoraster/isave.h
@@ -0,0 +1,111 @@
+/* Copyright (C) 1991, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* isave.h */
+/* Interface to Ghostscript save/restore machinery */
+/* Requires imemory.h */
+
+/*
+ * According to the PostScript language definition, save objects are simple,
+ * not composite. Consequently, we cannot use their natural representation,
+ * namely a t_struct pointing to an alloc_save_t, since we aren't willing to
+ * allocate them all in global VM and rely on garbage collection to clean
+ * them up. Instead, we assign each one a unique "save ID", and store this
+ * in the alloc_save_t object. Mapping the number to the object requires
+ * at most searching the local save chain for the current gs_dual_memory_t,
+ * and this approach means we don't have to do anything to invalidate
+ * save objects when we do a restore.
+ */
+#ifndef alloc_save_t_DEFINED /* also in inamedef.h */
+typedef struct alloc_save_s alloc_save_t;
+# define alloc_save_t_DEFINED
+#endif
+
+/* Initialize the save machinery. */
+extern void alloc_save_init(P1(gs_dual_memory_t *));
+
+/* Map a save ID to its save object. Return 0 if the ID is invalid. */
+alloc_save_t *alloc_find_save(P2(const gs_dual_memory_t *, ulong));
+
+/* Save the state. Return 0 if we can't allocate the save object, */
+/* otherwise return the save ID. The second argument is a client data */
+/* pointer, assumed to point to an object. */
+ulong alloc_save_state(P2(gs_dual_memory_t *, void *));
+
+/* Get the client pointer passed to alloc_saved_state. */
+void *alloc_save_client_data(P1(const alloc_save_t *));
+
+/* Return the current level of save nesting. */
+int alloc_save_level(P1(const gs_dual_memory_t *));
+
+/* Return the innermost externally visible save object. */
+alloc_save_t *alloc_save_current(P1(const gs_dual_memory_t *));
+
+/* Check whether a pointer refers to an object allocated since a given save. */
+bool alloc_is_since_save(P2(const void *, const alloc_save_t *));
+
+/* Check whether a name was created since a given save. */
+bool alloc_name_is_since_save(P2(const ref *, const alloc_save_t *));
+bool alloc_name_index_is_since_save(P2(uint, const alloc_save_t *));
+
+/* Check whether any names have been created since a given save */
+/* that might be released by the restore. */
+bool alloc_any_names_since_save(P1(const alloc_save_t *));
+
+/* Do one step of restoring the state. Return true if the argument */
+/* was the innermost save, in which case this is the last (or only) step. */
+/* Assume the caller obtained the argument by calling alloc_find_save; */
+/* if this is the case, the operation cannot fail. */
+bool alloc_restore_state_step(P1(alloc_save_t *));
+
+/* Forget a save -- like committing a transaction (restore is like */
+/* aborting a transaction). Assume the caller obtained the argument */
+/* by calling alloc_find_save. Note that forgetting a save does not */
+/* require checking pointers for recency. */
+void alloc_forget_save(P1(alloc_save_t *));
+
+/* Release all memory -- like doing a restore "past the bottom". */
+void alloc_restore_all(P1(gs_dual_memory_t *));
+
+/*
+ * If we are in a save, we want to save the old contents if l_new is
+ * not set; if we are not in a save, we never want to save old contents.
+ * We can test this quickly with a single mask that is l_new if we are
+ * in a save, and -1 if we are not, since type_attrs of a valid ref
+ * cannot be 0; this is the test_mask in a gs_dual_memory_t. Similarly,
+ * we want to set the l_new bit in newly allocated objects iff we are in
+ * a save; this is the new_mask in a gs_dual_memory_t.
+ *
+ * We have to pass the pointer to the containing object to alloc_save_change
+ * for two reasons:
+ *
+ * - We need to know which VM the containing object is in, so we can
+ * know on which chain of saved changes to put the new change.
+ *
+ * - We need to know whether the object is an array of refs (which
+ * includes dictionaries) or a struct, so we can properly trace and
+ * relocate the pointer to it from the change record during garbage
+ * collection.
+ */
+int alloc_save_change(P4(gs_dual_memory_t *, const ref *pcont,
+ ref_packed *ptr, client_name_t cname));
diff --git a/pstoraster/iscan.c b/pstoraster/iscan.c
new file mode 100644
index 000000000..6953cd255
--- /dev/null
+++ b/pstoraster/iscan.c
@@ -0,0 +1,1034 @@
+/* Copyright (C) 1989, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* iscan.c */
+/* Token scanner for Ghostscript interpreter */
+#include "ghost.h"
+#include "memory_.h"
+#include "stream.h"
+#include "errors.h"
+#include "files.h" /* for fptr */
+#include "ialloc.h"
+#include "idict.h" /* for //name lookup */
+#include "dstack.h" /* ditto */
+#include "ilevel.h"
+#include "iname.h"
+#include "ipacked.h"
+#include "iparray.h"
+#include "strimpl.h" /* for string decoding */
+#include "sfilter.h" /* ditto */
+#include "ostack.h" /* for accumulating proc bodies; */
+ /* must precede iscan.h */
+#include "iscan.h" /* defines interface */
+#include "iscannum.h"
+#include "istream.h"
+#include "istruct.h" /* for gs_reloc_refs */
+#include "iutil.h"
+#include "ivmspace.h"
+#include "store.h"
+#include "scanchar.h"
+
+/* Array packing flag */
+ref ref_array_packing; /* t_boolean */
+/* Binary object format flag. This will never be set non-zero */
+/* unless the binary token feature is enabled. */
+ref ref_binary_object_format; /* t_integer */
+#define recognize_btokens()\
+ (ref_binary_object_format.value.intval != 0 && level2_enabled)
+
+/* Procedure for binary tokens. Set at initialization if Level 2 */
+/* features are included; only called if recognize_btokens() is true. */
+/* Returns 0 or scan_BOS on success, <0 on failure. */
+int (*scan_btoken_proc)(P3(stream *, ref *, scanner_state *)) = NULL;
+
+/* Stream template for scanning ASCII85 literals. */
+/* Set at initialization if Level 2 features are included. */
+const stream_template _ds *scan_ascii85_template = NULL;
+
+#ifdef DEBUG
+/* Dummy comment processing procedure for testing. */
+private int
+no_comment_proc(const byte *str, uint len)
+{ return 0;
+}
+#endif
+
+/* Procedure for handling DSC comments if desired. */
+/* Set at initialization if a DSC handling module is included. */
+int (*scan_dsc_proc)(P2(const byte *, uint)) = NULL;
+
+/* Procedure for handling all comments if desired. */
+/* Set at initialization if a comment handling module is included. */
+/* If both scan_comment_proc and scan_dsc_proc are set, */
+/* scan_comment_proc is called only for non-DSC comments. */
+int (*scan_comment_proc)(P2(const byte *, uint)) = NULL;
+
+/*
+ * Level 2 includes some changes in the scanner:
+ * - \ is always recognized in strings, regardless of the data source;
+ * - << and >> are legal tokens;
+ * - <~ introduces an ASCII85 encoded string (terminated by ~>);
+ * - Character codes above 127 introduce binary objects.
+ * We explicitly enable or disable these changes here.
+ */
+#define scan_enable_level2 level2_enabled /* from ilevel.h */
+
+/* ------ Dynamic strings ------ */
+
+/* Begin collecting a dynamically allocated string. */
+#define dynamic_init(pda, mem)\
+ ((pda)->is_dynamic = false,\
+ (pda)->limit = (pda)->buf + da_buf_size,\
+ (pda)->next = (pda)->base = (pda)->buf,\
+ (pda)->memory = (mem))
+
+/* Free a dynamic string. */
+private void
+dynamic_free(da_ptr pda)
+{ if ( pda->is_dynamic )
+ gs_free_string(pda->memory, pda->base, da_size(pda), "scanner");
+}
+
+/* Resize a dynamic string. */
+/* If the allocation fails, return e_VMerror; otherwise, return 0. */
+private int
+dynamic_resize(register da_ptr pda, uint new_size)
+{ uint old_size = da_size(pda);
+ uint pos = pda->next - pda->base;
+ gs_memory_t *mem = pda->memory;
+ byte *base;
+ if ( pda->is_dynamic )
+ { base = gs_resize_string(mem, pda->base, old_size,
+ new_size, "scanner");
+ if ( base == 0 )
+ return_error(e_VMerror);
+ }
+ else /* switching from static to dynamic */
+ { base = gs_alloc_string(mem, new_size, "scanner");
+ if ( base == 0 )
+ return_error(e_VMerror);
+ memcpy(base, pda->base, min(old_size, new_size));
+ pda->is_dynamic = true;
+ }
+ pda->base = base;
+ pda->next = base + pos;
+ pda->limit = base + new_size;
+ return 0;
+}
+
+/* Grow a dynamic string. */
+/* Return 0 if the allocation failed, the new 'next' ptr if OK. */
+/* Return 0 or an error code, updating pda->next to point to the first */
+/* available byte after growing. */
+private int
+dynamic_grow(register da_ptr pda, byte *next, uint max_size)
+{ uint old_size = da_size(pda);
+ uint new_size = (old_size < 10 ? 20 :
+ old_size >= (max_size >> 1) ? max_size :
+ old_size << 1);
+ int code;
+ pda->next = next;
+ if ( old_size == max_size )
+ return_error(e_limitcheck);
+ while ( (code = dynamic_resize(pda, new_size)) < 0 &&
+ new_size > old_size
+ )
+ { /* Try trimming down the requested new size. */
+ new_size -= (new_size - old_size + 1) >> 1;
+ }
+ return code;
+}
+
+/* Ensure that a dynamic string is either on the heap or in the */
+/* private buffer. */
+private void
+dynamic_save(da_ptr pda)
+{ if ( !pda->is_dynamic && pda->base != pda->buf )
+ { memcpy(pda->buf, pda->base, da_size(pda));
+ pda->next = pda->buf + da_size(pda);
+ pda->base = pda->buf;
+ }
+}
+
+/* Finish collecting a dynamic string. */
+private int
+dynamic_make_string(ref *pref, da_ptr pda, byte *next)
+{ uint size = (pda->next = next) - pda->base;
+ int code = dynamic_resize(pda, size);
+ if ( code < 0 )
+ return code;
+ make_tasv_new(pref, t_string,
+ a_all | imemory_space((gs_ref_memory_t *)pda->memory),
+ size, bytes, pda->base);
+ return 0;
+}
+
+/* ------ Main scanner ------ */
+
+/* GC procedures */
+#define ssptr ((scanner_state *)vptr)
+#define ssarray ssptr->s_ss.binary.bin_array
+private CLEAR_MARKS_PROC(scanner_clear_marks) {
+ r_clear_attrs(&ssarray, l_mark);
+}
+private ENUM_PTRS_BEGIN(scanner_enum_ptrs) return 0;
+ case 0:
+ *pep = 0;
+ if ( ssptr->s_scan_type == scanning_none ||
+ !ssptr->s_da.is_dynamic
+ )
+ break;
+ ssptr->s_da.str.data = ssptr->s_da.base;
+ ssptr->s_da.str.size = da_size(&ssptr->s_da);
+ ENUM_RETURN_STRING_PTR(scanner_state, s_da.str);
+ case 1:
+ if ( ssptr->s_scan_type != scanning_binary )
+ return 0;
+ *pep = &ssarray;
+ return ptr_ref_type;
+ENUM_PTRS_END
+private RELOC_PTRS_BEGIN(scanner_reloc_ptrs) {
+ if ( ssptr->s_scan_type != scanning_none && ssptr->s_da.is_dynamic )
+ { RELOC_STRING_PTR(scanner_state, s_da.str);
+ ssptr->s_da.limit = ssptr->s_da.str.data + ssptr->s_da.str.size;
+ ssptr->s_da.next = ssptr->s_da.str.data + (ssptr->s_da.next - ssptr->s_da.base);
+ ssptr->s_da.base = ssptr->s_da.str.data;
+ }
+ if ( ssptr->s_scan_type == scanning_binary )
+ { gs_reloc_refs((ref_packed *)&ssarray,
+ (ref_packed *)(&ssarray + 1),
+ gcst);
+ r_clear_attrs(&ssarray, l_mark);
+ }
+} RELOC_PTRS_END
+#undef ssptr
+/* Structure type */
+public_st_scanner_state();
+
+/* Initialize the scanner. */
+void
+scan_init(void)
+{ make_false(&ref_array_packing);
+ make_int(&ref_binary_object_format, 0);
+}
+
+/* Handle a scan_Refill return from scan_token. */
+/* This may return o_push_estack, 0 (meaning just call scan_token again), */
+/* or an error code. */
+int
+scan_handle_refill(const ref *fop, scanner_state *sstate, bool save,
+ bool push_file, int (*cont)(P1(os_ptr)))
+{ stream *s = fptr(fop);
+ uint avail = sbufavailable(s);
+ int status;
+
+ if ( s->end_status == EOFC )
+ { /* More data needed, but none available, */
+ /* so this is a syntax error. */
+ return_error(e_syntaxerror);
+ }
+ status = s_process_read_buf(s);
+ if ( sbufavailable(s) > avail )
+ return 0;
+ if ( status == 0 )
+ status = s->end_status;
+ switch ( status )
+ {
+ case EOFC:
+ /* We just discovered that we're at EOF. */
+ /* Let the caller find this out. */
+ return 0;
+ case ERRC:
+ return_error(e_ioerror);
+ case INTC:
+ case CALLC:
+ { ref rstate[2];
+ scanner_state *pstate;
+ int nstate = (push_file ? 2 : 1);
+
+ if ( save )
+ { pstate =
+ ialloc_struct(scanner_state, &st_scanner_state,
+ "scan_handle_refill");
+ if ( pstate == 0 )
+ return_error(e_VMerror);
+ *pstate = *sstate;
+ }
+ else
+ pstate = sstate;
+ /* If push_file is true, we want to push the file on the */
+ /* o-stack before the state, for the continuation proc. */
+ /* Since the refs passed to s_handle_read_exception */
+ /* are pushed on the e-stack, we must ensure they are */
+ /* literal, and also pass them in the opposite order! */
+ make_istruct(&rstate[0], 0, pstate);
+ rstate[1] = *fop;
+ r_clear_attrs(&rstate[1], a_executable);
+ return s_handle_read_exception(status, fop,
+ rstate, nstate, cont);
+ }
+ }
+ /* No more data available, but no exception. How can this be? */
+ lprintf("Can't refill scanner input buffer!");
+ return_error(e_Fatal);
+}
+
+/* Handle a comment. The 'saved' argument is needed only for */
+/* tracing printout. */
+private int
+scan_comment(const byte *base, const byte *end, bool saved)
+{ uint len = (uint)(end - base);
+#ifdef DEBUG
+ const char *sstr = (saved ? ">" : "");
+#endif
+ if ( len > 1 && base[1] == '%' && scan_dsc_proc != NULL )
+ {
+#ifdef DEBUG
+ if ( gs_debug_c('%') )
+ { dprintf2("[%%%%%s%c]", sstr, (len >= 3 ? '+' : '-'));
+ fwrite(base, 1, len, dstderr);
+ dputs("\n");
+ }
+#endif
+ if ( end - base >= 3 )
+ return (*scan_dsc_proc)(base, len);
+ }
+ else if ( scan_comment_proc != NULL )
+ {
+#ifdef DEBUG
+ if ( gs_debug_c('%') )
+ { dprintf2("[%% %s%c]", sstr, (len >= 2 ? '+' : '-'));
+ fwrite(base, 1, len, dstderr);
+ dputs("\n");
+ }
+#endif
+ if ( end - base >= 2 )
+ return (*scan_comment_proc)(base, len);
+ }
+ return 0;
+}
+
+/* Read a token from a string. */
+/* Update the string if succesful. */
+int
+scan_string_token(ref *pstr, ref *pref)
+{ stream st;
+ stream *s = &st;
+ scanner_state state;
+ int code;
+
+ if ( !r_has_attr(pstr, a_read) )
+ return_error(e_invalidaccess);
+ sread_string(s, pstr->value.bytes, r_size(pstr));
+ scanner_state_init(&state, true);
+ switch ( code = scan_token(s, pref, &state) )
+ {
+ case 0: /* read a token */
+ case scan_BOS:
+ { uint pos = stell(s);
+ pstr->value.bytes += pos;
+ r_dec_size(pstr, pos);
+ } break;
+ case scan_Refill: /* error */
+ code = gs_note_error(e_syntaxerror);
+ default: /* error */
+ ;
+ }
+ return code;
+}
+
+/*
+ * Read a token from a stream.
+ * Return 0 if an ordinary token was read,
+ * scan_BOS for a binary object sequence, scan_EOF for end-of-stream,
+ * scan_Refill if more data needed, or a (negative) error code.
+ * If the token required a terminating character (i.e., was a name or
+ * number) and the next character was whitespace, read and discard
+ * that character. Note that the state is relevant for e_VMerror
+ * as well as for scan_Refill.
+ */
+int
+scan_token(register stream *s, ref *pref, scanner_state *pstate)
+{ ref *myref = pref;
+ int retcode = 0;
+ register int c;
+ s_declare_inline(s, sptr, endptr);
+#define scan_begin_inline() s_begin_inline(s, sptr, endptr)
+#define scan_getc() sgetc_inline(s, sptr, endptr)
+#define scan_putback() sputback_inline(s, sptr, endptr)
+#define scan_end_inline() s_end_inline(s, sptr, endptr)
+ const byte *newptr;
+ byte *daptr;
+#define sreturn(code)\
+ { retcode = gs_note_error(code); goto sret; }
+#define sreturn_no_error(code)\
+ { scan_end_inline(); return(code); }
+#define if_not_spush1()\
+ if ( osp < ostop ) osp++;\
+ else if ( (retcode = ref_stack_push(&o_stack, 1)) >= 0 )\
+ ;\
+ else
+#define spop1()\
+ if ( osp >= osbot ) osp--;\
+ else ref_stack_pop(&o_stack, 1)
+ int max_name_ctype =
+ (recognize_btokens() ? ctype_name : ctype_btoken);
+#define scan_sign(sign, ptr)\
+ switch ( *ptr ) {\
+ case '-': sign = -1; ptr++; break;\
+ case '+': sign = 1; ptr++; break;\
+ default: sign = 0;\
+ }
+#define ensure2_back(styp,nback)\
+ if ( sptr >= endptr ) { sptr -= nback; scan_type = styp; goto pause; }
+#define ensure2(styp) ensure2_back(styp, 1)
+ byte s1[2];
+ register const byte _ds *decoder = scan_char_decoder;
+ int status;
+ scanner_state sstate;
+#define pstack sstate.s_pstack
+#define pdepth sstate.s_pdepth
+#define scan_type sstate.s_scan_type
+#define da sstate.s_da
+#define name_type sstate.s_ss.s_name.s_name_type
+#define try_number sstate.s_ss.s_name.s_try_number
+ if ( pstate->s_pstack != 0 )
+ { if_not_spush1()
+ return retcode;
+ myref = osp;
+ }
+ /* Check whether we are resuming after an interruption. */
+ if ( pstate->s_scan_type != scanning_none )
+ { sstate = *pstate;
+ if ( !da.is_dynamic && da.base != da.buf )
+ { /* The da contains some self-referencing pointers. */
+ /* Fix them up now. */
+ uint size = da.next - da.base;
+ da.base = da.buf;
+ da.next = da.buf + size;
+ da.limit = da.buf + da_buf_size;
+ }
+ daptr = da.next;
+ switch ( scan_type )
+ {
+ case scanning_binary:
+ retcode = (*sstate.s_ss.binary.cont)(s, myref, &sstate);
+ scan_begin_inline();
+ if ( retcode == scan_Refill )
+ goto pause;
+ goto sret;
+ case scanning_comment:
+ scan_begin_inline();
+ goto cont_comment;
+ case scanning_name:
+ goto cont_name;
+ case scanning_string:
+ goto cont_string;
+ default:
+ return_error(e_Fatal);
+ }
+ }
+ /* Fetch any state variables that are relevant even if */
+ /* scan_type == scanning_none. */
+ pstack = pstate->s_pstack;
+ pdepth = pstate->s_pdepth;
+ scan_begin_inline();
+ /*
+ * Loop invariants:
+ * If pstack != 0, myref = osp, and *osp is a valid slot.
+ */
+top: c = scan_getc();
+ if_debug1('S', (c >= 32 && c <= 126 ? "`%c'" : c >= 0 ? "`\\%03o'" : "`%d'"), c);
+ switch ( c )
+ {
+ case ' ': case '\f': case '\t':
+ case char_CR: case char_EOL:
+ case char_NULL: case char_VT:
+ goto top;
+ case char_EOT:
+ case char_DOS_EOF:
+ case '[':
+ case ']':
+ s1[0] = (byte)c;
+ retcode = name_ref(s1, 1, myref, 1); /* can't fail */
+ r_set_attrs(myref, a_executable);
+ break;
+ case '<':
+ if ( scan_enable_level2 )
+ { ensure2(scanning_none);
+ c = scan_getc();
+ switch ( c )
+ {
+ case '<':
+ scan_putback();
+ name_type = 0;
+ try_number = false;
+ goto try_funny_name;
+ case '~':
+ s_A85D_init_inline(&sstate.s_ss.a85d);
+ sstate.s_ss.st.template =
+ scan_ascii85_template;
+ goto str;
+ }
+ scan_putback();
+ }
+ s_AXD_init_inline(&sstate.s_ss.axd);
+ sstate.s_ss.st.template = &s_AXD_template;
+str: scan_end_inline();
+ dynamic_init(&da, imemory);
+cont_string: for ( ; ; )
+ { stream_cursor_write w;
+ w.ptr = da.next - 1;
+ w.limit = da.limit - 1;
+ status = (*sstate.s_ss.st.template->process)
+ (&sstate.s_ss.st, &s->cursor.r, &w,
+ s->end_status == EOFC);
+ da.next = w.ptr + 1;
+ switch ( status )
+ {
+ case 0:
+ status = s->end_status;
+ if ( status < 0 )
+ { if ( status == EOFC )
+ sreturn(e_syntaxerror);
+ break;
+ }
+ s_process_read_buf(s);
+ continue;
+ case 1:
+ retcode = dynamic_grow(&da, da.next,
+ max_string_size);
+ if ( retcode == e_VMerror )
+ { scan_type = scanning_string;
+ goto suspend;
+ }
+ else if ( retcode < 0 )
+ sreturn(retcode);
+ continue;
+ }
+ break;
+ }
+ scan_begin_inline();
+ switch ( status )
+ {
+ default:
+ /*case ERRC:*/
+ sreturn(e_syntaxerror);
+ case INTC:
+ case CALLC:
+ scan_type = scanning_string;
+ goto pause;
+ case EOFC:
+ ;
+ }
+ retcode = dynamic_make_string(myref, &da, da.next);
+ if ( retcode < 0 ) /* VMerror */
+ { sputback(s); /* rescan ) */
+ scan_type = scanning_string;
+ goto suspend;
+ }
+ break;
+ case '(':
+ sstate.s_ss.pssd.from_string =
+ pstate->s_from_string && !scan_enable_level2;
+ s_PSSD_init_inline(&sstate.s_ss.pssd);
+ sstate.s_ss.st.template = &s_PSSD_template;
+ goto str;
+ case '{':
+ if ( pstack == 0 ) /* outermost procedure */
+ { if_not_spush1()
+ { scan_putback();
+ scan_type = scanning_none;
+ goto pause_ret;
+ }
+ pdepth = ref_stack_count_inline(&o_stack);
+ }
+ make_int(osp, pstack);
+ pstack = ref_stack_count_inline(&o_stack);
+ if_debug3('S', "[S{]d=%d, s=%d->%d\n",
+ pdepth, (int)osp->value.intval, pstack);
+ goto snext;
+ case '>':
+ if ( scan_enable_level2 )
+ { ensure2(scanning_none);
+ name_type = 0;
+ try_number = false;
+ goto try_funny_name;
+ }
+ /* falls through */
+ case ')':
+ sreturn(e_syntaxerror);
+ case '}':
+ if ( pstack == 0 )
+ sreturn(e_syntaxerror);
+ osp--;
+ { uint size = ref_stack_count_inline(&o_stack) - pstack;
+ ref arr;
+ if_debug4('S', "[S}]d=%d, s=%d->%ld, c=%d\n",
+ pdepth, pstack,
+ (pstack == pdepth ? 0 :
+ ref_stack_index(&o_stack, size)->value.intval),
+ size + pstack);
+ myref = (pstack == pdepth ? pref : &arr);
+ if ( ref_array_packing.value.boolval )
+ { retcode = make_packed_array(myref, &o_stack,
+ size, "scanner(packed)");
+ if ( retcode < 0 ) /* must be VMerror */
+ { osp++;
+ scan_putback();
+ scan_type = scanning_none;
+ goto pause_ret;
+ }
+ r_set_attrs(myref, a_executable);
+ }
+ else
+ { retcode = ialloc_ref_array(myref,
+ a_executable + a_all, size,
+ "scanner(proc)");
+ if ( retcode < 0 ) /* must be VMerror */
+ { osp++;
+ scan_putback();
+ scan_type = scanning_none;
+ goto pause_ret;
+ }
+ retcode = ref_stack_store(&o_stack, myref,
+ size, 0, 1, false, "scanner");
+ if ( retcode < 0 )
+ { ifree_ref_array(myref, "scanner(proc)");
+ sreturn(retcode);
+ }
+ ref_stack_pop(&o_stack, size);
+ }
+ if ( pstack == pdepth )
+ { /* This was the top-level procedure. */
+ spop1();
+ pstack = 0;
+ }
+ else
+ { if ( osp < osbot )
+ ref_stack_pop_block(&o_stack);
+ pstack = osp->value.intval;
+ *osp = arr;
+ goto snext;
+ }
+ }
+ break;
+ case '/':
+ ensure2(scanning_none);
+ c = scan_getc();
+ if ( c == '/' )
+ { name_type = 2;
+ c = scan_getc();
+ }
+ else
+ name_type = 1;
+ try_number = false;
+ switch ( decoder[c] )
+ {
+ case ctype_name:
+ default:
+ goto do_name;
+ case ctype_btoken:
+ if ( !recognize_btokens() ) goto do_name;
+ /* otherwise, an empty name */
+ case ctype_exception:
+ case ctype_space:
+ /*
+ * Amazingly enough, the Adobe implementations don't accept
+ * / or // followed by [, ], <<, or >>, so we do the same.
+ * (Older versions of our code had a ctype_other case here
+ * that handled these specially.)
+ */
+ case ctype_other:
+ da.base = da.limit = daptr = 0;
+ da.is_dynamic = false;
+ goto nx;
+ }
+ case '%':
+ { /* Scan as much as possible within the buffer. */
+ const byte *base = sptr;
+ const byte *end;
+ while ( ++sptr < endptr ) /* stop 1 char early */
+ switch ( *sptr )
+ {
+ case char_CR:
+ end = sptr;
+ if ( sptr[1] == char_EOL )
+ sptr++;
+cend: /* Check for externally processed comments. */
+ retcode = scan_comment(base, end, false);
+ if ( retcode < 0 )
+ goto sret;
+ goto top;
+ case char_EOL:
+ case '\f':
+ end = sptr;
+ goto cend;
+ }
+ /*
+ * We got to the end of the buffer while inside a comment.
+ * If there is a possibility that we must pass the comment
+ * to an external procedure, move what we have collected
+ * so far into a private buffer now.
+ */
+#define comment_line da.buf
+ --sptr;
+ comment_line[1] = 0;
+ if ( scan_comment_proc != NULL ||
+ ((sptr == base || base[1] == '%') &&
+ scan_dsc_proc != NULL)
+ )
+ { /* Could be an externally processable comment. */
+ uint len = sptr + 1 - base;
+ memcpy(comment_line, base, len);
+ daptr = comment_line + len;
+ }
+ else
+ { /* Not a DSC comment. */
+ daptr = comment_line + (max_comment_line + 1);
+ }
+ da.base = comment_line;
+ da.is_dynamic = false;
+ /* Enter here to continue scanning a comment. */
+ /* daptr must be set. */
+cont_comment: for ( ; ; )
+ { switch ( (c = scan_getc()) )
+ {
+ default:
+ if ( c < 0 )
+ switch ( c )
+ {
+ case INTC:
+ case CALLC:
+ da.next = daptr;
+ scan_type = scanning_comment;
+ goto pause;
+ case EOFC:
+ /*
+ * One would think that an EOF in a comment
+ * should be a syntax error, but there are
+ * quite a number of files that end that way.
+ */
+ goto end_comment;
+ default:
+ sreturn(e_syntaxerror);
+ }
+ if ( daptr < comment_line + max_comment_line )
+ *daptr++ = c;
+ continue;
+ case char_CR:
+ case char_EOL:
+ case '\f':
+end_comment: retcode = scan_comment(comment_line, daptr, true);
+ if ( retcode < 0 )
+ goto sret;
+ goto top;
+ }
+ }
+#undef comment_line
+ /*NOTREACHED*/
+ }
+ case EOFC:
+ if ( pstack != 0 )
+ sreturn(e_syntaxerror)
+ retcode = scan_EOF;
+ break;
+ case ERRC:
+ sreturn(e_ioerror);
+
+ /* Check for a Level 2 funny name (<< or >>). */
+ /* c is '<' or '>'. We already did an ensure2. */
+try_funny_name:
+ { int c1 = scan_getc();
+ if ( c1 == c )
+ { s1[0] = s1[1] = c;
+ name_ref(s1, 2, myref, 1); /* can't fail */
+ goto have_name;
+ }
+ scan_putback();
+ } sreturn(e_syntaxerror);
+
+ /* Handle separately the names that might be a number. */
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ case '.':
+ /* We must stop the scan 1 character early, to be sure that */
+ /* we can test for CR+LF within the buffer. That's why */
+ /* we pass endptr rather than endptr + 1. */
+ retcode = scan_number(sptr, endptr, 0, myref, &newptr);
+nr: if ( retcode == 1 && decoder[newptr[-1]] == ctype_space )
+ { sptr = newptr - 1;
+ if ( *sptr == char_CR && sptr[1] == char_EOL )
+ sptr++;
+ retcode = 0;
+ break;
+ }
+ name_type = 0;
+ try_number = true;
+ goto do_name;
+ case '+':
+ retcode = scan_number(sptr + 1, endptr, 1, myref, &newptr);
+ goto nr;
+ case '-':
+ retcode = scan_number(sptr + 1, endptr, -1, myref, &newptr);
+ goto nr;
+
+ /* Check for a binary object */
+#define case4(c) case c: case c+1: case c+2: case c+3
+ case4(128): case4(132): case4(136): case4(140):
+ case4(144): case4(148): case4(152): case4(156):
+#undef case4
+ if ( recognize_btokens() )
+ { scan_end_inline();
+ retcode = (*scan_btoken_proc)(s, myref, &sstate);
+ scan_begin_inline();
+ if ( retcode == scan_Refill )
+ goto pause;
+ break;
+ }
+ /* Not a binary object, fall through. */
+
+ /* The default is a name. */
+ default:
+ if ( c < 0 )
+ { dynamic_init(&da, name_memory()); /* da state must be clean */
+ scan_type = scanning_none;
+ goto pause;
+ }
+ /* Populate the switch with enough cases to force */
+ /* simple compilers to use a dispatch rather than tests. */
+ case '!': case '"': case '#': case '$': case '&': case '\'':
+ case '*': case ',': case '=': case ':': case ';': case '?': case '@':
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+ case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': case 'M':
+ case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S':
+ case 'T': case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z':
+ case '\\': case '^': case '_': case '`':
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+ case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': case 'm':
+ case 'n': case 'o': case 'p': case 'q': case 'r': case 's':
+ case 't': case 'u': case 'v': case 'w': case 'x': case 'y': case 'z':
+ case '|': case '~':
+ /* Common code for scanning a name. */
+ /* try_number and name_type are already set. */
+ /* We know c has ctype_name (or maybe ctype_btoken) */
+ /* or is a digit. */
+ name_type = 0;
+ try_number = false;
+do_name:
+ { /* Try to scan entirely within the stream buffer. */
+ /* We stop 1 character early, so we don't switch buffers */
+ /* looking ahead if the name is terminated by \r\n. */
+ const byte *endp1 = endptr - 1;
+ da.base = (byte *)sptr;
+ da.is_dynamic = false;
+ do
+ { if ( sptr >= endp1 ) /* stop 1 early! */
+ goto dyn_name;
+ }
+ while ( decoder[*++sptr] <= max_name_ctype ); /* digit or name */
+ /* Name ended within the buffer. */
+ daptr = (byte *)sptr;
+ c = *sptr;
+ goto nx;
+dyn_name: /* Name extended past end of buffer. */
+ scan_end_inline();
+ /* Initialize the dynamic area. */
+ /* We have to do this before the next */
+ /* sgetc, which will overwrite the buffer. */
+ da.limit = (byte *)++sptr;
+ da.memory = name_memory();
+ retcode = dynamic_grow(&da, da.limit, name_max_string);
+ if ( retcode < 0 )
+ { dynamic_save(&da);
+ if ( retcode != e_VMerror )
+ sreturn(retcode);
+ scan_type = scanning_name;
+ goto pause_ret;
+ }
+ daptr = da.next;
+ /* Enter here to continue scanning a name. */
+ /* daptr must be set. */
+cont_name: scan_begin_inline();
+ while ( decoder[c = scan_getc()] <= max_name_ctype )
+ { if ( daptr == da.limit )
+ { retcode = dynamic_grow(&da, daptr,
+ name_max_string);
+ if ( retcode < 0 )
+ { dynamic_save(&da);
+ if ( retcode != e_VMerror )
+ sreturn(retcode);
+ scan_putback();
+ scan_type = scanning_name;
+ goto pause_ret;
+ }
+ daptr = da.next;
+ }
+ *daptr++ = c;
+ }
+nx: switch ( decoder[c] )
+ {
+ case ctype_btoken:
+ case ctype_other:
+ scan_putback();
+ break;
+ case ctype_space:
+ /* Check for \r\n */
+ if ( c == char_CR )
+ { if ( sptr >= endptr ) /* ensure2 */
+ { /* We have to check specially for */
+ /* the case where the very last */
+ /* character of a file is a CR. */
+ if ( s->end_status != EOFC )
+ { sptr--;
+ goto pause_name;
+ }
+ }
+ else if ( sptr[1] == char_EOL )
+ sptr++;
+ }
+ break;
+ case ctype_exception:
+ switch ( c )
+ {
+ case INTC:
+ case CALLC:
+ goto pause_name;
+ case ERRC:
+ sreturn(e_ioerror);
+ case EOFC:
+ break;
+ }
+ }
+ /* Check for a number */
+ if ( try_number )
+ { int sign;
+ const byte *base = da.base;
+ scan_sign(sign, base);
+ retcode = scan_number(base, daptr, sign, myref, &newptr);
+ if ( retcode == 1 )
+ retcode = 0;
+ else if ( retcode != e_syntaxerror )
+ { dynamic_free(&da);
+ if ( name_type == 2 )
+ sreturn(e_syntaxerror);
+ break; /* might be e_limitcheck */
+ }
+ }
+ if ( da.is_dynamic )
+ { /* We've already allocated the string on the heap. */
+ uint size = daptr - da.base;
+ retcode = name_ref(da.base, size, myref, -1);
+ if ( retcode >= 0 )
+ { dynamic_free(&da);
+ }
+ else
+ { retcode = dynamic_resize(&da, size);
+ if ( retcode < 0 ) /* VMerror */
+ { if ( c != EOFC )
+ scan_putback();
+ scan_type = scanning_name;
+ goto pause_ret;
+ }
+ retcode = name_ref(da.base, size, myref, 2);
+ }
+ }
+ else
+ { retcode = name_ref(da.base, (uint)(daptr - da.base),
+ myref, 1);
+ }
+ }
+ /* Done scanning. Check for preceding /'s. */
+ if ( retcode < 0 )
+ { if ( retcode != e_VMerror )
+ sreturn(retcode);
+ if ( !da.is_dynamic )
+ { da.next = daptr;
+ dynamic_save(&da);
+ }
+ if ( c != EOFC )
+ scan_putback();
+ scan_type = scanning_name;
+ goto pause_ret;
+ }
+have_name: switch ( name_type )
+ {
+ case 0: /* ordinary executable name */
+ if ( r_has_type(myref, t_name) ) /* i.e., not a number */
+ r_set_attrs(myref, a_executable);
+ case 1: /* quoted name */
+ break;
+ case 2: /* immediate lookup */
+ { ref *pvalue;
+ if ( !r_has_type(myref, t_name) )
+ sreturn(e_undefined);
+ if ( (pvalue = dict_find_name(myref)) == 0 )
+ sreturn(e_undefined);
+ if ( pstack != 0 &&
+ r_space(pvalue) > ialloc_space(idmemory)
+ )
+ sreturn(e_invalidaccess);
+ ref_assign_new(myref, pvalue);
+ }
+ }
+ }
+sret: if ( retcode < 0 )
+ { scan_end_inline();
+ if ( pstack != 0 )
+ ref_stack_pop(&o_stack,
+ ref_stack_count(&o_stack) - (pdepth - 1));
+ return retcode;
+ }
+ /* If we are at the top level, return the object, */
+ /* otherwise keep going. */
+ if ( pstack == 0 )
+ { scan_end_inline();
+ return retcode;
+ }
+snext: if_not_spush1()
+ { scan_end_inline();
+ scan_type = scanning_none;
+ goto save;
+ }
+ myref = osp;
+ goto top;
+
+ /* Pause for an interrupt or callout. */
+pause_name:
+ /* If we're still scanning within the stream buffer, */
+ /* move the characters to the private buffer (da.buf) now. */
+ da.next = daptr;
+ dynamic_save(&da);
+ scan_type = scanning_name;
+pause:
+ retcode = scan_Refill;
+pause_ret:
+ scan_end_inline();
+suspend:
+ if ( pstack != 0 )
+ osp--; /* myref */
+save:
+ sstate.s_from_string = pstate->s_from_string;
+ *pstate = sstate;
+ return retcode;
+}
diff --git a/pstoraster/iscan.h b/pstoraster/iscan.h
new file mode 100644
index 000000000..e3c92cdba
--- /dev/null
+++ b/pstoraster/iscan.h
@@ -0,0 +1,148 @@
+/* Copyright (C) 1992, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* iscan.h */
+/* Interface to Ghostscript scanner */
+/* Requires gsstruct.h, ostack.h, stream.h */
+#include "sa85x.h"
+#include "sstring.h"
+
+/*
+ * Define the state of the scanner. Before calling scan_token initially,
+ * the caller must initialize the state by calling scanner_state_init.
+ * Most of the state is only used if scanning is suspended because of
+ * an interrupt or a callout.
+ *
+ * We expose the entire state definition to the caller so that
+ * the state can normally be allocated on the stack.
+ */
+typedef struct scanner_state_s scanner_state;
+
+/*
+ * Define a structure for dynamically growable strings.
+ * If is_dynamic is true, base/next/limit point to a string on the heap;
+ * if is_dynamic is false, base/next/limit point either to the local buffer
+ * or (only while control is inside scan_token) into the source stream buffer.
+ */
+#define max_comment_line 255 /* max size of an externally processable comment */
+#define max_dsc_line max_comment_line /* backward compatibility */
+#define da_buf_size (max_comment_line + 2)
+typedef struct dynamic_area_s {
+ gs_string str; /* for GC */
+ byte *base;
+ byte *next;
+ byte *limit;
+ bool is_dynamic;
+ byte buf[da_buf_size]; /* initial buffer */
+ gs_memory_t *memory;
+} dynamic_area;
+#define da_size(pda) ((uint)((pda)->limit - (pda)->base))
+typedef dynamic_area _ss *da_ptr;
+
+/* Define state specific to binary tokens and binary object sequences. */
+typedef struct scan_binary_state_s {
+ int num_format;
+ int (*cont)(P3(stream *, ref *, scanner_state *));
+ ref bin_array;
+ uint index;
+ uint max_array_index; /* largest legal index in objects */
+ uint min_string_index; /* smallest legal index in strings */
+ uint top_size;
+ uint size;
+} scan_binary_state;
+
+/* Define the scanner state. */
+struct scanner_state_s {
+ uint s_pstack; /* stack depth when starting current */
+ /* procedure, after pushing old pstack */
+ uint s_pdepth; /* pstack for very first { encountered, */
+ /* for error recovery */
+ bool s_from_string; /* true if string is source of data */
+ /* (for Level 1 `\' handling) */
+ enum {
+ scanning_none,
+ scanning_binary,
+ scanning_comment,
+ scanning_name,
+ scanning_string
+ } s_scan_type;
+ dynamic_area s_da;
+ union sss_ { /* scanning sub-state */
+ scan_binary_state binary; /* binary */
+ struct sns_ { /* name */
+ int s_name_type; /* number of /'s preceding a name */
+ bool s_try_number; /* true if should try scanning name */
+ /* as number */
+ } s_name;
+ stream_state st; /* string */
+ stream_A85D_state a85d; /* string */
+ stream_AXD_state axd; /* string */
+ stream_PSSD_state pssd; /* string */
+ } s_ss;
+};
+/* The type descriptor is public only for checking. */
+extern_st(st_scanner_state);
+#define public_st_scanner_state() /* in iscan.c */\
+ gs_public_st_complex_only(st_scanner_state, scanner_state, "scanner state",\
+ scanner_clear_marks, scanner_enum_ptrs, scanner_reloc_ptrs, 0)
+#define scanner_state_init(pstate, from_string)\
+ ((pstate)->s_scan_type = scanning_none,\
+ (pstate)->s_pstack = 0,\
+ (pstate)->s_from_string = from_string)
+
+/*
+ * Read a token from a stream. As usual, 0 is a normal return,
+ * <0 is an error. There are also three special return codes:
+ */
+#define scan_BOS 1 /* binary object sequence */
+#define scan_EOF 2 /* end of stream */
+#define scan_Refill 3 /* get more input data, then call again */
+int scan_token(P3(stream *s, ref *pref, scanner_state *pstate));
+
+/*
+ * Read a token from a string. Return like scan_token, but also
+ * update the string to move past the token (if no error).
+ */
+int scan_string_token(P2(ref *pstr, ref *pref));
+
+/*
+ * Handle a scan_Refill return from scan_token.
+ * This may return o_push_estack, 0 (meaning just call scan_token again),
+ * or an error code.
+ */
+int scan_handle_refill(P5(const ref *fop, scanner_state *pstate, bool save,
+ bool push_file, int (*cont)(P1(os_ptr))));
+
+/*
+ * Define the procedure "hook" for parsing DSC comments. If not NULL,
+ * this procedure is called for every DSC comment seen by the scanner.
+ */
+extern int (*scan_dsc_proc)(P2(const byte *, uint));
+
+/*
+ * Define the procedure "hook" for parsing general comments. If not NULL,
+ * this procedure is called for every comment seen by the scanner.
+ * If both scan_dsc_proc and scan_comment_proc are set,
+ * scan_comment_proc is called only for non-DSC comments.
+ */
+extern int (*scan_comment_proc)(P2(const byte *, uint));
diff --git a/pstoraster/iscanbin.c b/pstoraster/iscanbin.c
new file mode 100644
index 000000000..cbbcafaac
--- /dev/null
+++ b/pstoraster/iscanbin.c
@@ -0,0 +1,581 @@
+/* Copyright (C) 1989, 1992, 1993, 1994 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* iscanbin.c */
+/* Ghostscript binary token scanner */
+#include "math_.h"
+#include "memory_.h"
+#include "ghost.h"
+#include "gsutil.h"
+#include "stream.h"
+#include "strimpl.h" /* for sfilter.h */
+#include "sfilter.h" /* for iscan.h */
+#include "errors.h"
+#include "ialloc.h"
+#include "idict.h"
+#include "dstack.h" /* for immediately evaluated names */
+#include "ostack.h" /* must precede iscan.h */
+#include "iname.h"
+#include "iscan.h" /* for scan_Refill */
+#include "iutil.h"
+#include "ivmspace.h"
+#include "store.h"
+#include "bseq.h"
+#include "btoken.h"
+#include "ibnum.h"
+
+/* Define the number of required initial bytes for binary tokens. */
+const byte bin_token_bytes[] = { bin_token_bytes_values };
+
+/* Import the system and user name tables */
+extern ref system_names, user_names;
+
+/* Forward references */
+private int scan_bin_num_array_continue(P3(stream *, ref *, scanner_state *));
+private int scan_bin_string_continue(P3(stream *, ref *, scanner_state *));
+private int scan_bos_continue(P3(stream *, ref *, scanner_state *));
+private byte *scan_bos_resize(P3(scanner_state *, uint, uint));
+private int scan_bos_string_continue(P3(stream *, ref *, scanner_state *));
+
+/* Scan a binary token. Called from the main scanner */
+/* when it encounters an ASCII code 128-159, */
+/* if binary tokens are being recognized (object format != 0). */
+#define pbs (&pstate->s_ss.binary)
+int
+scan_binary_token(stream *s, ref *pref, scanner_state *pstate)
+{ s_declare_inline(s, p, rlimit);
+#define return_skip(n) { p += n; s_end_inline(s, p, rlimit); return 0; }
+ int num_format, code;
+ uint arg;
+ uint wanted;
+ uint rcnt;
+ s_begin_inline(s, p, rlimit);
+ wanted = binary_token_bytes(*p) - 1;
+ rcnt = rlimit - p;
+#define return_refill()\
+ { sputback_inline(s, p, rlimit); s_end_inline(s, p, rlimit);\
+ pstate->s_scan_type = scanning_none; return scan_Refill;\
+ }
+ if ( rcnt < wanted )
+ return_refill();
+ switch ( *p )
+ {
+ case bt_seq_IEEE_msb:
+ num_format = num_msb + num_float_IEEE; goto bseq;
+ case bt_seq_IEEE_lsb:
+ num_format = num_lsb + num_float_IEEE; goto bseq;
+ case bt_seq_native_msb:
+ num_format = num_msb + num_float_native; goto bseq;
+ case bt_seq_native_lsb:
+ num_format = num_lsb + num_float_native;
+bseq: pbs->num_format = num_format;
+ { uint top_size = p[1];
+ uint hsize, size;
+ if ( top_size == 0 )
+ { /* Extended header (2-byte array size, */
+ /* 4-byte length) */
+ ulong lsize;
+ if ( rcnt < 7 )
+ return_refill();
+ top_size = sdecodeshort(p + 2, num_format);
+ lsize = sdecodelong(p + 4, num_format);
+ if ( (size = lsize) != lsize )
+ return_error(e_limitcheck);
+ hsize = 8;
+ }
+ else
+ { /* Normal header (1-byte array size, 2-byte length). */
+ /* We already checked rcnt >= 3. */
+ size = sdecodeshort(p + 2, num_format);
+ hsize = 4;
+ }
+ if ( size < hsize )
+ return_error(e_syntaxerror);
+ /* Preallocate an array large enough for the worst case, */
+ /* namely, all objects and no strings. */
+ code = ialloc_ref_array(&pbs->bin_array,
+ a_all + a_executable,
+ size / sizeof(ref),
+ "binary object sequence(objects)");
+ if ( code < 0 )
+ return code;
+ p += hsize - 1;
+ size -= hsize;
+ s_end_inline(s, p, rlimit);
+ pbs->max_array_index = pbs->top_size = top_size;
+ pbs->min_string_index = pbs->size = size;
+ pbs->index = 0;
+ }
+ pstate->s_da.is_dynamic = false;
+ pstate->s_da.base = pstate->s_da.next =
+ pstate->s_da.limit = pstate->s_da.buf;
+ code = scan_bos_continue(s, pref, pstate);
+ if ( code == scan_Refill || code < 0 )
+ { /* Clean up array for GC. */
+ uint index = pbs->index;
+ refset_null(pbs->bin_array.value.refs + index,
+ r_size(&pbs->bin_array) - index);
+ pbs->cont = scan_bos_continue;
+ }
+ return code;
+ case bt_int32_msb:
+ num_format = num_msb + num_int32; goto num;
+ case bt_int32_lsb:
+ num_format = num_lsb + num_int32; goto num;
+ case bt_int16_msb:
+ num_format = num_msb + num_int16; goto num;
+ case bt_int16_lsb:
+ num_format = num_lsb + num_int16; goto num;
+ case bt_int8:
+ make_int(pref, (p[1] ^ 128) - 128);
+ return_skip(1);
+ case bt_fixed:
+ num_format = p[1];
+ if ( !num_is_valid(num_format) )
+ return_error(e_syntaxerror);
+ wanted = 1 + encoded_number_bytes(num_format);
+ if ( rcnt < wanted )
+ return_refill();
+ code = sdecode_number(p + 2, num_format, pref);
+ goto rnum;
+ case bt_float_IEEE_msb:
+ num_format = num_msb + num_float_IEEE; goto num;
+ case bt_float_IEEE_lsb:
+ num_format = num_lsb + num_float_IEEE; goto num;
+ case bt_float_native:
+ num_format = num_float_native;
+num: code = sdecode_number(p + 1, num_format, pref);
+rnum: switch ( code )
+ {
+ case t_integer: case t_real:
+ r_set_type(pref, code);
+ break;
+ case t_null:
+ return_error(e_syntaxerror);
+ default:
+ return code;
+ }
+ return_skip(wanted);
+ case bt_boolean:
+ arg = p[1];
+ if ( arg & ~1 )
+ return_error(e_syntaxerror);
+ make_bool(pref, arg);
+ return_skip(1);
+ case bt_string_256:
+ arg = p[1]; p++; goto str;
+ case bt_string_64k_msb:
+ arg = (p[1] << 8) + p[2]; p += 2; goto str;
+ case bt_string_64k_lsb:
+ arg = p[1] + (p[2] << 8); p += 2; goto str;
+str: { byte *str = ialloc_string(arg, "string token");
+ if ( str == 0 )
+ return_error(e_VMerror);
+ s_end_inline(s, p, rlimit);
+ pstate->s_da.base = pstate->s_da.next = str;
+ pstate->s_da.limit = str + arg;
+ code = scan_bin_string_continue(s, pref, pstate);
+ if ( code == scan_Refill || code < 0 )
+ { pstate->s_da.is_dynamic = true;
+ make_null(&pbs->bin_array); /* clean up for GC */
+ pbs->cont = scan_bin_string_continue;
+ }
+ return code;
+ }
+ case bt_litname_system:
+ code = array_get(&system_names, p[1], pref);
+ goto lname;
+ case bt_execname_system:
+ code = array_get(&system_names, p[1], pref);
+ goto xname;
+ case bt_litname_user:
+ code = array_get(&user_names, p[1], pref);
+lname: if ( code < 0 ) return code;
+ if ( !r_has_type(pref, t_name) )
+ return_error(e_undefined);
+ return_skip(1);
+ case bt_execname_user:
+ code = array_get(&user_names, p[1], pref);
+xname: if ( code < 0 ) return code;
+ if ( !r_has_type(pref, t_name) )
+ return_error(e_undefined);
+ r_set_attrs(pref, a_executable);
+ return_skip(1);
+ case bt_num_array:
+ num_format = p[1];
+ if ( !num_is_valid(num_format) )
+ return_error(e_syntaxerror);
+ arg = sdecodeshort(p + 2, num_format);
+ code = ialloc_ref_array(&pbs->bin_array, a_all, arg,
+ "number array token");
+ if ( code < 0 )
+ return code;
+ pbs->num_format = num_format;
+ pbs->index = 0;
+ p += 3;
+ s_end_inline(s, p, rlimit);
+ code = scan_bin_num_array_continue(s, pref, pstate);
+ if ( code == scan_Refill || code < 0 )
+ { /* Make sure the array is clean for the GC. */
+ refset_null(pbs->bin_array.value.refs + pbs->index,
+ arg - pbs->index);
+ pbs->cont = scan_bin_num_array_continue;
+ }
+ return code;
+ }
+ return_error(e_syntaxerror);
+}
+
+/* Continue collecting a binary string. */
+private int
+scan_bin_string_continue(stream *s, ref *pref, scanner_state *pstate)
+{ byte *q = pstate->s_da.next;
+ uint wanted = pstate->s_da.limit - q;
+ uint rcnt;
+ sgets(s, q, wanted, &rcnt);
+ if ( rcnt == wanted )
+ { /* Finished collecting the string. */
+ make_string(pref, a_all | icurrent_space,
+ pstate->s_da.limit - pstate->s_da.base,
+ pstate->s_da.base);
+ return 0;
+ }
+ pstate->s_da.next = q + rcnt;
+ pstate->s_scan_type = scanning_binary;
+ return scan_Refill;
+}
+
+/* Continue scanning a binary number array. */
+private int
+scan_bin_num_array_continue(stream *s, ref *pref, scanner_state *pstate)
+{ uint index = pbs->index;
+ ref *np = pbs->bin_array.value.refs + index;
+ uint wanted = encoded_number_bytes(pbs->num_format);
+ for ( ; index < r_size(&pbs->bin_array); index++, np++ )
+ { int code;
+ if ( sbufavailable(s) < wanted )
+ { pbs->index = index;
+ pstate->s_scan_type = scanning_binary;
+ return scan_Refill;
+ }
+ code = sdecode_number(sbufptr(s), pbs->num_format, np);
+ switch ( code )
+ {
+ case t_integer: case t_real:
+ r_set_type(np, code);
+ sbufskip(s, wanted);
+ break;
+ case t_null:
+ return_error(e_syntaxerror);
+ default:
+ return code;
+ }
+ }
+ *pref = pbs->bin_array;
+ return 0;
+}
+
+/*
+ * Continue scanning a binary object sequence. We preallocated space for
+ * the largest possible number of objects, but not for strings, since
+ * the latter would probably be a gross over-estimate. Instead,
+ * we wait until we see the first string or name, and allocate string space
+ * based on the hope that its string index is the smallest one we will see.
+ * If this turns out to be wrong, we may have to reallocate, and adjust
+ * all the pointers.
+ */
+#define sizeof_bin_seq_obj ((uint)sizeof(bin_seq_obj))
+private int
+scan_bos_continue(register stream *s, ref *pref, scanner_state *pstate)
+{ s_declare_inline(s, p, rlimit);
+ uint max_array_index = pbs->max_array_index;
+ uint min_string_index = pbs->min_string_index;
+ int format = pbs->num_format;
+#if arch_is_big_endian
+# define must_swap_bytes num_is_lsb(format)
+#else
+# define must_swap_bytes !num_is_lsb(format)
+#endif
+ uint index = pbs->index;
+ uint size = pbs->size;
+ ref *abase = pbs->bin_array.value.refs;
+ int code;
+ s_begin_inline(s, p, rlimit);
+ for ( ; index < max_array_index; index++ )
+ { bin_seq_obj ob;
+ byte bt;
+ ref *op = abase + index;
+ uint atype, sattrs;
+ s_end_inline(s, p, rlimit); /* in case of error */
+ if ( rlimit - p < sizeof_bin_seq_obj )
+ { pbs->index = index;
+ pbs->max_array_index = max_array_index;
+ pbs->min_string_index = min_string_index;
+ pstate->s_scan_type = scanning_binary;
+ return scan_Refill;
+ }
+ memcpy(&ob, p + 1, sizeof_bin_seq_obj);
+ p += sizeof_bin_seq_obj;
+#define do_swap_size()\
+ bt = ob.size.b[0], ob.size.b[0] = ob.size.b[1], ob.size.b[1] = bt
+#define swap_size()\
+ if ( must_swap_bytes ) do_swap_size()
+#define do_swap_value()\
+ bt = ob.value.b[0], ob.value.b[0] = ob.value.b[3], ob.value.b[3] = bt,\
+ bt = ob.value.b[1], ob.value.b[1] = ob.value.b[2], ob.value.b[2] = bt
+#define swap_value()\
+ if ( must_swap_bytes ) do_swap_value()
+#define swap_size_also_value()\
+ if ( must_swap_bytes ) do_swap_size(), do_swap_value()
+ switch ( ob.tx & 0x7f )
+ {
+ case bs_null:
+ make_null(op); break;
+ case bs_integer:
+ swap_value();
+ make_int(op, ob.value.w);
+ break;
+ case bs_real:
+ if ( ob.size.w != 0 ) /* fixed-point number */
+ { swap_size_also_value();
+ ob.value.f = (float)ldexp((float)ob.value.w,
+ -ob.size.w);
+ }
+ else if ( (format & ~(num_lsb | num_msb)) !=
+ num_float_native
+ )
+ { ob.value.f = sdecodefloat(ob.value.b, format);
+ }
+ make_real(op, ob.value.f);
+ break;
+ case bs_boolean:
+ swap_value();
+ make_bool(op, (ob.value.w == 0 ? 0 : 1));
+ break;
+ case bs_string:
+ swap_size_also_value();
+ sattrs = (ob.tx < 128 ? a_all : a_all + a_executable);
+str: if ( ob.size.w == 0 )
+ { /* For zero-length strings, the offset */
+ /* doesn't matter, and may be zero. */
+ make_empty_string(op, sattrs);
+ break;
+ }
+ if ( ob.value.w < max_array_index * sizeof_bin_seq_obj ||
+ ob.value.w + ob.size.w > size
+ )
+ return_error(e_syntaxerror);
+ if ( ob.value.w < min_string_index )
+ { /* We have to (re)allocate the strings. */
+ uint str_size = size - ob.value.w;
+ byte *sbase;
+ if ( pstate->s_da.is_dynamic )
+ sbase = scan_bos_resize(pstate, str_size,
+ index);
+ else
+ sbase = ialloc_string(str_size,
+ "bos strings");
+ if ( sbase == 0 )
+ return_error(e_VMerror);
+ pstate->s_da.is_dynamic = true;
+ pstate->s_da.base = pstate->s_da.next = sbase;
+ pstate->s_da.limit = sbase + str_size;
+ min_string_index = ob.value.w;
+ }
+ make_string(op, sattrs | icurrent_space, ob.size.w,
+ pstate->s_da.base +
+ (ob.value.w - min_string_index));
+ break;
+ case bs_name:
+ sattrs = (ob.tx < 128 ? 0 : a_executable);
+ goto nam;
+ case bs_eval_name:
+ sattrs = a_readonly;
+nam: swap_size_also_value();
+ switch ( ob.size.w )
+ {
+ case 0:
+ code = array_get(&user_names, ob.value.w, op);
+ goto usn;
+ case 0xffff:
+ code = array_get(&system_names, ob.value.w, op);
+usn: if ( code < 0 )
+ return code;
+ if ( !r_has_type(op, t_name) )
+ return_error(e_undefined);
+ r_set_attrs(op, sattrs);
+ break;
+ default:
+ goto str;
+ }
+ break;
+ case bs_array:
+ swap_size_also_value();
+ atype = t_array;
+arr: if ( ob.value.w + ob.size.w > min_string_index ||
+ ob.value.w & (sizeof_bin_seq_obj - 1)
+ )
+ return_error(e_syntaxerror);
+ { uint aindex = ob.value.w / sizeof_bin_seq_obj;
+ max_array_index =
+ max(max_array_index, aindex + ob.size.w);
+ make_tasv_new(op, atype,
+ (ob.tx < 128 ? a_all :
+ a_all + a_executable) | icurrent_space,
+ ob.size.w, refs, abase + aindex);
+ }
+ break;
+ case bs_dictionary: /* EXTENSION */
+ swap_size_also_value();
+ if ( (ob.size.w & 1) != 0 && ob.size.w != 1 )
+ return_error(e_syntaxerror);
+ atype = t_mixedarray; /* mark as dictionary */
+ goto arr;
+ case bs_mark:
+ make_mark(op); break;
+ default:
+ return_error(e_syntaxerror);
+ }
+ }
+ s_end_inline(s, p, rlimit);
+ /* Shorten the objects to remove the space that turned out */
+ /* to be used for strings. */
+ iresize_ref_array(&pbs->bin_array, max_array_index,
+ "binary object sequence(objects)");
+ code = scan_bos_string_continue(s, pref, pstate);
+ if ( code == scan_Refill )
+ { pbs->cont = scan_bos_string_continue;
+ }
+ return code;
+}
+
+/* Reallocate the strings for a binary object sequence, */
+/* adjusting all the pointers to them from objects. */
+private byte *
+scan_bos_resize(scanner_state *pstate, uint new_size, uint index)
+{ uint old_size = da_size(&pstate->s_da);
+ byte *old_base = pstate->s_da.base;
+ byte *new_base = iresize_string(old_base, old_size, new_size,
+ "scan_bos_resize");
+ byte *relocated_base = new_base + (new_size - old_size);
+ uint i;
+ ref *aptr = pbs->bin_array.value.refs;
+ if ( new_base == 0 )
+ return 0;
+ /* Since the allocator normally extends strings downward, */
+ /* it's quite possible that new and old addresses are the same. */
+ if ( relocated_base != old_base )
+ for ( i = index; i != 0; i--, aptr++ )
+ if ( r_has_type(aptr, t_string) && r_size(aptr) != 0 )
+ aptr->value.bytes =
+ aptr->value.bytes - old_base + relocated_base;
+ return new_base;
+}
+
+/* Continue reading the strings for a binary object sequence. */
+private int
+scan_bos_string_continue(register stream *s, ref *pref, scanner_state *pstate)
+{ ref rstr;
+ ref *op = pbs->bin_array.value.refs;
+ int code = scan_bin_string_continue(s, &rstr, pstate);
+ uint space = ialloc_space(idmemory);
+ bool rescan = false;
+ uint i;
+
+ if ( code != 0 )
+ return code;
+ /* Finally, fix up names and dictionaries. */
+ for ( i = r_size(&pbs->bin_array); i != 0; i--, op++ )
+ switch ( r_type(op) )
+ {
+ case t_string:
+ if ( r_has_attr(op, a_write) ) /* a real string */
+ break;
+ /* This is actually a name; look it up now. */
+ { uint sattrs =
+ (r_has_attr(op, a_executable) ? a_executable : 0);
+ code = name_ref(op->value.bytes, r_size(op), op, 1);
+ if ( code < 0 )
+ return code;
+ r_set_attrs(op, sattrs);
+ }
+ /* falls through */
+ case t_name:
+ if ( r_has_attr(op, a_read) ) /* bs_eval_name */
+ { ref *defp = dict_find_name(op);
+ if ( defp == 0 )
+ return_error(e_undefined);
+ store_check_space(space, defp);
+ ref_assign(op, defp);
+ }
+ break;
+ case t_mixedarray: /* actually a dictionary */
+ { uint count = r_size(op);
+ ref rdict;
+ if ( count == 1 )
+ { /* Indirect reference. */
+ if ( op->value.refs < op )
+ ref_assign(&rdict, op->value.refs);
+ else
+ { rescan = true;
+ continue;
+ }
+ }
+ else
+ { code = dict_create(count >> 1, &rdict);
+ if ( code < 0 )
+ return code;
+ while ( count )
+ { count -= 2;
+ code = dict_put(&rdict,
+ &op->value.refs[count],
+ &op->value.refs[count + 1]);
+ if ( code < 0 )
+ return code;
+ }
+ }
+ r_set_attrs(&rdict, a_all);
+ r_copy_attrs(&rdict, a_executable, op);
+ ref_assign(op, &rdict);
+ }
+ break;
+ }
+ /* If there were any forward indirect references, */
+ /* fix them up now. */
+ if ( rescan )
+ for ( op = pbs->bin_array.value.refs, i = r_size(&pbs->bin_array);
+ i != 0; i--, op++
+ )
+ if ( r_has_type(op, t_mixedarray) )
+ { const ref *piref = op->value.const_refs;
+ ref rdict;
+ if ( r_has_type(piref, t_mixedarray) ) /* ref to indirect */
+ return_error(e_syntaxerror);
+ ref_assign(&rdict, piref);
+ r_copy_attrs(&rdict, a_executable, op);
+ ref_assign(op, &rdict);
+ }
+ ref_assign(pref, &pbs->bin_array);
+ r_set_size(pref, pbs->top_size);
+ return scan_BOS;
+}
diff --git a/pstoraster/iscannum.c b/pstoraster/iscannum.c
new file mode 100644
index 000000000..25ee224aa
--- /dev/null
+++ b/pstoraster/iscannum.c
@@ -0,0 +1,357 @@
+/* Copyright (C) 1994, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* iscannum.c */
+/* Number scanner for Ghostscript interpreter */
+#include "math_.h"
+#include "ghost.h"
+#include "errors.h"
+#include "scommon.h"
+#include "iscannum.h" /* defines interface */
+#include "scanchar.h"
+#include "store.h"
+
+#define is_digit(d, c)\
+ ((d = decoder[c]) < 10)
+
+#define scan_sign(sign, ptr)\
+ switch ( *ptr ) {\
+ case '-': sign = -1; ptr++; break;\
+ case '+': sign = 1; ptr++; break;\
+ default: sign = 0;\
+ }
+
+/* Note that the number scanning procedures use a byte ** and a byte * */
+/* rather than a stream. (It makes quite a difference in performance.) */
+#define ngetc(cvar, sp, exit)\
+ if ( sp >= end ) { exit; } else cvar = *sp++
+
+/* Scan a number. If the number consumes the entire string, return 0; */
+/* if not, set *psp to the first character beyond the number and return 1. */
+int
+scan_number(register const byte *sp, const byte *end, int sign,
+ ref *pref, const byte **psp)
+{ /* Powers of 10 up to 6 can be represented accurately as */
+ /* a single-precision float. */
+#define num_powers_10 6
+ static const float powers_10[num_powers_10+1] =
+ { 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6 };
+ static const double neg_powers_10[num_powers_10+1] =
+ { 1e0, 1e-1, 1e-2, 1e-3, 1e-4, 1e-5, 1e-6 };
+ int ival;
+ long lval;
+ double dval;
+ int exp10;
+ int code = 0;
+ register int c, d;
+ register const byte _ds *decoder = scan_char_decoder;
+ ngetc(c, sp, return_error(e_syntaxerror));
+#define would_overflow(val, d, maxv)\
+ (val >= maxv / 10 && (val > maxv / 10 || d > (int)(maxv % 10)))
+ if ( !is_digit(d, c) )
+ { if ( c != '.' )
+ return_error(e_syntaxerror);
+ /* Might be a number starting with '.'. */
+ ngetc(c, sp, return_error(e_syntaxerror));
+ if ( !is_digit(d, c) )
+ return_error(e_syntaxerror);
+ ival = 0;
+ goto i2r;
+ }
+
+ /* Accumulate an integer in ival. */
+ /* Do up to 4 digits without a loop, */
+ /* since we know this can't overflow and since */
+ /* most numbers have 4 (integer) digits or fewer. */
+ ival = d;
+ if ( end - sp >= 3 ) /* just check once */
+ { if ( !is_digit(d, (c = *sp)) )
+ { sp++;
+ goto ind;
+ }
+ ival = ival * 10 + d;
+ if ( !is_digit(d, (c = sp[1])) )
+ { sp += 2;
+ goto ind;
+ }
+ ival = ival * 10 + d;
+ sp += 3;
+ if ( !is_digit(d, (c = sp[-1])) )
+ goto ind;
+ ival = ival * 10 + d;
+ }
+ for ( ; ; ival = ival * 10 + d )
+ { ngetc(c, sp, goto iret);
+ if ( !is_digit(d, c) )
+ break;
+ if ( would_overflow(ival, d, max_int) )
+ goto i2l;
+ }
+ind: switch ( c )
+ {
+ case '.':
+ ngetc(c, sp, c = EOFC);
+ goto i2r;
+ default:
+ *psp = sp;
+ code = 1;
+ break;
+ case 'e': case 'E':
+ if ( sign < 0 )
+ ival = -ival;
+ dval = ival;
+ exp10 = 0;
+ goto fe;
+ case '#':
+ { ulong uval = 0, lmax;
+#define radix (uint)ival
+ if ( sign || radix < min_radix || radix > max_radix )
+ return_error(e_syntaxerror);
+ /* Avoid multiplies for power-of-2 radix. */
+ if ( !(radix & (radix - 1)) )
+ { int shift;
+ switch ( radix )
+ {
+#define set_shift(n)\
+ shift = n; lmax = max_ulong >> n
+ case 2: set_shift(1); break;
+ case 4: set_shift(2); break;
+ case 8: set_shift(3); break;
+ case 16: set_shift(4); break;
+ case 32: set_shift(5); break;
+#undef set_shift
+ default: /* can't happen */
+ return_error(e_rangecheck);
+ }
+ for ( ; ; uval = (uval << shift) + d )
+ { ngetc(c, sp, break);
+ d = decoder[c];
+ if ( d >= radix )
+ { *psp = sp;
+ code = 1;
+ break;
+ }
+ if ( uval > lmax )
+ return_error(e_limitcheck);
+ }
+ }
+ else
+ { int lrem = max_ulong % radix;
+ lmax = max_ulong / radix;
+ for ( ; ; uval = uval * radix + d )
+ { ngetc(c, sp, break);
+ d = decoder[c];
+ if ( d >= radix )
+ { *psp = sp;
+ code = 1;
+ break;
+ }
+ if ( uval >= lmax &&
+ (uval > lmax || d > lrem)
+ )
+ return_error(e_limitcheck);
+ }
+ }
+#undef radix
+ make_int_new(pref, uval);
+ return code;
+ }
+ }
+iret: make_int_new(pref, (sign < 0 ? -ival : ival));
+ return code;
+
+ /* Accumulate a long in lval. */
+i2l: for ( lval = ival; ; )
+ { if ( would_overflow(lval, d, max_long) )
+ { /* Make a special check for entering the smallest */
+ /* (most negative) integer. */
+ if ( lval == max_long / 10 &&
+ d == (int)(max_long % 10) + 1 && sign < 0
+ )
+ { ngetc(c, sp, c = EOFC);
+ dval = -(double)min_long;
+ if ( c == 'e' || c == 'E' || c == '.' )
+ { exp10 = 0;
+ goto fs;
+ }
+ else if ( !is_digit(d, c) )
+ { lval = min_long;
+ break;
+ }
+ }
+ else
+ dval = lval;
+ goto l2d;
+ }
+ lval = lval * 10 + d;
+ ngetc(c, sp, goto lret);
+ if ( !is_digit(d, c) )
+ break;
+ }
+ switch ( c )
+ {
+ case '.':
+ ngetc(c, sp, c = EOFC);
+ exp10 = 0;
+ goto l2r;
+ default:
+ *psp = sp;
+ code = 1;
+ break;
+ case 'e': case 'E':
+ exp10 = 0;
+ goto le;
+ case '#':
+ return_error(e_syntaxerror);
+ }
+lret: make_int_new(pref, (sign < 0 ? -lval : lval));
+ return code;
+
+ /* Accumulate a double in dval. */
+l2d: exp10 = 0;
+ for ( ; ; )
+ { dval = dval * 10 + d;
+ ngetc(c, sp, c = EOFC);
+ if ( !is_digit(d, c) )
+ goto fs;
+ }
+
+ /* We saw a '.' while accumulating an integer in ival. */
+i2r: exp10 = 0;
+ while ( is_digit(d, c) )
+ { if ( would_overflow(ival, d, max_int) )
+ { lval = ival;
+ goto l2r;
+ }
+ ival = ival * 10 + d;
+ exp10--;
+ ngetc(c, sp, c = EOFC);
+ }
+ if ( sign < 0 )
+ ival = -ival;
+ /* Take a shortcut for the common case */
+ if ( !(c == 'e' || c == 'E' || exp10 < -num_powers_10) )
+ { /* Check for trailing garbage */
+ if ( c != EOFC )
+ *psp = sp, code = 1;
+ make_real_new(pref, ival * neg_powers_10[-exp10]);
+ return code;
+ }
+ dval = ival;
+ goto fe;
+
+ /* We saw a '.' while accumulating a long in lval. */
+l2r: while ( is_digit(d, c) )
+ { if ( would_overflow(lval, d, max_long) )
+ { dval = lval;
+ goto fd;
+ }
+ lval = lval * 10 + d;
+ exp10--;
+ ngetc(c, sp, c = EOFC);
+ }
+le: if ( sign < 0 )
+ lval = -lval;
+ dval = lval;
+ goto fe;
+
+ /* Now we are accumulating a double in dval. */
+fd: while ( is_digit(d, c) )
+ { dval = dval * 10 + d;
+ exp10--;
+ ngetc(c, sp, c = EOFC);
+ }
+fs: if ( sign < 0 )
+ dval = -dval;
+fe: /* Now dval contains the value, negated if necessary. */
+ switch ( c )
+ {
+ case 'e': case 'E':
+ { /* Check for a following exponent. */
+ int esign = 0;
+ int iexp;
+ ngetc(c, sp, return_error(e_syntaxerror));
+ switch ( c )
+ {
+ case '-':
+ esign = 1;
+ case '+':
+ ngetc(c, sp, return_error(e_syntaxerror));
+ }
+ /* Scan the exponent. We limit it arbitrarily to 999. */
+ if ( !is_digit(d, c) )
+ return_error(e_syntaxerror);
+ iexp = d;
+ for ( ; ; iexp = iexp * 10 + d )
+ { ngetc(c, sp, break);
+ if ( !is_digit(d, c) )
+ { *psp = sp;
+ code = 1;
+ break;
+ }
+ if ( iexp > 99 )
+ return_error(e_limitcheck);
+ }
+ if ( esign )
+ exp10 -= iexp;
+ else
+ exp10 += iexp;
+ break;
+ }
+ default:
+ *psp = sp;
+ code = 1;
+ case EOFC:
+ ;
+ }
+ /* Compute dval * 10^exp10. */
+ if ( exp10 > 0 )
+ { while ( exp10 > num_powers_10 )
+ dval *= powers_10[num_powers_10],
+ exp10 -= num_powers_10;
+ if ( exp10 > 0 )
+ dval *= powers_10[exp10];
+ }
+ else if ( exp10 < 0 )
+ { while ( exp10 < -num_powers_10 )
+ dval /= powers_10[num_powers_10],
+ exp10 += num_powers_10;
+ if ( exp10 < 0 )
+ dval /= powers_10[-exp10];
+ }
+ /*
+ * Check for an out-of-range result. Currently we don't check for
+ * absurdly large numbers of digits in the accumulation loops,
+ * but we should.
+ */
+ if ( dval >= 0 )
+ { if ( dval > MAX_FLOAT )
+ return_error(e_limitcheck);
+ }
+ else
+ { if ( dval < -MAX_FLOAT )
+ return_error(e_limitcheck);
+ }
+ make_real_new(pref, dval);
+ return code;
+}
diff --git a/pstoraster/iscannum.h b/pstoraster/iscannum.h
new file mode 100644
index 000000000..6d9cc3cae
--- /dev/null
+++ b/pstoraster/iscannum.h
@@ -0,0 +1,30 @@
+/* Copyright (C) 1994, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* iscannum.h */
+/* Interface to Ghostscript number scanner */
+
+/* Scan a number. If the number consumes the entire string, return 0; */
+/* if not, set *psp to the first character beyond the number and return 1. */
+int scan_number(P5(const byte *sp, const byte *end, int sign, ref *pref,
+ const byte **psp));
diff --git a/pstoraster/iscantab.c b/pstoraster/iscantab.c
new file mode 100644
index 000000000..15c80fffc
--- /dev/null
+++ b/pstoraster/iscantab.c
@@ -0,0 +1,114 @@
+/* Copyright (C) 1994, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* iscantab.c */
+/* Scanner table for Ghostscript interpreter */
+#include "stdpre.h"
+#include "scommon.h"
+#include "scanchar.h" /* defines interface */
+
+/* Define the character scanning table (see scanchar.h). */
+const byte scan_char_array[max_stream_exception + 256] =
+{ stream_exception_repeat(ctype_exception),
+ /* Control characters 0-31. */
+ ctype_space, /* NULL - standard only in Level 2 */
+ ctype_name, ctype_name, ctype_name,
+ ctype_other, /* EOT (^D, \004) - not PostScript standard */
+ ctype_name,
+ ctype_name, ctype_name, ctype_name,
+ ctype_space, /* TAB (\t) */
+ ctype_space, /* LF (\n) */
+ ctype_space, /* VT - not PostScript standard */
+ ctype_space, /* FF (\f) */
+ ctype_space, /* CR (\r) */
+ ctype_name, ctype_name, ctype_name, ctype_name, ctype_name,
+ ctype_name, ctype_name, ctype_name, ctype_name, ctype_name,
+ ctype_name, ctype_name,
+ ctype_other, /* DOS EOF - not PostScript standard */
+ ctype_name, ctype_name, ctype_name, ctype_name, ctype_name,
+ /* Printable characters 32-63 */
+ ctype_space, /* space (\s) */
+ ctype_name, ctype_name, ctype_name, ctype_name,
+ ctype_other, /* % */
+ ctype_name, ctype_name,
+ ctype_other, /* ( */
+ ctype_other, /* ) */
+ ctype_name, ctype_name, ctype_name, ctype_name, ctype_name,
+ ctype_other, /* / */
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, /* digits 0-9 */
+ ctype_name, ctype_name,
+ ctype_other, /* < */
+ ctype_name,
+ ctype_other, /* > */
+ ctype_name,
+ /* Printable characters 64-95 */
+ ctype_name,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
+ 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
+ 30, 31, 32, 33, 34, 35,
+ ctype_other, /* [ */
+ ctype_name,
+ ctype_other, /* ] */
+ ctype_name, ctype_name,
+ /* Printable characters 96-126 and DEL */
+ ctype_name,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
+ 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
+ 30, 31, 32, 33, 34, 35,
+ ctype_other, /* { */
+ ctype_name,
+ ctype_other, /* } */
+ ctype_name, ctype_name,
+ /* Characters 128-159, binary tokens */
+ ctype_btoken, ctype_btoken, ctype_btoken, ctype_btoken, ctype_btoken,
+ ctype_btoken, ctype_btoken, ctype_btoken, ctype_btoken, ctype_btoken,
+ ctype_btoken, ctype_btoken, ctype_btoken, ctype_btoken, ctype_btoken,
+ ctype_btoken, ctype_btoken, ctype_btoken, ctype_btoken, ctype_btoken,
+ ctype_btoken, ctype_btoken, ctype_btoken, ctype_btoken, ctype_btoken,
+ ctype_btoken, ctype_btoken, ctype_btoken, ctype_btoken, ctype_btoken,
+ ctype_btoken, ctype_btoken,
+ /* Characters 160-191, not defined */
+ ctype_name, ctype_name, ctype_name, ctype_name, ctype_name,
+ ctype_name, ctype_name, ctype_name, ctype_name, ctype_name,
+ ctype_name, ctype_name, ctype_name, ctype_name, ctype_name,
+ ctype_name, ctype_name, ctype_name, ctype_name, ctype_name,
+ ctype_name, ctype_name, ctype_name, ctype_name, ctype_name,
+ ctype_name, ctype_name, ctype_name, ctype_name, ctype_name,
+ ctype_name, ctype_name,
+ /* Characters 192-223, not defined */
+ ctype_name, ctype_name, ctype_name, ctype_name, ctype_name,
+ ctype_name, ctype_name, ctype_name, ctype_name, ctype_name,
+ ctype_name, ctype_name, ctype_name, ctype_name, ctype_name,
+ ctype_name, ctype_name, ctype_name, ctype_name, ctype_name,
+ ctype_name, ctype_name, ctype_name, ctype_name, ctype_name,
+ ctype_name, ctype_name, ctype_name, ctype_name, ctype_name,
+ ctype_name, ctype_name,
+ /* Characters 224-255, not defined */
+ ctype_name, ctype_name, ctype_name, ctype_name, ctype_name,
+ ctype_name, ctype_name, ctype_name, ctype_name, ctype_name,
+ ctype_name, ctype_name, ctype_name, ctype_name, ctype_name,
+ ctype_name, ctype_name, ctype_name, ctype_name, ctype_name,
+ ctype_name, ctype_name, ctype_name, ctype_name, ctype_name,
+ ctype_name, ctype_name, ctype_name, ctype_name, ctype_name,
+ ctype_name, ctype_name
+};
diff --git a/pstoraster/isstate.h b/pstoraster/isstate.h
new file mode 100644
index 000000000..15e944bca
--- /dev/null
+++ b/pstoraster/isstate.h
@@ -0,0 +1,40 @@
+/* Copyright (C) 1993, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* isstate.h */
+/* State structure for Ghostscript save/restore machinery */
+/* Requires isave.h */
+
+/* Saved state of allocator and other things as needed. */
+/*typedef struct alloc_save_s alloc_save_t;*/ /* in isave.h */
+struct alloc_save_s {
+ gs_ref_memory_t state; /* must be first for subclassing */
+ gs_dual_memory_t *dmem;
+ bool restore_names;
+ bool is_current;
+ ulong id;
+ void *client_data;
+};
+#define private_st_alloc_save() /* in isave.c */\
+ gs_private_st_suffix_add1(st_alloc_save, alloc_save_t, "alloc_save",\
+ save_enum_ptrs, save_reloc_ptrs, st_ref_memory, client_data)
diff --git a/pstoraster/istack.c b/pstoraster/istack.c
new file mode 100644
index 000000000..14d036939
--- /dev/null
+++ b/pstoraster/istack.c
@@ -0,0 +1,490 @@
+/* Copyright (C) 1992, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* istack.c */
+/* Ghostscript expandable stack manager */
+#include "memory_.h"
+#include "ghost.h"
+#include "gsstruct.h"
+#include "gsutil.h"
+#include "errors.h"
+#include "ialloc.h"
+#include "istack.h"
+#include "istruct.h" /* for gs_reloc_refs */
+#include "iutil.h"
+#include "ivmspace.h" /* for local/global test */
+#include "store.h"
+
+/* Forward references */
+private void init_block(P3(ref_stack *, ref *, uint));
+int ref_stack_push_block(P3(ref_stack *, uint, uint));
+
+/* GC procedures */
+#define sptr ((ref_stack *)vptr)
+private CLEAR_MARKS_PROC(ref_stack_clear_marks) {
+ r_clear_attrs(&sptr->current, l_mark);
+}
+private ENUM_PTRS_BEGIN(ref_stack_enum_ptrs) return 0;
+ case 0:
+ *pep = &sptr->current;
+ return ptr_ref_type;
+} }
+private RELOC_PTRS_BEGIN(ref_stack_reloc_ptrs) {
+#if stacks_are_segmented
+ /* In a segmented environment, the top block can't move, */
+ /* so we don't need to relocate pointers to it. */
+#else
+ /* Note that the relocation must be a multiple of sizeof(ref_packed) */
+ /* * align_packed_per_ref, but it need not be a multiple of */
+ /* sizeof(ref). Therefore, we must do the adjustments using */
+ /* ref_packed pointers rather than ref pointers. */
+ ref_packed *bot = (ref_packed *)sptr->current.value.refs;
+ long reloc;
+ gs_reloc_refs((ref_packed *)&sptr->current,
+ (ref_packed *)(&sptr->current + 1),
+ gcst);
+ r_clear_attrs(&sptr->current, l_mark);
+ reloc = bot - (ref_packed *)sptr->current.value.refs;
+#define reloc_p(p)\
+ sptr->p = (ref *)((ref_packed *)sptr->p - reloc);
+ reloc_p(p);
+ reloc_p(bot);
+ reloc_p(top);
+#undef reloc_p
+#endif
+} RELOC_PTRS_END
+/* Structure type for a ref_stack. */
+public_st_ref_stack();
+
+/* Initialize a stack. */
+void
+ref_stack_init(register ref_stack *pstack, ref *psb,
+ uint bot_guard, uint top_guard, ref *pguard, gs_ref_memory_t *mem)
+{ uint size = r_size(psb);
+ uint avail = size - (stack_block_refs + bot_guard + top_guard);
+ ref_stack_block *pblock = (ref_stack_block *)psb->value.refs;
+ s_ptr body = (s_ptr)(pblock + 1);
+ pstack->bot = body + bot_guard;
+ pstack->p = pstack->bot - 1;
+ pstack->top = pstack->p + avail;
+ pstack->current = *psb;
+ pstack->extension_size = 0;
+ pstack->extension_used = 0;
+
+ make_int(&pstack->max_stack, avail);
+ pstack->requested = 0;
+
+ pstack->bot_guard = bot_guard;
+ pstack->top_guard = top_guard;
+ pstack->block_size = size - segmented_guard(bot_guard + top_guard);
+ pstack->body_size = avail;
+ if ( pguard != 0 )
+ pstack->guard_value = *pguard;
+ else
+ make_tav(&pstack->guard_value, t__invalid, 0, intval, 0);
+ pstack->underflow_error = -1; /* bogus, caller must set */
+ pstack->overflow_error = -1; /* bogus, caller must set */
+ pstack->allow_expansion = true; /* default, caller may reset */
+ pstack->memory = mem;
+ init_block(pstack, psb, 0);
+ refset_null(pstack->bot, avail);
+ make_empty_array(&pblock->next, 0);
+}
+
+/* Set the maximum number of elements allowed on a stack. */
+int
+ref_stack_set_max_count(ref_stack *pstack, long nmax)
+{ uint nmin = pstack->extension_size + (pstack->top - pstack->bot + 1);
+ if ( nmax < nmin )
+ nmax = nmin;
+ if ( nmax > max_uint / sizeof(ref) )
+ nmax = max_uint / sizeof(ref);
+ if ( !pstack->allow_expansion )
+ { uint ncur = pstack->body_size;
+ if ( nmax > ncur )
+ nmax = ncur;
+ }
+ pstack->max_stack.value.intval = nmax;
+ return 0;
+}
+
+/* Return the number of elements on a stack. */
+uint
+ref_stack_count(const ref_stack *pstack)
+{ return pstack->extension_used + (pstack->p - pstack->bot + 1);
+}
+
+/* Retrieve a given element from the stack, counting from */
+/* 0 as the top element. */
+ref *
+ref_stack_index(const ref_stack *pstack, long idx)
+{ ref_stack_block *pblock;
+ uint used = pstack->p + 1 - pstack->bot;
+ if ( idx < 0 )
+ return NULL;
+ if ( idx < used ) /* common case */
+ return pstack->p - (uint)idx;
+ pblock = (ref_stack_block *)pstack->current.value.refs;
+ do
+ { pblock = (ref_stack_block *)pblock->next.value.refs;
+ if ( pblock == 0 )
+ return NULL;
+ idx -= used;
+ used = r_size(&pblock->used);
+ }
+ while ( idx >= used );
+ return pblock->used.value.refs + (used - 1 - (uint)idx);
+}
+
+/* Count the number of elements down to and including the first mark. */
+/* If no mark is found, return 0. */
+uint
+ref_stack_counttomark(const ref_stack *pstack)
+{ uint scanned = 0;
+ STACK_LOOP_BEGIN(pstack, p, used)
+ { uint count = used;
+ p += used - 1;
+ for ( ; count; count--, p-- )
+ if ( r_has_type(p, t_mark) )
+ return scanned + (used - count + 1);
+ scanned += used;
+ }
+ STACK_LOOP_END(p, used)
+ return 0;
+}
+
+/* Store the top elements of a stack into an array, */
+/* with or without store/undo checking. */
+/* May return e_rangecheck or e_invalidaccess. */
+int
+ref_stack_store(const ref_stack *pstack, ref *parray, uint count, uint skip,
+ int age, bool check, client_name_t cname)
+{ uint left, pass;
+ ref *to;
+ uint space = r_space(parray);
+ if ( count > ref_stack_count(pstack) || count > r_size(parray) )
+ return_error(e_rangecheck);
+ if ( check && space != avm_local )
+ { /* Pre-check the elements being stored. */
+ left = count, pass = skip;
+ STACK_LOOP_BEGIN(pstack, ptr, size)
+ if ( size <= pass )
+ pass -= size;
+ else
+ { int code;
+ if ( pass != 0 )
+ size -= pass, pass = 0;
+ ptr += size;
+ if ( size > left )
+ size = left;
+ left -= size;
+ code = refs_check_space(ptr - size, size, space);
+ if ( code < 0 )
+ return code;
+ if ( left == 0 )
+ break;
+ }
+ STACK_LOOP_END(ptr, size)
+ }
+ to = parray->value.refs + count;
+ left = count, pass = skip;
+ STACK_LOOP_BEGIN(pstack, from, size)
+ if ( size <= pass )
+ pass -= size;
+ else
+ { if ( pass != 0 )
+ size -= pass, pass = 0;
+ from += size;
+ if ( size > left )
+ size = left;
+ left -= size;
+ switch ( age )
+ {
+ case -1: /* not an array */
+ while ( size-- )
+ { from--, to--;
+ ref_assign(to, from);
+ }
+ break;
+ case 0: /* old array */
+ while ( size-- )
+ { from--, to--;
+ ref_assign_old(parray, to, from, cname);
+ }
+ break;
+ case 1: /* new array */
+ while ( size-- )
+ { from--, to--;
+ ref_assign_new(to, from);
+ }
+ break;
+ }
+ if ( left == 0 )
+ break;
+ }
+ STACK_LOOP_END(from, size)
+ r_set_size(parray, count);
+ return 0;
+}
+
+/* Pop a given number of elements off a stack. */
+/* The number must not exceed the number of elements in use. */
+void
+ref_stack_pop(register ref_stack *pstack, uint count)
+{ uint used;
+ while ( (used = pstack->p + 1 - pstack->bot) < count )
+ { count -= used;
+ pstack->p = pstack->bot - 1;
+ ref_stack_pop_block(pstack);
+ }
+ pstack->p -= count;
+}
+
+/* Pop the top block off a stack. */
+int
+ref_stack_pop_block(register ref_stack *pstack)
+{ s_ptr bot = pstack->bot;
+ uint count = pstack->p + 1 - bot;
+ ref_stack_block *pcur =
+ (ref_stack_block *)pstack->current.value.refs;
+ register ref_stack_block *pnext =
+ (ref_stack_block *)pcur->next.value.refs;
+ uint used;
+ ref *body;
+ ref next;
+ if ( pnext == 0 )
+ return_error(pstack->underflow_error);
+ used = r_size(&pnext->used);
+ body = (ref *)(pnext + 1) + flat_guard(pstack->bot_guard);
+ next = pcur->next;
+ /*
+ * If we're on a segmented system, the top block does not move,
+ * so we move up the used part of the top block, copy the contents
+ * of the next block under it, and free the next block.
+ * We also do this on non-segmented systems if the contents of the
+ * two blocks won't fit in a single block; in this case we copy up
+ * as much as will fit. On non-segmented systems where the contents
+ * of both blocks fit in a single block, we copy the used part
+ * of the top block to the top of the next block, and free
+ * the top block.
+ */
+ if ( used + count > pstack->body_size )
+ { /* Move as much into the top block as will fit. */
+ uint moved = pstack->body_size - count;
+ uint left;
+ if ( moved == 0 )
+ return_error(e_Fatal);
+ memmove(bot + moved, bot, count * sizeof(ref));
+ left = used - moved;
+ memcpy(bot, body + left, moved * sizeof(ref));
+ refset_null(body + left, moved);
+ r_dec_size(&pnext->used, moved);
+ pstack->p = pstack->top;
+ pstack->extension_used -= moved;
+ }
+ else
+ {
+#if stacks_are_segmented
+ /* We know there are no guard elements in the next block. */
+ memmove(bot + used, bot, count * sizeof(ref));
+ memcpy(bot, body, used * sizeof(ref));
+ pcur->next = pnext->next;
+ gs_free_ref_array(pstack->memory, &next, "ref_stack_pop_block");
+#else
+ memcpy(body + used, bot, count * sizeof(ref));
+ pstack->bot = bot = body;
+ pstack->top = bot + pstack->body_size - 1;
+ gs_free_ref_array(pstack->memory, &pstack->current,
+ "ref_stack_pop_block");
+ pstack->current = next;
+#endif
+ pstack->p = bot + (used + count - 1);
+ pstack->extension_size -= pstack->body_size;
+ pstack->extension_used -= used;
+ }
+ return 0;
+}
+
+/* Extend a stack to recover from an overflow condition. */
+/* May return overflow_error or e_VMerror. */
+int
+ref_stack_extend(ref_stack *pstack, uint request)
+{ uint keep = (pstack->top - pstack->bot + 1) / 3;
+ uint count = pstack->p - pstack->bot + 1;
+
+ if ( pstack->p < pstack->bot )
+ { /* Adding another block can't help things. */
+ return_error(pstack->overflow_error);
+ }
+ if ( keep + request > pstack->body_size )
+ keep = pstack->body_size - request;
+ if ( keep > count )
+ keep = count; /* required by ref_stack_push_block */
+ return ref_stack_push_block(pstack, keep, request);
+}
+
+/* Push N empty slots onto a stack. These slots are not initialized; */
+/* the caller must fill them immediately. May return overflow_error */
+/* (if max_stack would be exceeded, or the stack has no allocator) */
+/* or e_VMerror. */
+int
+ref_stack_push(register ref_stack *pstack, uint count)
+{ /* Don't bother to pre-check for overflow: we must be able to */
+ /* back out in the case of a VMerror anyway, and */
+ /* ref_stack_push_block will make the check itself. */
+ uint needed = count;
+ uint added;
+ for ( ; (added = pstack->top - pstack->p) < needed; needed -= added )
+ { int code;
+ pstack->p = pstack->top;
+ code =
+ ref_stack_push_block(pstack,
+ (pstack->top - pstack->bot + 1) / 3,
+ count);
+ if ( code < 0 )
+ { /* Back out. */
+ ref_stack_pop(pstack, count - needed);
+ pstack->requested = count;
+ return code;
+ }
+ }
+ pstack->p += needed;
+ return 0;
+}
+
+/* Push a block onto the stack, specifying how many elements of */
+/* the current top block should remain in the top block and also */
+/* how many elements we are trying to add. */
+/* May return overflow_error or e_VMerror. */
+/* Must have keep <= count. */
+int
+ref_stack_push_block(register ref_stack *pstack, uint keep, uint add)
+{ uint count = pstack->p - pstack->bot + 1;
+ uint move = count - keep;
+ ref_stack_block *pcur = (ref_stack_block *)pstack->current.value.refs;
+ ref next;
+ ref_stack_block *pnext;
+ ref *body;
+ int code;
+ if ( keep > count )
+ return_error(e_Fatal);
+ /* Check for overflowing the maximum size, */
+ /* or expansion not allowed. */
+ if ( pstack->memory == 0 ||
+ pstack->extension_used + (pstack->top - pstack->bot) + add >=
+ pstack->max_stack.value.intval ||
+ !pstack->allow_expansion
+ )
+ return_error(pstack->overflow_error);
+ code = gs_alloc_ref_array(pstack->memory, &next, 0,
+ pstack->block_size, "ref_stack_push_block");
+ if ( code < 0 )
+ return code;
+ pnext = (ref_stack_block *)next.value.refs;
+ body = (ref *)(pnext + 1);
+#if stacks_are_segmented
+ /* Copy all but the top keep elements into the new block, */
+ /* and move the top elements down. */
+ /* We know there are no guard elements in the new block. */
+ memcpy(body, pstack->bot, move * sizeof(ref));
+ /* Clear the elements above the top of the new block. */
+ refset_null(body + move, pstack->body_size - move);
+ if ( keep <= move )
+ { /* No overlap, memcpy is safe. */
+ memcpy(pstack->bot, pstack->bot + move, keep * sizeof(ref));
+ }
+ else
+ { uint i;
+ s_ptr bot = pstack->bot;
+ s_ptr up = bot + move;
+ for ( i = 0; i < keep; i++ )
+ bot[i] = up[i];
+ }
+ pnext->next = pcur->next;
+ pnext->used = next;
+ pcur->next = next;
+ pnext->used.value.refs = body;
+ r_set_size(&pnext->used, move);
+#else
+ /* Copy the top keep elements into the new block, */
+ /* and make the new block the top block. */
+ init_block(pstack, &next, keep);
+ body += pstack->bot_guard;
+ memcpy(body, pstack->bot + move, keep * sizeof(ref));
+ /* Clear the elements above the top of the new block. */
+ refset_null(body + keep, pstack->body_size - keep);
+ /* Clear the elements above the top of the old block. */
+ refset_null(pstack->bot + move, keep);
+ pnext->next = pstack->current;
+ pcur->used.value.refs = pstack->bot;
+ r_set_size(&pcur->used, move);
+ pstack->current = next;
+ pstack->bot = body;
+ pstack->top = pstack->bot + pstack->body_size - 1;
+#endif
+ pstack->p = pstack->bot + keep - 1;
+ pstack->extension_size += pstack->body_size;
+ pstack->extension_used += move;
+ return 0;
+}
+
+/* Clean up a stack for garbage collection. */
+void
+ref_stack_cleanup(ref_stack *pstack)
+{ ref_stack_block *pblock =
+ (ref_stack_block *)pstack->current.value.refs;
+ refset_null(pstack->p + 1, pstack->top - pstack->p);
+ pblock->used = pstack->current; /* set attrs */
+ pblock->used.value.refs = pstack->bot;
+ r_set_size(&pblock->used, pstack->p + 1 - pstack->bot);
+}
+
+/* ------ Internal routines ------ */
+
+/* Initialize the guards and body of a stack block. */
+/* Note that this always initializes the guards, so it should not be used */
+/* for extension blocks in a segmented environments. */
+private void
+init_block(ref_stack *pstack, ref *psb, uint used)
+{ ref *brefs = psb->value.refs;
+#define pblock ((ref_stack_block *)brefs)
+ register uint i;
+ register ref *p;
+ for ( i = pstack->bot_guard, p = brefs + stack_block_refs;
+ i != 0; i--, p++
+ )
+ ref_assign(p, &pstack->guard_value);
+ /* The top guard elements will never be read, */
+ /* but we need to initialize them for the sake of the GC. */
+ /* We can use refset_null for this, because even though it uses */
+ /* make_null_new and stack elements must not be marked new, */
+ /* these slots will never actually be read or written. */
+ if ( pstack->top_guard )
+ { ref *top = brefs + r_size(psb);
+ int top_guard = pstack->top_guard;
+ refset_null(top - top_guard, top_guard);
+ }
+ pblock->used = *psb;
+ pblock->used.value.refs = brefs + stack_block_refs + pstack->bot_guard;
+ r_set_size(&pblock->used, 0);
+}
diff --git a/pstoraster/istack.h b/pstoraster/istack.h
new file mode 100644
index 000000000..bd69c0fda
--- /dev/null
+++ b/pstoraster/istack.h
@@ -0,0 +1,235 @@
+/* Copyright (C) 1992, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* istack.h */
+/* Definitions for expandable Ghostscript stacks */
+/* Requires iref.h */
+
+#ifndef istack_INCLUDED
+# define istack_INCLUDED
+
+/* Define an opaque allocator type. */
+#ifndef gs_ref_memory_DEFINED
+# define gs_ref_memory_DEFINED
+typedef struct gs_ref_memory_s gs_ref_memory_t;
+#endif
+
+/*
+ * The 3 principal Ghostscript stacks (operand, execution, and dictionary)
+ * are implemented as a linked list of blocks. On segmented MS-DOS and
+ * MS Windows systems, where there is a substantial performance advantage
+ * to keeping the top of the stack in the primary data segment, the top
+ * block is stored there, and copied to and from blocks in the heap;
+ * on systems with flat address spaces, the top block is stored in the heap
+ * like other blocks. Note that in environments with multiple PostScript
+ * contexts, the MS-DOS approach requires some combination of keeping
+ * multiple top blocks in the primary data segment, and copying top blocks
+ * to and from the heap when switching contexts.
+ *
+ * Since all operators exit cleanly in case of stack under- or overflow,
+ * we handle all issues related to stack blocks in the top-level error
+ * recovery code in interp.c. A few situations require special treatment:
+ * see ostack.h, estack.h, and dstack.h for details.
+ */
+
+#define stacks_are_segmented arch_ptrs_are_segmented
+#if stacks_are_segmented
+# define flat_guard(n) 0
+# define segmented_guard(n) (n)
+#else
+# define flat_guard(n) (n)
+# define segmented_guard(n) 0
+#endif
+
+typedef ref _ds *s_ptr;
+typedef const ref _ds *const_s_ptr;
+
+/*
+ * Define the structure for a stack block.
+ * In order to simplify allocation, stack blocks are implemented as
+ * t_array objects, with the first few elements used for special purposes.
+ * The actual layout is as follows:
+ * ref_stack_block structure
+ * bottom guard if any (see below)
+ * used elements of block
+ * unused elements of block
+ * top guard if any (see below)
+ * The `next' member of the next higher stack block includes all of this.
+ * The `used' member only includes the used elements of this block.
+ * Notes:
+ * - In the top block, the size of the `used' member may not be correct.
+ * - In all blocks but the top, we fill the unused elements with nulls.
+ */
+typedef struct ref_stack_block_s {
+ ref next; /* t_array, next lower block on stack */
+ ref used; /* t_array, subinterval of this block */
+ /* Actual stack starts here */
+} ref_stack_block;
+#define stack_block_refs (sizeof(ref_stack_block) / sizeof(ref))
+
+/*
+ * In order to detect under- and overflow with minimum overhead, we may put
+ * guard elements at the top and bottom of the stacks (see dstack.h,
+ * estack.h, and ostack.h for details of the individual stacks).
+ * On segmented systems, we only put guard elements around the top block;
+ * on other systems, we put guard elements around every block. Note that
+ * in the latter case, the 'current' and 'next' arrays include the
+ * guard elements.
+ */
+
+/*
+ * The garbage collector requires that the entire contents of every block
+ * be 'clean', i.e., contain legitimate refs; we also need to ensure that
+ * at GC time, pointers in unused areas of a block will not be followed
+ * (since they may be dangling). We ensure this as follows:
+ * - When allocating a new block, we set the entire body to nulls.
+ * This is necessary because the block may be freed before the next GC,
+ * and the GC must be able to scan (parse) refs even if they are free.
+ * - When adding a new block to the top of the stack, we set to nulls
+ * the unused area of the new next-to-top blocks.
+ * - At the beginning of garbage collection, we set to nulls the unused
+ * elements of the top block.
+ */
+
+/*
+ * Define the (statically allocated) state of a stack.
+ * Note that the total size of a stack cannot exceed max_uint,
+ * because it has to be possible to copy a stack to a PostScript array.
+ */
+#ifndef ref_stack_DEFINED
+typedef struct ref_stack_s ref_stack; /* also defined in idebug.h */
+# define ref_stack_DEFINED
+#endif
+struct ref_stack_s {
+ /* Following are updated dynamically. */
+ s_ptr p; /* current top element */
+ /* Following are updated when adding or deleting blocks. */
+ s_ptr bot; /* bottommost valid element */
+ s_ptr top; /* topmost valid element */
+ ref current; /* t_array for current top block */
+ uint extension_size; /* total sizes of extn. blocks */
+ uint extension_used; /* total used sizes of extn. blocks */
+ /* Following are updated rarely. */
+ ref max_stack; /* t_integer, Max...Stack user param */
+ uint requested; /* amount of last failing */
+ /* push or pop request */
+ /* Following are set at initialization. */
+ uint bot_guard; /* # of guard elements below bot */
+ uint top_guard; /* # of guard elements above top */
+ uint block_size; /* size of each block */
+ uint body_size; /* # of data slots in each block */
+ ref guard_value; /* t__invalid or t_operator, */
+ /* bottom guard value */
+ int underflow_error; /* error code for underflow */
+ int overflow_error; /* error code for overflow */
+ bool allow_expansion; /* if false, don't expand */
+ gs_ref_memory_t *memory; /* allocator for blocks */
+};
+#define public_st_ref_stack() /* in istack.c */\
+ gs_public_st_complex_only(st_ref_stack, ref_stack, "ref_stack",\
+ ref_stack_clear_marks, ref_stack_enum_ptrs, ref_stack_reloc_ptrs, 0)
+
+/*
+ * Define the loop control for enumerating the elements of a stack,
+ * as follows:
+
+ STACK_LOOP_BEGIN(pstack, ptr, size)
+ ...
+ STACK_LOOP_END(ptr, size)
+
+ * Each time through the loop, we set ptr to the bottom of a block
+ * and size to the size of the block.
+ */
+#define STACK_LOOP_BEGIN(pstack, ptr, size)\
+{ ref_stack_block *pblock_ = (ref_stack_block *)(pstack)->current.value.refs;\
+ ref *ptr = (pstack)->bot; uint size = (pstack)->p + 1 - (pstack)->bot;\
+ for ( ; ; ) {
+#define STACK_LOOP_END(ptr, size)\
+ pblock_ = (ref_stack_block *)pblock_->next.value.refs;\
+ if ( pblock_ == 0 ) break;\
+ ptr = pblock_->used.value.refs; size = r_size(&pblock_->used);\
+ }\
+}
+
+/* ------ Procedural interface ------ */
+
+/* Initialize a stack. Note that on segmented systems, */
+/* the body of the stack (the elements of the array given by the first ref) */
+/* must be in the _ds segment. */
+void ref_stack_init(P6(ref_stack *, ref *, uint, uint, ref *,
+ gs_ref_memory_t *));
+
+/* Set the maximum number of elements allowed on a stack. */
+/* Note that the value is a long, not a uint or a ulong. */
+int ref_stack_set_max_count(P2(ref_stack *, long));
+
+/* Return the number of elements on a stack. */
+uint ref_stack_count(P1(const ref_stack *));
+#define ref_stack_count_inline(pstk)\
+ ((pstk)->extension_size == 0 ?\
+ (pstk)->p + 1 - (pstk)->bot : ref_stack_count(pstk))
+
+/* Return the maximum number of elements allowed on a stack. */
+#define ref_stack_max_count(pstk) (uint)((pstk)->max_stack.value.intval)
+
+/* Return a pointer to a given element from the stack, counting from */
+/* 0 as the top element. If the index is out of range, return 0. */
+/* Note that the index is a long, not a uint or a ulong. */
+ref *ref_stack_index(P2(const ref_stack *, long));
+
+/* Count the number of elements down to and including the first mark. */
+/* If no mark is found, return 0. */
+uint ref_stack_counttomark(P1(const ref_stack *));
+
+/* Store N elements of a stack, starting M elements below the top, */
+/* into an array, with or without store/undo checking. */
+/* age=-1 for no check, 0 for old, 1 for new. */
+/* May return e_rangecheck or e_invalidaccess. */
+int ref_stack_store(P7(const ref_stack *, ref *, uint, uint, int, bool, client_name_t));
+
+/* Pop the top N elements off a stack. */
+/* The number must not exceed the number of elements in use. */
+void ref_stack_pop(P2(ref_stack *, uint));
+#define ref_stack_clear(pstk) ref_stack_pop(pstk, ref_stack_count(pstk))
+#define ref_stack_pop_to(pstk, depth)\
+ ref_stack_pop(pstk, ref_stack_count(pstk) - (depth))
+
+/* Pop the top block off a stack. */
+/* May return underflow_error. */
+int ref_stack_pop_block(P1(ref_stack *));
+
+/* Extend a stack to recover from an overflow condition. */
+/* Uses the requested value to decide what to do. */
+/* May return overflow_error or e_VMerror. */
+int ref_stack_extend(P2(ref_stack *, uint));
+
+/* Push N empty slots onto a stack. These slots are not initialized; */
+/* the caller must immediately fill them. May return overflow_error */
+/* (if max_stack would be exceeded, or the stack has no allocator) */
+/* or e_VMerror. */
+int ref_stack_push(P2(ref_stack *, uint));
+
+/* Clean up a stack for garbage collection. */
+void ref_stack_cleanup(P1(ref_stack *));
+
+#endif /* istack_INCLUDED */
diff --git a/pstoraster/istream.h b/pstoraster/istream.h
new file mode 100644
index 000000000..21c71e43f
--- /dev/null
+++ b/pstoraster/istream.h
@@ -0,0 +1,37 @@
+/* Copyright (C) 1994, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* istream.h */
+/* Interpreter-level stream procedures */
+/* Requires scommon.h, ostack.h */
+
+/* Procedures exported by zfproc.c */
+ /* for zfilter.c - procedure stream initialization */
+int sread_proc(P2(ref *, stream **));
+int swrite_proc(P2(ref *, stream **));
+ /* for interp.c, zfileio.c, zpaint.c - handle a procedure */
+ /* callback or an interrupt */
+int s_handle_read_exception(P5(int, const ref *, const ref *, int,
+ int (*)(P1(os_ptr))));
+int s_handle_write_exception(P5(int, const ref *, const ref *, int,
+ int (*)(P1(os_ptr))));
diff --git a/pstoraster/istruct.h b/pstoraster/istruct.h
new file mode 100644
index 000000000..7f01fdb5a
--- /dev/null
+++ b/pstoraster/istruct.h
@@ -0,0 +1,60 @@
+/* Copyright (C) 1994 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* istruct.h */
+/* Interpreter-level extension of gsstruct.h */
+
+#ifndef istruct_INCLUDED
+# define istruct_INCLUDED
+
+#include "gsstruct.h"
+
+/* ================ Refs ================ */
+
+/* The structure type descriptor for (blocks of) refs. */
+/* This is defined in igc.c and exported for isave.c. */
+extern_st(st_refs);
+
+/* Relocate a pointer to a ref[_packed]. */
+ptr_proc_reloc(gs_reloc_ref_ptr, ref_packed);
+
+/* Relocate a block of ref[_packed]s. */
+void gs_reloc_refs(P3(ref_packed *from, ref_packed *to, gc_state_t *gcst));
+
+/*
+ * Define an object allocated as a struct, but actually containing refs.
+ * Such objects are useful as the client_data of library structures
+ * (currently only gstates and fonts).
+ */
+struct_proc_clear_marks(ref_struct_clear_marks);
+struct_proc_enum_ptrs(ref_struct_enum_ptrs);
+struct_proc_reloc_ptrs(ref_struct_reloc_ptrs);
+#define gs__st_ref_struct(scope_st, stname, stype, sname)\
+ gs__st_complex_only(scope_st, stname, stype, sname, ref_struct_clear_marks,\
+ ref_struct_enum_ptrs, ref_struct_reloc_ptrs, 0)
+#define gs_public_st_ref_struct(stname, stype, sname)\
+ gs__st_ref_struct(public_st, stname, stype, sname)
+#define gs_private_st_ref_struct(stname, stype, sname)\
+ gs__st_ref_struct(private_st, stname, stype, sname)
+
+#endif /* istruct_INCLUDED */
diff --git a/pstoraster/iutil.c b/pstoraster/iutil.c
new file mode 100644
index 000000000..15aa509e1
--- /dev/null
+++ b/pstoraster/iutil.c
@@ -0,0 +1,536 @@
+/* Copyright (C) 1989, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* iutil.c */
+/* Utilities for Ghostscript interpreter */
+#include "math_.h" /* for fabs */
+#include "memory_.h"
+#include "string_.h"
+#include "ghost.h"
+#include "errors.h"
+#include "idict.h"
+#include "imemory.h"
+#include "iname.h"
+#include "ipacked.h" /* for array_get */
+#include "iutil.h" /* for checking prototypes */
+#include "ivmspace.h"
+#include "oper.h"
+#include "store.h"
+#include "gsmatrix.h"
+#include "gsutil.h"
+
+/* ------ Object utilities ------ */
+
+/* Copy refs from one place to another. */
+int
+refcpy_to_old(ref *aref, uint index, register const ref *from,
+ register uint size, client_name_t cname)
+{ register ref *to = aref->value.refs + index;
+ int code = refs_check_space(from, size, r_space(aref));
+ if ( code < 0 )
+ return code;
+ /* We have to worry about aliasing.... */
+ if ( to <= from || from + size <= to )
+ while ( size-- )
+ ref_assign_old(aref, to, from, cname), to++, from++;
+ else
+ for ( from += size, to += size; size--; )
+ from--, to--, ref_assign_old(aref, to, from, cname);
+ return 0;
+}
+void
+refcpy_to_new(register ref *to, register const ref *from, register uint size)
+{ while ( size-- )
+ ref_assign_new(to, from), to++, from++;
+}
+
+/* Fill a new object with nulls. */
+void
+refset_null(register ref *to, register uint size)
+{ while ( size-- ) make_null_new(to), to++;
+}
+
+/* Compare two objects for equality. */
+bool
+obj_eq(register const ref *pref1, register const ref *pref2)
+{ ref nref;
+ if ( r_type(pref1) != r_type(pref2) )
+ { /* Only a few cases need be considered here: */
+ /* integer/real (and vice versa), name/string */
+ /* (and vice versa), and extended operators. */
+ switch ( r_type(pref1) )
+ {
+ case t_integer:
+ return (r_has_type(pref2, t_real) &&
+ pref2->value.realval == pref1->value.intval);
+ case t_real:
+ return (r_has_type(pref2, t_integer) &&
+ pref2->value.intval == pref1->value.realval);
+ case t_name:
+ if ( !r_has_type(pref2, t_string) )
+ return false;
+ name_string_ref(pref1, &nref);
+ pref1 = &nref;
+ break;
+ case t_save:
+ return (r_has_type(pref2, t_save) &&
+ pref2->value.saveid == pref1->value.saveid);
+ case t_string:
+ if ( !r_has_type(pref2, t_name) )
+ return false;
+ name_string_ref(pref2, &nref);
+ pref2 = &nref;
+ break;
+ default:
+ if ( r_btype(pref1) != r_btype(pref2) )
+ return false;
+ }
+ }
+ /* Now do a type-dependent comparison. */
+ /* This would be very simple if we always filled in */
+ /* all 8 bytes of a ref, but we currently don't. */
+ switch ( r_btype(pref1) )
+ {
+ case t_array:
+ return (pref1->value.refs == pref2->value.refs &&
+ r_size(pref1) == r_size(pref2));
+ case t_mixedarray:
+ case t_shortarray:
+ return (pref1->value.packed == pref2->value.packed &&
+ r_size(pref1) == r_size(pref2));
+ case t_boolean:
+ return (pref1->value.boolval == pref2->value.boolval);
+ case t_dictionary:
+ return (pref1->value.pdict == pref2->value.pdict);
+ case t_file:
+ return (pref1->value.pfile == pref2->value.pfile &&
+ r_size(pref1) == r_size(pref2));
+ case t_integer:
+ return (pref1->value.intval == pref2->value.intval);
+ case t_mark:
+ case t_null:
+ return true;
+ case t_name:
+ return (pref1->value.pname == pref2->value.pname);
+ case t_oparray:
+ case t_operator:
+ return (op_index(pref1) == op_index(pref2));
+ case t_real:
+ return (pref1->value.realval == pref2->value.realval);
+ case t_string:
+ return (!bytes_compare(pref1->value.bytes, r_size(pref1),
+ pref2->value.bytes, r_size(pref2)));
+ case t_device:
+ return (pref1->value.pdevice == pref2->value.pdevice);
+ case t_fontID:
+ case t_struct:
+ case t_astruct:
+ return (pref1->value.pstruct == pref2->value.pstruct);
+ }
+ return false; /* shouldn't happen! */
+}
+
+/* Compare two objects for identity. */
+bool
+obj_ident_eq(register const ref *pref1, register const ref *pref2)
+{ if ( r_type(pref1) != r_type(pref2) )
+ return false;
+ if ( r_has_type(pref1, t_string) )
+ return (pref1->value.bytes == pref2->value.bytes &&
+ r_size(pref1) == r_size(pref2));
+ return obj_eq(pref1, pref2);
+}
+
+/*
+ * Create a printable representation of an object, a la cvs (full_print =
+ * false) or == (full_print = true). Return 0 if OK, <0 if the destination
+ * wasn't large enough or the object's contents weren't readable.
+ * If the object was a string or name, store a pointer to its characters
+ * even if it was too large.
+ */
+int
+obj_cvp(const ref *op, byte *str, uint len, uint *prlen, const byte **pchars,
+ bool full_print)
+{ char buf[30]; /* big enough for any float */
+ const byte *pstr = (const byte *)buf;
+ uint plen;
+ ref nref;
+
+ if ( full_print )
+ switch ( r_btype(op))
+ {
+ case t_boolean:
+ case t_integer:
+ case t_real:
+ break;
+ default:
+ return_error(e_typecheck);
+ }
+ switch ( r_btype(op) )
+ {
+ case t_boolean:
+ pstr = (const byte *)(op->value.boolval ? "true" : "false");
+ break;
+ case t_integer:
+ sprintf(buf, "%ld", op->value.intval);
+ break;
+ case t_name:
+ name_string_ref(op, &nref); /* name string */
+cvname: pstr = nref.value.bytes;
+ plen = r_size(&nref);
+ if ( pchars != 0 )
+ *pchars = pstr;
+ goto nl;
+ case t_oparray:
+ { uint index = op_index(op);
+ const op_array_table *opt = op_index_op_array_table(index);
+ name_index_ref(opt->nx_table[index - opt->base_index], &nref);
+ }
+ name_string_ref(&nref, &nref);
+ goto cvname;
+ case t_operator:
+ { /* Recover the name from the initialization table. */
+ uint index = op_index(op);
+ /* Check the validity of the index. (An out-of-bounds */
+ /* index is only possible when examining an invalid */
+ /* object using the debugger.) */
+ if ( index > 0 && index < op_def_count )
+ { pstr = (const byte *)(op_def_table[index]->oname + 1);
+ break;
+ }
+ }
+ /* Internal operator, no name. */
+ sprintf(buf, "@0x%lx", (ulong)op->value.opproc);
+ break;
+ case t_real:
+ /*
+ * To get fully accurate output results for IEEE single-
+ * precision floats (24 bits of mantissa), the ANSI
+ * %g default of 6 digits is not enough; 9 are needed.
+ * Unfortunately, using %.9g for floats (as opposed to
+ * doubles) produces unfortunate artifacts such as 0.01 5 mul
+ * printing as 0.049999997. Therefore, we print using %g,
+ * and if the result isn't accurate enough, print again
+ * using %.9g. Unfortunately, a few PostScript programs
+ * 'know' that the printed representation of floats fits
+ * into 6 digits (e.g., with cvs). We resolve this by letting
+ * cvs, cvrs, and = do what the Adobe interpreters appear
+ * to do (use %g), and only produce accurate output for ==,
+ * for which there is no analogue of cvs. What a hack!
+ */
+ { float value = op->value.realval;
+ sprintf(buf, "%g", value);
+ if ( full_print )
+ { float scanned;
+ sscanf(buf, "%f", &scanned);
+ if ( scanned != value )
+ sprintf(buf, "%.9g", value);
+ }
+ }
+ /*
+ * Make sure the output has a decimal point.
+ * This is needed for compatibility with
+ * Adobe (and other) interpreters.
+ */
+ if ( strchr(buf, '.') != NULL ) break;
+ { char *ept = strchr(buf, 'e');
+ if ( ept == NULL )
+ strcat(buf, ".0");
+ else
+ { /* Insert the .0 before the exponent. */
+ /* What a nuisance! */
+ char buf1[30];
+ strcpy(buf1, ept);
+ strcpy(ept, ".0");
+ strcat(buf, buf1);
+ }
+ }
+ break;
+ case t_string:
+ check_read(*op);
+ pstr = op->value.bytes;
+ plen = r_size(op);
+ if ( pchars != 0 )
+ *pchars = pstr;
+ goto nl;
+ default:
+ pstr = (const byte *)"--nostringval--";
+ }
+ plen = strlen((const char *)pstr);
+nl: *prlen = plen;
+ if ( plen > len )
+ return_error(e_rangecheck);
+ memcpy(str, pstr, plen);
+ return 0;
+}
+
+/* Find the index of an operator that doesn't have one stored in it. */
+ushort
+op_find_index(const ref *pref /* t_operator */)
+{ op_proc_p proc = real_opproc(pref);
+ register const op_def_ptr *opp = op_def_table;
+ register const op_def_ptr *opend = opp + op_def_count;
+ for ( ; ++opp < opend; )
+ { if ( (*opp)->proc == proc )
+ return opp - op_def_table;
+ }
+ /* Lookup failed! This isn't possible.... */
+ return 0;
+}
+
+/*
+ * Convert an operator index to an operator or oparray ref.
+ * This is only used for debugging and for 'get' from packed arrays,
+ * so it doesn't have to be very fast.
+ */
+void
+op_index_ref(uint index, ref *pref)
+{ const op_array_table *opt;
+ if ( op_index_is_operator(index) )
+ { make_oper(pref, index, op_index_proc(index));
+ return;
+ }
+ opt = op_index_op_array_table(index);
+ make_tasv(pref, t_oparray, opt->attrs, index,
+ const_refs, (opt->table.value.const_refs
+ + index - opt->base_index));
+}
+
+/* Get an element from an array of some kind. */
+/* This is also used to index into Encoding vectors, */
+/* the error name vector, etc. */
+int
+array_get(const ref *aref, long index_long, ref *pref)
+{ if ( (ulong)index_long >= r_size(aref) )
+ return_error(e_rangecheck);
+ switch ( r_type(aref) )
+ {
+ case t_array:
+ { const ref *pvalue =
+ aref->value.refs + (uint)index_long;
+ ref_assign(pref, pvalue);
+ } return 0;
+ case t_mixedarray:
+ { const ref_packed *packed = aref->value.packed;
+ uint index = (uint)index_long;
+ for ( ; index--; ) packed = packed_next(packed);
+ packed_get(packed, pref);
+ } return 0;
+ case t_shortarray:
+ { const ref_packed *packed =
+ aref->value.packed + (uint)index_long;
+ packed_get(packed, pref);
+ } return 0;
+ default:
+ return_error(e_typecheck);
+ }
+}
+
+/* Get an element from a packed array. */
+/* (This works for ordinary arrays too.) */
+/* Source and destination are allowed to overlap if the source is packed, */
+/* or if they are identical. */
+void
+packed_get(const ref_packed *packed, ref *pref)
+{ const ref_packed elt = *packed;
+ uint value = elt & packed_value_mask;
+ switch ( elt >> r_packed_type_shift )
+ {
+ default: /* (shouldn't happen) */
+ make_null(pref);
+ break;
+ case pt_executable_operator:
+ op_index_ref(value, pref);
+ break;
+ case pt_integer:
+ make_int(pref, (int)value + packed_min_intval);
+ break;
+ case pt_literal_name:
+ name_index_ref(value, pref);
+ break;
+ case pt_executable_name:
+ name_index_ref(value, pref);
+ r_set_attrs(pref, a_executable);
+ break;
+ case pt_full_ref:
+ case pt_full_ref+1:
+ ref_assign(pref, (const ref *)packed);
+ }
+}
+
+/* Check to make sure an interval contains no object references */
+/* to a space younger than a given one. */
+/* Return 0 or e_invalidaccess. */
+int
+refs_check_space(register const ref *bot, register uint size, uint space)
+{ for ( ; size--; bot++ )
+ store_check_space(space, bot);
+ return 0;
+}
+
+/* ------ String utilities ------ */
+
+/* Convert a C string to a Ghostscript string */
+int
+string_to_ref(const char *cstr, ref *pref, gs_ref_memory_t *mem,
+ client_name_t cname)
+{ uint size = strlen(cstr);
+ int code = gs_alloc_string_ref(mem, pref, a_all, size, cname);
+ if ( code < 0 )
+ return code;
+ memcpy(pref->value.bytes, cstr, size);
+ return 0;
+}
+
+/* Convert a Ghostscript string to a C string. */
+/* Return 0 iff the buffer can't be allocated. */
+char *
+ref_to_string(const ref *pref, gs_memory_t *mem, client_name_t cname)
+{ uint size = r_size(pref);
+ char *str = (char *)gs_alloc_string(mem, size + 1, cname);
+ if ( str == 0 )
+ return 0;
+ memcpy(str, (const char *)pref->value.bytes, size);
+ str[size] = 0;
+ return str;
+}
+
+/* ------ Operand utilities ------ */
+
+/* Get N numeric operands from the stack or an array. */
+/* Return a bit-mask indicating which ones are integers, */
+/* or a (negative) error indication. */
+/* The 1-bit in the bit-mask refers to the first operand. */
+/* Store float versions of the operands at pval. */
+/* The stack underflow check (check for t__invalid) is harmless */
+/* if the operands come from somewhere other than the stack. */
+int
+num_params(const ref *op, int count, float *pval)
+{ int mask = 0;
+ pval += count;
+ while ( --count >= 0 )
+ { mask <<= 1;
+ switch ( r_type(op) )
+ {
+ case t_real:
+ *--pval = op->value.realval;
+ break;
+ case t_integer:
+ *--pval = op->value.intval;
+ mask++;
+ break;
+ case t__invalid:
+ return_error(e_stackunderflow);
+ default:
+ return_error(e_typecheck);
+ }
+ op--;
+ }
+ /* If count is very large, mask might overflow. */
+ /* In this case we clearly don't care about the value of mask. */
+ return (mask < 0 ? 0 : mask);
+}
+
+/* Get a single real parameter. */
+/* The only possible error is e_typecheck. */
+/* If an error is returned, the return value is not updated. */
+int
+real_param(const ref *op, float *pparam)
+{ switch ( r_type(op) )
+ {
+ case t_integer:
+ *pparam = op->value.intval;
+ break;
+ case t_real:
+ *pparam = op->value.realval;
+ break;
+ default:
+ return_error(e_typecheck);
+ }
+ return 0;
+}
+
+/* Get an integer parameter in a given range. */
+int
+int_param(const ref *op, int max_value, int *pparam)
+{ check_int_leu(*op, max_value);
+ *pparam = (int)op->value.intval;
+ return 0;
+}
+
+/* Make real values on the operand stack. */
+void
+make_reals(ref *op, const float *pval, int count)
+{ for ( ; count--; op++, pval++ )
+ make_real(op, *pval);
+}
+
+/* Compute the error code when check_proc fails. */
+/* Note that the client, not this procedure, uses return_error. */
+/* The stack underflow check is harmless in the off-stack case. */
+int
+check_proc_failed(const ref *pref)
+{ return (r_is_array(pref) ? e_invalidaccess :
+ r_has_type(pref, t__invalid) ? e_stackunderflow :
+ e_typecheck);
+}
+
+/* Compute the error code when a type check on the stack fails. */
+/* Note that the client, not this procedure, uses return_error. */
+int
+check_type_failed(const ref *op)
+{ return (r_has_type(op, t__invalid) ? e_stackunderflow : e_typecheck);
+}
+
+/* ------ Matrix utilities ------ */
+
+/* Read a matrix operand. */
+/* Return 0 if OK, error code if not. */
+int
+read_matrix(const ref *op, gs_matrix *pmat)
+{ int code;
+ check_read_type(*op, t_array);
+ if ( r_size(op) != 6 )
+ return_error(e_rangecheck);
+ code = num_params(op->value.refs + 5, 6, (float *)pmat);
+ return (code < 0 ? code : 0);
+}
+
+/* Write a matrix operand. */
+/* Return 0 if OK, error code if not. */
+int
+write_matrix(register ref *op, const gs_matrix *pmat)
+{ ref *aptr;
+ const float *pel;
+ int i;
+ check_write_type(*op, t_array);
+ if ( r_size(op) != 6 )
+ return_error(e_rangecheck);
+ aptr = op->value.refs;
+ pel = (const float *)pmat;
+ for ( i = 5; i >= 0; i--, aptr++, pel++ )
+ { ref_save(op, aptr, "write_matrix");
+ make_real_new(aptr, *pel);
+ }
+ return 0;
+}
diff --git a/pstoraster/iutil.h b/pstoraster/iutil.h
new file mode 100644
index 000000000..a892a2249
--- /dev/null
+++ b/pstoraster/iutil.h
@@ -0,0 +1,106 @@
+/* Copyright (C) 1991, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* iutil.h */
+/* Prototypes for procedures in iutil.c */
+/* Requires imemory.h, ostack.h */
+
+/* ------ Object utilities ------ */
+
+/* Copy refs from one place to another. */
+/* (If we are copying to the stack, we can just use memcpy.) */
+void refcpy_to_new(P3(ref *to, const ref *from, uint size));
+int refcpy_to_old(P5(ref *aref, uint index, const ref *from, uint size, client_name_t cname));
+
+/* Fill an array with nulls. */
+void refset_null(P2(ref *to, uint size));
+
+/* Compare two objects for equality. */
+bool obj_eq(P2(const ref *, const ref *));
+
+/* Compare two objects for identity. */
+/* (This is not a standard PostScript concept.) */
+bool obj_ident_eq(P2(const ref *, const ref *));
+
+/*
+ * Create a printable representation of an object, a la cvs (full_print =
+ * false) or == (full_print = true). Return 0 if OK, <0 if the destination
+ * wasn't large enough or the object's contents weren't readable.
+ * If the object was a string or name, store a pointer to its characters
+ * even if it was too large. Note that if full_print is true, the only
+ * allowed types are boolean, integer, and real.
+ */
+int obj_cvp(P6(const ref *op, byte *str, uint len, uint *prlen,
+ const byte **pchars, bool full_print));
+#define obj_cvs(op, str, len, prlen, pchars)\
+ obj_cvp(op, str, len, prlen, pchars, false)
+
+/* Get an element from an array (packed or not). */
+int array_get(P3(const ref *, long, ref *));
+
+/* Get an element from a packed array. */
+/* (This works for ordinary arrays too.) */
+/* Source and destination are allowed to overlap if the source is packed, */
+/* or if they are identical. */
+void packed_get(P2(const ref_packed *, ref *));
+
+/* Check to make sure an interval contains no object references */
+/* to a space younger than a given one. */
+/* Return 0 or e_invalidaccess. */
+int refs_check_space(P3(const ref *refs, uint size, uint space));
+
+/* ------ String utilities ------ */
+
+/* Convert a C string to a string object. */
+int string_to_ref(P4(const char *, ref *, gs_ref_memory_t *, client_name_t));
+
+/* Convert a string object to a C string. */
+/* Return 0 iff the buffer can't be allocated. */
+char *ref_to_string(P3(const ref *, gs_memory_t *, client_name_t));
+
+/* ------ Operand utilities ------ */
+
+/* Get N numeric operands from the stack or an array. */
+int num_params(P3(const ref *, int, float *));
+
+/* Get a single real parameter. */
+/* The only possible error is e_typecheck. */
+int real_param(P2(const ref *, float *));
+
+/* Get an integer parameter in a given range. */
+int int_param(P3(const ref *, int, int *));
+
+/* Make real values on the stack. */
+void make_reals(P3(ref *, const float *, int));
+
+/* Define the gs_matrix type if necessary. */
+#ifndef gs_matrix_DEFINED
+# define gs_matrix_DEFINED
+typedef struct gs_matrix_s gs_matrix;
+#endif
+
+/* Read a matrix operand. */
+int read_matrix(P2(const ref *, gs_matrix *));
+
+/* Write a matrix operand. */
+int write_matrix(P2(ref *, const gs_matrix *));
diff --git a/pstoraster/iutil2.c b/pstoraster/iutil2.c
new file mode 100644
index 000000000..1624efd76
--- /dev/null
+++ b/pstoraster/iutil2.c
@@ -0,0 +1,100 @@
+/* Copyright (C) 1993, 1994 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* iutil2.c */
+/* Level 2 utilities for Ghostscript interpreter */
+#include "memory_.h"
+#include "string_.h"
+#include "ghost.h"
+#include "errors.h"
+#include "opcheck.h"
+#include "gsparam.h"
+#include "gsutil.h" /* bytes_compare prototype */
+#include "imemory.h" /* for iutil.h */
+#include "iutil.h"
+#include "iutil2.h"
+
+/* ------ Password utilities ------ */
+
+/* Read a password from a parameter list. */
+/* Return 0 if present, 1 if absent, or an error code. */
+int
+param_read_password(gs_param_list *plist, const char _ds *kstr, password *ppass)
+{ gs_param_string ps;
+ long ipass;
+ int code;
+ ps.data = (const byte *)ppass->data, ps.size = ppass->size,
+ ps.persistent = false;
+ code = param_read_string(plist, kstr, &ps);
+ switch ( code )
+ {
+ case 0: /* OK */
+ if ( ps.size > max_password )
+ return_error(e_limitcheck);
+ /* Copy the data back. */
+ memcpy(ppass->data, ps.data, ps.size);
+ ppass->size = ps.size;
+ return 0;
+ case 1: /* key is missing */
+ return 1;
+ }
+ /* We might have gotten a typecheck because */
+ /* the supplied password was an integer. */
+ if ( code != e_typecheck)
+ return code;
+ code = param_read_long(plist, kstr, &ipass);
+ if ( code != 0 ) /* error or missing */
+ return code;
+ sprintf((char *)ppass->data, "%ld", ipass);
+ ppass->size = strlen((char *)ppass->data);
+ return 0;
+}
+/* Write a password to a parameter list. */
+int
+param_write_password(gs_param_list *plist, const char _ds *kstr, const password *ppass)
+{ gs_param_string ps;
+ ps.data = (const byte *)ppass->data, ps.size = ppass->size,
+ ps.persistent = false;
+ if ( ps.size > max_password )
+ return_error(e_limitcheck);
+ return param_write_string(plist, kstr, &ps);
+}
+
+/* Check a password from a parameter list. */
+/* Return 0 if OK, 1 if not OK, or an error code. */
+int
+param_check_password(gs_param_list *plist, const password *ppass)
+{ if ( ppass->size != 0 )
+ { password pass;
+ int code = param_read_password(plist, "Password", &pass);
+ if ( code )
+ return code;
+ if ( pass.size != ppass->size ||
+ bytes_compare(&pass.data[0], pass.size,
+ &ppass->data[0],
+ ppass->size) != 0
+ )
+ return 1;
+ }
+ return 0;
+}
diff --git a/pstoraster/iutil2.h b/pstoraster/iutil2.h
new file mode 100644
index 000000000..786a6005d
--- /dev/null
+++ b/pstoraster/iutil2.h
@@ -0,0 +1,46 @@
+/* Copyright (C) 1993, 1994 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* iutil2.h */
+/* Interface to procedures in iutil2.c */
+
+/* ------ Password utilities ------ */
+
+/* Define the password structure. */
+#define max_password 64 /* must be at least 11 */
+typedef struct password_s {
+ uint size;
+ byte data[max_password];
+} password;
+# define NULL_PASSWORD {0, {0}}
+
+/* Define the system password(s). */
+extern password SystemParamsPassword;
+
+/* Transmit a password to or from a parameter list. */
+int param_read_password(P3(gs_param_list *, const char _ds *, password *));
+int param_write_password(P3(gs_param_list *, const char _ds *, const password *));
+
+/* Check a password from a parameter list. */
+/* Return 0 if OK, 1 if not OK, or an error code. */
+int param_check_password(P2(gs_param_list *, const password *));
diff --git a/pstoraster/ivmspace.h b/pstoraster/ivmspace.h
new file mode 100644
index 000000000..8568dc373
--- /dev/null
+++ b/pstoraster/ivmspace.h
@@ -0,0 +1,128 @@
+/* Copyright (C) 1992, 1993, 1994 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* ivmspace.h */
+/* Local/global space management */
+/* Requires iref.h */
+
+#ifndef ivmspace_INCLUDED
+# define ivmspace_INCLUDED
+
+/*
+ * r_space_bits and r_space_shift, which define the bits in a ref
+ * that carry VM space information, are defined in iref.h.
+ * r_space_bits must be at least 2.
+ */
+#define a_space (((1 << r_space_bits) - 1) << r_space_shift)
+/*
+ * avm_foreign is internal, the rest are visible to the programmer.
+ * The most static space (avm_foreign) must have a value of 0,
+ * so that we don't have to set any space bits in scalar refs.
+ */
+typedef enum {
+ avm_foreign = (0 << r_space_shift), /* must be 0 */
+ avm_system = (1 << r_space_shift),
+ avm_global = (2 << r_space_shift),
+ avm_local = (3 << r_space_shift),
+ avm_max = avm_local
+} avm_space;
+#define r_space(rp) (avm_space)(r_type_attrs(rp) & a_space)
+#define r_set_space(rp,space) r_store_attrs(rp, a_space, (uint)space)
+
+/* Define an array of allocators indexed by space. */
+#ifndef gs_ref_memory_DEFINED
+# define gs_ref_memory_DEFINED
+typedef struct gs_ref_memory_s gs_ref_memory_t;
+#endif
+typedef union vm_spaces_s {
+ gs_ref_memory_t *indexed[1 << r_space_bits];
+ struct _ssn {
+ gs_ref_memory_t *foreign;
+ gs_ref_memory_t *system;
+ gs_ref_memory_t *global;
+ gs_ref_memory_t *local;
+ } named;
+} vm_spaces;
+/* By convention, the vm_spaces member of a structure is named spaces. */
+#define space_foreign spaces.named.foreign
+#define space_system spaces.named.system
+#define space_global spaces.named.global
+#define space_local spaces.named.local
+
+/*
+ * According to the PostScript language specification, attempting to store
+ * a reference to a local object into a global object must produce an
+ * invalidaccess error. However, systemdict must be able to refer to
+ * a number of local dictionaries such as userdict and errordict.
+ * Therefore, we implement a special hack in 'def' that allows such stores
+ * if the dictionary being stored into is systemdict (which is normally
+ * only writable during initialization) or a dictionary that appears
+ * in systemdict (such as level2dict), and the current save level is zero
+ * (to guarantee that we can't get dangling pointers).
+ * We could allow this for any global dictionary, except that the garbage
+ * collector must treat any such dictionaries as roots when collecting
+ * local VM without collecting global VM.
+ * We make a similar exception for .makeglobaloperator; this requires
+ * treating the operator table as a GC root as well.
+ *
+ * We extend the local-into-global store check because we have four VM
+ * spaces (local, global, system, and foreign), and we allow PostScript
+ * programs to create objects in any of the first three. If we define
+ * the "generation" of an object as foreign = 0, system = 1, global = 2,
+ * and local = 3, then a store is legal iff the generation of the object
+ * into which a pointer is being stored is greater than or equal to
+ * the generation of the object into which the store is occurring.
+ *
+ * We must check for local-into-global stores in three categories of places:
+ *
+ * - The scanner, when it encounters a //name inside {}.
+ *
+ * - All operators that allocate ref-containing objects and also
+ * store into them:
+ * packedarray gstate makepattern?
+ * makefont scalefont definefont filter
+ *
+ * - All operators that store refs into existing objects
+ * ("operators" marked with * are actually PostScript procedures):
+ * put(array) putinterval(array) astore copy(to array)
+ * def store* put(dict) copy(dict)
+ * dictstack execstack .make(global)operator
+ * currentgstate defineusername?
+ */
+
+/* Test whether an object is in local space, */
+/* which implies that we need not check when storing into it. */
+#define r_is_local(rp) (r_space(rp) == avm_local)
+/* Test whether an object is foreign, i.e., outside known space. */
+#define r_is_foreign(rp) (r_space(rp) == avm_foreign)
+/* Check whether a store is allowed. */
+#define store_check_space(destspace,rpnew)\
+ if ( r_space(rpnew) > (destspace) )\
+ return_error(e_invalidaccess)
+#define store_check_dest(rpdest,rpnew)\
+ store_check_space(r_space(rpdest), rpnew)
+/* BACKWARD COMPATIBILITY (not used by any Ghostscript code per se) */
+#define check_store_space(rdest,rnewcont)\
+ store_check_dest(&(rdest),&(rnewcont))
+
+#endif /* ivmspace_INCLUDED */
diff --git a/pstoraster/main.h b/pstoraster/main.h
new file mode 100644
index 000000000..130d09fb8
--- /dev/null
+++ b/pstoraster/main.h
@@ -0,0 +1,93 @@
+/* Copyright (C) 1992, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* main.h */
+/* Backward-compatible interface to gsmain.c */
+#include "iminst.h"
+
+/*
+ * This file adds to imain.h some backward-compatible procedures and
+ * data elements that assume there is only a single instance of
+ * the interpreter.
+ */
+
+/* ================ Data elements ================ */
+
+/* Clients should never access these directly. */
+
+#define gs_user_errors (gs_main_instance_default()->user_errors)
+#define gs_lib_path (gs_main_instance_default()->lib_path)
+/* gs_lib_paths removed in release 3.65 */
+/* gs_lib_env_path removed in release 3.65 */
+
+/* ================ Exported procedures from gsmain.c ================ */
+
+/* ---------------- Initialization ---------------- */
+
+#define gs_init0(in, out, err, mlp)\
+ gs_main_init0(gs_main_instance_default(), in, out, err, mlp)
+
+#define gs_init1()\
+ gs_main_init1(gs_main_instance_default())
+
+#define gs_init2()\
+ gs_main_init2(gs_main_instance_default())
+
+#define gs_add_lib_path(path)\
+ gs_main_add_lib_path(gs_main_instance_default(), path)
+
+#define gs_set_lib_paths()\
+ gs_main_set_lib_paths(gs_main_instance_default())
+
+#define gs_lib_open(fname, pfile)\
+ gs_main_lib_open(gs_main_instance_default(), fname, pfile)
+
+/* ---------------- Execution ---------------- */
+
+#define gs_run_file(fn, ue, pec, peo)\
+ gs_main_run_file(gs_main_instance_default(), fn, ue, pec, peo)
+
+#define gs_run_string(str, ue, pec, peo)\
+ gs_main_run_string(gs_main_instance_default(), str, ue, pec, peo)
+
+#define gs_run_string_with_length(str, len, ue, pec, peo)\
+ gs_main_run_string_with_length(gs_main_instance_default(),\
+ str, len, ue, pec, peo)
+
+#define gs_run_file_open(fn, pfref)\
+ gs_main_run_file_open(gs_main_instance_default(), fn, pfref)
+
+#define gs_run_string_begin(ue, pec, peo)\
+ gs_main_run_string_begin(gs_main_instance_default(), ue, pec, peo)
+
+#define gs_run_string_continue(str, len, ue, pec, peo)\
+ gs_main_run_string_continue(gs_main_instance_default(),\
+ str, len, ue, pec, peo)
+
+#define gs_run_string_end(ue, pec, peo)\
+ gs_main_run_string_end(gs_main_instance_default(), ue, pec, peo)
+
+/* ---------------- Termination ---------------- */
+
+#define gs_finit(status, code)\
+ gs_main_finit(gs_main_instance_default(), status, code)
diff --git a/pstoraster/malloc_.h b/pstoraster/malloc_.h
new file mode 100644
index 000000000..43768934c
--- /dev/null
+++ b/pstoraster/malloc_.h
@@ -0,0 +1,51 @@
+/* Copyright (C) 1989, 1992, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* malloc_.h */
+/* Generic substitute for Unix malloc.h */
+
+/* We must include std.h before any file that includes sys/types.h. */
+#include "std.h"
+
+#ifdef __TURBOC__
+# include <alloc.h>
+#else
+# if defined(BSD4_2) || defined(apollo) || defined(vax) || defined(sequent) || defined(UTEK) || defined(_IBMR2)
+ extern char *malloc();
+ extern void free();
+# else /* should really be a POSIX define */
+# if defined(_HPUX_SOURCE) || defined(__CONVEX__) || defined(__convex__) || defined(__OSF__) || defined(__386BSD__) || defined(__STDC__) || defined(VMS)
+# include <stdlib.h>
+# else
+# include <malloc.h>
+# endif /* !_HPUX_SOURCE, ... */
+# endif /* !BSD4_2, ... */
+#endif /* !__TURBOC__ */
+
+/* (At least some versions of) Linux don't have a working realloc.... */
+#ifdef linux
+# define malloc__need_realloc
+void *gs_realloc(P3(void *, size_t, size_t));
+#else
+# define gs_realloc(ptr, old_size, new_size) realloc(ptr, new_size)
+#endif
diff --git a/pstoraster/math_.h b/pstoraster/math_.h
new file mode 100644
index 000000000..fe2c51fdb
--- /dev/null
+++ b/pstoraster/math_.h
@@ -0,0 +1,86 @@
+/* Copyright (C) 1989, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* math_.h */
+/* Generic substitute for math.h */
+
+/* We must include std.h before any file that includes sys/types.h. */
+#include "std.h"
+
+#if defined(VMS) && defined(__GNUC__)
+/* DEC VAX/VMS C comes with a math.h file, but GNU VAX/VMS C does not. */
+# include "vmsmath.h"
+#else
+# include <math.h>
+#endif
+
+/* math.h is different for Turbo and Unix.... */
+#ifndef M_PI
+# ifdef PI
+# define M_PI PI
+# else
+# define M_PI 3.14159265358979324
+# endif
+#endif
+
+/* Factors for converting between degrees and radians */
+#define degrees_to_radians (M_PI / 180.0)
+#define radians_to_degrees (180.0 / M_PI)
+
+/* Define the maximum value of a single-precision float. */
+/* This doesn't seem to be defined in any standard place, */
+/* and we need an exact value for it. */
+#undef MAX_FLOAT /* just in case */
+#if defined(vax) || defined(VAX) || defined(__vax) || defined(__VAX)
+/* Maximum exponent is +127, 23 bits of fraction. */
+# define MAX_FLOAT\
+ ((0x800000 - 1.0) * 0x1000000 * 0x1000000 * 0x10000000 * 0x10000000)
+#else
+/* IEEE, maximum exponent is +127, 23 bits of fraction + an implied '1'. */
+# define MAX_FLOAT\
+ ((0x1000000 - 1.0) * 0x1000000 * 0x1000000 * 0x10000000 * 0x10000000)
+#endif
+
+/* Define the hypot procedure on those few systems that don't provide it. */
+#ifdef _IBMR2
+/* The RS/6000 has hypot, but math.h doesn't declare it! */
+extern double hypot(double, double);
+#else
+# if !defined(__TURBOC__) && !defined(BSD4_2) && !defined(VMS)
+# define hypot(x,y) sqrt((x)*(x)+(y)*(y))
+# endif
+#endif
+
+#ifdef OSK
+/* OSK has atan2 and ldexp, but math.h doesn't declare them! */
+extern double atan2(), ldexp();
+#endif
+
+#ifdef DEBUG
+
+/* Intercept calls on sqrt for debugging. */
+extern double gs_sqrt(P3(double, const char *, int));
+#undef sqrt
+#define sqrt(x) gs_sqrt(x, __FILE__, __LINE__)
+
+#endif /* DEBUG */
diff --git a/pstoraster/memory_.h b/pstoraster/memory_.h
new file mode 100644
index 000000000..e915346ae
--- /dev/null
+++ b/pstoraster/memory_.h
@@ -0,0 +1,86 @@
+/* Copyright (C) 1989, 1992, 1993, 1994 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* memory_.h */
+/* Generic substitute for Unix memory.h */
+
+/* We must include std.h before any file that includes sys/types.h. */
+#include "std.h"
+
+/****** Note: the System V bcmp routine only returns zero or non-zero, ******/
+/****** unlike memcmp which returns -1, 0, or 1. ******/
+
+#ifdef __TURBOC__
+/* Define inline functions */
+# ifdef __WIN32__
+# define memcmp_inline(b1,b2,len) memcmp(b1,b2,len)
+# else
+# define memcmp_inline(b1,b2,len) __memcmp__(b1,b2,len)
+# endif
+/* The Turbo C implementation of memset swaps the arguments and calls */
+/* the non-standard routine setmem. We may as well do it in advance. */
+# undef memset /* just in case */
+# include <mem.h>
+# ifndef memset /* Borland C++ can inline this */
+# define memset(dest,chr,cnt) setmem(dest,cnt,chr)
+# endif
+#else
+ /* Not Turbo C, no inline functions */
+# define memcmp_inline(b1,b2,len) memcmp(b1,b2,len)
+ /* Apparently the newer VMS compilers include prototypes */
+ /* for the mem... routines in <string.h>. Unfortunately, */
+ /* gcc lies on Sun systems: it defines __STDC__ even if */
+ /* the header files in /usr/include are broken. */
+ /* However, Solaris systems, which define __svr4__, do have */
+ /* correct header files. */
+# if defined(VMS) || defined(_POSIX_SOURCE) || (defined(__STDC__) && (!defined(sun) || defined(__svr4__))) || defined(_HPUX_SOURCE) || defined(__WATCOMC__) || defined(THINK_C) || defined(bsdi)
+# include <string.h>
+# else
+# if defined(BSD4_2) || defined(UTEK)
+ extern bcopy(), bcmp(), bzero();
+# define memcpy(dest,src,len) bcopy(src,dest,len)
+# define memcmp(b1,b2,len) bcmp(b1,b2,len)
+ /* Define our own versions of missing routines (in gsmisc.c). */
+# define memory__need_memmove
+# define memmove(dest,src,len) gs_memmove(dest,src,len)
+# include <sys/types.h> /* for size_t */
+ void *gs_memmove(P3(void *, const void *, size_t));
+# define memory__need_memset
+# define memset(dest,ch,len) gs_memset(dest,ch,len)
+ void *gs_memset(P3(void *, int, size_t));
+# if defined(UTEK)
+# define memory__need_memchr
+# define memchr(ptr,ch,len) gs_memchr(ptr,ch,len)
+ const char *gs_memchr(P3(const void *, int, size_t));
+# endif /* UTEK */
+# else
+# include <memory.h>
+# if defined(__SVR3) || defined(sun) /* Not sure this is right.... */
+# define memory__need_memmove
+# define memmove(dest,src,len) gs_memmove(dest,src,len)
+# include <sys/types.h> /* for size_t */
+ void *gs_memmove(P3(void *, const void *, size_t));
+# endif /* __SVR3 or sun */
+# endif /* BSD4_2 or UTEK */
+# endif /* VMS, POSIX, ... */
+#endif /* !__TURBOC__ */
diff --git a/pstoraster/opcheck.h b/pstoraster/opcheck.h
new file mode 100644
index 000000000..1407afc90
--- /dev/null
+++ b/pstoraster/opcheck.h
@@ -0,0 +1,72 @@
+/* Copyright (C) 1993, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* opcheck.h */
+/* Operand checking for Ghostscript operators */
+/* Requires ialloc.h (for imemory), iref.h, errors.h */
+
+/*
+ * Check the type of an object. Operators almost always use check_type,
+ * which is defined in oper.h; check_type_only is for checking
+ * subsidiary objects obtained from places other than the stack.
+ */
+#define check_type_only(rf,typ)\
+ if ( !r_has_type(&rf,typ) ) return_error(e_typecheck)
+#define check_stype_only(rf,styp)\
+ if ( !r_has_stype(&rf,imemory,styp) ) return_error(e_typecheck)
+/* Check for array */
+#define check_array_else(rf,errstat)\
+ if ( !r_has_type(&rf, t_array) ) errstat
+#define check_array_only(rf)\
+ check_array_else(rf, return_error(e_typecheck))
+/* Check for procedure. check_proc_failed includes the stack underflow */
+/* check, but it doesn't do any harm in the off-stack case. */
+int check_proc_failed(P1(const ref *));
+#define check_proc(rf)\
+ if ( !r_is_proc(&rf) ) return_error(check_proc_failed(&rf));
+#define check_proc_only(rf) check_proc(rf)
+
+/* Check for read, write, or execute access. */
+#define check_access(rf,acc1)\
+ if ( !r_has_attr(&rf,acc1) ) return_error(e_invalidaccess)
+#define check_read(rf) check_access(rf,a_read)
+#define check_write(rf) check_access(rf,a_write)
+#define check_execute(rf) check_access(rf,a_execute)
+#define check_type_access_only(rf,typ,acc1)\
+ if ( !r_has_type_attrs(&rf,typ,acc1) )\
+ return_error((!r_has_type(&rf,typ) ? e_typecheck : e_invalidaccess))
+#define check_read_type_only(rf,typ)\
+ check_type_access_only(rf,typ,a_read)
+#define check_write_type_only(rf,typ)\
+ check_type_access_only(rf,typ,a_write)
+
+/* Check for an integer value within an unsigned bound. */
+#define check_int_leu(orf, u)\
+ check_type(orf, t_integer);\
+ if ( (ulong)(orf).value.intval > (u) ) return_error(e_rangecheck)
+#define check_int_leu_only(rf, u)\
+ check_type_only(rf, t_integer);\
+ if ( (ulong)(rf).value.intval > (u) ) return_error(e_rangecheck)
+#define check_int_ltu(orf, u)\
+ check_type(orf, t_integer);\
+ if ( (ulong)(orf).value.intval >= (u) ) return_error(e_rangecheck)
diff --git a/pstoraster/opdef.h b/pstoraster/opdef.h
new file mode 100644
index 000000000..0b39d06fa
--- /dev/null
+++ b/pstoraster/opdef.h
@@ -0,0 +1,143 @@
+/* Copyright (C) 1991, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* opdef.h */
+/* Operator definition interface for Ghostscript */
+
+/*
+ * Operator procedures take the pointer to the top of the o-stack
+ * as their argument. They return 0 for success, a negative code
+ * for an error, or a positive code for some uncommon situations (see below).
+ */
+
+/* Structure for initializing the operator table. */
+/*
+ * Each operator file declares an array of these, of the following kind:
+
+BEGIN_OP_DEFS(my_defs) {
+ {"1name", zname},
+ ...
+END_OP_DEFS(iproc) }
+
+ * where iproc is an initialization procedure for the file, or 0.
+ * This definition always appears at the END of the file,
+ * to avoid the need for forward declarations for all the
+ * operator procedures.
+ *
+ * Operators may be stored in dictionaries other than systemdict.
+ * We support this with op_def entries of a special form:
+
+ op_def_begin_dict("dictname"),
+
+ */
+typedef struct {
+ const char _ds *oname;
+ op_proc_p proc;
+} op_def;
+typedef const op_def *op_def_ptr;
+#define op_def_begin_dict(dname) {dname, 0}
+#define op_def_begin_filter() op_def_begin_dict("filterdict")
+#define op_def_begin_level2() op_def_begin_dict("level2dict")
+#define op_def_is_begin_dict(def) ((def)->proc == 0)
+#define op_def_end(iproc) {(char _ds *)0, (op_proc_p)iproc}
+
+/*
+ * We need to define each op_defs table as a procedure that returns
+ * the actual table, because of cross-segment linking restrictions
+ * in the Borland C compiler for MS Windows.
+ */
+
+#define BEGIN_OP_DEFS(xx_op_defs)\
+const op_def *xx_op_defs(P0())\
+{ static const far_data op_def op_defs_[] =
+
+#define END_OP_DEFS(iproc)\
+ op_def_end(iproc)\
+ };\
+ return op_defs_;
+
+/*
+ * Internal operators whose names begin with %, such as continuation
+ * operators, do not appear in systemdict. Ghostscript assumes
+ * that these operators cannot appear anywhere (in executable form)
+ * except on the e-stack; to maintain this invariant, the execstack
+ * operator converts them to literal form, and cvx refuses to convert
+ * them back. As a result of this invariant, they do not need to
+ * push themselves back on the e-stack when executed, since the only
+ * place they could have come from was the e-stack.
+ */
+#define op_def_is_internal(def) ((def)->oname[1] == '%')
+
+/*
+ * All operators are catalogued in a table; this is necessary because
+ * they must have a short packed representation for the sake of 'bind'.
+ * The `size' of an operator is normally its index in this table;
+ * however, internal operators have a `size' of 0, and their true index
+ * must be found by searching the table for their procedure address.
+ */
+ushort op_find_index(P1(const ref *));
+#define op_index(opref)\
+ (r_size(opref) == 0 ? op_find_index(opref) : r_size(opref))
+/*
+ * There are actually two kinds of operators: the real ones (t_operator),
+ * and ones defined by procedures (t_oparray). The catalog for t_operators
+ * is op_def_table, and their index is in the range [1..op_def_count-1].
+ */
+#define op_index_is_operator(index) ((index) < op_def_count)
+/*
+ * Because of a bug in Sun's SC1.0 compiler,
+ * we have to spell out the typedef for op_def_ptr here:
+ */
+extern const op_def **op_def_table;
+extern uint op_def_count;
+#define op_num_args(opref) (op_def_table[op_index(opref)]->oname[0] - '0')
+#define op_index_proc(index) (op_def_table[index]->proc)
+/*
+ * There are two catalogs for t_oparrays, one global and one local.
+ * Operator indices for the global table are in the range
+ * [op_def_count..op_def_count+op_array_global.count-1]
+ * Operator indices for the local table are in the range
+ * [op_def_count+r_size(&op_array_global.table)..
+ * op_def_count+r_size(&op_array_global.table)+op_array_local.count-1]
+ */
+typedef struct op_array_table_s {
+ ref table; /* t_array */
+ ushort *nx_table; /* name indices */
+ uint count; /* # of occupied entries */
+ uint base_index; /* operator index of first entry */
+ uint attrs; /* ref attrs of ops in this table */
+ ref *root_p; /* self-pointer for GC root */
+} op_array_table;
+extern op_array_table
+ op_array_table_global,
+ op_array_table_local;
+#define op_index_op_array_table(index)\
+ ((index) < op_array_table_local.base_index ?\
+ &op_array_table_global : &op_array_table_local)
+
+/*
+ * Convert an operator index to an operator or oparray ref.
+ * This is only used for debugging and for 'get' from packed arrays,
+ * so it doesn't have to be very fast.
+ */
+void op_index_ref(P2(uint, ref *));
diff --git a/pstoraster/oper.h b/pstoraster/oper.h
new file mode 100644
index 000000000..c50f1fecf
--- /dev/null
+++ b/pstoraster/oper.h
@@ -0,0 +1,100 @@
+/* Copyright (C) 1989, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* oper.h */
+/* Definitions for Ghostscript operators */
+#include "ostack.h"
+#include "opdef.h"
+#include "opextern.h"
+#include "opcheck.h"
+#include "iutil.h"
+
+/*
+ * In order to combine typecheck and stackunderflow error checking
+ * into a single test, we guard the bottom of the o-stack with
+ * additional entries of type t__invalid. However, if a type check fails,
+ * we must make an additional check to determine which error
+ * should be reported. In order not to have to make this check in-line
+ * in every type check in every operator, we define a procedure that takes
+ * an o-stack pointer and returns e_stackunderflow if it points to
+ * a guard entry, e_typecheck otherwise.
+ *
+ * Note that we only need to do this for typecheck, not for any other
+ * kind of error such as invalidaccess, since any operator that can
+ * generate the latter will do a check_type or a check_op first.
+ * (See ostack.h for more information.)
+ *
+ * We define the operand type of check_type_failed as const ref * rather than
+ * const_os_ptr simply because there are a number of routines that might
+ * be used either with a stack operand or with a general operand, and
+ * the check for t__invalid is harmless when applied to off-stack refs.
+ */
+int check_type_failed(P1(const ref *));
+
+/*
+ * Check the type of an object. Operators almost always use check_type,
+ * which includes the stack underflow check described just above;
+ * check_type_only is for checking subsidiary objects obtained from
+ * places other than the stack.
+ */
+#define return_op_typecheck(op)\
+ return_error(check_type_failed(op))
+#define check_type(orf,typ)\
+ if ( !r_has_type(&orf,typ) ) return_op_typecheck(&orf)
+#define check_stype(orf,styp)\
+ if ( !r_has_stype(&orf,imemory,styp) ) return_op_typecheck(&orf)
+#define check_array(orf)\
+ check_array_else(orf, return_op_typecheck(&orf))
+#define check_type_access(orf,typ,acc1)\
+ if ( !r_has_type_attrs(&orf,typ,acc1) )\
+ return_error((!r_has_type(&orf,typ) ? check_type_failed(&orf) :\
+ e_invalidaccess))
+#define check_read_type(orf,typ)\
+ check_type_access(orf,typ,a_read)
+#define check_write_type(orf,typ)\
+ check_type_access(orf,typ,a_write)
+
+/* Macro for as yet unimplemented operators. */
+/* The if ( 1 ) is to prevent the compiler from complaining about */
+/* unreachable code. */
+#define NYI(msg) if ( 1 ) return_error(e_undefined)
+
+/*
+ * If an operator has popped or pushed something on the control stack,
+ * it must return o_pop_estack or o_push_estack respectively,
+ * rather than 0, to indicate success.
+ * It is OK to return o_pop_estack if nothing has been popped,
+ * but it is not OK to return o_push_estack if nothing has been pushed.
+ *
+ * If an operator has suspended the current context and wants the
+ * interpreter to call the scheduler, it must return o_reschedule.
+ * It may also have pushed or popped elements on the control stack.
+ * (This is only used when the Display PostScript option is included.)
+ *
+ * These values must be greater than 1, and far enough apart from zero and
+ * from each other not to tempt a compiler into implementing a 'switch'
+ * on them using indexing rather than testing.
+ */
+#define o_push_estack 5
+#define o_pop_estack 14
+#define o_reschedule 22
diff --git a/pstoraster/opextern.h b/pstoraster/opextern.h
new file mode 100644
index 000000000..8b026eb93
--- /dev/null
+++ b/pstoraster/opextern.h
@@ -0,0 +1,108 @@
+/* Copyright (C) 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* opextern.h */
+/* Externally accessible operator declarations */
+
+/*
+ * Normally, the procedures that implement PostScript operators (named zX
+ * where X is the name of the operator, e.g., zadd) are private to the
+ * file in which they are defined. There are, however, a surprising
+ * number of these procedures that are used from other files.
+ * This file, opextern.h, declares all z* procedures that are
+ * - referenced from outside their defining file, and
+ * - present in *all* configurations of the interpreter.
+ * For z* procedures referenced from outside their file but not present
+ * in all configurations (e.g., Level 2 operators), the file making the
+ * reference must include a local extern. Not pretty, but c'est la vie.
+ */
+
+/* Operators exported for the special operator encoding in interp.c. */
+int zadd(P1(os_ptr));
+int zdef(P1(os_ptr));
+int zdup(P1(os_ptr));
+int zexch(P1(os_ptr));
+int zif(P1(os_ptr));
+int zifelse(P1(os_ptr));
+int zindex(P1(os_ptr));
+int zpop(P1(os_ptr));
+int zroll(P1(os_ptr));
+int zsub(P1(os_ptr));
+
+/* Operators exported for server loop implementations. */
+int zflush(P1(os_ptr));
+int zflushpage(P1(os_ptr));
+int zsave(P1(os_ptr));
+int zrestore(P1(os_ptr));
+
+/* Operators exported for save/restore. */
+int zgsave(P1(os_ptr));
+int zgrestore(P1(os_ptr));
+
+/* Operators exported for Level 2 pagedevice facilities. */
+int zcopy_gstate(P1(os_ptr));
+int zcurrentgstate(P1(os_ptr));
+int zgrestoreall(P1(os_ptr));
+int zgstate(P1(os_ptr));
+int zreadonly(P1(os_ptr));
+int zsetdevice(P1(os_ptr));
+int zsetgstate(P1(os_ptr));
+
+/* Operators exported for Level 2 "wrappers". */
+int zimage(P1(os_ptr));
+int zimagemask(P1(os_ptr));
+int zwhere(P1(os_ptr));
+
+/* Operators exported for specific-VM operators. */
+int zarray(P1(os_ptr));
+int zdict(P1(os_ptr));
+int zpackedarray(P1(os_ptr));
+int zstring(P1(os_ptr));
+
+/* Operators exported for user path decoding. */
+/* Note that only operators defined in all configurations are declared here. */
+int zclosepath(P1(os_ptr));
+int zcurveto(P1(os_ptr));
+int zlineto(P1(os_ptr));
+int zmoveto(P1(os_ptr));
+int zrcurveto(P1(os_ptr));
+int zrlineto(P1(os_ptr));
+int zrmoveto(P1(os_ptr));
+
+/* Operators exported for CIE cache loading. */
+int zcvx(P1(os_ptr));
+int zexec(P1(os_ptr)); /* also for .runexec */
+int zfor(P1(os_ptr));
+
+/* Odds and ends */
+int zbegin(P1(os_ptr));
+int zcleartomark(P1(os_ptr));
+int zend(P1(os_ptr));
+int zclosefile(P1(os_ptr)); /* for runexec_cleanup */
+int zsetfont(P1(os_ptr)); /* for cshow_continue */
+
+/* Operators exported for special customer needs. */
+int zcurrentdevice(P1(os_ptr));
+int ztoken(P1(os_ptr));
+int ztokenexec(P1(os_ptr));
+int zwrite(P1(os_ptr));
diff --git a/pstoraster/ostack.h b/pstoraster/ostack.h
new file mode 100644
index 000000000..6abc09af2
--- /dev/null
+++ b/pstoraster/ostack.h
@@ -0,0 +1,93 @@
+/* Copyright (C) 1991, 1994, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* ostack.h */
+/* Definitions for Ghostscript operand stack */
+
+#ifndef ostack_INCLUDED
+# define ostack_INCLUDED
+
+#include "istack.h"
+
+/* Define the operand stack pointers. */
+typedef s_ptr os_ptr;
+typedef const_s_ptr const_os_ptr;
+extern ref_stack o_stack;
+#define osbot (o_stack.bot)
+#define osp (o_stack.p)
+#define ostop (o_stack.top)
+
+/* Macro to ensure enough room on the operand stack */
+#define check_ostack(n)\
+ if ( ostop - osp < (n) )\
+ { o_stack.requested = (n); return_error(e_stackoverflow); }
+
+/* Operand stack manipulation. */
+
+/* Note that push sets osp to (the new value of) op. */
+/* The do... avoids problems with a possible enclosing 'if'. */
+#define push(n)\
+ do { if ( (op += (n)) > ostop )\
+ { o_stack.requested = (n); return_error(e_stackoverflow); }\
+ else osp = op;\
+ } while (0)
+
+/*
+ * Note that the pop macro only decrements osp, not op. For this reason,
+ *
+ * >>> pop should only be used just before returning, <<<
+ * >>> or else op must be decremented explicitly. <<<
+ */
+#define pop(n) (osp -= (n))
+
+/*
+ * Note that the interpreter does not check for operand stack underflow
+ * before calling the operator procedure. There are "guard" entries
+ * with invalid types and attributes just below the bottom of the
+ * operand stack: if the operator returns with a typecheck error,
+ * the interpreter checks for underflow at that time.
+ * Operators that don't typecheck their arguments must check for
+ * operand stack underflow explicitly; operators that take a variable
+ * number of arguments must also check for stack underflow in those cases
+ * where they expect more than their minimum number of arguments.
+ * (This is because the interpreter can only recognize that a typecheck
+ * is really a stackunderflow when the stack has fewer than the
+ * operator's declared minimum number of entries.)
+ */
+#define check_op(nargs)\
+ if ( op < osbot + ((nargs) - 1) ) return_error(e_stackunderflow)
+/*
+ * Similarly, in order to simplify some overflow checks, we allocate
+ * a few guard entries just above the top of the o-stack.
+ */
+
+/*
+ * The operand stack is implemented as a linked list of blocks;
+ * operators that can push or pop an unbounded number of values, or that
+ * access the entire o-stack, must take this into account. These are:
+ * (int)copy index roll clear count cleartomark
+ * counttomark aload astore packedarray
+ * getdeviceprops putdeviceprops
+ */
+
+#endif /* ostack_INCLUDED */
diff --git a/pstoraster/pdf_2ps.ps b/pstoraster/pdf_2ps.ps
new file mode 100644
index 000000000..f386ed92f
--- /dev/null
+++ b/pstoraster/pdf_2ps.ps
@@ -0,0 +1,270 @@
+% Copyright (C) 1994, 1996 Aladdin Enterprises. All rights reserved.
+%
+% This file is part of GNU Ghostscript.
+%
+% GNU Ghostscript is distributed in the hope that it will be useful, but
+% WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+% anyone for the consequences of using it or for whether it serves any
+% particular purpose or works at all, unless he says so in writing. Refer to
+% the GNU General Public License for full details.
+%
+% Everyone is granted permission to copy, modify and redistribute GNU
+% Ghostscript, but only under the conditions described in the GNU General
+% Public License. A copy of this license is supposed to have been given to
+% you along with GNU Ghostscript so you can know your rights and
+% responsibilities. It should be in a file named COPYING. Among other
+% things, the copyright notice and this notice must be preserved on all
+% copies.
+%
+% Aladdin Enterprises is not affiliated with the Free Software Foundation or
+% the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+% does not depend on any other GNU software.
+
+% pdf_2ps.ps
+% PDF to PostScript additions to PDF reader.
+
+GS_PDF_ProcSet begin
+pdfdict begin
+
+/.setlanguagelevel where { pop 2 .setlanguagelevel } if
+.currentglobal true .setglobal
+
+userdict /pdf2psdict 30 dict put
+pdf2psdict begin
+
+% Generate a sufficiently unique name (at least unique within the current
+% save/restore environment).
+/uniqueid#
+ { userdict length
+ { dup neg =string cvs dup 0 (_) putinterval cvn
+ userdict 1 index known not { exch pop exit } if pop 1 add
+ }
+ loop
+ } bdef
+
+% "Wrap" all the runtime operators so they call #exec.
+% Make sure we have enough room in the current dictionary for this.
+currentdict dup maxlength numargsdict length add .setmaxlength
+numargsdict
+ { 1 index load exch 2 index exch /#exec cvx 4 packedarray cvx def
+ }
+forall
+/pdfmark
+ /pdfmark load dup type /operatortype eq { 1 packedarray cvx } if
+ { /pdfmark counttomark 1 sub #exec } bind
+ aload length 1 add packedarray cvx
+def
+% Define #exec so it also prints out its arguments.
+/dictwrite# % <file> <dict> dictwrite# -
+ { dup length 240 le
+ { 1 index (mark ) writestring
+ { exch 2 index exch write#
+ 1 index ( ) writestring 1 index exch write#
+ 2 index (\n) writestring
+ }
+ forall dup (.dicttomark) writestring
+ }
+ { 2 copy length write=only 1 index ( dict\n) writestring
+ { exch 2 index dup (dup ) writestring exch write#
+ 1 index dup ( ) writestring exch write#
+ dup ( put\n) writestring
+ }
+ forall
+ }
+ ifelse pop
+ } bdef
+/fontwrite# % <file> <font> fontwrite# -
+ { % Find the named font and then modify it.
+ 2 copy /FontName get =string cvs
+ { dup dup length 1 sub 1 getinterval (%) ne { exit } if
+ 0 1 index length 1 sub getinterval
+ }
+ loop cvn write===only 1 index ( findfont ) writestring
+ % Load the appropriate Encoding, by name if possible.
+ dup /Encoding get
+ dup dup StandardEncoding eq exch ISOLatin1Encoding eq or
+ % Stack: file font encoding stdbool
+ { StandardEncoding eq { (StandardEncoding) } { (ISOLatin1Encoding) } ifelse
+ 2 index exch writestring
+ }
+ { 2 index exch write#
+ }
+ ifelse
+ % Check for modified Metrics.
+ dup /Metrics .knownget
+ { 2 index ( ) writestring
+ 2 index exch write#
+ }
+ { 1 index ( null) writestring
+ }
+ ifelse
+ 1 index ( .updatefont { /_ exch definefont } if\n) writestring
+ pop pop
+ } bdef
+/write#dict 10 dict dup begin
+ /arraytype
+ { dup xcheck { (}) ({) } { (]) ([) } ifelse
+ 2 index length 0 eq
+ { 3 index exch writestring exch
+ }
+ { 3 -1 roll
+ { 3 index 2 index writestring 3 index exch write# pop (\n) }
+ forall
+ }
+ ifelse pop writestring
+ } bdef
+ /dicttype
+ { null userdict { 3 index eq { exch pop exit } if pop } forall
+ dup null eq
+ { pop 2 copy dup /FID known { fontwrite# } { dictwrite# } ifelse
+ 1 index ( userdict ) writestring
+ uniqueid# 2 index 1 index write# 2 index ( 2 index put) writestring
+ userdict exch 3 -1 roll put pop
+ }
+ { exch pop cvx write===only
+ }
+ ifelse
+ } bdef
+ /marktype
+ { pop ([) writestring
+ } bdef
+ /packedarraytype
+ /arraytype load def
+ /realtype
+ { dup abs 16#ffffff le { dup dup cvi eq { cvi } if } if write=only
+ } bdef
+end def
+/write#
+ { dup type //write#dict exch .knownget { exec } { write===only } ifelse
+ } bind def
+
+% Rebind the procedures that conditionally write out PostScript.
+
+/# % <arg1> ... <argN> <opname> <N> # -
+ { 1 index load 3 1 roll #exec
+ } bdef
+/#? % - #? <writing>
+ { /PSout where { pop true } { false } ifelse
+ } bdef
+/defined# % <name> defined# <bool>
+ { dup where { exch get } { pop false } ifelse
+ } bdef
+/#exec % <arg1> ... <argN> <proc|operator> <opname> <N> #exec -
+ { /PSout where
+ { pop dup ([) eq { pop counttomark 1 sub } if
+ -1 1 { 1 add index PSout exch write# PSout ( ) writestring } for
+ PSout exch write=
+ }
+ { pop pop
+ }
+ ifelse exec
+ } bdef
+/#dsc % mark <obj1> ... #dsc -
+ { /PSout where
+ { pop counttomark
+ { counttomark -1 roll PSout exch write=only }
+ repeat pop PSout (\n) writestring
+ }
+ { cleartomark
+ }
+ ifelse
+ } bdef
+/copyfile# % <filename> copyfile# -
+ { findlibfile
+ { exch pop }
+ { (r) file } % let the error happen
+ ifelse
+ { dup =string readline pop (%BEGIN) eq { exit } if
+ }
+ loop
+ { dup =string readline not { pop exit } if
+ dup (%END) eq { pop exit } if
+ { ( ) anchorsearch
+ { pop }
+ { (\t) anchorsearch { pop } { exit } ifelse }
+ ifelse
+ }
+ loop
+ dup () eq { true } { dup 0 1 getinterval (%) eq } ifelse
+ { pop }
+ { (%) search { exch pop exch pop } if mark exch #dsc }
+ ifelse
+ }
+ loop closefile
+ } bdef
+/#dscfile % <filename> #dscfile -
+ { /PSout where
+ { pop /PSNoProcSet defined#
+ { PSout exch write===only PSout ( runlibfile\n) writestring }
+ { copyfile# }
+ ifelse
+ }
+ { pop
+ }
+ ifelse
+ } bdef
+
+% Rebind Is, which constructs a data source for an image.
+% pdf_draw defined it to simply retrieve the stream.
+/ID_draw /ID load def
+/Is_draw /Is load def
+/EI_draw /EI load def
+userdict /Is_string null put % establish a binding
+userdict /Is_data null put % ditto
+/ID_proc1 {/ASCIIHexDecode filter} def % no bind
+/ID_proc2 {/ASCII85Decode filter /RunLengthDecode filter} def % no bind
+/ID
+ { /PSout where
+ { pop dup length 1 add dict copy
+ dup /FilterProc PSLevel1 { /ID_proc1 } { /ID_proc2 } ifelse load
+ /PSBinaryOK defined# { dup length 2 sub 2 exch getinterval } if
+ dup length 0 eq { pop pop pop } { put } ifelse
+ }
+ if ID_draw
+ } bdef
+/Is % <imagedict> Is <imagedict> <datasource>
+ { /PSout where
+ { pop dup /DataSource get string /Is_string exch store
+ /Is_data [ PSout
+ PSLevel1
+ { /PSBinaryOK defined#
+ { /NullEncode } { /ASCIIHexEncode } ifelse
+ filter
+ }
+ { /PSBinaryOK defined#
+ { Is_string length /RunLengthEncode filter
+ }
+ { /ASCII85Encode filter
+ dup Is_string length /RunLengthEncode filter exch
+ }
+ ifelse
+ }
+ ifelse ] store
+ Is_draw
+ { Is_string readstring pop Is_data 0 get 1 index writestring }
+ aload length 1 add packedarray cvx
+ }
+ { Is_draw
+ }
+ ifelse
+ } bdef
+/EI
+ { /PSout where { pop Is_data { closefile } forall } { EI_draw } ifelse
+ } bdef
+
+% Rebind readfontfilter, which constructs the filter that
+% reads the text of an embedded Type 1 (and eventually Type 3) font.
+/readfontfilter_orig /readfontfilter load def
+/readfontfilter % <proc> readfontfilter <filter>
+ { /copyfontdata cvx 2 array astore cvx
+ 0 () /SubFileDecode filter
+ } bdef
+/copyfontdata % <string> <origproc> copyfontdata <substring>
+ { exec /PSout where { pop PSout 1 index writestring } if
+ } bdef
+
+currentdict readonly pop end % pdf2psdict
+
+.setglobal
+end % pdfdict
+end % GS_PDF_ProcSet
diff --git a/pstoraster/pdf_base.ps b/pstoraster/pdf_base.ps
new file mode 100644
index 000000000..62914a2cb
--- /dev/null
+++ b/pstoraster/pdf_base.ps
@@ -0,0 +1,392 @@
+% Copyright (C) 1994, 1996 Aladdin Enterprises. All rights reserved.
+%
+% This file is part of GNU Ghostscript.
+%
+% GNU Ghostscript is distributed in the hope that it will be useful, but
+% WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+% anyone for the consequences of using it or for whether it serves any
+% particular purpose or works at all, unless he says so in writing. Refer to
+% the GNU General Public License for full details.
+%
+% Everyone is granted permission to copy, modify and redistribute GNU
+% Ghostscript, but only under the conditions described in the GNU General
+% Public License. A copy of this license is supposed to have been given to
+% you along with GNU Ghostscript so you can know your rights and
+% responsibilities. It should be in a file named COPYING. Among other
+% things, the copyright notice and this notice must be preserved on all
+% copies.
+%
+% Aladdin Enterprises is not affiliated with the Free Software Foundation or
+% the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+% does not depend on any other GNU software.
+
+% pdf_base.ps
+% Basic parser for PDF reader.
+
+% This handles basic parsing of the file (including the trailer
+% and cross-reference table), as well as objects, object references,
+% and streams; it doesn't include any facilities for making marks on
+% the page.
+
+/.setlanguagelevel where { pop 2 .setlanguagelevel } if
+.currentglobal true .setglobal
+/pdfdict where { pop } { /pdfdict 100 dict def } ifelse
+pdfdict begin
+
+% Since PDF files can include extremely large dictionaries,
+% we increase the operand stack size here.
+/setuserparams where
+ { pop mark /MaxOpStack 10000 .dicttomark setuserparams
+ } if
+
+% We rebind #, #?, #dsc, and #dscfile later if we're writing out PostScript.
+/# % <arg1> ... <argN> <opname> <N> # -
+ { pop cvx exec
+ } bind def
+/#?
+ { false
+ } bind def
+/#dsc % mark <obj1> ... #dsc -
+ { cleartomark
+ } bind def
+/#dscfile % <filename> #dscfile -
+ { pop
+ } bind def
+
+% Define the name interpretation dictionary for reading values.
+/valueopdict mark
+ (<<) cvn { mark } bind % don't push an actual mark!
+ (>>) cvn /.dicttomark load
+ ([) cvn { mark } bind % ditto
+ (]) cvn dup load
+ /true true
+ /false false
+ /null null
+ /F dup cvx % see Objects section below
+ /R dup cvx % see Objects section below
+ /stream dup cvx % see Streams section below
+.dicttomark readonly def
+
+% ------ Utilities ------ %
+
+% Define a scratch string. The PDF language definition says that
+% no line in a PDF file can exceed 255 characters.
+/pdfstring 255 string def
+
+% Read the previous line of a file. If we aren't at a line boundary,
+% read the line containing the current position.
+% Skip any blank lines.
+/prevline % - prevline <startpos> <substring>
+ { PDFfile fileposition dup () pdfstring
+ 2 index 257 sub 0 .max PDFfile exch setfileposition
+ { % Stack: initpos linepos line string
+ PDFfile fileposition
+ PDFfile 2 index readline pop
+ dup length 0 gt
+ { 3 2 roll 5 -2 roll pop pop 2 index }
+ { pop }
+ ifelse
+ % Stack: initpos linepos line string startpos
+ PDFfile fileposition 5 index ge { exit } if
+ pop
+ }
+ loop pop pop 3 -1 roll pop
+ } bind def
+
+% Execute a file, interpreting its executable names in a given
+% dictionary. The name procedures may do whatever they want
+% to the operand stack.
+/.pdfrun % <file> <opdict> .pdfrun -
+ { % Construct a procedure with the file and opdict bound into it.
+ 1 index cvlit mark mark 4 2 roll
+ { token not { (%%EOF) cvn cvx } if
+ dup xcheck
+ { DEBUG { dup == flush } if
+ 2 copy .knownget
+ { exch pop exch pop exec }
+ { (%stderr) (w) file
+ dup (****************Unknown operator: ) writestring
+ dup 3 -1 roll .writecvs dup (\n) writestring flushfile
+ pop
+ }
+ ifelse
+ }
+ { exch pop DEBUG { dup ==only ( ) print flush } if
+ }
+ ifelse
+ }
+ aload pop .packtomark cvx
+ /loop cvx 2 packedarray cvx
+ { stopped /PDFsource } aload pop
+ PDFsource
+ { store { stop } if } aload pop .packtomark cvx
+ /PDFsource 3 -1 roll store exec
+ } bind def
+
+% ------ File reading ------ %
+
+% Read the cross-reference entry for an (unresolved) object.
+% The caller must save and restore the PDFfile position if desired.
+% For invalid (free) objects, we return 0.
+/readxrefentry % <object#> readxrefentry <objpos>
+ { dup Objects exch get
+ PDFfile exch setfileposition
+ PDFfile token pop % object position
+ PDFfile token pop % generation #
+ PDFfile token pop % n or f
+ dup /n eq
+ { pop 1 add dup 255 gt
+ { Generations type /stringtype eq
+ { % Convert Generations from a string to an array.
+ Generations length array dup
+ 0 1 2 index length 1 sub
+ { Generations 1 index get put dup
+ }
+ for pop /Generations exch store
+ }
+ if
+ }
+ if
+ }
+ { /f eq
+ { pop 0 }
+ { /readxrefentry cvx /syntaxerror signalerror }
+ ifelse
+ }
+ ifelse
+ % Stack: obj# objpos 1+gen#
+ Generations 4 -1 roll 3 -1 roll put
+ } bind def
+
+% ================================ Objects ================================ %
+
+% We represent an unresolved object reference by a procedure of the form
+% {obj# gen# resolveR}. This is not a possible PDF object, because PDF has
+% no way to represent procedures. Since PDF in fact has no way to represent
+% any PostScript object that doesn't evaluate to itself, we can 'force'
+% a possibly indirect object painlessly with 'exec'.
+% Note that since we represent streams by executable dictionaries
+% (see below), we need both an xcheck and a type check to determine
+% whether an object has been resolved.
+/unresolved? % <object#> unresolved? <bool>
+ { Objects exch get dup xcheck exch type /integertype eq and
+ } bind def
+/oforce /exec load def
+/oget % <array> <index> oget <object>
+ % <dict> <key> oget <object>
+ { 2 copy get dup xcheck
+ { exec dup 4 1 roll put }
+ { exch pop exch pop }
+ ifelse
+ } bind def
+% A null value in a dictionary is equivalent to an omitted key;
+% we must check for this specially.
+/knownoget
+ { 2 copy known
+ { oget dup null eq { pop false } { true } ifelse }
+ { pop pop false }
+ ifelse
+ } bind def
+
+% PDF 1.1 defines a 'foreign file reference', but not its meaning.
+% Per the specification, we convert these to nulls.
+/F % <file#> <object#> <generation#> F <object>
+ { % Some PDF 1.1 files use F as a synonym for f!
+ count 3 lt { setfillcolor /fill fsexec } { pop pop pop null } ifelse
+ } bind def
+
+% We keep track of objects in a pair of arrays, Objects and Generations.
+% Generations[N] is 1+ the current generation number for object number N.
+% (As far as we can tell, this is needed only for error checking.)
+% If object N is loaded, Objects[N] is the actual object;
+% otherwise, Objects[N] is an executable integer giving the file offset
+% of the object's entry in the cross-reference table.
+% For free objects, Generations[N] is 0.
+/checkgeneration % <object#> <generation#> checkgeneration <object#> <OK>
+ { Generations 2 index get 1 sub 1 index eq
+ { pop true
+ }
+ { (Warning: wrong generation: ) print 1 index =only ( ) print = false
+ }
+ ifelse
+ } bind def
+/R % <object#> <generation#> R <object>
+ { 1 index unresolved?
+ { /resolveR cvx 3 packedarray cvx }
+ { checkgeneration { Objects exch get } { pop null } ifelse }
+ ifelse
+ } bind def
+
+% If we encounter an object definition while reading sequentially,
+% we just store it away and keep going.
+/objopdict mark
+ valueopdict { } forall
+ /endobj dup cvx
+.dicttomark readonly def
+/obj % <object#> <generation#> obj <object>
+ { PDFfile objopdict .pdfrun
+ } bind def
+/endobj % <object#> <generation#> <object> endobj <object>
+ { 3 1 roll
+ % Read the xref entry if we haven't yet done so.
+ % This is only needed for generation # checking.
+ 1 index unresolved?
+ { PDFfile fileposition
+ 2 index readxrefentry pop
+ PDFoffset add PDFfile exch setfileposition
+ } if
+ checkgeneration { Objects exch 2 index put } { pop pop null } ifelse
+ } bind def
+
+% When resolving an object reference, we stop at the endobj.
+/resolveopdict mark
+ valueopdict { } forall
+ /endobj { endobj exit } bind
+.dicttomark readonly def
+/resolveR % <object#> <generation#> resolveR <object>
+ { DEBUG { (%Resolving: ) print 2 copy 2 array astore == } if
+ 1 index unresolved?
+ { PDFfile fileposition 3 1 roll
+ 1 index readxrefentry
+ 3 1 roll checkgeneration
+ { % Stack: savepos objpos obj#
+ exch PDFoffset add PDFfile exch setfileposition
+ PDFfile token pop 2 copy ne
+ { (xref error!\n) print /resolveR cvx /rangecheck signalerror
+ }
+ if pop PDFfile token pop
+ PDFfile token pop /obj ne
+ { (xref error!\n) print /resolveR cvx /rangecheck signalerror
+ }
+ if
+ pdf_run_resolve % PDFfile resolveopdict .pdfrun
+ }
+ { Objects exch null put pop null
+ }
+ ifelse exch PDFfile exch setfileposition
+ }
+ { pop Objects exch get
+ }
+ ifelse
+ } bind def
+
+%================================ Streams ================================ %
+
+% We represent a stream by an executable dictionary that contains,
+% in addition to the contents of the original stream dictionary:
+% /File - the file or string where the stream contents are stored;
+% /FilePosition - iff File is a file, the position in the file
+% where the contents start.
+% /StreamKey - the key used to decrypt this stream if any
+% We do the real work of constructing the data stream only when the
+% contents are needed.
+
+% Construct a stream. The length is not reliable in the face of
+% different end-of-line conventions, but it's all we've got.
+%
+% PDF files are inconsistent about what may fall between the 'stream' keyword
+% and the actual stream data, and it appears that no one algorithm can
+% detect this reliably. Furthermore, if we are already reading from a stream,
+% we can't back it up to determine what terminated the keyword.
+% We think the following is the best we can do:
+% If we are already reading from a stream, skip the next character
+% after the keyword terminator iff it is a \n.
+% This is chancy if the data are in binary form, but such files are
+% questionable to begin with.
+/streamskipeols
+ { % We used to use the following algorithm:
+% { PDFsource read not { /stream cvx /syntaxerror signalerror } if
+% dup 10 eq 1 index 13 eq or not { PDFsource exch unread exit } if pop
+% }
+% loop
+ % But the one described above seems to work more often:
+ PDFsource read not { /stream cvx /syntaxerror signalerror } if
+ dup 10 ne { PDFsource exch unread } { pop } ifelse
+ } bind def
+/stream
+ { PDFsource PDFfile eq
+ { dup /File PDFfile put
+ % We used to do the following:
+% prevline pop pop
+ streamskipeols
+ dup /FilePosition PDFfile fileposition put
+ DEBUG { (%FilePosition: ) print dup /FilePosition get == } if
+ PDFfile fileposition 1 index /Length oget add
+ PDFfile exch setfileposition
+ }
+ { % We're already reading from a stream, which we can't reposition.
+ % Capture the sub-stream contents in a string.
+ streamskipeols
+ dup /Length oget string PDFsource exch readstring
+ not
+ { (Unexpected EOF in stream!\n) print
+ /stream cvx /rangecheck signalerror
+ }
+ if
+ 1 index exch /File exch put
+ }
+ ifelse
+ PDFsource token pop
+ /endstream ne { /stream cvx /syntaxerror signalerror } if
+ cvx
+ } bind def
+
+% Resolve a stream dictionary to a PostScript stream.
+% Streams with no filters require special handling:
+% - If we are going to interpret their contents, we let endstream
+% terminate the interpretation loop;
+% - If we are just going to read data from them, we impose
+% a SubFileDecode filter that reads just the requisite amount of data.
+% Note that, in general, resolving a stream repositions PDFfile.
+% Clients must save and restore the position of PDFfile themselves.
+/resolvestream % <streamdict> <readdata?> resolvestream <stream>
+ { exch dup /FilePosition .knownget
+ { 1 index /File get exch setfileposition }
+ if
+ % Stack: readdata? dict
+ dup /DecodeParms .knownget not { null } if
+ 1 index /Filter .knownget not { {} } if
+ dup type /nametype eq
+ { 1 array astore
+ 1 index null ne { exch 1 array astore exch } if
+ }
+ if
+ % Stack: readdata? dict parms filternames
+ 2 index /File get exch
+ % Stack: readdata? dict parms file/string filternames
+ pdf_decrypt_stream % add decryption if needed
+ dup length 0 eq
+ { % All the PDF filters have EOD markers, but in this case
+ % there is no specified filter.
+ pop exch pop
+ % Stack: readdata? dict file/string
+ 2 index
+ { % We're going to read data; use a SubFileDecode filter.
+ 1 index /Length oget () /SubFileDecode filter
+ }
+ { dup type /filetype ne
+ { % Use a SubFileDecode filter to read from a string.
+ 0 () SubFileDecode filter
+ }
+ if
+ }
+ ifelse
+ }
+ { 2 index null eq
+ { { filter }
+ }
+ { % Stack: readdata? dict parms file/string filtername
+ { 2 index 0 get dup null eq { pop } { exch } ifelse filter
+ exch dup length 1 sub 1 exch getinterval exch
+ }
+ }
+ ifelse forall exch pop
+ }
+ ifelse
+ % Stack: readdata? dict file
+ exch pop exch pop
+ } bind def
+/endstream { exit } def
+
+end % pdfdict
+.setglobal
diff --git a/pstoraster/pdf_draw.ps b/pstoraster/pdf_draw.ps
new file mode 100644
index 000000000..f25d8b76e
--- /dev/null
+++ b/pstoraster/pdf_draw.ps
@@ -0,0 +1,320 @@
+% Copyright (C) 1994, 1995 Aladdin Enterprises. All rights reserved.
+%
+% This file is part of GNU Ghostscript.
+%
+% GNU Ghostscript is distributed in the hope that it will be useful, but
+% WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+% anyone for the consequences of using it or for whether it serves any
+% particular purpose or works at all, unless he says so in writing. Refer to
+% the GNU General Public License for full details.
+%
+% Everyone is granted permission to copy, modify and redistribute GNU
+% Ghostscript, but only under the conditions described in the GNU General
+% Public License. A copy of this license is supposed to have been given to
+% you along with GNU Ghostscript so you can know your rights and
+% responsibilities. It should be in a file named COPYING. Among other
+% things, the copyright notice and this notice must be preserved on all
+% copies.
+%
+% Aladdin Enterprises is not affiliated with the Free Software Foundation or
+% the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+% does not depend on any other GNU software.
+
+% pdf_draw.ps
+% PDF drawing operations (graphics, text, and images).
+
+% We don't handle the following PDF elements yet (identified by
+% page number in the reference manual):
+% style strings (63-64), except in a few known fonts
+% font descriptor resources (71-75), except for MissingWidth
+% text clipping modes (104)
+% What do these mean??
+
+/.setlanguagelevel where { pop 2 .setlanguagelevel } if
+.currentglobal true .setglobal
+/pdfdict where { pop } { /pdfdict 100 dict def } ifelse
+GS_PDF_ProcSet begin
+pdfdict begin
+
+% For simplicity, we use a single interpretation dictionary for all
+% PDF graphics operations, even though this is too liberal.
+/drawopdict 100 dict def
+
+% ================================ Graphics ================================ %
+
+% ---------------- Graphics state management ---------------- %
+
+drawopdict begin
+ % Graphics state stack
+ /q { q } def
+ /Q { Q } def
+ % Graphics state setting
+ /cm { cm } def
+ /i { i } def
+ /J { J } def
+ /d { d } def
+ /j { j } def
+ /w { w } def
+ /M { M } def
+ /gs { pop } def
+end
+
+% ---------------- Color setting ---------------- %
+
+/csgray % <op> <csop> csgray -
+ { Page /Resources pget
+ { /ColorSpace knownoget
+ { /DefaultGray knownoget { 3 -1 roll exch 3 1 roll } if }
+ if
+ }
+ if pop cvx exec
+ } bdef
+/csrgb % <op> <csop> csrgb -
+ { Page /Resources pget
+ { /ColorSpace knownoget
+ { /DefaultRGB knownoget { 3 -1 roll exch 3 1 roll } if }
+ if
+ }
+ if pop cvx exec
+ } bdef
+/cscmyk % <op> <csop> cscmyk -
+ { pop cvx exec
+ } bdef
+/csresolve % <csresource> csresolve <colorspace>
+ { Page /Resources pget not { 0 dict } if
+ /ColorSpace oget exch oget resolvecolorspace
+ dup type /nametype ne { dup length 1 eq { 0 get } if } if
+ } bdef
+/cset % <c0> ... [- <sc1> - <sc3> <sc4>] <colorspace> cset -
+ { gsave setcolorspace mark currentcolor counttomark
+ grestore dup 2 add 1 roll cleartomark get exec
+ } bdef
+
+%**************** sc and SC don't work, because the color space information
+%**************** isn't available at conversion time.
+drawopdict begin
+ /g { /g { cs sc1 } csgray } bdef
+ /rg { /rg { cs sc3 } csrgb } bdef
+ /k { /k { cs sc4 } cscmyk } bdef
+ /cs { csresolve cs } bdef
+ /sc { { null sc1 null sc3 sc4 } FillColorSpace cset } bdef
+ /G { /G { CS SC1 } csgray } bdef
+ /RG { /RG { CS SC3 } csrgb } bdef
+ /K { /K { CS SC4 } cscmyk } bdef
+ /CS { csresolve CS } bdef
+ /SC { { null SC1 null SC3 SC4 } StrokeColorSpace cset } bdef
+end
+
+% ---------------- Paths ---------------- %
+
+drawopdict begin
+ % Path construction
+ /m { m } def
+ /l { l } def
+ /c { c } def
+ /v { v } def
+ /y { y } def
+ /re { re } def
+ /h { h } def
+ % Path painting and clipping
+ /n { n } def
+ /S { S } def
+ /s { s } def
+ /f { f } def
+ /f* { f* } def
+ /B { B } def
+ /b { b } def
+ /B* { B* } def
+ /b* { b* } def
+ /W { W } def
+ /W* { W* } def
+end
+
+% ---------------- XObjects ---------------- %
+
+/xobjectprocs mark % <dict> -proc- -
+ /Image { DoImage }
+ /Form { DoForm }
+.dicttomark readonly def
+
+/ncompdict mark
+ /DeviceGray 1
+ /CalGray 1
+ /DeviceRGB 3
+ /CalRGB 3
+ /Lab 3
+ /DeviceCMYK 4
+ /CalCMYK 4
+.dicttomark readonly def
+/defaultdecodedict mark
+ /DeviceGray [0 1] readonly
+ /CalGray 1 index
+ /DeviceRGB [0 1 0 1 0 1] readonly
+ /CalRGB 1 index
+ /Lab
+ { 0 100 2 index 1 get /Range .knownget not { {-100 100 -100 100} } if
+ aload pop 6 array astore readonly
+ } bind
+ /DeviceCMYK [0 1 0 1 0 1 0 1] readonly
+ /CalCMYK 1 index
+.dicttomark readonly def
+/resolvecolorspace % <cspace> resolvecolorspace <cspace>
+ { dup type /arraytype eq
+ { dup 0 get /Indexed eq
+ { dup 3 oget dup type /stringtype eq
+ { pop
+ }
+ { % The color lookup table is a stream.
+ % Get its contents.
+ true resolvestream
+ 1 index 2 get 1 add
+ ncompdict 3 index 1 get get mul
+ string readstring pop
+ 1 index 3 3 -1 roll put
+ }
+ ifelse
+ }
+ if
+ }
+ if
+ } bdef
+/DoImage
+ { dup length dict
+ 1 index /ColorSpace knownoget
+ { resolvecolorspace
+ dup type /arraytype eq { dup length 1 eq { 0 get } if } if
+ exch begin /ColorSpace exch def
+ }
+ { begin
+ }
+ ifelse
+ /ImageType 1 def
+ % Always define ImageMask appropriately.
+ dup /ImageMask knownoget dup { and } if
+ /ImageMask exch def
+ /Width 2 copy oget def
+ /Height 2 copy oget def
+ /BitsPerComponent 2 copy oget def
+ /Decode 2 copy knownoget not
+ { % Decode is required for the PostScript image operators.
+ ImageMask
+ { [0 1]
+ }
+ { ColorSpace dup type /arraytype eq { 0 get } if dup /Indexed eq
+ { pop [ 0 1 BitsPerComponent bitshift 1 sub ] }
+ { defaultdecodedict exch get exec }
+ ifelse
+ }
+ ifelse
+ }
+ if def
+ /Interpolate 2 copy knownoget { def } { pop } ifelse
+ /ImageMatrix Width 0 0 Height neg 0 Height 6 array astore def
+ % Define DataSource as the width of the row buffer,
+ % which is what is needed if we're writing PostScript.
+ /DataSource
+ Width BitsPerComponent mul
+ ImageMask not { Decode length 2 idiv mul } if
+ 7 add 8 idiv
+ def
+ % Even though we're going to read data,
+ % pass false to resolvestream so that
+ % it doesn't try to use Length (which may not be present).
+ false resolvestream /Is_stream exch store
+ currentdict end ID
+ } bdef
+% Redefine Is, which constructs the data source for the image,
+% to retrieve the stream. (pdf_2ps.ps redefines Is to copy the data too.)
+userdict /Is_stream null put
+/Is % <imagedict> Is <imagedict> <datasource>
+ { Is_stream
+ } bdef
+
+/DoForm
+ { dup [ /pop load 2 index
+ { false resolvestream pdfopdict .pdfrun }
+ aload pop ] cvx /PaintProc exch put
+ execform
+ } bdef
+
+drawopdict begin
+ /Do
+ { PDFfile fileposition exch
+ Page /Resources pget not { 0 dict } if
+ /XObject oget exch oget
+ dup /Subtype get xobjectprocs exch get exec
+ PDFfile exch setfileposition
+ } bdef
+end
+
+% ---------------- In-line images ---------------- %
+
+% Undo the abbreviations in an in-line image dictionary.
+% Note that these can appear as keys, values, or members of array values.
+% /I is ambiguous; we check specially for it below.
+/unabbrevdict mark
+ % Top-level dictionary keys
+ /BPC /BitsPerComponent /CS /ColorSpace /D /Decode /DP /DecodeParms
+ /F /Filter /H /Height /IM /ImageMask /W /Width
+ % Values
+ /AHx /ASCIIHexDecode /A85 /ASCII85Decode /CC /CalCMYK
+ /CCF /CCITTFaxDecode /CG /CalGray /CR /CalRGB
+ /DCT /DCTDecode /CMYK /DeviceCMYK /G /DeviceGray /RGB /DeviceRGB
+ /I /Indexed /LZW /LZWDecode /RL /RunLengthDecode
+.dicttomark readonly def
+/unabbrev % <obj> unabbrev <obj'>
+ { dup type /nametype eq
+ { unabbrevdict 1 index .knownget { exch pop } if
+ }
+ { dup type /arraytype eq
+ { dup 0 1 2 index length 1 sub
+ { 2 copy get unabbrev put dup
+ }
+ for pop
+ }
+ if
+ }
+ ifelse
+ } bdef
+
+drawopdict begin
+ /BI { mark } bdef
+ /ID
+ { counttomark
+ { counttomark 1 roll
+ dup /I eq { pop /Interpolate } { unabbrev } ifelse
+ }
+ repeat
+ /File PDFsource
+ .dicttomark DoImage
+ PDFsource token pop /EI ne { /ID cvx /syntaxerror signalerror } if
+ } bdef
+end
+
+% ================================ Text ================================ %
+
+drawopdict begin
+ % Text control
+ /BT { BT } def
+ /ET { ET } def
+ /Tc { Tc } def
+ /TL { TL } def
+ /Tr { Tr } def
+ /Ts { Ts } def
+ /Tw { Tw } def
+ /Tz { Tz } def
+ % Text positioning
+ /Td { Td } def
+ /TD { TD } def
+ /Tm { Tm } def
+ /T* { T* } def
+ % Text painting
+ /Tj { Tj } def
+ /' { ' } def
+ /" { " } def
+ /TJ { mark exch aload pop TJ } def
+end
+
+end % pdfdict
+end % GS_PDF_ProcSet
+.setglobal
diff --git a/pstoraster/pdf_font.ps b/pstoraster/pdf_font.ps
new file mode 100644
index 000000000..307f5edd2
--- /dev/null
+++ b/pstoraster/pdf_font.ps
@@ -0,0 +1,374 @@
+% Copyright (C) 1994, 1996 Aladdin Enterprises. All rights reserved.
+%
+% This file is part of GNU Ghostscript.
+%
+% GNU Ghostscript is distributed in the hope that it will be useful, but
+% WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+% anyone for the consequences of using it or for whether it serves any
+% particular purpose or works at all, unless he says so in writing. Refer to
+% the GNU General Public License for full details.
+%
+% Everyone is granted permission to copy, modify and redistribute GNU
+% Ghostscript, but only under the conditions described in the GNU General
+% Public License. A copy of this license is supposed to have been given to
+% you along with GNU Ghostscript so you can know your rights and
+% responsibilities. It should be in a file named COPYING. Among other
+% things, the copyright notice and this notice must be preserved on all
+% copies.
+%
+% Aladdin Enterprises is not affiliated with the Free Software Foundation or
+% the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+% does not depend on any other GNU software.
+
+% pdf_font.ps
+% PDF font operations.
+
+/.setlanguagelevel where { pop 2 .setlanguagelevel } if
+.currentglobal true .setglobal
+/pdfdict where { pop } { /pdfdict 100 dict def } ifelse
+GS_PDF_ProcSet begin
+pdfdict begin
+
+% We cache the PostScript font in an additional element of the
+% font resource dictionary, called PSFont.
+
+% ---------------- Encodings ---------------- %
+
+% Apply a list of differences to an Encoding.
+/updateencoding % <encoding> <differences> updateencoding <enc'>
+ { exch dup length array copy
+ exch dup 0 get exch dup length 1 sub
+ 1 exch getinterval
+ { dup type /nametype ne
+ { exch pop }
+ { 3 copy put pop 1 add }
+ ifelse
+ }
+ forall pop
+ } bdef
+
+% Get the Encoding for a font.
+/getencoding % <base-encoding> <font-resource> getencoding <enc>
+ { /Encoding knownoget
+ { dup type /nametype eq
+ { exch pop findencoding
+ }
+ { dup /BaseEncoding knownoget
+ { findencoding 3 -1 roll pop exch
+ }
+ if
+ /Differences knownoget { updateencoding } if
+ }
+ ifelse
+ }
+ if
+ } bdef
+
+% Adjust a font according to the Encoding and Widths in the font resource.
+/adjustfont % <font-resource> <font> adjustfont
+ % <font'> <changed>
+ { getfontencoding getfontmetrics 4 -1 roll pop .updatefont
+ { dup /FontName 2 copy get genfontname dup 5 1 roll put definefont }
+ if
+ } bind def
+
+% Get the (possibly modified) encoding of a font.
+/getfontencoding % <font-resource> <font> getfontencoding
+ % <font-resource> <font> <encoding>
+ { dup /Encoding get 2 index getencoding
+ } bdef
+
+% Get the metrics of a font, if specified.
+/getfontmetrics % <font-resource> <font> <encoding> getfontmetrics
+ % <font-resource> <font> <encoding> <Metrics|null>
+ { 2 index /Widths known
+ { 2 dict begin
+ /Encoding exch def
+ /Metrics Encoding length dict def
+ exch
+ % Stack: font font-res
+ % Note that widths are always based on a 1000-unit
+ % character space, but the FontMatrix may specify
+ % some other scale factor. Compensate for this here,
+ % by scaling the Widths if necessary.
+ 0.001 2 index /FontMatrix get 0 get div
+ % Stack: font font-res mscale
+ 1 index /FirstChar oget dup 1 4 index /LastChar oget
+ { % Stack: font font-res mscale first-char index
+ Encoding 1 index get
+ 4 index /Widths oget 2 index 4 index sub get
+ % Stack: font font-res mscale first-char index charname width
+ 4 index mul
+ % There is a hack here to deal with encodings where the
+ % same character appears more than once, because the Metrics
+ % dictionary works by character name, not by character code.
+ % Because of this, we can't deal with Width vectors that
+ % specify different widths for the same character name
+ % appearing multiple times in the Encoding.
+ Metrics 2 index .knownget not { 0 } if 0 ne
+ { pop pop }
+ { Metrics 3 1 roll put }
+ ifelse pop
+ }
+ for pop
+ % Now fill in the MissingWidth for any encoded characters
+ % that aren't in Metrics already.
+ % Stack: font font-res mscale
+ Metrics 2 index /FontDescriptor oget
+ /MissingWidth knownoget { 2 index mul } { 0 } ifelse exch
+ Encoding
+ { % Stack: font font-res mscale missing-width metrics charname
+ 2 copy known not { 2 copy 4 index put } if pop
+ }
+ forall pop pop pop
+ exch Encoding Metrics end
+ }
+ { null
+ }
+ ifelse
+ } bdef
+
+% ---------------- Descriptors ---------------- %
+
+% Partial descriptors for the 14 built-in fonts.
+/standardfontdescriptors mark
+ /Courier mark /Flags 16#23 .dicttomark
+ /Courier-Oblique 1 index
+ /Courier-Bold 1 index
+ /Courier-BoldOblique 1 index
+ /Helvetica mark /Flags 16#20 .dicttomark
+ /Helvetica-Oblique 1 index
+ /Helvetica-Bold 1 index
+ /Helvetica-BoldOblique 1 index
+ /Times-Roman mark /Flags 16#22 .dicttomark
+ /Times-Bold 1 index
+ /Times-Italic mark /Flags 16#62 .dicttomark
+ /Times-BoldItalic 1 index
+ /Symbol mark /Flags 16#4 .dicttomark
+ /ZapfDingbats 1 index
+.dicttomark readonly def
+
+% ---------------- Utilities ---------------- %
+
+% Fabricate a font name by adding %'s on the end.
+/genfontname % <name> genfontname <name>
+ { dup length string cvs
+ { (%) concatstrings
+ dup cvn FontDirectory exch known not { cvn exit } if
+ }
+ loop
+ } bdef
+
+% Find a font, and adjust its encoding if necessary.
+/pdffindfont % <font-resource> <fontname> pdffindfont <font>
+ { findfont adjustfont
+ } bdef
+
+% ---------------- Type 1 fonts ---------------- %
+
+/buildType1 % <Type1-font-resource> buildType1 <font>
+ { dup /BaseFont get pdffindfont
+ } bdef
+
+% The state dictionary for the embedded Type 1 font reading procedure
+% has the following keys and values:
+% data - stream (filter)
+% buffer, buffer2 - string
+% leftstr - string containing (non-negative) integer
+% sectionstr - string containing a character 0 .. 2
+% stream - (stream) dictionary
+% proc - procedure of the form {-dict- type1read}
+% When the procedure is executing, this dictionary is current.
+% leftstr and sectionstr are strings so that we can change their values
+% reliably in case the font executes a restore!
+
+% Read an embedded Type 1 font.
+/readfontfilter % <proc> readfontfilter <filter>
+ { % We make this a separate procedure so that we can
+ % redefine it when we're writing PostScript.
+ 0 () /SubFileDecode filter
+ } bdef
+/readtype1 % <font-resource> <stream-dict> readtype1 <font>
+ { % Read the definition, using a procedure-based filter
+ % that turns binary/hex conversion on and off
+ % at the right times.
+ PDFfile fileposition 3 1 roll
+ 7 dict begin
+ /leftstr ( ) 10 string copy def
+ dup /Length1 oget leftstr cvs pop
+ /sectionstr <00> 1 string copy def
+ /stream 1 index def
+ true resolvestream /data exch def
+ /buffer 1000 string def % arbitrary
+ /buffer2 buffer length 2.1 div cvi 1 sub string def
+ currentdict end
+ /type1read cvx 2 array astore cvx dup 0 get /proc 2 index put
+ readfontfilter
+ % Some buggy embedded fonts leave extra junk on the stack,
+ % so we have to make a closure that records the stack depth
+ % in a fail-safe way.
+ //systemdict begin
+ { run } aload pop count 1 sub 2 packedarray cvx exec
+ end
+ count exch sub { pop } repeat
+ PDFfile 3 -1 roll setfileposition
+ /FontDescriptor oget /FontName oget findfont
+ } bdef
+
+% Execute the appropriate reading procedure.
+/type1read % <dict> type1read <string>
+ { begin leftstr cvi
+ { type1read1 type1read2 type1read3 } sectionstr 0 get get exec
+ ( ) leftstr copy cvs pop end
+ } bdef
+
+% Read the next block of data into the buffer.
+/type1readdata % <left> <buffer> type1readdata <substring> <left'>
+ { 0 2 index 2 index length min getinterval
+ % Adobe requires readstring to signal an error if given
+ % an empty string. Work around this nonsense here.
+ dup length 0 ne { data exch readstring pop } if
+ dup length 3 -1 roll exch sub
+ DEBUG
+ { dup =only ( read ) print
+ 1 index length =only (: ) print
+ 1 index == flush
+ } if
+ } bdef
+
+% Read the next block of the initial text portion.
+/type1read1 % <left> type1read1 <string> <left'>
+ { DEBUG { (read1 ) print } if
+ dup 0 eq
+ { pop sectionstr 0 1 put
+ stream /Length2 oget type1read2
+ }
+ { buffer type1readdata
+ }
+ ifelse
+ } bdef
+
+% Read the next block of the encrypted portion.
+/type1trailer
+(0000000000000000000000000000000000000000000000000000000000000000\n\
+0000000000000000000000000000000000000000000000000000000000000000\n\
+0000000000000000000000000000000000000000000000000000000000000000\n\
+0000000000000000000000000000000000000000000000000000000000000000\n\
+0000000000000000000000000000000000000000000000000000000000000000\n\
+0000000000000000000000000000000000000000000000000000000000000000\n\
+0000000000000000000000000000000000000000000000000000000000000000\n\
+0000000000000000000000000000000000000000000000000000000000000000\n\
+cleartomark\n)
+readonly def
+/type1read2 % <left> type1read2 <string> <left'>
+ { DEBUG { (read2 ) print } if
+ dup 0 eq
+ { pop sectionstr 0 2 put
+ stream /Length3 oget
+ dup 0 eq
+ { DEBUG { (trailer ) print } if
+ type1trailer exch
+ }
+ { type1read3
+ }
+ ifelse
+ }
+ { buffer2 type1readdata exch
+ buffer /ASCIIHexEncode filter dup 3 -1 roll writestring closefile
+ buffer (>) search pop exch pop exch pop exch
+ }
+ ifelse
+ } bdef
+
+% Read the next block of the final text portion.
+% When finished, this procedure returns an empty string.
+/type1read3 % <left> type1read3 <string> <left'>
+ { DEBUG { (read3 ) print } if
+ buffer type1readdata
+ } bdef
+
+% ---------------- Type 3 fonts ---------------- %
+
+/.notdefEncoding 256 { /.notdef } repeat 256 packedarray def
+
+/buildType3 % <Type3-font-resource> buildType3 <font>
+ { 8 dict begin
+ /FontType 3 def
+ /FontBBox 1 index /FontBBox get cvx def
+ /FontMatrix 1 index /FontMatrix oget def
+ /CharProcs 1 index /CharProcs oget def
+ /FontName 1 index /Name get genfontname def
+ /Encoding .notdefEncoding 2 index getencoding def
+ /BuildGlyph
+ { exch /CharProcs get exch oget
+ PDFfile fileposition exch
+ false resolvestream
+ % Don't let setgcolor set the color inside the BuildGlyph
+ % procedure, because this causes an /undefined error.
+ q_ null /FillColor gput null /StrokeColor gput
+ pdfopdict .pdfrun
+ Q_
+ PDFfile exch setfileposition
+ } bdef
+ FontName currentdict end definefont exch pop
+ } bdef
+
+% ---------------- TrueType fonts ---------------- %
+
+/TTfonts mark
+ /Arial /Helvetica
+ /Arial,Italic /Helvetica-Oblique
+ /Arial,Bold /Helvetica-Bold
+ /Arial,BoldItalic /Helvetica-BoldOblique
+ /TimesNewRoman /Times-Roman
+ /TimesNewRoman,Italic /Times-Italic
+ /TimesNewRoman,Bold /Times-Bold
+ /TimesNewRoman,BoldItalic /Times-BoldItalic
+.dicttomark readonly def
+
+/buildTrueType % <TrueType-font-resource> buildTrueType <font>
+ { dup /BaseFont get
+ dup TTfonts exch .knownget { exch pop } if pdffindfont
+ } bdef
+
+% ---------------- Font lookup ---------------- %
+
+/fonttypeprocs mark % <font-resource> -proc- <font>
+ /Type1 /buildType1 cvx
+ /MMType1 1 index
+ /Type3 /buildType3 cvx
+ /TrueType /buildTrueType cvx
+.dicttomark readonly def
+
+/resourcefont % <font-resource> resourcefont <font>
+ { dup /PSFont .knownget
+ { /FID .knownget { type /fonttype eq } { false } ifelse }
+ { false }
+ ifelse
+ { /PSFont get
+ }
+ { dup dup /FontDescriptor knownoget
+ { /FontFile knownoget }
+ { false }
+ ifelse
+ { 1 index 3 1 roll readtype1 adjustfont }
+ { dup /Subtype get fonttypeprocs exch get exec }
+ ifelse
+ 2 copy /PSFont exch put
+ exch pop
+ }
+ ifelse
+ } bdef
+
+drawopdict begin
+ /d0 /setcharwidth load def
+ /d1 /setcachedevice load def
+ /Tf
+ { exch Page /Resources oget /Font oget exch oget resourcefont
+ exch Tf
+ } bdef
+end
+
+end % pdfdict
+end % GS_PDF_ProcSet
+.setglobal
diff --git a/pstoraster/pdf_main.ps b/pstoraster/pdf_main.ps
new file mode 100644
index 000000000..96092868f
--- /dev/null
+++ b/pstoraster/pdf_main.ps
@@ -0,0 +1,478 @@
+% Copyright (C) 1994, 1996 Aladdin Enterprises. All rights reserved.
+%
+% This file is part of GNU Ghostscript.
+%
+% GNU Ghostscript is distributed in the hope that it will be useful, but
+% WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+% anyone for the consequences of using it or for whether it serves any
+% particular purpose or works at all, unless he says so in writing. Refer to
+% the GNU General Public License for full details.
+%
+% Everyone is granted permission to copy, modify and redistribute GNU
+% Ghostscript, but only under the conditions described in the GNU General
+% Public License. A copy of this license is supposed to have been given to
+% you along with GNU Ghostscript so you can know your rights and
+% responsibilities. It should be in a file named COPYING. Among other
+% things, the copyright notice and this notice must be preserved on all
+% copies.
+%
+% Aladdin Enterprises is not affiliated with the Free Software Foundation or
+% the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+% does not depend on any other GNU software.
+
+% pdf_main.ps
+% PDF file- and page-level operations.
+
+% We handle the following PDF 1.2 constructs:
+% page number rather than page object in Dest array
+
+/.setlanguagelevel where { pop 2 .setlanguagelevel } if
+.currentglobal true .setglobal
+/pdfdict where { pop } { /pdfdict 100 dict def } ifelse
+pdfdict begin
+
+% For simplicity, we use a single interpretation dictionary for all
+% PDF graphics execution, even though this is too liberal.
+/pdfopdict mark
+ objopdict { } forall
+ drawopdict { } forall
+ /endstream { exit } bind
+ (%%EOF) cvn { exit } bind % for filters
+.dicttomark readonly def
+
+% ======================== Main program ======================== %
+
+end % pdfdict
+userdict begin
+
+/defaultfontname /Times-Roman def
+
+% Make sure the registered encodings are loaded, so we don't run the risk
+% that some of the indices for their names will overflow the packed
+% representation. (Yes, this is a hack.)
+SymbolEncoding pop
+DingbatsEncoding pop
+
+% Redefine 'run' so it recognizes PDF files.
+systemdict begin
+/runps /run load def
+/runpdfstring 50 string def % length is arbitrary
+/run
+ { dup type /filetype ne { (r) file } if
+ dup read
+ { dup (%) 0 get eq
+ { pop dup //runpdfstring
+ % Some invalid files might have extra-long first lines....
+ { { readline } .internalstopped not { pop pop exit } if
+ pop =string
+ }
+ loop
+ //runpdfstring (PDF-) anchorsearch
+ { pop pop runpdf }
+ { pop cvx .runexec }
+ ifelse
+ }
+ { 2 copy unread pop cvx .runexec
+ }
+ ifelse
+ }
+ { closefile
+ }
+ ifelse
+ } bind odef
+/runpdf % <file> runpdf -
+ { userdict begin
+ /PSFile where { pop PSFile (w) file /PSout exch def } if
+ /Page# null def
+ /Page null def
+ /DSCPageCount 0 def
+ /PDFSave null def
+ GS_PDF_ProcSet begin
+ pdfdict begin
+ pdfopen begin
+ Trailer /Root oget /Pages oget /CropBox knownoget
+ { mark /CropBox 3 -1 roll /PAGES pdfmark
+ }
+ if
+ /FirstPage where { pop FirstPage } { 1 } ifelse
+ 1
+ /LastPage where { pop LastPage } { pdfpagecount } ifelse
+ QUIET not
+ { (Processing pages ) print 2 index =only ( through ) print dup =only
+ (.\n) print flush
+ }
+ if
+ { dup /Page# exch store
+ QUIET not { (Page ) print dup == flush } if
+ pdfgetpage pdfshowpage
+ } for
+ currentdict pdfclose
+ end % temporary dict
+ end % pdfdict
+ end % userdict
+ } bind def
+% Rebind procedures that invoke 'run'.
+% Note that .runstdin will fail if stdin is not seekable.
+/runlibfile
+ { findlibfile dup pop
+ { exch pop run }
+ { /undefinedfilename signalerror }
+ ifelse
+ } bind def
+/.runlibfile /runlibfile load def
+/.runstdin
+ { (%stdin) (r) file run
+ } bind def
+end % systemdict
+
+end % userdict
+pdfdict begin
+
+% ======================== File parsing ======================== %
+
+% Read the cross-reference and trailer sections.
+
+/traileropdict mark
+ (<<) cvn { mark } bind
+ (>>) cvn /.dicttomark load
+ ([) cvn { mark } bind % ditto
+ (]) cvn dup load
+ /true true
+ /false false
+ /null null
+ /R { /resolveR cvx 3 packedarray cvx } bind % see Objects below
+ /startxref /exit load
+.dicttomark readonly def
+
+% Because of EOL conversion, lines with fixed contents might be followed
+% by one or more blanks.
+/lineeq % <filestr> <conststr> lineeq <bool>
+ { anchorsearch
+ { pop { ( ) anchorsearch not { () eq exit } if pop } loop }
+ { pop false }
+ ifelse
+ } bind def
+/linene { lineeq not } bind def
+
+% Read (mostly scan) the cross-reference table.
+/readxref % <pos> readxref <trailerdict>
+ { PDFoffset add PDFfile exch setfileposition
+ PDFfile pdfstring readline pop
+ (xref) linene { /readxref cvx /syntaxerror signalerror } if
+ % Store the xref table entry position for each object.
+ % We only need to read the run headers, not every entry.
+ { PDFfile token pop % first object # or trailer
+ dup /trailer eq { pop exit } if
+ PDFfile pdfstring readline pop
+ token pop % entry count
+ exch pop exch
+ % This section might be adding new objects:
+ % ensure that Objects and Generations are big enough.
+ % Stack: count obj#
+ 2 copy add
+ dup Objects length gt
+ { dup array Objects 1 index copy pop /Objects exch def }
+ if
+ dup Generations length gt
+ { dup string Generations 1 index copy pop /Generations exch def }
+ if
+ pop
+ PDFfile fileposition 3 -1 roll
+ { Objects 2 index get null eq % later update might have set it
+ { Objects 2 index 2 index cvx put }
+ if exch 1 add exch 20 add
+ }
+ repeat PDFfile exch setfileposition pop
+ } loop
+ PDFfile traileropdict .pdfrun
+ } bind def
+
+% Open a PDF file and read the trailer and cross-reference.
+/pdfopen % <file> pdfopen <dict>
+ { pdfdict readonly pop % can't do it any earlier than this
+ /PSout where { pop /pdf2psdict where { pop pdf2psdict begin } if } if
+ 10 dict begin
+ /PSLevel1 where { pop } { /PSLevel1 false def } ifelse
+ cvlit /PDFfile exch def
+ /PDFsource PDFfile def
+ PDFfile dup 0 setfileposition pdfstring readstring
+ not {/pdfopen cvx /syntaxerror signalerror} if
+ (%PDF-) search not {/pdfopen cvx /syntaxerror signalerror} if
+ length /PDFoffset exch def pop pop
+ PDFfile dup dup 0 setfileposition bytesavailable
+ % Scan backwards over trailing control-character garbage
+ % (nulls, ^Zs, EOLs).
+ { 1 sub 2 copy setfileposition 1 index read pop
+ 32 ge {exit} if
+ } loop 1 sub setfileposition
+ prevline (%%EOF) linene { /pdfopen cvx /syntaxerror signalerror } if
+ PDFfile exch setfileposition
+ prevline cvi % xref start position
+ exch PDFfile exch setfileposition
+ prevline (startxref) linene { /pdfopen cvx /syntaxerror signalerror } if
+ pop
+ % Stack: xrefpos
+ /Objects [] def
+ /Generations <> def
+ % Read the last cross-reference table.
+ readxref /Trailer exch def
+ Trailer /Encrypt known
+ { pdf_process_Encrypt % signal error
+ }
+ if
+ % Read any previous cross-reference tables.
+ Trailer { /Prev .knownget not { exit } if readxref } loop
+ % Create and initialize some caches.
+ /PageCount pdfpagecount def
+ /PageNumbers PageCount dict def
+ /PageIndex PageCount array def
+ % Write the DSC header if appropriate.
+ [ (%!PS-Adobe-1.0) #dsc
+ [ (%%Pages: (atend)) #dsc
+ [ (%%EndComments) #dsc
+ [ (%%BeginProlog) #dsc
+%%%% MRS - Sorry, because we have a two-line copyright now this doesn't work!
+% [ (% This copyright applies to everything between here and the %%EndProlog:) #dsc
+% [ (% ) copyright #dsc
+ (gs_pdf.ps) #dscfile
+ PSLevel1 { (gs_l2img.ps) #dscfile } if
+ [ (%%EndProlog) #dsc
+ % Copy bookmarks (outline) to the output.
+ #?
+ { Trailer /Root oget /Outlines knownoget
+ { /First knownoget
+ { { dup writeoutline /Next knownoget not { exit } if } loop }
+ if
+ }
+ if
+ }
+ if
+ currentdict end
+ } bind def
+
+% Write the outline structure for a file. Uses linkdest (below).
+/writeoutline % <outlinedict> writeoutline -
+ { mark
+ 0 2 index /First knownoget
+ { { exch 1 add exch /Next knownoget not { exit } if } loop }
+ if
+ % stack: dict mark count
+ dup 0 eq
+ { pop 1 index
+ }
+ { 2 index /Count knownoget { 0 lt { neg } if } if
+ /Count exch 3 index
+ }
+ ifelse linkdest /Title oget /Title exch /OUT pdfmark
+ /First knownoget
+ { { dup writeoutline /Next knownoget not { exit } if } loop }
+ if
+ } bind def
+
+% Close a PDF file.
+/pdfclose % <dict> pdfclose -
+ { begin
+ /PSout where
+ { pop
+ [ (%%Trailer) #dsc
+ [ (%%Pages: ) DSCPageCount #dsc
+ PSout closefile
+ }
+ if
+ PDFfile closefile
+ end
+ pdf2psdict where { pop currentdict pdf2psdict eq { end } if } if
+ } bind def
+
+% ======================== Page accessing ======================== %
+
+% Get a (possibly inherited) attribute of a page.
+/pget % <pagedict> <key> pget <value> -true-
+ % <pagedict> <key> pget -false-
+ { 2 copy knownoget
+ { exch pop exch pop true
+ }
+ { exch /Parent knownoget
+ { exch pget }
+ { pop false }
+ ifelse
+ }
+ ifelse
+ } bind def
+
+% Get the total number of pages in the document.
+/pdfpagecount % - pdfpagecount <int>
+ { Trailer /Root oget /Pages oget /Count oget
+ } bind def
+
+% Find the N'th page of the document by iterating through the Pages tree.
+% The first page is numbered 1.
+/pdffindpage % <int> pdffindpage <pagedict>
+ { dup Trailer /Root oget /Pages oget
+ { % We should be able to tell when we reach a leaf
+ % by finding a Type unequal to /Pages. Unfortunately,
+ % some files distributed by Adobe lack the Type key
+ % in some of the Pages nodes! Instead, we check for Kids.
+ dup /Kids knownoget not { exit } if
+ exch pop null
+ 0 1 3 index length 1 sub
+ { 2 index exch oget
+ dup /Kids known { dup /Count oget } { 1 } ifelse
+ % Stack: index kids null node count
+ dup 5 index ge { pop exch pop exit } if
+ 5 -1 roll exch sub 4 1 roll pop
+ }
+ for exch pop
+ dup null eq { pop pop 1 null exit } if
+ }
+ loop
+ % Stack: index countleft node
+ 1 index 1 ne { pop pop /pdffindpage cvx /rangecheck signalerror } if
+ exch pop
+ PageIndex 2 index 1 sub 2 index put
+ PageNumbers 1 index 3 index put
+ exch pop
+ } bind def
+
+% Find the N'th page of the document.
+% The first page is numbered 1.
+/pdfgetpage % <int> pdfgetpage <pagedict>
+ { PageIndex 1 index 1 sub get dup null ne
+ { exch pop }
+ { pop pdffindpage }
+ ifelse
+ } bind def
+
+% Find the page number of a page object (inverse of pdfgetpage).
+/pdfpagenumber % <pagedict> pdfpagenumber <int>
+ { % We use the simplest and stupidest of all possible algorithms....
+ PageNumbers 1 index .knownget
+ { exch pop
+ }
+ { 1 1 PageCount 1 add % will give a rangecheck if not found
+ { dup pdfgetpage oforce 2 index eq { exit } if pop
+ }
+ for exch pop
+ }
+ ifelse
+ } bind def
+
+% Display a given page.
+/boxrect % [<llx> <lly> <urx> <ury>] boxrect <x> <y> <w> <h>
+ { aload pop exch 3 index sub exch 2 index sub
+ } bind def
+/linkdest % <link|outline> linkdest
+ % ([/Page <n>] /View <view> | ) <link|outline>
+ { dup /Dest knownoget
+ { % Check for a name, to be looked up in Dests.
+ dup type /nametype eq
+ { Trailer /Root oget /Dests oget exch oget
+ dup type /dicttype eq { /D get } if
+ }
+ if
+ dup 0 oget
+ dup null eq
+ { pop }
+ { dup type /integertype ne { pdfpagenumber } if /Page exch 4 -2 roll }
+ ifelse
+ dup length 1 sub 1 exch getinterval /View exch 3 -1 roll
+ }
+ if
+ } bind def
+/annottypes 5 dict dup begin
+ /Text
+ { mark exch
+ { /Rect /Open /Contents }
+ { 2 copy knownoget { 3 -1 roll } { pop } ifelse }
+ forall pop /ANN pdfmark
+ } bind def
+ /Link
+ { mark exch
+ { /Rect /Border }
+ { 2 copy knownoget { 3 -1 roll } { pop } ifelse }
+ forall linkdest pop /LNK pdfmark
+ } bind def
+end def
+
+/pdfshowpage % <pagedict> pdfshowpage -
+ { dup /Page exch store
+ pdfshowpage_init
+ pdfshowpage_setpage
+ save /PDFSave exch store
+ (before exec) VMDEBUG
+ pdfshowpage_finish
+ (after exec) VMDEBUG
+ PDFSave restore
+ } bind def
+
+/pdfpagecontents % <pagedict> pdfpagecontents <contents>
+ { } bind def
+
+/pdfshowpage_init % <pagedict> pdfshowpage_init <pagedict>
+ { gsave
+ [ (%%Page: ) Page# ( )
+ DSCPageCount 1 add /DSCPageCount 1 index store #dsc
+ [ (GS_PDF_ProcSet begin) #dsc
+ } bind def
+
+/pdfshowpage_setpage % <pagedict> pdfshowpage_setpage <pagedict>
+ {
+ 3 dict % for setpagedevice
+ % Stack: pagedict setpagedict
+ % We want to look at Rotate for displays, but not for printers.
+ % The following is a hack, but we don't know a better way to do this.
+ currentpagedevice /OutputFile known not
+ { dup /Orientation 3 index /Rotate pget not { 0 } if 90 idiv
+ % Rotate specifies *clockwise* rotation!
+ neg 3 and put
+ }
+ if
+ % Stack: pagedict setpagedict
+ 1 index /MediaBox pget
+ { % Set the page size.
+ boxrect [ 2 index 5 index sub 2 index 5 index sub ]
+ % Stack: pagedict setpagedict llx lly urx ury pagesize
+ 5 index exch /PageSize exch put
+ % Stack: pagedict contents setpagedict llx lly urx ury
+ pop pop
+ neg exch neg exch
+ [ 3 1 roll ]
+ % Stack: pagedict setpagedict pageoffset
+ 1 index exch /PageOffset exch put
+ }
+ if
+ % Stack: pagedict setpagedict
+ setpagedevice
+ } bind def
+
+/pdfshowpage_finish % <pagedict> pdfshowpage_finish -
+ {
+ % Copy crop box.
+ dup /CropBox pget
+ { boxrect rectclip
+ dup /CropBox knownoget { mark /CropBox 3 -1 roll /PAGE pdfmark } if
+ }
+ if
+
+ % Copy annotations and links.
+ dup /Annots knownoget
+ { 0 1 2 index length 1 sub
+ { 1 index exch oget
+ dup /Subtype oget annottypes exch .knownget { exec } { pop } ifelse
+ }
+ for pop
+ }
+ if
+
+ % Display the actual page contents.
+ matrix currentmatrix /beginpage 0 # setmatrix
+ /Contents knownoget not { 0 array } if
+ dup type /arraytype ne { 1 array astore } if
+ { oforce false resolvestream pdfopdict .pdfrun } forall
+ /endpage 0 #
+ grestore
+ [ (end) #dsc
+ } bind def
+
+end % pdfdict
+.setglobal
diff --git a/pstoraster/pdf_sec.ps b/pstoraster/pdf_sec.ps
new file mode 100644
index 000000000..a9f2ba5c0
--- /dev/null
+++ b/pstoraster/pdf_sec.ps
@@ -0,0 +1,58 @@
+% Copyright (C) 1996 Aladdin Enterprises. All rights reserved.
+%
+% This file is part of GNU Ghostscript.
+%
+% GNU Ghostscript is distributed in the hope that it will be useful, but
+% WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+% anyone for the consequences of using it or for whether it serves any
+% particular purpose or works at all, unless he says so in writing. Refer to
+% the GNU General Public License for full details.
+%
+% Everyone is granted permission to copy, modify and redistribute GNU
+% Ghostscript, but only under the conditions described in the GNU General
+% Public License. A copy of this license is supposed to have been given to
+% you along with GNU Ghostscript so you can know your rights and
+% responsibilities. It should be in a file named COPYING. Among other
+% things, the copyright notice and this notice must be preserved on all
+% copies.
+%
+% Aladdin Enterprises is not affiliated with the Free Software Foundation or
+% the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+% does not depend on any other GNU software.
+
+% pdf_sec.ps
+% Security hooks for PDF reader.
+
+% This file contains the procedures that have to take encryption into
+% account when reading a PDF file. There is no actual decryption code here,
+% because U.S. export control laws might prohibit making this file available
+% to anyone outside the U.S. if the code were included. Instead, you can
+% get the real version of this file from
+% http://www.ozemail.com.au/~geoffk/pdfencrypt/pdf_sec.ps
+% or, if the ~ character upsets your software,
+% http://www.ozemail.com.au/%7Egeoffk/pdfencrypt/pdf_sec.ps
+
+/.setlanguagelevel where { pop 2 .setlanguagelevel } if
+.currentglobal true .setglobal
+/pdfdict where { pop } { /pdfdict 100 dict def } ifelse
+pdfdict begin
+
+% Process the encryption information in the Trailer.
+/pdf_process_Encrypt
+ { (****This file is encrypted and cannot be processed.\n) print flush
+ /pdfopen cvx /invalidfileaccess signalerror
+ } bind def
+
+% Run the code to resolve an object reference.
+/pdf_run_resolve
+ { PDFfile resolveopdict .pdfrun
+ } bind def
+
+% Prefix a decryption filter to a stream if needed.
+% Stack: readdata? dict parms file/string filternames
+/pdf_decrypt_stream
+ {
+ } bind def
+
+end % pdfdict
+.setglobal
diff --git a/pstoraster/pfbtogs.ps b/pstoraster/pfbtogs.ps
new file mode 100644
index 000000000..646a76bca
--- /dev/null
+++ b/pstoraster/pfbtogs.ps
@@ -0,0 +1,117 @@
+% Copyright (C) 1991, 1992, 1994 Aladdin Enterprises. All rights reserved.
+%
+% This file is part of GNU Ghostscript.
+%
+% GNU Ghostscript is distributed in the hope that it will be useful, but
+% WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+% anyone for the consequences of using it or for whether it serves any
+% particular purpose or works at all, unless he says so in writing. Refer to
+% the GNU General Public License for full details.
+%
+% Everyone is granted permission to copy, modify and redistribute GNU
+% Ghostscript, but only under the conditions described in the GNU General
+% Public License. A copy of this license is supposed to have been given to
+% you along with GNU Ghostscript so you can know your rights and
+% responsibilities. It should be in a file named COPYING. Among other
+% things, the copyright notice and this notice must be preserved on all
+% copies.
+%
+% Aladdin Enterprises is not affiliated with the Free Software Foundation or
+% the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+% does not depend on any other GNU software.
+
+% pfbtogs.ps
+% Convert a PFB file to a Ghostscript font.
+
+% A .pfb file is a sequence of packets. Each packet starts with byte
+% 0x80. The second byte in the packet gives the type of packet: 1
+% means it's a packet of ascii data which should be sent out as is
+% (except for translating \r to the appropriate end-of-line
+% character(s)); 2 means it's a packet of binary data (which may be
+% translated into hex); 3 means EOF. For types 1 and 2, the type byte
+% is followed by four bytes giving the length of the packet, least
+% significant first.
+
+/envPFB 20 dict def
+envPFB begin
+
+% ------ Packet writing routines ------ %
+
+ /pfbtext % str ->
+ { { (\r) search
+ { ofile exch writestring pop
+ ofile (\n) writestring
+ }
+ { ofile exch writestring exit
+ }
+ ifelse
+ } loop
+ } def
+
+ /pfbbinary % str ->
+ { { dup length 30 gt
+ { dup 0 30 getinterval ofile exch writehexstring
+ ofile (\n) writestring
+ dup length 30 sub 30 exch getinterval
+ }
+ { ofile exch writehexstring exit
+ }
+ ifelse
+ } loop ofile (\n) writestring
+ } def
+
+ /pfbcopy % count proc ->
+ { exch % proc count
+ { dup bufsize min
+ buf 0 3 -1 roll getinterval
+ 2 index exec
+ bufsize sub dup 0 le { exit } if
+ } loop pop pop
+ } def
+
+% ------ The main program ------ %
+
+ /bufsize 30000 def
+ /buf bufsize string def
+
+ /pfbtogs % infilename outfilename pfbtogs ->
+ { /psname exch def
+ /pfbname exch def
+
+ pfbname (r) file /ifile exch def
+ /packet 6 string def
+ ifile packet readstring
+ { dup length 6 eq { 0 get 128 eq } { pop false } ifelse }
+ { pop false }
+ ifelse
+ not { (Not a valid .PFB file.\n) print flush stop } if
+
+ ifile 0 setfileposition
+ psname (w) file /ofile exch def
+
+ { ifile packet readstring
+ not { exit } if
+ (packet: ) print packet { ( ) print =only } forall (\n) print flush
+ packet 5 get 256 mul packet 4 get add
+ 256 mul packet 3 get add 256 mul packet 2 get add
+ packet 1 get 1 sub
+ { { { ifile exch readstring pop pfbtext } pfbcopy }
+ { { ifile exch readstring pop pfbbinary } pfbcopy }
+ { exit }
+ } exch get exec
+ } loop
+
+ ofile closefile
+ ifile closefile
+
+ } bind def
+
+end
+
+% Enter the main program in the current dictionary.
+/pfbtogs
+ { envPFB begin pfbtogs end
+ } bind def
+
+% If the program was invoked from the command line, run it now.
+shellarguments { pfbtogs } if
diff --git a/pstoraster/pstoraster.c b/pstoraster/pstoraster.c
new file mode 100644
index 000000000..b994ecd68
--- /dev/null
+++ b/pstoraster/pstoraster.c
@@ -0,0 +1,196 @@
+/*
+ * "$Id$"
+ *
+ * PostScript RIP filter main entry for the Common UNIX Printing System
+ * (CUPS).
+ *
+ * Copyright 1993-1999 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * This code and any derivative of it may be used and distributed
+ * freely under the terms of the GNU General Public License when
+ * used with GNU Ghostscript or its derivatives. Use of the code
+ * (or any derivative of it) with software other than GNU
+ * GhostScript (or its derivatives) is governed by the CUPS license
+ * agreement.
+ *
+ * Contents:
+ *
+ * main() - Main entry for pstoraster.
+ * define_string() - Define a string value...
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include <cups/string.h>
+#include <stdlib.h>
+#include "ghost.h"
+#include "imain.h"
+#include "iminst.h"
+#include "ostack.h"
+#include "gscdefs.h"
+#include "store.h"
+
+
+/*
+ * Local functions...
+ */
+
+static void define_string(char *, char *);
+
+
+/*
+ * 'main()' - Main entry for pstoraster.
+ */
+
+int /* O - Exit status */
+main(int argc, /* I - Number of command-line arguments */
+ char *argv[]) /* I - Command-line arguments */
+{
+ FILE *stdfiles[3]; /* Copies of stdin, stdout, and stderr */
+ gs_main_instance *minst; /* Interpreter instance */
+ ref vtrue; /* True value */
+ int code; /* Run status code */
+ int exit_code; /* Exit code */
+ ref error_object; /* Error object */
+ char *content_type; /* CONTENT_TYPE environment variable */
+
+
+ /*
+ * Force the locale to "C" to avoid bugs...
+ */
+
+ putenv("LANG=C");
+
+ /*
+ * Create a PostScript interpreter instance...
+ */
+
+ minst = gs_main_instance_default();
+
+ /*
+ * Grab the old stdin/stdout/stderr...
+ */
+
+ gs_get_real_stdio(stdfiles);
+
+ /*
+ * Initialize basic interpreter stuff and read from the named file or
+ * from stdin...
+ */
+
+ if (argc > 6)
+ gs_main_init0(minst, fopen(argv[6], "r"), stdfiles[1], stdfiles[2], 8);
+ else
+ gs_main_init0(minst, stdfiles[0], stdfiles[1], stdfiles[2], 8);
+
+ /*
+ * Tell the interpreter where to find its files...
+ */
+
+ minst->lib_path.final = gs_lib_default_path;
+ gs_main_set_lib_paths(minst);
+
+ /*
+ * Set interpreter options...
+ */
+
+ make_true(&vtrue);
+ gs_main_init1(minst);
+ initial_enter_name("QUIET", &vtrue);
+ initial_enter_name("NOPAUSE", &vtrue);
+ if ((content_type = getenv("CONTENT_TYPE")) != NULL &&
+ strcmp(content_type, "application/pdf") == 0)
+ {
+ fputs("INFO: Converting PDF file to PostScript...\n", stderr);
+ define_string("PSFile", "%stdout");
+ initial_enter_name("NODISPLAY", &vtrue);
+ }
+ else
+ define_string("OutputFile", "-");
+ define_string("FONTPATH", CUPS_DATADIR "/fonts");
+
+ /*
+ * Start the interpreter...
+ */
+
+ gs_main_init2(minst);
+ code = gs_main_run_string(minst, ".runstdin", minst->user_errors,
+ &exit_code, &error_object);
+
+ /*
+ * Make sure that the last page was printed...
+ */
+
+ zflush(osp);
+ zflushpage(osp);
+
+ /*
+ * And the exit when we're done...
+ */
+
+ gs_exit(exit_code);
+
+ return (0);
+}
+
+
+/*
+ * 'define_string()' - Define a string value...
+ */
+
+static void
+define_string(char *name, /* I - Variable to set */
+ char *s) /* I - Value */
+{
+ int len; /* Length of string */
+ char *copy; /* Copy of string */
+ ref value; /* Value object */
+
+
+ /*
+ * Get the string value and copy it using strdup(). Note that this uses
+ * the malloc() function, but since we are only running gsrip on "real"
+ * operating systems (no Windows/PC build), this is not a problem...
+ */
+
+ if (s == NULL)
+ {
+ len = 0;
+ copy = strdup("");
+ }
+ else
+ {
+ len = strlen(s);
+ copy = strdup(s);
+ };
+
+ /*
+ * Enter the name in systemdict...
+ */
+
+ make_const_string(&value, a_readonly | avm_foreign, len, (const byte *)copy);
+ initial_enter_name(name, &value);
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/pstoraster/sa85x.h b/pstoraster/sa85x.h
new file mode 100644
index 000000000..ddc0159c5
--- /dev/null
+++ b/pstoraster/sa85x.h
@@ -0,0 +1,45 @@
+/* Copyright (C) 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* sa85x.h */
+/* Definitions for ASCII85 streams */
+/* Requires scommon.h; strimpl.h if any templates are referenced */
+
+/* ASCII85Encode */
+/* (no state) */
+extern const stream_template s_A85E_template;
+
+/* ASCII85Decode */
+typedef struct stream_A85D_state_s {
+ stream_state_common;
+ int odd; /* # of odd digits */
+ ulong word; /* word being accumulated */
+} stream_A85D_state;
+#define private_st_A85D_state() /* in sfilter2.c */\
+ gs_private_st_simple(st_A85D_state, stream_A85D_state,\
+ "ASCII85Decode state")
+/* We define the initialization procedure here, so that the scanner */
+/* can avoid a procedure call. */
+#define s_A85D_init_inline(ss)\
+ ((ss)->word = 0, (ss)->odd = 0)
+extern const stream_template s_A85D_template;
diff --git a/pstoraster/sbcp.c b/pstoraster/sbcp.c
new file mode 100644
index 000000000..f059009f0
--- /dev/null
+++ b/pstoraster/sbcp.c
@@ -0,0 +1,237 @@
+/* Copyright (C) 1993, 1994, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* sbcp.c */
+/* BCP and TBCP filters */
+#include "stdio_.h"
+#include "strimpl.h"
+#include "sfilter.h"
+
+#define CtrlA 0x01
+#define CtrlC 0x03
+#define CtrlD 0x04
+#define CtrlE 0x05
+#define CtrlQ 0x11
+#define CtrlS 0x13
+#define CtrlT 0x14
+#define ESC 0x1b
+#define CtrlBksl 0x1c
+
+/* The following is not used yet. */
+/*private const char *TBCP_end_protocol_string = "\033%-12345X";*/
+
+/* ------ BCPEncode and TBCPEncode ------ */
+
+/* Process a buffer */
+private int
+s_xBCPE_process(stream_state *st, stream_cursor_read *pr,
+ stream_cursor_write *pw, bool last, const byte _ds *escaped)
+{ const byte *p = pr->ptr;
+ const byte *rlimit = pr->limit;
+ uint rcount = rlimit - p;
+ byte *q = pw->ptr;
+ uint wcount = pw->limit - q;
+ const byte *end = p + min(rcount, wcount);
+ while ( p < end )
+ { byte ch = *++p;
+ if ( ch <= 31 && escaped[ch] )
+ { if ( p == rlimit )
+ { p--;
+ break;
+ }
+ *++q = CtrlA;
+ ch ^= 0x40;
+ if ( --wcount < rcount )
+ end--;
+ }
+ *++q = ch;
+ }
+ pr->ptr = p;
+ pw->ptr = q;
+ return (p == rlimit ? 0 : 1);
+}
+
+/* Actual process procedures */
+private int
+s_BCPE_process(stream_state *st, stream_cursor_read *pr,
+ stream_cursor_write *pw, bool last)
+{ static const byte escaped[32] = {
+ 0,1,0,1,1,1,0,0, 0,0,0,0,0,0,0,0,
+ 0,1,0,1,1,0,0,0, 0,0,0,0,1,0,0,0
+ };
+ return s_xBCPE_process(st, pr, pw, last, escaped);
+}
+private int
+s_TBCPE_process(stream_state *st, stream_cursor_read *pr,
+ stream_cursor_write *pw, bool last)
+{ static const byte escaped[32] = {
+ 0,1,0,1,1,1,0,0, 0,0,0,0,0,0,0,0,
+ 0,1,0,1,1,0,0,0, 0,0,0,1,1,0,0,0
+ };
+ return s_xBCPE_process(st, pr, pw, last, escaped);
+}
+
+/* Stream templates */
+const stream_template s_BCPE_template =
+{ &st_stream_state, NULL, s_BCPE_process, 1, 2
+};
+const stream_template s_TBCPE_template =
+{ &st_stream_state, NULL, s_TBCPE_process, 1, 2
+};
+
+/* ------ BCPDecode and TBCPDecode ------ */
+
+private_st_BCPD_state();
+
+#define ss ((stream_BCPD_state *)st)
+
+/* Initialize the state */
+private int
+s_BCPD_init(stream_state *st)
+{ ss->escaped = 0;
+ ss->matched = ss->copy_count = 0;
+ return 0;
+}
+
+/* Process a buffer */
+private int
+s_xBCPD_process(stream_state *st, stream_cursor_read *pr,
+ stream_cursor_write *pw, bool last, bool tagged)
+{ const byte *p = pr->ptr;
+ const byte *rlimit = pr->limit;
+ byte *q = pw->ptr;
+ byte *wlimit = pw->limit;
+ int copy_count = ss->copy_count;
+ int status;
+ bool escaped = ss->escaped;
+ for ( ; ; )
+ { byte ch;
+ if ( copy_count )
+ { if ( q == wlimit )
+ { status = (p < rlimit ? 1 : 0);
+ break;
+ }
+ *++q = *++(ss->copy_ptr);
+ copy_count--;
+ continue;
+ }
+ if ( p == rlimit )
+ { status = 0;
+ break;
+ }
+ ch = *++p;
+ if ( ch <= 31 )
+ switch ( ch )
+ {
+ case CtrlA:
+ if ( escaped )
+ { status = ERRC;
+ goto out;
+ }
+ escaped = true;
+ continue;
+ case CtrlC:
+ status = (*ss->signal_interrupt)(st);
+ if ( status < 0 )
+ goto out;
+ continue;
+ case CtrlD:
+ if ( escaped )
+ { status = ERRC;
+ goto out;
+ }
+ status = EOFC;
+ goto out;
+ case CtrlE:
+ continue;
+ case CtrlQ:
+ continue;
+ case CtrlS:
+ continue;
+ case CtrlT:
+ status = (*ss->request_status)(st);
+ if ( status < 0 )
+ goto out;
+ continue;
+ case CtrlBksl:
+ continue;
+ }
+ if ( q == wlimit )
+ { p--;
+ status = 1;
+ break;
+ }
+ if ( escaped )
+ { escaped = false;
+ switch ( ch )
+ { case '[':
+ if ( !tagged )
+ { status = ERRC;
+ goto out;
+ }
+ /* falls through */
+ case 'A': case 'C': case 'D': case 'E':
+ case 'Q': case 'S': case 'T': case '\\':
+ ch ^= 0x40;
+ break;
+ case 'M':
+ if ( !tagged )
+ { status = ERRC;
+ goto out;
+ }
+ continue;
+ default:
+ status = ERRC;
+ goto out;
+ }
+ }
+ *++q = ch;
+ }
+out: ss->copy_count = copy_count;
+ ss->escaped = escaped;
+ pr->ptr = p;
+ pw->ptr = q;
+ return status;
+}
+
+/* Actual process procedures */
+private int
+s_BCPD_process(stream_state *st, stream_cursor_read *pr,
+ stream_cursor_write *pw, bool last)
+{ return s_xBCPD_process(st, pr, pw, last, false);
+}
+private int
+s_TBCPD_process(stream_state *st, stream_cursor_read *pr,
+ stream_cursor_write *pw, bool last)
+{ return s_xBCPD_process(st, pr, pw, last, true);
+}
+
+/* Stream templates */
+const stream_template s_BCPD_template =
+{ &st_BCPD_state, s_BCPD_init, s_BCPD_process, 1, 1,
+ NULL, NULL, s_BCPD_init
+};
+const stream_template s_TBCPD_template =
+{ &st_BCPD_state, s_BCPD_init, s_TBCPD_process, 1, 1,
+ NULL, NULL, s_BCPD_init
+};
diff --git a/pstoraster/sbhc.c b/pstoraster/sbhc.c
new file mode 100644
index 000000000..ac172e2df
--- /dev/null
+++ b/pstoraster/sbhc.c
@@ -0,0 +1,274 @@
+/* Copyright (C) 1994, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* sbhc.c */
+/* Bounded Huffman code filters */
+#include "memory_.h"
+#include "stdio_.h"
+#include "gdebug.h"
+#include "strimpl.h"
+#include "sbhc.h"
+#include "shcgen.h"
+
+/* ------ BoundedHuffmanEncode ------ */
+
+private_st_BHCE_state();
+
+#define ss ((stream_BHCE_state *)st)
+
+/* Initialize BoundedHuffmanEncode filter. */
+private int
+s_BHCE_reinit(stream_state *st)
+{ ss->encode.count = ss->definition.num_values;
+ s_bhce_init_inline(ss);
+ return 0;
+}
+private int
+s_BHCE_init(register stream_state *st)
+{ hce_code *encode = ss->encode.codes =
+ (hce_code *)gs_alloc_byte_array(st->memory,
+ ss->definition.num_values,
+ sizeof(hce_code), "BHCE encode");
+ if ( encode == 0 )
+ return ERRC; /****** WRONG ******/
+ hc_make_encoding(encode, &ss->definition);
+ return s_BHCE_reinit(st);
+}
+
+/* Release the filter. */
+private void
+s_BHCE_release(stream_state *st)
+{ gs_free_object(st->memory, ss->encode.codes, "BHCE encode");
+}
+
+/* Process a buffer. */
+private int
+s_BHCE_process(stream_state *st, stream_cursor_read *pr,
+ stream_cursor_write *pw, bool last)
+{ const byte *p = pr->ptr;
+ const byte *rlimit = pr->limit;
+ byte *q = pw->ptr;
+ byte *wlimit = pw->limit - (hc_bits_size >> 3);
+ const hce_code *encode = ss->encode.codes;
+ uint num_values = ss->definition.num_values;
+ uint zero_runs = ss->EncodeZeroRuns;
+ uint zero_max = num_values - zero_runs + (ss->EndOfData ? 0 : 1);
+ uint zero_value = (zero_max > 1 ? 0 : 0x100);
+ int zeros = ss->zeros;
+ int status = 0;
+ hce_declare_state;
+
+ hce_load_state();
+ while ( p < rlimit && q < wlimit )
+ { uint value = *++p;
+ const hce_code *cp;
+ if ( value >= num_values )
+ { status = ERRC;
+ break;
+ }
+ if ( value == zero_value )
+ { /* Accumulate a run of zeros. */
+ ++zeros;
+ if ( zeros != zero_max )
+ continue;
+ /* We've scanned the longest run we can encode. */
+ cp = &encode[zeros - 2 + zero_runs];
+ zeros = 0;
+ hc_put_code((stream_hc_state *)ss, q, cp);
+ continue;
+ }
+ /* Check whether we need to put out a zero run. */
+ if ( zeros > 0 )
+ { --p;
+ cp = (zeros == 1 ? &encode[0] :
+ &encode[zeros - 2 + zero_runs]);
+ zeros = 0;
+ hc_put_code((stream_hc_state *)ss, q, cp);
+ continue;
+ }
+ cp = &encode[value];
+ hc_put_code((stream_hc_state *)ss, q, cp);
+ }
+ if ( q >= wlimit )
+ status = 1;
+ wlimit = pw->limit;
+ if ( last && status == 0 )
+ { if ( zeros > 0 )
+ { /* Put out a final run of zeros. */
+ const hce_code *cp = (zeros == 1 ? &encode[0] :
+ &encode[zeros - 2 + zero_runs]);
+ if ( !hce_bits_available(cp->code_length) )
+ status = 1;
+ else
+ { hc_put_code((stream_hc_state *)ss, q, cp);
+ zeros = 0;
+ }
+ }
+ if ( ss->EndOfData )
+ { /* Put out the EOD code if we have room. */
+ const hce_code *cp = &encode[num_values - 1];
+ if ( !hce_bits_available(cp->code_length) )
+ status = 1;
+ else
+ hc_put_code((stream_hc_state *)ss, q, cp);
+ }
+ else
+ { if ( q >= wlimit )
+ status = 1;
+ }
+ if ( !status )
+ { q = hc_put_last_bits((stream_hc_state *)ss, q);
+ goto ns;
+ }
+ }
+ hce_store_state();
+ns: pr->ptr = p;
+ pw->ptr = q;
+ ss->zeros = zeros;
+ return (p == rlimit ? 0 : 1);
+}
+
+#undef ss
+
+/* Stream template */
+const stream_template s_BHCE_template =
+{ &st_BHCE_state, s_BHCE_init, s_BHCE_process,
+ 1, hc_bits_size >> 3, s_BHCE_release, NULL, s_BHCE_reinit
+};
+
+/* ------ BoundedHuffmanDecode ------ */
+
+private_st_BHCD_state();
+
+#define ss ((stream_BHCD_state *)st)
+
+#define hcd_initial_bits 7 /* arbitrary, >= 1 and <= 8 */
+
+/* Initialize BoundedHuffmanDecode filter. */
+private int
+s_BHCD_reinit(stream_state *st)
+{ ss->decode.count = ss->definition.num_values;
+ s_bhcd_init_inline(ss);
+ return 0;
+}
+private int
+s_BHCD_init(register stream_state *st)
+{ uint initial_bits = ss->decode.initial_bits =
+ min(hcd_initial_bits, ss->definition.num_counts);
+ uint dsize = hc_sizeof_decoding(&ss->definition, initial_bits);
+ hcd_code *decode = ss->decode.codes =
+ (hcd_code *)gs_alloc_byte_array(st->memory, dsize,
+ sizeof(hcd_code), "BHCD decode");
+ if ( decode == 0 )
+ return ERRC; /****** WRONG ******/
+ hc_make_decoding(decode, &ss->definition, initial_bits);
+ return s_BHCD_reinit(st);
+}
+
+/* Release the filter. */
+private void
+s_BHCD_release(stream_state *st)
+{ gs_free_object(st->memory, ss->decode.codes, "BHCD decode");
+}
+
+/* Process a buffer. */
+private int
+s_BHCD_process(stream_state *st, stream_cursor_read *pr,
+ stream_cursor_write *pw, bool last)
+{ bhcd_declare_state;
+ byte *q = pw->ptr;
+ byte *wlimit = pw->limit;
+ const hcd_code *decode = ss->decode.codes;
+ uint initial_bits = ss->decode.initial_bits;
+ uint zero_runs = ss->EncodeZeroRuns;
+ int status = 0;
+ int eod = (ss->EndOfData ? ss->definition.num_values - 1 : -1);
+
+ bhcd_load_state();
+z: for ( ; zeros > 0; --zeros )
+ { if ( q >= wlimit )
+ { status = 1;
+ goto out;
+ }
+ *++q = 0;
+ }
+ for ( ; ; )
+ { const hcd_code *cp;
+ int clen;
+ hcd_ensure_bits(initial_bits, x1);
+ cp = &decode[hcd_peek_var_bits(initial_bits)];
+w1: if ( q >= wlimit )
+ { status = 1;
+ break;
+ }
+ if ( (clen = cp->code_length) > initial_bits )
+ { if ( !hcd_bits_available(clen) )
+ { /* We don't have enough bits for */
+ /* all possible codes that begin this way, */
+ /* but we might have enough for */
+ /* the next code. */
+ /****** NOT IMPLEMENTED YET ******/
+ break;
+ }
+ clen -= initial_bits;
+ hcd_skip_bits(initial_bits);
+ hcd_ensure_bits(clen, out); /* can't exit */
+ cp = &decode[cp->value + hcd_peek_var_bits(clen)];
+ hcd_skip_bits(cp->code_length);
+ }
+ else
+ { hcd_skip_bits(clen);
+ }
+ if ( cp->value >= zero_runs )
+ { if ( cp->value == eod )
+ { status = EOFC;
+ goto out;
+ }
+ /* This code represents a run of zeros, */
+ /* not a single output value. */
+ zeros = cp->value - zero_runs + 2;
+ goto z;
+ }
+ *++q = cp->value;
+ continue;
+ /* We don't have enough bits for all possible */
+ /* codes, but we might have enough for */
+ /* the next code. */
+x1: cp = &decode[(bits & ((1 << bits_left) - 1)) <<
+ (initial_bits - bits_left)];
+ if ( (clen = cp->code_length) <= bits_left )
+ goto w1;
+ break;
+ }
+out: bhcd_store_state();
+ pw->ptr = q;
+ return status;
+}
+
+#undef ss
+
+/* Stream template */
+const stream_template s_BHCD_template =
+{ &st_BHCD_state, s_BHCD_init, s_BHCD_process, 1, 1, s_BHCD_release,
+ NULL, s_BHCD_reinit
+};
diff --git a/pstoraster/sbhc.h b/pstoraster/sbhc.h
new file mode 100644
index 000000000..940552a2f
--- /dev/null
+++ b/pstoraster/sbhc.h
@@ -0,0 +1,90 @@
+/* Copyright (C) 1994 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* sbhc.h */
+/* BoundedHuffman filter state definition */
+/* Requires strimpl.h */
+#include "shc.h"
+
+/*
+ * The BoundedHuffman filters extend the basic Huffman coding model by
+ * providing the ability to encode runs of zeros as a single data item,
+ * and by providing an end-of-data (EOD) marker.
+ */
+#define max_zero_run 100
+
+/* Common state */
+#define stream_BHC_state_common\
+ stream_hc_state_common;\
+ hc_definition definition;\
+ /* The client sets the following before initialization. */\
+ bool EndOfData;\
+ uint EncodeZeroRuns;\
+ /* The following are updated dynamically. */\
+ int zeros /* # of zeros scanned or left to output */
+typedef struct stream_BHC_state_s {
+ stream_BHC_state_common;
+} stream_BHC_state;
+
+/* BoundedHuffmanEncode */
+typedef struct stream_BHCE_state_s {
+ stream_BHC_state_common;
+ hce_table encode;
+} stream_BHCE_state;
+#define private_st_BHCE_state() /* in sbhc.c */\
+ gs_private_st_ptrs3(st_BHCE_state, stream_BHCE_state,\
+ "BoundedHuffmanEncode state", bhce_enum_ptrs, bhce_reloc_ptrs,\
+ definition.counts, definition.values, encode.codes)
+extern const stream_template s_BHCE_template;
+
+#define s_bhce_init_inline(ss)\
+ (s_hce_init_inline(ss), (ss)->zeros = 0)
+
+/* BoundedHuffmanDecode */
+typedef struct stream_BHCD_state_s {
+ stream_BHC_state_common;
+ hcd_table decode;
+} stream_BHCD_state;
+#define private_st_BHCD_state() /* in sbhc.c */\
+ gs_private_st_ptrs3(st_BHCD_state, stream_BHCD_state,\
+ "BoundedHuffmanDecode state", bhcd_enum_ptrs, bhcd_reloc_ptrs,\
+ definition.counts, definition.values, decode.codes)
+extern const stream_template s_BHCD_template;
+
+#define s_bhcd_init_inline(ss)\
+ (s_hcd_init_inline(ss), (ss)->zeros = 0)
+
+/* Declare variables that hold the decoder state. */
+#define bhcd_declare_state\
+ hcd_declare_state;\
+ int zeros
+
+/* Load the state from the stream. */
+/* Free variables: pr, ss, p, rlimit, bits, bits_left, zeros. */
+#define bhcd_load_state()\
+ hcd_load_state(), zeros = ss->zeros
+
+/* Store the state back in the stream. */
+/* Free variables: pr, ss, p, bits, bits_left, zeros. */
+#define bhcd_store_state()\
+ hcd_store_state(), ss->zeros = zeros
diff --git a/pstoraster/sbtx.h b/pstoraster/sbtx.h
new file mode 100644
index 000000000..111ff81d2
--- /dev/null
+++ b/pstoraster/sbtx.h
@@ -0,0 +1,39 @@
+/* Copyright (C) 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* sbtx.h */
+/* Definitions for BTE/BTD streams */
+/* Requires scommon.h; strimpl.h if any templates are referenced */
+
+/* ByteTranslateEncode/Decode */
+typedef struct stream_BT_state_s {
+ stream_state_common;
+ byte table[256];
+} stream_BT_state;
+typedef stream_BT_state stream_BTE_state;
+typedef stream_BT_state stream_BTD_state;
+#define private_st_BT_state() /* in sfilter1.c */\
+ gs_private_st_simple(st_BT_state, stream_BT_state, "ByteTranslateEncode/Decode state")
+extern const stream_template s_BT_template;
+#define s_BTD_template s_BT_template
+#define s_BTE_template s_BT_template
diff --git a/pstoraster/sbwbs.c b/pstoraster/sbwbs.c
new file mode 100644
index 000000000..6f9649812
--- /dev/null
+++ b/pstoraster/sbwbs.c
@@ -0,0 +1,476 @@
+/* Copyright (C) 1994, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* sbwbs.c */
+/* Burrows/Wheeler block sorting compression filters */
+#include "stdio_.h"
+#include "memory_.h"
+#include <stdlib.h> /* for qsort */
+#include "gdebug.h"
+#include "strimpl.h"
+#include "sfilter.h"
+#include "sbwbs.h"
+
+/* ------ Common code for streams that buffer a block ------ */
+
+private_st_buffered_state();
+
+#define ss ((stream_buffered_state *)st)
+
+/* Initialize */
+private int
+s_buffered_no_block_init(stream_state *st)
+{ ss->buffer = 0;
+ ss->filling = true;
+ ss->bpos = 0;
+ return 0;
+}
+private int
+s_buffered_block_init(stream_state *st)
+{ s_buffered_no_block_init(st);
+ ss->buffer = gs_alloc_bytes(st->memory, ss->BlockSize, "buffer");
+ if ( ss->buffer == 0 )
+ return ERRC; /****** WRONG ******/
+ return 0;
+}
+
+/* Continue filling the buffer if needed. */
+/* Return 0 if the buffer isn't full yet, 1 if it is full or if */
+/* we reached the end of input data. */
+/* In the latter case, also set filling = false. */
+/* Note that this procedure doesn't take pw as an argument. */
+private int
+s_buffered_process(stream_state *st, stream_cursor_read *pr, bool last)
+{ register const byte *p = pr->ptr;
+ const byte *rlimit = pr->limit;
+ uint count = rlimit - p;
+ uint left = ss->bsize - ss->bpos;
+ if ( !ss->filling )
+ return 1;
+ if ( left < count )
+ count = left;
+ if_debug3('w', "[w]buffering %d bytes to position %d, last = %s\n",
+ count, ss->bpos, (last ? "true" : "false"));
+ memcpy(ss->buffer + ss->bpos, p + 1, count);
+ pr->ptr = p += count;
+ ss->bpos += count;
+ if ( ss->bpos == ss->bsize || (p == rlimit && last) )
+ { ss->filling = false;
+ return 1;
+ }
+ return 0;
+}
+
+/* Release */
+private void
+s_buffered_release(stream_state *st)
+{ gs_free_object(st->memory, ss->buffer, "buffer");
+}
+
+#undef ss
+
+/* ------ Common code for Burrows/Wheeler block sorting filters ------ */
+
+private_st_BWBS_state();
+private void s_BWBS_release(P1(stream_state *));
+
+#define ss ((stream_BWBS_state *)st)
+
+/* Initialize */
+private int
+bwbs_init(stream_state *st, uint osize)
+{ int code;
+ ss->bsize = ss->BlockSize;
+ code = s_buffered_block_init(st);
+ if ( code != 0 )
+ return code;
+ ss->offsets = (void *)gs_alloc_bytes(st->memory, osize,
+ "BWBlockSort offsets");
+ if ( ss->offsets == 0 )
+ { s_BWBS_release(st);
+ return ERRC; /****** WRONG ******/
+ }
+ ss->I = -1; /* haven't read I yet */
+ return 0;
+}
+
+/* Release the filter. */
+private void
+s_BWBS_release(stream_state *st)
+{ gs_free_object(st->memory, ss->offsets, "BWBlockSort offsets");
+ s_buffered_release(st);
+}
+
+/* ------ BWBlockSortEncode ------ */
+
+/* Initialize */
+private int
+s_BWBSE_init(stream_state *st)
+{ return bwbs_init(st, ss->BlockSize * sizeof(int));
+}
+
+/* Compare two rotated strings for sorting. */
+private stream_BWBS_state *bwbs_compare_ss;
+private int
+bwbs_compare_rotations(const void *p1, const void *p2)
+{ const byte *buffer = bwbs_compare_ss->buffer;
+ const byte *s1 = buffer + *(const int *)p1;
+ const byte *s2 = buffer + *(const int *)p2;
+ const byte *start1;
+ const byte *end;
+ int swap;
+ if ( *s1 != *s2 )
+ return (*s1 < *s2 ? -1 : 1);
+ if ( s1 < s2 )
+ swap = 1;
+ else
+ { const byte *t = s1; s1 = s2; s2 = t;
+ swap = -1;
+ }
+ start1 = s1;
+ end = buffer + bwbs_compare_ss->N;
+ for ( s1++, s2++; s2 < end; s1++, s2++ )
+ if ( *s1 != *s2 )
+ return (*s1 < *s2 ? -swap : swap);
+ s2 = buffer;
+ for ( ; s1 < end; s1++, s2++ )
+ if ( *s1 != *s2 )
+ return (*s1 < *s2 ? -swap : swap);
+ s1 = buffer;
+ for ( ; s1 < start1; s1++, s2++ )
+ if ( *s1 != *s2 )
+ return (*s1 < *s2 ? -swap : swap);
+ return 0;
+}
+/* Sort the strings. */
+private void
+bwbse_sort(const byte *buffer, uint *indices, int N)
+{ offsets_full Cs;
+#define C Cs.v
+ /* Sort the strings. We start with a radix sort. */
+ uint sum = 0, j, ch;
+ memset(C, 0, sizeof(Cs));
+ for ( j = 0; j < N; j++ )
+ C[buffer[j]]++;
+ for ( ch = 0; ch <= 255; ch++ )
+ { sum += C[ch];
+ C[ch] = sum - C[ch];
+ }
+ for ( j = 0; j < N; j++ )
+ indices[C[buffer[j]]++] = j;
+ /* Now C[ch] = the number of strings that start */
+ /* with a character less than or equal to ch. */
+ sum = 0;
+ /* qsort each bucket produced by the radix sort. */
+ for ( ch = 0; ch <= 255; sum = C[ch], ch++ )
+ qsort(indices + sum, C[ch] - sum,
+ sizeof(*indices),
+ bwbs_compare_rotations);
+#undef C
+}
+
+/* Encode a buffer */
+private int
+s_BWBSE_process(stream_state *st, stream_cursor_read *pr,
+ stream_cursor_write *pw, bool last)
+{ register byte *q = pw->ptr;
+ byte *wlimit = pw->limit;
+ uint wcount = wlimit - q;
+ uint *indices = ss->offsets;
+ if ( ss->filling )
+ { int status, j, N;
+ byte *buffer = ss->buffer;
+ if ( wcount < sizeof(int) * 2 )
+ return 1;
+ /* Continue filling the buffer. */
+ status = s_buffered_process(st, pr, last);
+ if ( !status )
+ return 0;
+ ss->N = N = ss->bpos;
+ /* We reverse the string before encoding it, */
+ /* so it will come out of the decoder correctly. */
+ for ( j = N / 2 - 1; j >= 0; j-- )
+ { byte *p0 = &buffer[j];
+ byte *p1 = &buffer[N - 1 - j];
+ byte b = *p0;
+ *p0 = *p1;
+ *p1 = b;
+ }
+ /* Save st in a static, because that's the only way */
+ /* we can pass it to the comparison procedure (ugh). */
+ bwbs_compare_ss = ss;
+ /* Sort the strings. */
+ bwbse_sort(buffer, indices, N);
+ /* Find the unrotated string. */
+ for ( j = 0; j < N; j++ )
+ if ( indices[j] == 0 )
+ { ss->I = j;
+ break;
+ }
+ for ( j = sizeof(int); --j >= 0; )
+ *++q = (byte)(N >> (j * 8));
+ for ( j = sizeof(int); --j >= 0; )
+ *++q = (byte)(ss->I >> (j * 8));
+ ss->bpos = 0;
+ }
+ /* We're reading out of the buffer, writing the permuted string. */
+ while ( q < wlimit && ss->bpos < ss->N )
+ { int i = indices[ss->bpos++];
+ *++q = ss->buffer[(i == 0 ? ss->N - 1 : i - 1)];
+ }
+ if ( ss->bpos == ss->N )
+ { ss->filling = true;
+ ss->bpos = 0;
+ }
+ pw->ptr = q;
+ if ( q == wlimit )
+ return 1;
+ return 0;
+}
+
+/* Stream template */
+const stream_template s_BWBSE_template =
+{ &st_BWBS_state, s_BWBSE_init, s_BWBSE_process, sizeof(int) * 2, 1, s_BWBS_release
+};
+
+/* ------ BWBlockSortDecode ------ */
+
+#define SHORT_OFFSETS
+
+#ifdef SHORT_OFFSETS
+
+/*
+ * Letting S[0..N-1] be the data block before depermutation, we need
+ * a table P[0..N-1] that maps the index i to O(S[i],i), where O(c,i) is
+ * the number of occurrences of c in S before position i.
+ * We observe that for a fixed c, O(c,i) is monotonic with i,
+ * and falls in the range 0 .. i; consequently, if 0 <= i <= j,
+ * 0 <= O(c,j) - O(c,i) <= j - i. Proceeding from this observation,
+ * rather than allocate an entire int to each entry of P,
+ * we construct three tables as follows:
+ * P2[k,c] = O(c,k*65536) for k = 0 .. (N-1)/65536;
+ * each entry requires an int.
+ * P1[k,c] = O(c,k*4096) - P2[k/16,c] for k = 0 .. (N-1)/4096;
+ * each entry falls in the range 0 .. 15*4096 and hence
+ * requires 16 bits.
+ * P0[i] = O(S[i],i) - P1[i/4096,S[i]] for i = 0 .. N-1;
+ * each entry falls in the range 0 .. 4095 and hence
+ * requires 12 bits.
+ * Since the value we need in the decompression loop is actually
+ * P[i] + C[S[i]], where C[c] is the sum of O(0,N) ... O(c-1,N),
+ * we add C[c] into P2[k,c] for all k.
+ */
+/*typedef struct { uint v[256]; } offsets_full;*/ /* in sbwbs.h */
+typedef struct { bits16 v[256]; } offsets_4k;
+#if arch_sizeof_int > 2
+# define ceil_64k(n) (((n) + 0xffff) >> 16)
+#else
+# define ceil_64k(n) 1
+#endif
+#define ceil_4k(n) (((n) + 0xfff) >> 12)
+#define offset_space(bsize)\
+ (ceil_64k(bsize) * sizeof(offsets_full) +\
+ ceil_4k(bsize) * sizeof(offsets_4k) +\
+ ((bsize + 1) >> 1) * 3)
+
+#else /* !SHORT_OFFSETS */
+
+#define offset_space(bsize)\
+ (bsize * sizeof(int))
+
+#endif /* (!)SHORT_OFFSETS */
+
+/* Initialize */
+private int
+s_BWBSD_init(stream_state *st)
+{ uint bsize = ss->BlockSize;
+ return bwbs_init(st, offset_space(bsize));
+}
+
+/* Construct the decoding tables. */
+
+#ifdef SHORT_OFFSETS
+
+private void
+bwbsd_construct_offsets(stream_BWBS_state *sst, offsets_full *po64k,
+ offsets_4k *po4k, byte *po1, int N)
+{ offsets_full Cs;
+#define C Cs.v
+ uint i1;
+ byte *b = sst->buffer;
+ offsets_full *p2 = po64k - 1;
+ offsets_4k *p1 = po4k;
+ byte *p0 = po1;
+ memset(C, 0, sizeof(Cs));
+ for ( i1 = 0; i1 < ceil_4k(N); i1++, p1++ )
+ { int j;
+ if ( !(i1 & 15) )
+ *++p2 = Cs;
+ for ( j = 0; j < 256; j++ )
+ p1->v[j] = C[j] - p2->v[j];
+ j = (N + 1 - (i1 << 12)) >> 1;
+ if ( j > 4096/2 )
+ j = 4096/2;
+ for ( ; j > 0; j--, b += 2, p0 += 3 )
+ { byte b0 = b[0];
+ uint d0 = C[b0]++ - (p1->v[b0] + p2->v[b0]);
+ byte b1 = b[1];
+ uint d1 = C[b1]++ - (p1->v[b1] + p2->v[b1]);
+ p0[0] = d0 >> 4;
+ p0[1] = (byte)((d0 << 4) + (d1 >> 8));
+ p0[2] = (byte)d1;
+ }
+ }
+ /* If the block length is odd, discount the extra byte. */
+ if ( N & 1 )
+ C[sst->buffer[N]]--;
+ /* Compute the cumulative totals in C. */
+ { int sum = 0, ch;
+ for ( ch = 0; ch <= 255; ch++ )
+ { sum += C[ch];
+ C[ch] = sum - C[ch];
+ }
+ }
+ /* Add the C values back into the 64K table, */
+ /* which saves an addition of C[b] in the decoding loop. */
+ { int i2, ch;
+ for ( p2 = po64k, i2 = ceil_64k(N); i2 > 0; p2++, i2-- )
+ for ( ch = 0; ch < 256; ch++ )
+ p2->v[ch] += C[ch];
+ }
+#undef C
+}
+
+#else /* !SHORT_OFFSETS */
+
+private void
+bwbsd_construct_offsets(stream_BWBS_state *sst, int *po, int N)
+{ offsets_full Cs;
+#define C Cs.v
+ uint i;
+ byte *b = sst->buffer;
+ int *p = po;
+ memset(C, 0, sizeof(Cs));
+ for ( i = 0; i < N; i++, p++, b++ )
+ *p = C[*b]++;
+ /* Compute the cumulative totals in C. */
+ { int sum = 0, ch;
+ for ( ch = 0; ch <= 255; ch++ )
+ { sum += C[ch];
+ C[ch] = sum - C[ch];
+ }
+ }
+ /* Add the C values back into the offsets. */
+ for ( i = 0, b = sst->buffer, p = po; i < N; i++, b++, p++ )
+ *p += C[*b];
+#undef C
+}
+
+#endif /* (!)SHORT_OFFSETS */
+
+/* Decode a buffer */
+private int
+s_BWBSD_process(stream_state *st, stream_cursor_read *pr,
+ stream_cursor_write *pw, bool last)
+{ register const byte *p = pr->ptr;
+ const byte *rlimit = pr->limit;
+ uint count = rlimit - p;
+ register byte *q = pw->ptr;
+ byte *wlimit = pw->limit;
+#ifdef SHORT_OFFSETS
+ uint BlockSize = ss->BlockSize;
+ offsets_full *po64k = ss->offsets;
+ offsets_4k *po4k = (offsets_4k *)(po64k + ceil_64k(BlockSize));
+ byte *po1 = (byte *)(po4k + ceil_4k(BlockSize));
+#else /* !SHORT_OFFSETS */
+ int *po = ss->offsets;
+#endif /* (!)SHORT_OFFSETS */
+ if ( ss->I < 0 )
+ { /* Read block parameters */
+ int I, N, j;
+ if ( count < sizeof(int) * 2 )
+ return 0;
+ for ( N = 0, j = 0; j < sizeof(int); j++ )
+ N = (N << 8) + *++p;
+ for ( I = 0, j = 0; j < sizeof(int); j++ )
+ I = (I << 8) + *++p;
+ ss->N = N;
+ ss->I = I;
+ if_debug2('w', "[w]N=%d I=%d\n", N, I);
+ pr->ptr = p;
+ if ( N < 0 || N > ss->BlockSize || I < 0 || I >= N )
+ return ERRC;
+ if ( N == 0 )
+ return EOFC;
+ count -= sizeof(int) * 2;
+ ss->bpos = 0;
+ ss->bsize = N;
+ }
+ if ( ss->filling )
+ { /* Continue filling the buffer. */
+ if ( !s_buffered_process(st, pr, last) )
+ return 0;
+ /* Construct the inverse sort order. */
+#ifdef SHORT_OFFSETS
+ bwbsd_construct_offsets(ss, po64k, po4k, po1, ss->bsize);
+#else /* !SHORT_OFFSETS */
+ bwbsd_construct_offsets(ss, po, ss->bsize);
+#endif /* (!)SHORT_OFFSETS */
+ ss->bpos = 0;
+ ss->i = ss->I;
+ }
+ /* We're reading out of the buffer. */
+ while ( q < wlimit && ss->bpos < ss->bsize )
+ { int i = ss->i;
+ byte b = ss->buffer[i];
+#ifdef SHORT_OFFSETS
+ uint d;
+ const byte *pd = &po1[(i >> 1) + i];
+ *++q = b;
+ if ( !(i & 1) )
+ d = ((uint)pd[0] << 4) + (pd[1] >> 4);
+ else
+ d = ((pd[0] & 0xf) << 8) + pd[1];
+ ss->i = po64k[i >> 16].v[b] + po4k[i >> 12].v[b] + d;
+#else /* !SHORT_OFFSETS */
+ *++q = b;
+ ss->i = po[i];
+#endif /* (!)SHORT_OFFSETS */
+ ss->bpos++;
+ }
+ if ( ss->bpos == ss->bsize )
+ { ss->I = -1;
+ ss->filling = true;
+ }
+ pw->ptr = q;
+ if ( q == wlimit )
+ return 1;
+ return 0;
+}
+
+#undef ss
+
+/* Stream template */
+const stream_template s_BWBSD_template =
+{ &st_BWBS_state, s_BWBSD_init, s_BWBSD_process, 1, sizeof(int) * 2, s_BWBS_release
+};
diff --git a/pstoraster/sbwbs.h b/pstoraster/sbwbs.h
new file mode 100644
index 000000000..847d241fc
--- /dev/null
+++ b/pstoraster/sbwbs.h
@@ -0,0 +1,71 @@
+/* Copyright (C) 1994, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* sbwbs.h */
+/* Definitions for Burroughs/Wheeler block sorting compression streams */
+/* Requires scommon.h; strimpl.h if any templates are referenced */
+
+/* Common framework for streams that buffer a block for processing */
+#define stream_buffered_state_common\
+ stream_state_common;\
+ /* The client may set the following before initialization, */\
+ /* or the stream may set it later. */\
+ int BlockSize;\
+ /* The init procedure sets the following, */\
+ /* if BlockSize has been set. */\
+ byte *buffer; /* [BlockSize] */\
+ /* The following are updated dynamically. */\
+ bool filling; /* true if filling buffer, */\
+ /* false if emptying */\
+ int bsize; /* size of current block (<= BlockSize) */\
+ int bpos /* current index within buffer */
+typedef struct stream_buffered_state_s {
+ stream_buffered_state_common;
+} stream_buffered_state;
+#define private_st_buffered_state() /* in sbwbs.c */\
+ gs_private_st_ptrs1(st_buffered_state, stream_buffered_state,\
+ "stream_buffered state", sbuf_enum_ptrs, sbuf_reloc_ptrs, buffer)
+
+/* BWBlockSortEncode/Decode */
+typedef struct of_ {
+ uint v[256];
+} offsets_full;
+typedef struct stream_BWBS_state_s {
+ stream_buffered_state_common;
+ /* The init procedure sets the following. */
+ void *offsets; /* permutation indices when writing, */
+ /* multi-level indices when reading */
+ /* The following are updated dynamically. */
+ int N; /* actual length of block */
+ /* The following are only used when decoding. */
+ int I; /* index of unrotated string */
+ int i; /* next index in encoded string */
+} stream_BWBS_state;
+typedef stream_BWBS_state stream_BWBSE_state;
+typedef stream_BWBS_state stream_BWBSD_state;
+#define private_st_BWBS_state() /* in sbwbs.c */\
+ gs_private_st_suffix_add1(st_BWBS_state, stream_BWBS_state,\
+ "BWBlockSortEncode/Decode state", bwbs_enum_ptrs, bwbs_reloc_ptrs,\
+ st_buffered_state, offsets)
+extern const stream_template s_BWBSE_template;
+extern const stream_template s_BWBSD_template;
diff --git a/pstoraster/scanchar.h b/pstoraster/scanchar.h
new file mode 100644
index 000000000..95b75cb02
--- /dev/null
+++ b/pstoraster/scanchar.h
@@ -0,0 +1,69 @@
+/* Copyright (C) 1990, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* scanchar.h */
+/* Character scanning table for Ghostscript */
+/* Requires scommon.h */
+
+/*
+ * An array for fast scanning of names, numbers, and hex strings.
+ * Indexed by character code (including exceptions), it contains:
+ * 0 - max_radix-1 for valid digits,
+ * ctype_name for other characters valid in names,
+ * ctype_btoken for characters introducing binary tokens
+ * (if the binary token feature is enabled),
+ * ctype_space for whitespace characters,
+ * ctype_exception for exceptions (see scommon.h), and
+ * ctype_other for everything else.
+ * Exceptions are negative values; we bias the table origin accordingly.
+ *
+ * NOTE: This table is defined in iscantab.c and used in a variety of places.
+ * If any of the values below change, you must edit the table.
+ */
+extern const byte scan_char_array[max_stream_exception + 256];
+#define scan_char_decoder (&scan_char_array[max_stream_exception])
+#define min_radix 2
+#define max_radix 36
+#define ctype_name 100
+#define ctype_btoken 101
+#define ctype_space 102
+#define ctype_other 103
+#define ctype_exception 104
+/* Special characters with no \xxx representation */
+#define char_NULL 0
+#define char_EOT 004 /* ^D, job delimiter */
+#define char_VT 013 /* ^K, vertical tab */
+#define char_DOS_EOF 032 /* ^Z */
+/*
+ * Most systems define '\n' as 0x0a and '\r' as 0x0d; however, OS-9
+ * has '\n' = '\r' = 0x0d and '\l' = 0x0a. To deal with this,
+ * we introduce abstract characters char_CR and char_EOL such that
+ * any of [char_CR], [char_CR char_EOL], or [char_EOL] is recognized
+ * as an end-of-line sequence.
+ */
+#define char_CR '\r'
+#if '\r' == '\n'
+# define char_EOL 0x0a /* non-OS-9 compilers complain about '\l' */
+#else
+# define char_EOL '\n'
+#endif
diff --git a/pstoraster/scf.h b/pstoraster/scf.h
new file mode 100644
index 000000000..f9269eeeb
--- /dev/null
+++ b/pstoraster/scf.h
@@ -0,0 +1,173 @@
+/* Copyright (C) 1992, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* scf.h */
+/* Common definitions for CCITTFax encoding and decoding filters */
+#include "shc.h"
+
+/*
+ * The CCITT Group 3 (T.4) and Group 4 (T.6) fax specifications map
+ * run lengths to Huffman codes. White and black have different mappings.
+ * If the run length is 64 or greater, two or more codes are needed:
+ * - One or more 'make-up' codes for 2560 pixels;
+ * - A 'make-up' code that encodes the multiple of 64;
+ * - A 'termination' code for the remainder.
+ * For runs of 63 or less, only the 'termination' code is needed.
+ */
+
+/* ------ Encoding tables ------ */
+
+/* Define the maximum length of a scan line that we can encode. */
+/* Even though the natural values for this are 2560 * N + 63, */
+/* we don't want to impose a limit significantly smaller than max_int. */
+#define cfe_max_width 32000
+#define cfe_max_makeups (cfe_max_width / 2560)
+#define cfe_max_code_bytes (cfe_max_makeups * 2 + 2) /* conservative */
+
+typedef hce_code cfe_run;
+#define cfe_entry(c, len) hce_entry(c, len)
+
+/* Codes common to 1-D and 2-D encoding. */
+/* The decoding algorithms know that EOL is 0....01. */
+#define run_eol_code_length 12
+#define run_eol_code_value 1
+extern const cfe_run cf_run_eol;
+extern const cfe_run far_data cf_white_termination[64];
+extern const cfe_run far_data cf_white_make_up[41];
+extern const cfe_run far_data cf_black_termination[64];
+extern const cfe_run far_data cf_black_make_up[41];
+extern const cfe_run cf_uncompressed[6];
+extern const cfe_run cf_uncompressed_exit[10]; /* indexed by 2 x length of */
+ /* white run + (1 if next run black, 0 if white) */
+/* 1-D encoding. */
+extern const cfe_run cf1_run_uncompressed;
+/* 2-D encoding. */
+extern const cfe_run cf2_run_pass;
+#define cf2_run_pass_length 4
+#define cf2_run_pass_value 0x1
+#define cf2_run_vertical_offset 3
+extern const cfe_run cf2_run_vertical[7]; /* indexed by b1 - a1 + offset */
+extern const cfe_run cf2_run_horizontal;
+#define cf2_run_horizontal_value 1
+#define cf2_run_horizontal_length 3
+extern const cfe_run cf2_run_uncompressed;
+/* 2-D Group 3 encoding. */
+extern const cfe_run cf2_run_eol_1d;
+extern const cfe_run cf2_run_eol_2d;
+
+/* ------ Decoding tables ------ */
+
+typedef hcd_code cfd_node;
+#define run_length value
+
+/*
+ * The value in the decoding tables is either a white or black run length,
+ * or a (negative) exceptional value.
+ */
+#define run_error (-1)
+#define run_zeros (-2) /* EOL follows, possibly with more padding first */
+#define run_uncompressed (-3)
+/* 2-D codes */
+#define run2_pass (-4)
+#define run2_horizontal (-5)
+
+#define cfd_white_initial_bits 8
+extern const cfd_node far_data cf_white_decode[];
+#define cfd_black_initial_bits 7
+extern const cfd_node far_data cf_black_decode[];
+#define cfd_2d_initial_bits 7
+extern const cfd_node far_data cf_2d_decode[];
+#define cfd_uncompressed_initial_bits 6 /* must be 6 */
+extern const cfd_node far_data cf_uncompressed_decode[];
+
+/* ------ Run detection macros ------ */
+
+/*
+ * For the run detection macros:
+ * white_byte is 0 or 0xff for BlackIs1 or !BlackIs1 respectively;
+ * data holds p[-1], inverted if !BlackIs1;
+ * count is the number of valid bits remaining in the scan line.
+ */
+
+/* Aliases for bit processing tables. */
+#define cf_byte_run_length byte_bit_run_length_neg
+#define cf_byte_run_length_0 byte_bit_run_length_0
+
+/* Skip over white pixels to find the next black pixel in the input. */
+/* Store the run length in rlen, and update data, p, and count. */
+/* There are many more white pixels in typical input than black pixels, */
+/* and the runs of white pixels tend to be much longer, so we use */
+/* substantially different loops for the two cases. */
+
+#define skip_white_pixels(data, p, count, white_byte, rlen)\
+{ if ( (rlen = cf_byte_run_length[count & 7][data ^ 0xff]) >= 8 )\
+ { if ( white_byte == 0 )\
+ { if ( p[0] ) { data = p[0]; p += 1; rlen -= 8; }\
+ else if ( p[1] ) { data = p[1]; p += 2; }\
+ else\
+ { while ( !(p[2] | p[3] | p[4] | p[5]) ) p += 4, rlen += 32;\
+ if ( p[2] ) { data = p[2]; p += 3; rlen += 8; }\
+ else if ( p[3] ) { data = p[3]; p += 4; rlen += 16; }\
+ else if ( p[4] ) { data = p[4]; p += 5; rlen += 24; }\
+ else /* p[5] */ { data = p[5]; p += 6; rlen += 32; }\
+ }\
+ }\
+ else\
+ { if ( p[0] != 0xff ) { data = (byte)~p[0]; p += 1; rlen -= 8; }\
+ else if ( p[1] != 0xff ) { data = (byte)~p[1]; p += 2; }\
+ else\
+ { while ( (p[2] & p[3] & p[4] & p[5]) == 0xff ) p += 4, rlen += 32;\
+ if ( p[2] != 0xff ) { data = (byte)~p[2]; p += 3; rlen += 8; }\
+ else if ( p[3] != 0xff ) { data = (byte)~p[3]; p += 4; rlen += 16; }\
+ else if ( p[4] != 0xff ) { data = (byte)~p[4]; p += 5; rlen += 24; }\
+ else /* p[5] != 0xff */ { data = (byte)~p[5]; p += 6; rlen += 32; }\
+ }\
+ }\
+ rlen += cf_byte_run_length_0[data ^ 0xff];\
+ }\
+ count -= rlen;\
+}
+
+/* Skip over black pixels to find the next white pixel in the input. */
+/* Store the run length in rlen, and update data, p, and count. */
+
+#define skip_black_pixels(data, p, count, white_byte, rlen)\
+{ if ( (rlen = cf_byte_run_length[count & 7][data]) >= 8 )\
+ { if ( white_byte == 0 )\
+ for ( ; ; p += 4, rlen += 32 )\
+ { if ( p[0] != 0xff ) { data = p[0]; p += 1; rlen -= 8; break; }\
+ if ( p[1] != 0xff ) { data = p[1]; p += 2; break; }\
+ if ( p[2] != 0xff ) { data = p[2]; p += 3; rlen += 8; break; }\
+ if ( p[3] != 0xff ) { data = p[3]; p += 4; rlen += 16; break; }\
+ }\
+ else\
+ for ( ; ; p += 4, rlen += 32 )\
+ { if ( p[0] ) { data = (byte)~p[0]; p += 1; rlen -= 8; break; }\
+ if ( p[1] ) { data = (byte)~p[1]; p += 2; break; }\
+ if ( p[2] ) { data = (byte)~p[2]; p += 3; rlen += 8; break; }\
+ if ( p[3] ) { data = (byte)~p[3]; p += 4; rlen += 16; break; }\
+ }\
+ rlen += cf_byte_run_length_0[data];\
+ }\
+ count -= rlen;\
+}
diff --git a/pstoraster/scfd.c b/pstoraster/scfd.c
new file mode 100644
index 000000000..864fbcabb
--- /dev/null
+++ b/pstoraster/scfd.c
@@ -0,0 +1,771 @@
+/* Copyright (C) 1992, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* scfd.c */
+/* CCITTFax decoding filter */
+#include "stdio_.h" /* includes std.h */
+#include "memory_.h"
+#include "gdebug.h"
+#include "strimpl.h"
+#include "scf.h"
+#include "scfx.h"
+
+/* ------ CCITTFaxDecode ------ */
+
+private_st_CFD_state();
+
+#define ss ((stream_CFD_state *)st)
+
+/* Set default parameter values. */
+private void
+s_CFD_set_defaults(register stream_state *st)
+{ s_CFD_set_defaults_inline(ss);
+}
+
+/* Initialize CCITTFaxDecode filter */
+private int
+s_CFD_init(stream_state *st)
+{ int raster = ss->raster =
+ round_up((ss->Columns + 7) >> 3, ss->DecodedByteAlign);
+ byte white = (ss->BlackIs1 ? 0 : 0xff);
+ s_hcd_init_inline(ss);
+ ss->lbuf = gs_alloc_bytes(st->memory, raster + 1, "CFD lbuf");
+ ss->lprev = 0;
+ if ( ss->lbuf == 0 )
+ return ERRC; /****** WRONG ******/
+ if ( ss->K != 0 )
+ { ss->lprev = gs_alloc_bytes(st->memory, raster + 1, "CFD lprev");
+ if ( ss->lprev == 0 )
+ return ERRC; /****** WRONG ******/
+ /* Clear the initial reference line for 2-D encoding. */
+ memset(ss->lbuf, white, raster);
+ }
+ ss->k_left = min(ss->K, 0);
+ ss->run_color = 0;
+ ss->damaged_rows = 0;
+ ss->skipping_damage = false;
+ ss->cbit = 0;
+ ss->uncomp_run = 0;
+ ss->rows_left = (ss->Rows <= 0 ? -1 : ss->Rows + 1);
+ ss->rpos = ss->wpos = raster - 1;
+ ss->eol_count = 0;
+ ss->invert = white;
+ return 0;
+}
+
+/* Release the filter. */
+private void
+s_CFD_release(stream_state *st)
+{ gs_free_object(st->memory, ss->lprev, "CFD lprev(close)");
+ gs_free_object(st->memory, ss->lbuf, "CFD lbuf(close)");
+}
+
+/* Declare the variables that hold the state. */
+#define cfd_declare_state\
+ hcd_declare_state;\
+ register byte *q;\
+ int qbit
+/* Load the state from the stream. */
+#define cfd_load_state()\
+ hcd_load_state(),\
+ q = ss->lbuf + ss->wpos, qbit = ss->cbit
+/* Store the state back in the stream. */
+#define cfd_store_state()\
+ hcd_store_state(),\
+ ss->wpos = q - ss->lbuf, ss->cbit = qbit
+
+/* Macros to get blocks of bits from the input stream. */
+/* Invariants: 0 <= bits_left <= bits_size; */
+/* bits [bits_left-1..0] contain valid data. */
+
+#define avail_bits(n) hcd_bits_available(n)
+#define ensure_bits(n, outl) hcd_ensure_bits(n, outl)
+#define peek_bits(n) hcd_peek_bits(n)
+#define peek_var_bits(n) hcd_peek_var_bits(n)
+#define skip_bits(n) hcd_skip_bits(n)
+
+/* Get a run from the stream. */
+#define get_run(decode, initial_bits, runlen, str, outl)\
+{ const cfd_node *np;\
+ int clen;\
+ ensure_bits(initial_bits, outl);\
+ np = &decode[peek_bits(initial_bits)];\
+ if ( (clen = np->code_length) > initial_bits )\
+ { do_debug(uint init_bits = peek_bits(initial_bits));\
+ if ( !avail_bits(clen) ) goto outl;\
+ clen -= initial_bits;\
+ skip_bits(initial_bits);\
+ ensure_bits(clen, outl); /* can't goto outl */\
+ np = &decode[np->run_length + peek_var_bits(clen)];\
+ if_debug4('W', "%s xcode=0x%x,%d rlen=%d\n", str,\
+ (init_bits << np->code_length) +\
+ peek_var_bits(np->code_length),\
+ initial_bits + np->code_length,\
+ np->run_length);\
+ skip_bits(np->code_length);\
+ }\
+ else\
+ { if_debug4('W', "%s code=0x%x,%d rlen=%d\n", str,\
+ peek_var_bits(clen), clen, np->run_length);\
+ skip_bits(clen);\
+ }\
+ runlen = np->run_length;\
+}
+
+/* Skip data bits for a white run. */
+/* rlen is either less than 64, or a multiple of 64. */
+#define skip_data(rlen, makeup_label)\
+ if ( (qbit -= rlen) < 0 )\
+ { q -= qbit >> 3, qbit &= 7;\
+ if ( rlen >= 64 ) goto makeup_label;\
+ }
+
+/* Invert data bits for a black run. */
+/* If rlen >= 64, execute makeup_action: this is to handle */
+/* makeup codes efficiently, since these are always a multiple of 64. */
+#define invert_data(rlen, black_byte, makeup_action, d)\
+ if ( rlen > qbit )\
+ { *q++ ^= (1 << qbit) - 1;\
+ rlen -= qbit;\
+ switch ( rlen >> 3 )\
+ {\
+ default: /* original rlen >= 64 */\
+d: memset(q, black_byte, rlen >> 3);\
+ q += rlen >> 3;\
+ rlen &= 7;\
+ if ( !rlen ) qbit = 0, q--;\
+ else qbit = 8 - rlen, *q ^= 0xff << qbit;\
+ makeup_action;\
+ break;\
+ case 7: /* original rlen possibly >= 64 */\
+ if ( rlen + qbit >= 64 ) goto d;\
+ *q++ = black_byte;\
+ case 6: *q++ = black_byte;\
+ case 5: *q++ = black_byte;\
+ case 4: *q++ = black_byte;\
+ case 3: *q++ = black_byte;\
+ case 2: *q++ = black_byte;\
+ case 1: *q = black_byte;\
+ rlen &= 7;\
+ if ( !rlen ) { qbit = 0; break; }\
+ q++;\
+ case 0: /* know rlen != 0 */\
+ qbit = 8 - rlen;\
+ *q ^= 0xff << qbit;\
+ }\
+ }\
+ else\
+ qbit -= rlen,\
+ *q ^= ((1 << rlen) - 1) << qbit
+
+/* Buffer refill for CCITTFaxDecode filter */
+private int cf_decode_eol(P2(stream_CFD_state *, stream_cursor_read *));
+private int cf_decode_1d(P2(stream_CFD_state *, stream_cursor_read *));
+private int cf_decode_2d(P2(stream_CFD_state *, stream_cursor_read *));
+private int cf_decode_uncompressed(P2(stream_CFD_state *, stream_cursor_read *));
+private int
+s_CFD_process(stream_state *st, stream_cursor_read *pr,
+ stream_cursor_write *pw, bool last)
+{ int wstop = ss->raster - 1;
+ int eol_count = ss->eol_count;
+ int k_left = ss->k_left;
+ int rows_left = ss->rows_left;
+ int status = 0;
+top: if ( ss->skipping_damage )
+ { /* Skip until we reach an EOL. */
+ hcd_declare_state;
+ int skip;
+ status = 0;
+ do
+ { switch ( (skip = cf_decode_eol(ss, pr)) )
+ {
+ default: /* not EOL */
+ hcd_load_state();
+ skip_bits(-skip);
+ hcd_store_state();
+ continue;
+ case 0: /* need more input */
+ goto out;
+ case 1: /* EOL */
+ { /* Back up over the EOL. */
+ hcd_load_state();
+ bits_left += run_eol_code_length;
+ hcd_store_state();
+ }
+ ss->skipping_damage = false;
+ }
+ }
+ while ( ss->skipping_damage );
+ ss->damaged_rows++;
+ }
+ /*
+ * Check for a completed input scan line. This isn't quite as
+ * simple as it seems, because we could have run out of input data
+ * between a makeup code and a 0-length termination code, or in a
+ * 2-D line before a final horizontal code with a 0-length second
+ * run. There's probably a way to think about this situation that
+ * doesn't require a special check, but I haven't found it yet.
+ */
+ if ( ss->wpos == wstop && ss->cbit <= (-ss->Columns & 7) &&
+ (k_left == 0 ? !(ss->run_color & ~1) : ss->run_color == 0)
+ )
+ { /* Check for completed data to be copied to the client. */
+ /* (We could avoid the extra copy step for 1-D, but */
+ /* it's simpler not to, and it doesn't cost much.) */
+ if ( ss->rpos < ss->wpos )
+ { stream_cursor_read cr;
+ cr.ptr = ss->lbuf + ss->rpos;
+ cr.limit = ss->lbuf + ss->wpos;
+ status = stream_move(&cr, pw);
+ ss->rpos = cr.ptr - ss->lbuf;
+ if ( status )
+ goto out;
+ }
+ if ( rows_left > 0 && --rows_left == 0 )
+ { status = EOFC;
+ goto out;
+ }
+ if ( ss->K != 0 )
+ { byte *prev_bits = ss->lprev;
+ ss->lprev = ss->lbuf;
+ ss->lbuf = prev_bits;
+ if ( ss->K > 0 )
+ k_left = (k_left == 0 ? ss->K : k_left) - 1;
+ }
+ ss->rpos = ss->wpos = -1;
+ ss->eol_count = eol_count = 0;
+ ss->cbit = 0;
+ ss->invert = (ss->BlackIs1 ? 0 : 0xff);
+ memset(ss->lbuf, ss->invert, wstop + 1);
+ ss->run_color = 0;
+ if ( ss->EncodedByteAlign )
+ ss->bits_left &= ~7;
+ }
+ /* If we're between scan lines, scan for EOLs. */
+ if ( ss->wpos < 0 )
+ { while ( (status = cf_decode_eol(ss, pr)) > 0 )
+ { if_debug0('w', "[w]EOL\n");
+ /* If we are in a Group 3 mixed regime, */
+ /* check the next bit for 1- vs. 2-D. */
+ if ( ss->K > 0 )
+ { hcd_declare_state;
+ hcd_load_state();
+ ensure_bits(1, out); /* can't fail */
+ k_left = (peek_bits(1) ? 0 : 1);
+ skip_bits(1);
+ hcd_store_state();
+ }
+ ++eol_count;
+ if ( ss->EndOfBlock )
+ { /* Check for end-of-data sequence. */
+ if (eol_count == (ss->K < 0 ? 2 : 6))
+ { status = EOFC;
+ goto out;
+ }
+ }
+ else
+ break; /* >1 EOL is an error */
+ }
+ if ( status == 0 ) /* input empty while scanning EOLs */
+ goto out;
+ switch ( eol_count )
+ {
+ case 0:
+ if ( ss->EndOfLine )
+ { /* EOL is required, but none is present. */
+ status = ERRC;
+ goto check;
+ }
+ case 1:
+ break;
+ default:
+ status = ERRC;
+ goto check;
+ }
+ }
+ /* Now decode actual data. */
+ if ( k_left < 0 )
+ { if_debug0('w', "[w2]new row\n");
+ status = cf_decode_2d(ss, pr);
+ }
+ else if ( k_left == 0 )
+ { if_debug0('w', "[w1]new row\n");
+ status = cf_decode_1d(ss, pr);
+ }
+ else
+ { if_debug1('w', "[w1]new 2-D row, %d left\n", k_left);
+ status = cf_decode_2d(ss, pr);
+ }
+ if_debug3('w', "[w]CFD status = %d, wpos = %d, cbit = %d\n",
+ status, ss->wpos, ss->cbit);
+check: switch ( status )
+ {
+ case 1: /* output full */
+ goto top;
+ case ERRC:
+ /* Check for special handling of damaged rows. */
+ if ( ss->damaged_rows >= ss->DamagedRowsBeforeError ||
+ !(ss->EndOfLine && ss->K >= 0)
+ )
+ break;
+ /* Substitute undamaged data if appropriate. */
+ /****** NOT IMPLEMENTED YET ******/
+ { ss->wpos = wstop;
+ ss->cbit = -ss->Columns & 7;
+ ss->run_color = 0;
+ }
+ ss->skipping_damage = true;
+ goto top;
+ default:
+ ss->damaged_rows = 0; /* finished a good row */
+ }
+out: ss->k_left = k_left;
+ ss->rows_left = rows_left;
+ ss->eol_count = eol_count;
+ return status;
+}
+
+#undef ss
+
+/*
+ * Decode a leading EOL, if any.
+ * If an EOL is present, skip over it and return 1;
+ * if no EOL is present, read no input and return -N, where N is the
+ * number of initial bits that can be skipped in the search for an EOL;
+ * if more input is needed, return 0.
+ * Note that if we detected an EOL, we know that we can back up over it;
+ * if we detected an N-bit non-EOL, we know that at least N bits of data
+ * are available in the buffer.
+ */
+private int
+cf_decode_eol(stream_CFD_state *ss, stream_cursor_read *pr)
+{ hcd_declare_state;
+ int zeros;
+ int look_ahead;
+ hcd_load_state();
+ for ( zeros = 0; zeros < run_eol_code_length - 1; zeros++ )
+ { ensure_bits(1, out);
+ if ( peek_bits(1) )
+ return -(zeros + 1);
+ skip_bits(1);
+ }
+ /* We definitely have an EOL. Skip further zero bits. */
+ look_ahead = (ss->K > 0 ? 2 : 1);
+ for ( ; ; )
+ { ensure_bits(look_ahead, back);
+ if ( peek_bits(1) )
+ break;
+ skip_bits(1);
+ }
+ skip_bits(1);
+ hcd_store_state();
+ return 1;
+back: /*
+ * We ran out of data while skipping zeros.
+ * We know we are at a byte boundary, and have just skipped
+ * at least run_eol_code_length - 1 zeros. However,
+ * bits_left may be 1 if look_ahead == 2.
+ */
+ bits &= (1 << bits_left) - 1;
+ bits_left += run_eol_code_length - 1;
+ hcd_store_state();
+out: return 0;
+}
+
+/* Decode a 1-D scan line. */
+private int
+cf_decode_1d(stream_CFD_state *ss, stream_cursor_read *pr)
+{ cfd_declare_state;
+ byte black_byte = (ss->BlackIs1 ? 0xff : 0);
+ int end_bit = -ss->Columns & 7;
+ byte *stop = ss->lbuf - 1 + ss->raster;
+ int run_color = 0;
+ int status;
+ int bcnt;
+
+ cfd_load_state();
+ if_debug1('w', "[w1]entry run_color = %d\n", run_color);
+ if ( ss->run_color > 0 )
+ goto db;
+ else
+ goto dw;
+#define q_at_stop() (q >= stop && (qbit <= end_bit || q > stop))
+top: run_color = 0;
+ if ( q_at_stop() )
+ goto done;
+dw: /* Decode a white run. */
+ get_run(cf_white_decode, cfd_white_initial_bits, bcnt, "[w1]white", out0);
+ if ( bcnt < 0 ) /* exceptional situation */
+ { switch ( bcnt )
+ {
+ case run_uncompressed: /* Uncompressed data. */
+ cfd_store_state();
+ bcnt = cf_decode_uncompressed(ss, pr);
+ if ( bcnt < 0 )
+ return bcnt;
+ cfd_load_state();
+ if ( bcnt ) goto db;
+ else goto dw;
+ /*case run_error:*/
+ /*case run_zeros:*/ /* Premature end-of-line. */
+ default:
+ status = ERRC;
+ goto out;
+ }
+ }
+ skip_data(bcnt, dwx);
+ if ( q_at_stop() )
+ { run_color = 0; /* not inside a run */
+ goto done;
+ }
+ run_color = 1;
+db: /* Decode a black run. */
+ get_run(cf_black_decode, cfd_black_initial_bits, bcnt, "[w1]black", out1);
+ if ( bcnt < 0 )
+ { /* All exceptional codes are invalid here. */
+ /****** WRONG, uncompressed IS ALLOWED ******/
+ status = ERRC;
+ goto out;
+ }
+ /* Invert bits designated by black run. */
+ invert_data(bcnt, black_byte, goto dbx, idb);
+ goto top;
+dwx: /* If we run out of data after a makeup code, */
+ /* note that we are still processing a white run. */
+ run_color = -1;
+ goto dw;
+dbx: /* If we run out of data after a makeup code, */
+ /* note that we are still processing a black run. */
+ run_color = 2;
+ goto db;
+done: if ( q > stop || qbit < end_bit )
+ status = ERRC;
+ else
+ status = 1;
+out: cfd_store_state();
+ ss->run_color = run_color;
+ if_debug1('w', "[w1]exit run_color = %d\n", run_color);
+ return status;
+out0: /* We already set run_color to 0 or -1. */
+ status = 0;
+ goto out;
+out1: /* We already set run_color to 1 or 2. */
+ status = 0;
+ goto out;
+}
+
+/* Decode a 2-D scan line. */
+private int
+cf_decode_2d(stream_CFD_state *ss, stream_cursor_read *pr)
+{ cfd_declare_state;
+ byte invert_white = (ss->BlackIs1 ? 0 : 0xff);
+ byte black_byte = ~invert_white;
+ byte invert = ss->invert;
+ int end_count = -ss->Columns & 7;
+ uint raster = ss->raster;
+ byte *q0 = ss->lbuf;
+ byte *prev_q01 = ss->lprev + 1;
+ byte *endptr = q0 - 1 + raster;
+ int init_count = raster << 3;
+ register int count;
+ int rlen;
+ int status;
+ cfd_load_state();
+ count = ((endptr - q) << 3) + qbit;
+ endptr[1] = 0xa0; /* a byte with some 0s and some 1s, */
+ /* to ensure run scan will stop */
+ if_debug1('W', "[w2]raster=%d\n", raster);
+ switch ( ss->run_color )
+ {
+ case -1: ss->run_color = 0; goto hbw;
+ case 1: ss->run_color = 0; goto hwb;
+ /*case 0:*/
+ }
+top: if ( count <= end_count )
+ { status = (count < end_count ? ERRC : 1);
+ goto out;
+ }
+ /* If invert == invert_white, white and black have their */
+ /* correct meanings; if invert == ~invert_white, */
+ /* black and white are interchanged. */
+ if_debug1('W', "[w2]%4d:\n", count);
+#ifdef DEBUG
+ /* Check the invariant between q, qbit, and count. */
+ { int pcount = (endptr - q) * 8 + qbit;
+ if ( pcount != count )
+ dprintf2("[w2]Error: count=%d pcount=%d\n",
+ count, pcount);
+ }
+#endif
+ /* We could just use get_run here, but we can do better: */
+ ensure_bits(3, out0);
+#define vertical_0 (countof(cf2_run_vertical) / 2)
+ switch( peek_bits(3) )
+ {
+ default/*4..7*/: /* vertical(0) */
+ skip_bits(1);
+ rlen = vertical_0;
+ break;
+ case 2: /* vertical(+1) */
+ skip_bits(3);
+ rlen = vertical_0 + 1;
+ break;
+ case 3: /* vertical(-1) */
+ skip_bits(3);
+ rlen = vertical_0 - 1;
+ break;
+ case 1: /* horizontal */
+ skip_bits(3);
+ if ( invert == invert_white )
+ { /* White, then black. */
+hww: get_run(cf_white_decode, cfd_white_initial_bits,
+ rlen, " white", hback);
+ if ( (count -= rlen) < end_count )
+ { status = ERRC;
+ goto out;
+ }
+ skip_data(rlen, hww);
+ goto hwb;
+ }
+ else
+ { /* Black, then white. */
+hbb: get_run(cf_black_decode, cfd_black_initial_bits,
+ rlen, " black", hback);
+ if ( (count -= rlen) < end_count )
+ { status = ERRC;
+ goto out;
+ }
+ invert_data(rlen, black_byte, goto hbb, ihbb);
+ goto hbw;
+ }
+ case 0: /* everything else */
+ get_run(cf_2d_decode, cfd_2d_initial_bits, rlen,
+ "[w2]", out0);
+ /* rlen may be run2_pass, run_uncompressed, or */
+ /* 0..countof(cf2_run_vertical)-1. */
+ if ( rlen < 0 )
+ switch ( rlen )
+ {
+ case run2_pass:
+ break;
+ case run_uncompressed:
+ { int which;
+ cfd_store_state();
+ which = cf_decode_uncompressed(ss, pr);
+ if ( which < 0 )
+ { status = which;
+ goto out;
+ }
+ cfd_load_state();
+ /****** ADJUST count ******/
+ invert = (which ? ~invert_white : invert_white);
+ } goto top;
+ default: /* run_error, run_zeros */
+ status = ERRC;
+ goto out;
+ }
+ }
+ /* Interpreting the run requires scanning the */
+ /* previous ('reference') line. */
+ { int prev_count = count;
+ byte prev_data;
+ int dlen;
+ static const byte count_bit[8] =
+ { 0x80, 1, 2, 4, 8, 0x10, 0x20, 0x40 };
+ byte *prev_q = prev_q01 + (q - q0);
+ int plen;
+
+ if ( !(count & 7) )
+ prev_q++; /* because of skip macros */
+ prev_data = prev_q[-1] ^ invert;
+ /* Find the b1 transition. */
+ if ( (prev_data & count_bit[prev_count & 7]) &&
+ (prev_count < init_count || invert != invert_white )
+ )
+ { /* Look for changing white first. */
+ if_debug1('W', " data=0x%x", prev_data);
+ skip_black_pixels(prev_data, prev_q,
+ prev_count, invert, plen);
+ if ( prev_count < end_count ) /* overshot */
+ prev_count = end_count;
+ if_debug1('W', " b1 other=%d", prev_count);
+ }
+ if ( prev_count != end_count )
+ { if_debug1('W', " data=0x%x", prev_data);
+ skip_white_pixels(prev_data, prev_q,
+ prev_count, invert, plen);
+ if ( prev_count < end_count ) /* overshot */
+ prev_count = end_count;
+ if_debug1('W', " b1 same=%d", prev_count);
+ }
+ /* b1 = prev_count; */
+ if ( rlen == run2_pass )
+ { /* Pass mode. Find b2. */
+ if ( prev_count != end_count )
+ { if_debug1('W', " data=0x%x", prev_data);
+ skip_black_pixels(prev_data, prev_q,
+ prev_count, invert, plen);
+ if ( prev_count < end_count ) /* overshot */
+ prev_count = end_count;
+ }
+ /* b2 = prev_count; */
+ if_debug2('W', " b2=%d, pass %d\n",
+ prev_count, count - prev_count);
+ }
+ else
+ { /* Vertical coding. */
+ /* Remember that count counts *down*. */
+ prev_count += rlen - vertical_0; /* a1 */
+ if_debug2('W', " vertical %d -> %d\n",
+ rlen - vertical_0, prev_count);
+ }
+ /* Now either invert or skip from count */
+ /* to prev_count, and reset count. */
+ if ( invert == invert_white )
+ { /* Skip data bits. */
+ q = endptr - (prev_count >> 3);
+ qbit = prev_count & 7;
+ }
+ else
+ { /* Invert data bits. */
+ dlen = count - prev_count;
+ invert_data(dlen, black_byte, DO_NOTHING, idd);
+ }
+ count = prev_count;
+ if ( rlen >= 0 ) /* vertical mode */
+ invert = ~invert; /* polarity changes */
+ }
+ goto top;
+out: cfd_store_state();
+outs: ss->invert = invert;
+ return status;
+ /* Handle input exhausted after recognizing a horizontal */
+ /* mode and possibly some makeup codes, but before parsing */
+ /* the termination code of the black or white run. */
+ /* The mode flag is 3 bits; the longest black/white */
+ /* run code is 13 bits, so bits_left <= 12, and we can */
+ /* back up by just incrementing bits_left. */
+ /* To make this work even with makeup codes, */
+ /* we force the previous 3 bits to 001 (horizontal mode). */
+ /* In this case only, we must work around the provision */
+ /* in hcd_store_state that forces bits_left < 7. */
+hback: cfd_store_state();
+ ss->bits = bits = (bits & ((1 << bits_left) - 1)) + (1 << bits_left);
+ ss->bits_left = bits_left += 3;
+ status = 0;
+ goto outs;
+out0: status = 0;
+ goto out;
+ /* Handle input exhausted when parsing the second run */
+ /* of a horizontal sequence. We have to resume parsing */
+ /* at hwb or hbw depending on the run color. */
+outbw: ss->run_color = -1;
+ goto out0;
+outwb: ss->run_color = 1;
+ goto out0;
+ /* Handle the second half of a white-black horizontal code. */
+ /* We put this here so we can branch to it when resuming. */
+hwb: get_run(cf_black_decode, cfd_black_initial_bits, rlen,
+ " black", outwb);
+ if ( (count -= rlen) < end_count )
+ { status = ERRC;
+ goto out;
+ }
+ invert_data(rlen, black_byte, goto hwb, ihwb);
+ goto top;
+ /* Handle the second half of a black-white horizontal code. */
+ /* We put this here so we can branch to it when resuming. */
+hbw: get_run(cf_white_decode, cfd_white_initial_bits, rlen,
+ " white", outbw);
+ if ( (count -= rlen) < end_count )
+ { status = ERRC;
+ goto out;
+ }
+ skip_data(rlen, hbw);
+ goto top;
+}
+
+#if 1 /****************/
+private int
+cf_decode_uncompressed(stream_CFD_state *ss, stream_cursor_read *pr)
+{ return ERRC;
+}
+#else /****************/
+
+/* Decode uncompressed data. */
+/* (Not tested: no sample data available!) */
+/****** DOESN'T CHECK FOR OVERFLOWING SCAN LINE ******/
+private int
+cf_decode_uncompressed(stream *s)
+{ cfd_declare_state;
+ const cfd_node *np;
+ int clen, rlen;
+ cfd_load_state();
+ while ( 1 )
+ { ensure_bits(cfd_uncompressed_initial_bits, NOOUT);
+ np = &cf_uncompressed_decode[peek_bits(cfd_uncompressed_initial_bits)];
+ clen = np->code_length;
+ rlen = np->run_length;
+ if ( clen > cfd_uncompressed_initial_bits )
+ { /* Must be an exit code. */
+ break;
+ }
+ if ( rlen == cfd_uncompressed_initial_bits )
+ { /* Longest representable white run */
+ if_debug1('W', "[wu]%d\n", rlen);
+ if ( (qbit -= cfd_uncompressed_initial_bits) < 0 )
+ qbit += 8, q++;
+ }
+ else
+ { if_debug1('W', "[wu]%d+1\n", rlen);
+ if ( qbit -= rlen < 0 )
+ qbit += 8, q++;
+ *q ^= 1 << qbit;
+ }
+ skip_bits(clen);
+ }
+ clen -= cfd_uncompressed_initial_bits;
+ skip_bits(cfd_uncompressed_initial_bits);
+ ensure_bits(clen, NOOUT);
+ np = &cf_uncompressed_decode[rlen + peek_var_bits(clen)];
+ rlen = np->run_length;
+ skip_bits(np->code_length);
+ if_debug1('w', "[wu]exit %d\n", rlen);
+ if ( rlen >= 0 )
+ { /* Valid exit code, rlen = 2 * run length + next polarity */
+ if ( (qbit -= rlen >> 1) < 0 )
+ qbit += 8, q++;
+ rlen &= 1;
+ }
+out: /******* WRONG ******/
+ cfd_store_state();
+ return rlen;
+}
+
+#endif /****************/
+
+/* Stream template */
+const stream_template s_CFD_template =
+{ &st_CFD_state, s_CFD_init, s_CFD_process, 1, 1, s_CFD_release,
+ s_CFD_set_defaults
+};
diff --git a/pstoraster/scfdtab.c b/pstoraster/scfdtab.c
new file mode 100644
index 000000000..dc20d7caf
--- /dev/null
+++ b/pstoraster/scfdtab.c
@@ -0,0 +1,938 @@
+/* Copyright (C) 1992, 1993 Aladdin Enterprises. All rights reserved. */
+
+/* This file was generated automatically. It is governed by the same terms */
+/* as the files scftab.c and scfdgen.c from which it was derived. */
+/* Consult those files for the licensing terms and conditions. */
+
+/* scfdtab.c */
+/* Tables for CCITTFaxDecode filter. */
+
+#include "std.h"
+#include "scommon.h" /* for scf.h */
+#include "scf.h"
+
+/* White decoding table. */
+const cfd_node far_data cf_white_decode[] = {
+ { 256, 12 },
+ { 272, 12 },
+ { 29, 8 },
+ { 30, 8 },
+ { 45, 8 },
+ { 46, 8 },
+ { 22, 7 },
+ { 22, 7 },
+ { 23, 7 },
+ { 23, 7 },
+ { 47, 8 },
+ { 48, 8 },
+ { 13, 6 },
+ { 13, 6 },
+ { 13, 6 },
+ { 13, 6 },
+ { 20, 7 },
+ { 20, 7 },
+ { 33, 8 },
+ { 34, 8 },
+ { 35, 8 },
+ { 36, 8 },
+ { 37, 8 },
+ { 38, 8 },
+ { 19, 7 },
+ { 19, 7 },
+ { 31, 8 },
+ { 32, 8 },
+ { 1, 6 },
+ { 1, 6 },
+ { 1, 6 },
+ { 1, 6 },
+ { 12, 6 },
+ { 12, 6 },
+ { 12, 6 },
+ { 12, 6 },
+ { 53, 8 },
+ { 54, 8 },
+ { 26, 7 },
+ { 26, 7 },
+ { 39, 8 },
+ { 40, 8 },
+ { 41, 8 },
+ { 42, 8 },
+ { 43, 8 },
+ { 44, 8 },
+ { 21, 7 },
+ { 21, 7 },
+ { 28, 7 },
+ { 28, 7 },
+ { 61, 8 },
+ { 62, 8 },
+ { 63, 8 },
+ { 0, 8 },
+ { 320, 8 },
+ { 384, 8 },
+ { 10, 5 },
+ { 10, 5 },
+ { 10, 5 },
+ { 10, 5 },
+ { 10, 5 },
+ { 10, 5 },
+ { 10, 5 },
+ { 10, 5 },
+ { 11, 5 },
+ { 11, 5 },
+ { 11, 5 },
+ { 11, 5 },
+ { 11, 5 },
+ { 11, 5 },
+ { 11, 5 },
+ { 11, 5 },
+ { 27, 7 },
+ { 27, 7 },
+ { 59, 8 },
+ { 60, 8 },
+ { 288, 9 },
+ { 290, 9 },
+ { 18, 7 },
+ { 18, 7 },
+ { 24, 7 },
+ { 24, 7 },
+ { 49, 8 },
+ { 50, 8 },
+ { 51, 8 },
+ { 52, 8 },
+ { 25, 7 },
+ { 25, 7 },
+ { 55, 8 },
+ { 56, 8 },
+ { 57, 8 },
+ { 58, 8 },
+ { 192, 6 },
+ { 192, 6 },
+ { 192, 6 },
+ { 192, 6 },
+ { 1664, 6 },
+ { 1664, 6 },
+ { 1664, 6 },
+ { 1664, 6 },
+ { 448, 8 },
+ { 512, 8 },
+ { 292, 9 },
+ { 640, 8 },
+ { 576, 8 },
+ { 294, 9 },
+ { 296, 9 },
+ { 298, 9 },
+ { 300, 9 },
+ { 302, 9 },
+ { 256, 7 },
+ { 256, 7 },
+ { 2, 4 },
+ { 2, 4 },
+ { 2, 4 },
+ { 2, 4 },
+ { 2, 4 },
+ { 2, 4 },
+ { 2, 4 },
+ { 2, 4 },
+ { 2, 4 },
+ { 2, 4 },
+ { 2, 4 },
+ { 2, 4 },
+ { 2, 4 },
+ { 2, 4 },
+ { 2, 4 },
+ { 2, 4 },
+ { 3, 4 },
+ { 3, 4 },
+ { 3, 4 },
+ { 3, 4 },
+ { 3, 4 },
+ { 3, 4 },
+ { 3, 4 },
+ { 3, 4 },
+ { 3, 4 },
+ { 3, 4 },
+ { 3, 4 },
+ { 3, 4 },
+ { 3, 4 },
+ { 3, 4 },
+ { 3, 4 },
+ { 3, 4 },
+ { 128, 5 },
+ { 128, 5 },
+ { 128, 5 },
+ { 128, 5 },
+ { 128, 5 },
+ { 128, 5 },
+ { 128, 5 },
+ { 128, 5 },
+ { 8, 5 },
+ { 8, 5 },
+ { 8, 5 },
+ { 8, 5 },
+ { 8, 5 },
+ { 8, 5 },
+ { 8, 5 },
+ { 8, 5 },
+ { 9, 5 },
+ { 9, 5 },
+ { 9, 5 },
+ { 9, 5 },
+ { 9, 5 },
+ { 9, 5 },
+ { 9, 5 },
+ { 9, 5 },
+ { 16, 6 },
+ { 16, 6 },
+ { 16, 6 },
+ { 16, 6 },
+ { 17, 6 },
+ { 17, 6 },
+ { 17, 6 },
+ { 17, 6 },
+ { 4, 4 },
+ { 4, 4 },
+ { 4, 4 },
+ { 4, 4 },
+ { 4, 4 },
+ { 4, 4 },
+ { 4, 4 },
+ { 4, 4 },
+ { 4, 4 },
+ { 4, 4 },
+ { 4, 4 },
+ { 4, 4 },
+ { 4, 4 },
+ { 4, 4 },
+ { 4, 4 },
+ { 4, 4 },
+ { 5, 4 },
+ { 5, 4 },
+ { 5, 4 },
+ { 5, 4 },
+ { 5, 4 },
+ { 5, 4 },
+ { 5, 4 },
+ { 5, 4 },
+ { 5, 4 },
+ { 5, 4 },
+ { 5, 4 },
+ { 5, 4 },
+ { 5, 4 },
+ { 5, 4 },
+ { 5, 4 },
+ { 5, 4 },
+ { 14, 6 },
+ { 14, 6 },
+ { 14, 6 },
+ { 14, 6 },
+ { 15, 6 },
+ { 15, 6 },
+ { 15, 6 },
+ { 15, 6 },
+ { 64, 5 },
+ { 64, 5 },
+ { 64, 5 },
+ { 64, 5 },
+ { 64, 5 },
+ { 64, 5 },
+ { 64, 5 },
+ { 64, 5 },
+ { 6, 4 },
+ { 6, 4 },
+ { 6, 4 },
+ { 6, 4 },
+ { 6, 4 },
+ { 6, 4 },
+ { 6, 4 },
+ { 6, 4 },
+ { 6, 4 },
+ { 6, 4 },
+ { 6, 4 },
+ { 6, 4 },
+ { 6, 4 },
+ { 6, 4 },
+ { 6, 4 },
+ { 6, 4 },
+ { 7, 4 },
+ { 7, 4 },
+ { 7, 4 },
+ { 7, 4 },
+ { 7, 4 },
+ { 7, 4 },
+ { 7, 4 },
+ { 7, 4 },
+ { 7, 4 },
+ { 7, 4 },
+ { 7, 4 },
+ { 7, 4 },
+ { 7, 4 },
+ { 7, 4 },
+ { 7, 4 },
+ { 7, 4 },
+ { -2, 3 },
+ { -2, 3 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -3, 4 },
+ { 1792, 3 },
+ { 1792, 3 },
+ { 1984, 4 },
+ { 2048, 4 },
+ { 2112, 4 },
+ { 2176, 4 },
+ { 2240, 4 },
+ { 2304, 4 },
+ { 1856, 3 },
+ { 1856, 3 },
+ { 1920, 3 },
+ { 1920, 3 },
+ { 2368, 4 },
+ { 2432, 4 },
+ { 2496, 4 },
+ { 2560, 4 },
+ { 1472, 1 },
+ { 1536, 1 },
+ { 1600, 1 },
+ { 1728, 1 },
+ { 704, 1 },
+ { 768, 1 },
+ { 832, 1 },
+ { 896, 1 },
+ { 960, 1 },
+ { 1024, 1 },
+ { 1088, 1 },
+ { 1152, 1 },
+ { 1216, 1 },
+ { 1280, 1 },
+ { 1344, 1 },
+ { 1408, 1 }
+};
+
+/* Black decoding table. */
+const cfd_node far_data cf_black_decode[] = {
+ { 128, 12 },
+ { 160, 13 },
+ { 224, 12 },
+ { 256, 12 },
+ { 10, 7 },
+ { 11, 7 },
+ { 288, 12 },
+ { 12, 7 },
+ { 9, 6 },
+ { 9, 6 },
+ { 8, 6 },
+ { 8, 6 },
+ { 7, 5 },
+ { 7, 5 },
+ { 7, 5 },
+ { 7, 5 },
+ { 6, 4 },
+ { 6, 4 },
+ { 6, 4 },
+ { 6, 4 },
+ { 6, 4 },
+ { 6, 4 },
+ { 6, 4 },
+ { 6, 4 },
+ { 5, 4 },
+ { 5, 4 },
+ { 5, 4 },
+ { 5, 4 },
+ { 5, 4 },
+ { 5, 4 },
+ { 5, 4 },
+ { 5, 4 },
+ { 1, 3 },
+ { 1, 3 },
+ { 1, 3 },
+ { 1, 3 },
+ { 1, 3 },
+ { 1, 3 },
+ { 1, 3 },
+ { 1, 3 },
+ { 1, 3 },
+ { 1, 3 },
+ { 1, 3 },
+ { 1, 3 },
+ { 1, 3 },
+ { 1, 3 },
+ { 1, 3 },
+ { 1, 3 },
+ { 4, 3 },
+ { 4, 3 },
+ { 4, 3 },
+ { 4, 3 },
+ { 4, 3 },
+ { 4, 3 },
+ { 4, 3 },
+ { 4, 3 },
+ { 4, 3 },
+ { 4, 3 },
+ { 4, 3 },
+ { 4, 3 },
+ { 4, 3 },
+ { 4, 3 },
+ { 4, 3 },
+ { 4, 3 },
+ { 3, 2 },
+ { 3, 2 },
+ { 3, 2 },
+ { 3, 2 },
+ { 3, 2 },
+ { 3, 2 },
+ { 3, 2 },
+ { 3, 2 },
+ { 3, 2 },
+ { 3, 2 },
+ { 3, 2 },
+ { 3, 2 },
+ { 3, 2 },
+ { 3, 2 },
+ { 3, 2 },
+ { 3, 2 },
+ { 3, 2 },
+ { 3, 2 },
+ { 3, 2 },
+ { 3, 2 },
+ { 3, 2 },
+ { 3, 2 },
+ { 3, 2 },
+ { 3, 2 },
+ { 3, 2 },
+ { 3, 2 },
+ { 3, 2 },
+ { 3, 2 },
+ { 3, 2 },
+ { 3, 2 },
+ { 3, 2 },
+ { 3, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { -2, 4 },
+ { -2, 4 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -3, 5 },
+ { 1792, 4 },
+ { 1792, 4 },
+ { 1984, 5 },
+ { 2048, 5 },
+ { 2112, 5 },
+ { 2176, 5 },
+ { 2240, 5 },
+ { 2304, 5 },
+ { 1856, 4 },
+ { 1856, 4 },
+ { 1920, 4 },
+ { 1920, 4 },
+ { 2368, 5 },
+ { 2432, 5 },
+ { 2496, 5 },
+ { 2560, 5 },
+ { 18, 3 },
+ { 18, 3 },
+ { 18, 3 },
+ { 18, 3 },
+ { 18, 3 },
+ { 18, 3 },
+ { 18, 3 },
+ { 18, 3 },
+ { 52, 5 },
+ { 52, 5 },
+ { 640, 6 },
+ { 704, 6 },
+ { 768, 6 },
+ { 832, 6 },
+ { 55, 5 },
+ { 55, 5 },
+ { 56, 5 },
+ { 56, 5 },
+ { 1280, 6 },
+ { 1344, 6 },
+ { 1408, 6 },
+ { 1472, 6 },
+ { 59, 5 },
+ { 59, 5 },
+ { 60, 5 },
+ { 60, 5 },
+ { 1536, 6 },
+ { 1600, 6 },
+ { 24, 4 },
+ { 24, 4 },
+ { 24, 4 },
+ { 24, 4 },
+ { 25, 4 },
+ { 25, 4 },
+ { 25, 4 },
+ { 25, 4 },
+ { 1664, 6 },
+ { 1728, 6 },
+ { 320, 5 },
+ { 320, 5 },
+ { 384, 5 },
+ { 384, 5 },
+ { 448, 5 },
+ { 448, 5 },
+ { 512, 6 },
+ { 576, 6 },
+ { 53, 5 },
+ { 53, 5 },
+ { 54, 5 },
+ { 54, 5 },
+ { 896, 6 },
+ { 960, 6 },
+ { 1024, 6 },
+ { 1088, 6 },
+ { 1152, 6 },
+ { 1216, 6 },
+ { 64, 3 },
+ { 64, 3 },
+ { 64, 3 },
+ { 64, 3 },
+ { 64, 3 },
+ { 64, 3 },
+ { 64, 3 },
+ { 64, 3 },
+ { 13, 1 },
+ { 13, 1 },
+ { 13, 1 },
+ { 13, 1 },
+ { 13, 1 },
+ { 13, 1 },
+ { 13, 1 },
+ { 13, 1 },
+ { 13, 1 },
+ { 13, 1 },
+ { 13, 1 },
+ { 13, 1 },
+ { 13, 1 },
+ { 13, 1 },
+ { 13, 1 },
+ { 13, 1 },
+ { 23, 4 },
+ { 23, 4 },
+ { 50, 5 },
+ { 51, 5 },
+ { 44, 5 },
+ { 45, 5 },
+ { 46, 5 },
+ { 47, 5 },
+ { 57, 5 },
+ { 58, 5 },
+ { 61, 5 },
+ { 256, 5 },
+ { 16, 3 },
+ { 16, 3 },
+ { 16, 3 },
+ { 16, 3 },
+ { 17, 3 },
+ { 17, 3 },
+ { 17, 3 },
+ { 17, 3 },
+ { 48, 5 },
+ { 49, 5 },
+ { 62, 5 },
+ { 63, 5 },
+ { 30, 5 },
+ { 31, 5 },
+ { 32, 5 },
+ { 33, 5 },
+ { 40, 5 },
+ { 41, 5 },
+ { 22, 4 },
+ { 22, 4 },
+ { 14, 1 },
+ { 14, 1 },
+ { 14, 1 },
+ { 14, 1 },
+ { 14, 1 },
+ { 14, 1 },
+ { 14, 1 },
+ { 14, 1 },
+ { 14, 1 },
+ { 14, 1 },
+ { 14, 1 },
+ { 14, 1 },
+ { 14, 1 },
+ { 14, 1 },
+ { 14, 1 },
+ { 14, 1 },
+ { 15, 2 },
+ { 15, 2 },
+ { 15, 2 },
+ { 15, 2 },
+ { 15, 2 },
+ { 15, 2 },
+ { 15, 2 },
+ { 15, 2 },
+ { 128, 5 },
+ { 192, 5 },
+ { 26, 5 },
+ { 27, 5 },
+ { 28, 5 },
+ { 29, 5 },
+ { 19, 4 },
+ { 19, 4 },
+ { 20, 4 },
+ { 20, 4 },
+ { 34, 5 },
+ { 35, 5 },
+ { 36, 5 },
+ { 37, 5 },
+ { 38, 5 },
+ { 39, 5 },
+ { 21, 4 },
+ { 21, 4 },
+ { 42, 5 },
+ { 43, 5 },
+ { 0, 3 },
+ { 0, 3 },
+ { 0, 3 },
+ { 0, 3 }
+};
+
+/* 2-D decoding table. */
+const cfd_node far_data cf_2d_decode[] = {
+ { 128, 11 },
+ { 144, 10 },
+ { 6, 7 },
+ { 0, 7 },
+ { 5, 6 },
+ { 5, 6 },
+ { 1, 6 },
+ { 1, 6 },
+ { -4, 4 },
+ { -4, 4 },
+ { -4, 4 },
+ { -4, 4 },
+ { -4, 4 },
+ { -4, 4 },
+ { -4, 4 },
+ { -4, 4 },
+ { -5, 3 },
+ { -5, 3 },
+ { -5, 3 },
+ { -5, 3 },
+ { -5, 3 },
+ { -5, 3 },
+ { -5, 3 },
+ { -5, 3 },
+ { -5, 3 },
+ { -5, 3 },
+ { -5, 3 },
+ { -5, 3 },
+ { -5, 3 },
+ { -5, 3 },
+ { -5, 3 },
+ { -5, 3 },
+ { 4, 3 },
+ { 4, 3 },
+ { 4, 3 },
+ { 4, 3 },
+ { 4, 3 },
+ { 4, 3 },
+ { 4, 3 },
+ { 4, 3 },
+ { 4, 3 },
+ { 4, 3 },
+ { 4, 3 },
+ { 4, 3 },
+ { 4, 3 },
+ { 4, 3 },
+ { 4, 3 },
+ { 4, 3 },
+ { 2, 3 },
+ { 2, 3 },
+ { 2, 3 },
+ { 2, 3 },
+ { 2, 3 },
+ { 2, 3 },
+ { 2, 3 },
+ { 2, 3 },
+ { 2, 3 },
+ { 2, 3 },
+ { 2, 3 },
+ { 2, 3 },
+ { 2, 3 },
+ { 2, 3 },
+ { 2, 3 },
+ { 2, 3 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { -2, 4 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -3, 3 }
+};
+
+/* Uncompresssed decoding table. */
+const cfd_node far_data cf_uncompressed_decode[] = {
+ { 64, 12 },
+ { 5, 6 },
+ { 4, 5 },
+ { 4, 5 },
+ { 3, 4 },
+ { 3, 4 },
+ { 3, 4 },
+ { 3, 4 },
+ { 2, 3 },
+ { 2, 3 },
+ { 2, 3 },
+ { 2, 3 },
+ { 2, 3 },
+ { 2, 3 },
+ { 2, 3 },
+ { 2, 3 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { -1, 0 },
+ { -1, 0 },
+ { 8, 6 },
+ { 9, 6 },
+ { 6, 5 },
+ { 6, 5 },
+ { 7, 5 },
+ { 7, 5 },
+ { 4, 4 },
+ { 4, 4 },
+ { 4, 4 },
+ { 4, 4 },
+ { 5, 4 },
+ { 5, 4 },
+ { 5, 4 },
+ { 5, 4 },
+ { 2, 3 },
+ { 2, 3 },
+ { 2, 3 },
+ { 2, 3 },
+ { 2, 3 },
+ { 2, 3 },
+ { 2, 3 },
+ { 2, 3 },
+ { 3, 3 },
+ { 3, 3 },
+ { 3, 3 },
+ { 3, 3 },
+ { 3, 3 },
+ { 3, 3 },
+ { 3, 3 },
+ { 3, 3 },
+ { 0, 2 },
+ { 0, 2 },
+ { 0, 2 },
+ { 0, 2 },
+ { 0, 2 },
+ { 0, 2 },
+ { 0, 2 },
+ { 0, 2 },
+ { 0, 2 },
+ { 0, 2 },
+ { 0, 2 },
+ { 0, 2 },
+ { 0, 2 },
+ { 0, 2 },
+ { 0, 2 },
+ { 0, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 }
+};
+
+/* Dummy executable code to pacify compilers. */
+void
+cfd_dummy(void)
+{
+}
diff --git a/pstoraster/scfe.c b/pstoraster/scfe.c
new file mode 100644
index 000000000..27b442c2c
--- /dev/null
+++ b/pstoraster/scfe.c
@@ -0,0 +1,490 @@
+/* Copyright (C) 1992, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* scfe.c */
+/* CCITTFax encoding filter */
+#include "stdio_.h" /* includes std.h */
+#include "memory_.h"
+#include "gdebug.h"
+#include "strimpl.h"
+#include "scf.h"
+#include "scfx.h"
+
+/* ------ Macros and support routines ------ */
+
+/* Statistics */
+
+#ifdef DEBUG
+private struct _r1d { ulong termination[64], make_up[41]; } runs_1d[2];
+# define count_run(tab, i) ((tab)[i]++)
+#else
+# define count_run(cnt, n) DO_NOTHING
+#endif
+
+/* Put a run onto the output stream. */
+/* Free variables: q, wlimit, status. */
+
+#define cf_ensure_put_runs(n, color, out)\
+ if ( wlimit - q < (n) * cfe_max_code_bytes ) /* worst case */\
+ { ss->run_color = color;\
+ status = 1;\
+ goto out;\
+ }
+#define cf_put_run(ss, lenv, tt, mut, tab)\
+{ cfe_run rr;\
+ if ( lenv >= 64 )\
+ { while ( lenv >= 2560 + 64 )\
+ { rr = mut[40];\
+ count_run(tab.make_up, 40);\
+ hc_put_value(ss, q, rr.code, rr.code_length);\
+ lenv -= 2560;\
+ }\
+ rr = mut[lenv >> 6];\
+ count_run(tab.make_up, lenv >> 6);\
+ hc_put_value(ss, q, rr.code, rr.code_length);\
+ lenv &= 63;\
+ }\
+ rr = tt[lenv];\
+ count_run(tab.termination, lenv);\
+ hc_put_value(ss, q, rr.code, rr.code_length);\
+}
+
+#define cf_put_white_run(ss, lenv)\
+ cf_put_run(ss, lenv, cf_white_termination, cf_white_make_up, runs_1d[0])
+
+#define cf_put_black_run(ss, lenv)\
+ cf_put_run(ss, lenv, cf_black_termination, cf_black_make_up, runs_1d[1])
+
+/* ------ CCITTFaxEncode ------ */
+
+private_st_CFE_state();
+
+#define ss ((stream_CFE_state *)st)
+
+private void s_CFE_release(P1(stream_state *));
+
+/*
+ * For the 2-D encoding modes, we leave the previous complete scan line
+ * at the beginning of the buffer, and start the new data after it.
+ */
+
+/* Set default parameter values. */
+private void
+s_CFE_set_defaults(register stream_state *st)
+{ s_CFE_set_defaults_inline(ss);
+}
+
+/* Initialize CCITTFaxEncode filter */
+private int
+s_CFE_init(register stream_state *st)
+{ int columns = ss->Columns;
+ int raster = ss->raster =
+ round_up((columns + 7) >> 3, ss->DecodedByteAlign);
+ s_hce_init_inline(ss);
+ ss->count = raster << 3; /* starting a scan line */
+ ss->lbuf = ss->lprev = 0;
+ if ( columns > cfe_max_width )
+ return ERRC; /****** WRONG ******/
+ ss->lbuf = gs_alloc_bytes(st->memory, raster + 1,
+ "CFE lbuf");
+ if ( ss->lbuf == 0 )
+ { s_CFE_release(st);
+ return ERRC; /****** WRONG ******/
+ }
+ if ( ss->K != 0 )
+ { ss->lprev = gs_alloc_bytes(st->memory, raster + 1,
+ "CFE lprev");
+ if ( ss->lprev == 0 )
+ { s_CFE_release(st);
+ return ERRC; /****** WRONG ******/
+ }
+ /* Clear the initial reference line for 2-D encoding. */
+ /* Make sure it is terminated properly. */
+ memset(ss->lprev, (ss->BlackIs1 ? 0 : 0xff), raster);
+ if ( columns & 7 )
+ ss->lprev[raster - 1] ^= 0x80 >> (columns & 7);
+ else
+ ss->lprev[raster] = ~ss->lprev[0];
+ }
+ ss->copy_count = raster;
+ ss->new_line = true;
+ ss->k_left = (ss->K > 0 ? 1 : ss->K);
+ return 0;
+}
+
+/* Release the filter. */
+private void
+s_CFE_release(stream_state *st)
+{ gs_free_object(st->memory, ss->lprev, "CFE lprev(close)");
+ gs_free_object(st->memory, ss->lbuf, "CFE lbuf(close)");
+}
+
+/* Flush the buffer */
+private int cf_encode_1d(P4(stream_CFE_state *, const byte *,
+ stream_cursor_write *, uint));
+private int cf_encode_2d(P5(stream_CFE_state *, const byte *,
+ stream_cursor_write *, uint, const byte *));
+private int
+s_CFE_process(stream_state *st, stream_cursor_read *pr,
+ stream_cursor_write *pw, bool last)
+{ const byte *rlimit = pr->limit;
+ byte *wlimit = pw->limit;
+ int raster = ss->raster;
+ int initial_count = raster << 3;
+ int end_count = -ss->Columns & 7;
+ byte end_mask = 1 << (-ss->Columns & 7);
+ int status = 0;
+ hce_declare_state;
+
+ hce_load_state();
+ while ( pr->ptr < rlimit || ss->count != initial_count )
+ { byte *end = ss->lbuf + raster - 1;
+ if_debug7('w', "[w]CFE: copy_count = %d, pr = 0x%lx(%d)0x%lx, pw = 0x%lx(%d)0x%lx\n",
+ ss->copy_count, (ulong)pr->ptr,
+ (int)(rlimit - pr->ptr), (ulong)rlimit,
+ (ulong)pw->ptr, (int)(wlimit - pw->ptr),
+ (ulong)wlimit);
+ /* Check whether we are still accumulating a scan line. */
+ if ( ss->copy_count != 0 )
+ { int rcount = rlimit - pr->ptr;
+ int ccount = min(rcount, ss->copy_count);
+ memcpy(ss->lbuf + raster - ss->copy_count,
+ pr->ptr + 1, ccount);
+ pr->ptr += ccount;
+ if ( (ss->copy_count -= ccount) != 0 )
+ goto out;
+ /*
+ * Ensure that the scan line ends with two
+ * polarity changes.
+ */
+ { byte end_bit = *end & end_mask;
+ byte not_bit = end_bit ^ end_mask;
+ *end &= -end_mask;
+ if ( end_mask == 1 )
+ end[1] = (end_bit ? 0x40 : 0x80);
+ else if ( end_mask == 2 )
+ *end |= not_bit >> 1, end[1] = end_bit << 7;
+ else
+ *end |= (not_bit >> 1) | (end_bit >> 2);
+ }
+ }
+ if ( ss->new_line )
+ { /* Start a new scan line. */
+ byte *q = pw->ptr;
+ if ( wlimit - q < 4 + cfe_max_code_bytes * 2 ) /* byte align, aligned eol, run_horizontal + 2 runs */
+ { status = 1;
+ break;
+ }
+#ifdef DEBUG
+ if ( ss->K > 0 )
+ { if_debug1('w', "[w]new row, k_left=%d\n",
+ ss->k_left);
+ }
+ else
+ { if_debug0('w', "[w]new row\n");
+ }
+#endif
+ if ( ss->EndOfLine )
+ { const cfe_run *rp =
+ (ss->K <= 0 ? &cf_run_eol :
+ ss->k_left > 1 ? &cf2_run_eol_2d :
+ &cf2_run_eol_1d);
+ cfe_run run;
+ if ( ss->EncodedByteAlign )
+ { run = *rp;
+ /* Pad the run on the left */
+ /* so it winds up byte-aligned. */
+ run.code_length +=
+ (bits_left - run_eol_code_length) & 7;
+ if ( run.code_length > 16 ) /* <= 23 */
+ bits_left -= run.code_length & 7,
+ run.code_length = 16;
+ rp = &run;
+ }
+ hc_put_code(ss, q, rp);
+ pw->ptr = q;
+ }
+ else if ( ss->EncodedByteAlign )
+ bits_left &= ~7;
+ ss->run_color = 0;
+ ss->new_line = false;
+ }
+ hce_store_state();
+ if ( ss->K > 0 )
+ { /* Group 3, mixed encoding */
+ if ( --(ss->k_left) ) /* Use 2-D encoding */
+ { status = cf_encode_2d(ss, ss->lbuf, pw, end_count, ss->lprev);
+ if ( status )
+ { /* We didn't finish encoding */
+ /* the line, so back out. */
+ ss->k_left++;
+ }
+ }
+ else /* Use 1-D encoding */
+ { status = cf_encode_1d(ss, ss->lbuf, pw, end_count);
+ if ( status )
+ { /* Didn't finish encoding the line, */
+ /* back out. */
+ ss->k_left++;
+ }
+ else
+ ss->k_left = ss->K;
+ }
+ }
+ else /* Uniform encoding */
+ { status = (ss->K == 0 ?
+ cf_encode_1d(ss, ss->lbuf, pw, end_count) :
+ cf_encode_2d(ss, ss->lbuf, pw, end_count, ss->lprev));
+ }
+ hce_load_state();
+ if ( status )
+ break;
+ if ( ss->count == end_count )
+ { /* Finished a scan line, start a new one. */
+ ss->count = initial_count;
+ ss->new_line = true;
+ if ( ss->K != 0 )
+ { byte *temp = ss->lbuf;
+ ss->lbuf = ss->lprev;
+ ss->lprev = temp;
+ }
+ ss->copy_count = raster;
+ }
+ }
+ /* Check for end of data. */
+ if ( last && status == 0 )
+ { const cfe_run *rp =
+ (ss->K > 0 ? &cf2_run_eol_1d : &cf_run_eol);
+ int i = (!ss->EndOfBlock ? 0 : ss->K < 0 ? 2 : 6);
+ uint bits_to_write =
+ hc_bits_size - bits_left + i * rp->code_length;
+ byte *q = pw->ptr;
+ if ( wlimit - q < (bits_to_write + 7) >> 3 )
+ { status = 1;
+ goto out;
+ }
+ if ( ss->EncodedByteAlign )
+ bits_left &= ~7;
+ while ( --i >= 0 )
+ hc_put_code(ss, q, rp);
+ /* Force out the last byte or bytes. */
+ pw->ptr = q = hc_put_last_bits((stream_hc_state *)ss, q);
+ goto ns;
+ }
+out: hce_store_state();
+ns: if_debug9('w', "[w]CFE exit %d: count = %d, run_color = %d,\n pr = 0x%lx(%d)0x%lx; pw = 0x%lx(%d)0x%lx\n",
+ status, ss->count, ss->run_color,
+ (ulong)pr->ptr, (int)(rlimit - pr->ptr), (ulong)rlimit,
+ (ulong)pw->ptr, (int)(wlimit - pw->ptr), (ulong)wlimit);
+#ifdef DEBUG
+ if ( pr->ptr > rlimit || pw->ptr > wlimit )
+ { lprintf("Pointer overrun!\n");
+ status = ERRC;
+ }
+ if ( gs_debug_c('w') && status == 1 )
+ { int ti;
+ for ( ti = 0; ti < 2; ti++ )
+ { int i;
+ ulong total;
+ dprintf1("[w]runs[%d]", ti);
+ for ( i = 0, total = 0; i < 41; i++ )
+ dprintf1(" %lu", runs_1d[ti].make_up[i]),
+ total += runs_1d[ti].make_up[i];
+ dprintf1(" total=%lu\n\t", total);
+ for ( i = 0, total = 0; i < 64; i++ )
+ dprintf1(" %lu", runs_1d[ti].termination[i]),
+ total += runs_1d[ti].termination[i];
+ dprintf1(" total=%lu\n", total);
+ }
+ }
+#endif
+ return status;
+}
+
+#undef ss
+
+/*
+ * For all encoding methods, we know we have a full scan line of input,
+ * but we must be prepared to suspend if we run out of space to store
+ * the output.
+ */
+
+/* Encode a 1-D scan line. */
+private int
+cf_encode_1d(stream_CFE_state *ss, const byte *lbuf,
+ stream_cursor_write *pw, uint end_count)
+{ uint count = ss->count;
+ byte *q = pw->ptr;
+ byte *wlimit = pw->limit;
+ int rlen;
+ int status = 0;
+ hce_declare_state;
+
+ { register const byte *p = lbuf + ss->raster - ((count + 7) >> 3);
+ byte invert = (ss->BlackIs1 ? 0 : 0xff);
+ /* Invariant: data = p[-1] ^ invert. */
+ register uint data = *p++ ^ invert;
+
+ hce_load_state();
+ while ( count != end_count )
+ { /* Parse a white run. */
+ cf_ensure_put_runs(2, 0, out);
+ skip_white_pixels(data, p, count, invert, rlen);
+ cf_put_white_run(ss, rlen);
+ if ( count == end_count )
+ break;
+ /* Parse a black run. */
+ skip_black_pixels(data, p, count, invert, rlen);
+ cf_put_black_run(ss, rlen);
+ }
+ }
+
+out: hce_store_state();
+ pw->ptr = q;
+ ss->count = count;
+ return status;
+}
+
+/* Encode a 2-D scan line. */
+private int
+cf_encode_2d(stream_CFE_state *ss, const byte *lbuf,
+ stream_cursor_write *pw, uint end_count, const byte *lprev)
+{ byte invert_white = (ss->BlackIs1 ? 0 : 0xff);
+ byte invert = (ss->run_color ? ~invert_white : invert_white);
+ register uint count = ss->count;
+ const byte *p = lbuf + ss->raster - ((count + 7) >> 3);
+ byte *q = pw->ptr;
+ byte *wlimit = pw->limit;
+ register uint data = *p++ ^ invert;
+ int status = 0;
+ hce_declare_state;
+ /* In order to handle the nominal 'changing white' at the */
+ /* beginning of each scan line, we need to suppress the test for */
+ /* an initial black bit in the reference line when we are at */
+ /* the very beginning of the scan line. To avoid an extra test, */
+ /* we use two different mask tables. */
+ static const byte initial_count_bit[8] =
+ { 0, 1, 2, 4, 8, 0x10, 0x20, 0x40 };
+ static const byte further_count_bit[8] =
+ { 0x80, 1, 2, 4, 8, 0x10, 0x20, 0x40 };
+ const byte _ds *count_bit =
+ (count == ss->raster << 3 ? initial_count_bit : further_count_bit);
+
+ hce_load_state();
+ while ( count != end_count )
+ { /* If invert == invert_white, white and black have their */
+ /* correct meanings; if invert == ~invert_white, */
+ /* black and white are interchanged. */
+ uint a0 = count;
+ uint a1;
+#define b1 (a1 - diff) /* only for printing */
+ int diff;
+ uint prev_count = count;
+ const byte *prev_p = p - lbuf + lprev;
+ byte prev_data = prev_p[-1] ^ invert;
+ int rlen;
+
+ /* Make sure we have room for a run_horizontal plus */
+ /* two data runs. */
+ cf_ensure_put_runs(3, invert != invert_white, out);
+ /* Find the a1 and b1 transitions. */
+ skip_white_pixels(data, p, count, invert, rlen);
+ a1 = count;
+ if ( (prev_data & count_bit[prev_count & 7]) )
+ { /* Look for changing white first. */
+ skip_black_pixels(prev_data, prev_p, prev_count, invert, rlen);
+ }
+ count_bit = further_count_bit; /* no longer at beginning */
+pass: if ( prev_count != end_count )
+ { skip_white_pixels(prev_data, prev_p, prev_count, invert, rlen);
+ }
+ diff = a1 - prev_count; /* i.e., logical b1 - a1 */
+ /* In all the comparisons below, remember that count */
+ /* runs downward, not upward, so the comparisons are */
+ /* reversed. */
+ if ( diff <= -2 )
+ { /* Could be a pass mode. Find b2. */
+ if ( prev_count != end_count )
+ { skip_black_pixels(prev_data, prev_p,
+ prev_count, invert, rlen);
+ }
+ if ( prev_count > a1 )
+ { /* Use pass mode. */
+ if_debug4('W', "[W]pass: count = %d, a1 = %d, b1 = %d, new count = %d\n",
+ a0, a1, b1, prev_count);
+ hc_put_value(ss, q, cf2_run_pass_value,
+ cf2_run_pass_length);
+ cf_ensure_put_runs(3, invert != invert_white,
+ pass_out);
+ a0 = prev_count;
+ goto pass;
+pass_out: count = prev_count;
+ break;
+ }
+ }
+ /* Check for vertical coding. */
+ if ( diff <= 3 && diff >= -3 )
+ { /* Use vertical coding. */
+ const cfe_run *cp;
+ if_debug5('W', "[W]vertical %d: count = %d, a1 = %d, b1 = %d, new count = %d\n",
+ diff, a0, a1, b1, count);
+ cp = &cf2_run_vertical[diff + 3];
+ hc_put_code(ss, q, cp);
+ invert = ~invert; /* a1 polarity changes */
+ data ^= 0xff;
+ continue;
+ }
+ /* No luck, use horizontal coding. */
+ if ( count != end_count )
+ { skip_black_pixels(data, p, count, invert, rlen); /* find a2 */
+ }
+ hc_put_value(ss, q, cf2_run_horizontal_value,
+ cf2_run_horizontal_length);
+ a0 -= a1;
+ a1 -= count;
+ if ( invert == invert_white )
+ { if_debug3('W', "[W]horizontal: white = %d, black = %d, new count = %d\n",
+ a0, a1, count);
+ cf_put_white_run(ss, a0);
+ cf_put_black_run(ss, a1);
+ }
+ else
+ { if_debug3('W', "[W]horizontal: black = %d, white = %d, new count = %d\n",
+ a0, a1, count);
+ cf_put_black_run(ss, a0);
+ cf_put_white_run(ss, a1);
+#undef b1
+ }
+ }
+out: hce_store_state();
+ pw->ptr = q;
+ ss->count = count;
+ return status;
+}
+
+/* Stream template */
+const stream_template s_CFE_template =
+{ &st_CFE_state, s_CFE_init, s_CFE_process,
+ 2, 15, /* 31 left-over bits + 7 bits of padding + 6 13-bit EOLs */
+ s_CFE_release, s_CFE_set_defaults
+};
diff --git a/pstoraster/scfetab.c b/pstoraster/scfetab.c
new file mode 100644
index 000000000..d47537c59
--- /dev/null
+++ b/pstoraster/scfetab.c
@@ -0,0 +1,161 @@
+/* Copyright (C) 1992 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* scftab.c */
+/* Tables for CCITTFaxEncode filter */
+#include "std.h"
+#include "scommon.h" /* for scf.h */
+#include "scf.h"
+
+/* We make this a separate file so that it can be used by */
+/* the program that generates the tables for the CCITTFaxDecode filter. */
+
+/* ------ Run encoding tables ------ */
+
+/* Abbreviate cfe_entry to make the file smaller. */
+#define e_(c,len) cfe_entry(c,len)
+
+/* Define the end-of-line code. */
+/* Code in scfd.c and scfdgen.c knows that the run value is 1. */
+const cfe_run cf_run_eol = e_(run_eol_code_value, run_eol_code_length);
+
+/* Define the 1-D code that signals uncompressed data. */
+const cfe_run cf1_run_uncompressed = e_(0xf, 12);
+
+/* Define the 2-D run codes. */
+const cfe_run cf2_run_pass =
+ e_(cf2_run_pass_value, cf2_run_pass_length);
+const cfe_run cf2_run_vertical[7] = {
+ e_(0x3, 7),
+ e_(0x3, 6),
+ e_(0x3, 3),
+ e_(0x1, 1),
+ e_(0x2, 3),
+ e_(0x2, 6),
+ e_(0x2, 7)
+};
+const cfe_run cf2_run_horizontal =
+ e_(cf2_run_horizontal_value, cf2_run_horizontal_length);
+const cfe_run cf2_run_uncompressed = e_(0xf, 10);
+
+/* EOL codes for Group 3 2-D. */
+/* Code in scfd.c knows that these are 0...01x. */
+const cfe_run cf2_run_eol_1d =
+ e_((run_eol_code_value << 1) + 1, run_eol_code_length + 1);
+const cfe_run cf2_run_eol_2d =
+ e_((run_eol_code_value << 1) + 0, run_eol_code_length + 1);
+
+/* White run termination codes. */
+const cfe_run far_data cf_white_termination[64] = {
+ e_(0x35, 8), e_(0x7, 6), e_(0x7, 4), e_(0x8, 4),
+ e_(0xb, 4), e_(0xc, 4), e_(0xe, 4), e_(0xf, 4),
+ e_(0x13, 5), e_(0x14, 5), e_(0x7, 5), e_(0x8, 5),
+ e_(0x8, 6), e_(0x3, 6), e_(0x34, 6), e_(0x35, 6),
+ e_(0x2a, 6), e_(0x2b, 6), e_(0x27, 7), e_(0xc, 7),
+ e_(0x8, 7), e_(0x17, 7), e_(0x3, 7), e_(0x4, 7),
+ e_(0x28, 7), e_(0x2b, 7), e_(0x13, 7), e_(0x24, 7),
+ e_(0x18, 7), e_(0x2, 8), e_(0x3, 8), e_(0x1a, 8),
+ e_(0x1b, 8), e_(0x12, 8), e_(0x13, 8), e_(0x14, 8),
+ e_(0x15, 8), e_(0x16, 8), e_(0x17, 8), e_(0x28, 8),
+ e_(0x29, 8), e_(0x2a, 8), e_(0x2b, 8), e_(0x2c, 8),
+ e_(0x2d, 8), e_(0x4, 8), e_(0x5, 8), e_(0xa, 8),
+ e_(0xb, 8), e_(0x52, 8), e_(0x53, 8), e_(0x54, 8),
+ e_(0x55, 8), e_(0x24, 8), e_(0x25, 8), e_(0x58, 8),
+ e_(0x59, 8), e_(0x5a, 8), e_(0x5b, 8), e_(0x4a, 8),
+ e_(0x4b, 8), e_(0x32, 8), e_(0x33, 8), e_(0x34, 8)
+};
+
+/* White run make-up codes. */
+const cfe_run far_data cf_white_make_up[41] = {
+ e_(0, 0) /* dummy */, e_(0x1b, 5), e_(0x12, 5), e_(0x17, 6),
+ e_(0x37, 7), e_(0x36, 8), e_(0x37, 8), e_(0x64, 8),
+ e_(0x65, 8), e_(0x68, 8), e_(0x67, 8), e_(0xcc, 9),
+ e_(0xcd, 9), e_(0xd2, 9), e_(0xd3, 9), e_(0xd4, 9),
+ e_(0xd5, 9), e_(0xd6, 9), e_(0xd7, 9), e_(0xd8, 9),
+ e_(0xd9, 9), e_(0xda, 9), e_(0xdb, 9), e_(0x98, 9),
+ e_(0x99, 9), e_(0x9a, 9), e_(0x18, 6), e_(0x9b, 9),
+ e_(0x8, 11), e_(0xc, 11), e_(0xd, 11), e_(0x12, 12),
+ e_(0x13, 12), e_(0x14, 12), e_(0x15, 12), e_(0x16, 12),
+ e_(0x17, 12), e_(0x1c, 12), e_(0x1d, 12), e_(0x1e, 12),
+ e_(0x1f, 12)
+};
+
+/* Black run termination codes. */
+const cfe_run far_data cf_black_termination[64] = {
+ e_(0x37, 10), e_(0x2, 3), e_(0x3, 2), e_(0x2, 2),
+ e_(0x3, 3), e_(0x3, 4), e_(0x2, 4), e_(0x3, 5),
+ e_(0x5, 6), e_(0x4, 6), e_(0x4, 7), e_(0x5, 7),
+ e_(0x7, 7), e_(0x4, 8), e_(0x7, 8), e_(0x18, 9),
+ e_(0x17, 10), e_(0x18, 10), e_(0x8, 10), e_(0x67, 11),
+ e_(0x68, 11), e_(0x6c, 11), e_(0x37, 11), e_(0x28, 11),
+ e_(0x17, 11), e_(0x18, 11), e_(0xca, 12), e_(0xcb, 12),
+ e_(0xcc, 12), e_(0xcd, 12), e_(0x68, 12), e_(0x69, 12),
+ e_(0x6a, 12), e_(0x6b, 12), e_(0xd2, 12), e_(0xd3, 12),
+ e_(0xd4, 12), e_(0xd5, 12), e_(0xd6, 12), e_(0xd7, 12),
+ e_(0x6c, 12), e_(0x6d, 12), e_(0xda, 12), e_(0xdb, 12),
+ e_(0x54, 12), e_(0x55, 12), e_(0x56, 12), e_(0x57, 12),
+ e_(0x64, 12), e_(0x65, 12), e_(0x52, 12), e_(0x53, 12),
+ e_(0x24, 12), e_(0x37, 12), e_(0x38, 12), e_(0x27, 12),
+ e_(0x28, 12), e_(0x58, 12), e_(0x59, 12), e_(0x2b, 12),
+ e_(0x2c, 12), e_(0x5a, 12), e_(0x66, 12), e_(0x67, 12)
+};
+
+/* Black run make-up codes. */
+const cfe_run far_data cf_black_make_up[41] = {
+ e_(0, 0) /* dummy */, e_(0xf, 10), e_(0xc8, 12), e_(0xc9, 12),
+ e_(0x5b, 12), e_(0x33, 12), e_(0x34, 12), e_(0x35, 12),
+ e_(0x6c, 13), e_(0x6d, 13), e_(0x4a, 13), e_(0x4b, 13),
+ e_(0x4c, 13), e_(0x4d, 13), e_(0x72, 13), e_(0x73, 13),
+ e_(0x74, 13), e_(0x75, 13), e_(0x76, 13), e_(0x77, 13),
+ e_(0x52, 13), e_(0x53, 13), e_(0x54, 13), e_(0x55, 13),
+ e_(0x5a, 13), e_(0x5b, 13), e_(0x64, 13), e_(0x65, 13),
+ e_(0x8, 11), e_(0xc, 11), e_(0xd, 11), e_(0x12, 12),
+ e_(0x13, 12), e_(0x14, 12), e_(0x15, 12), e_(0x16, 12),
+ e_(0x17, 12), e_(0x1c, 12), e_(0x1d, 12), e_(0x1e, 12),
+ e_(0x1f, 12)
+};
+
+/* Uncompressed codes. */
+const cfe_run cf_uncompressed[6] = {
+ e_(1, 1),
+ e_(1, 2),
+ e_(1, 3),
+ e_(1, 4),
+ e_(1, 5),
+ e_(1, 6)
+};
+
+/* Uncompressed exit codes. */
+const cfe_run cf_uncompressed_exit[10] = {
+ e_(2, 8), e_(3, 8),
+ e_(2, 9), e_(3, 9),
+ e_(2, 10), e_(3, 10),
+ e_(2, 11), e_(3, 11),
+ e_(2, 12), e_(3, 12)
+};
+
+/* Some C compilers insist on having executable code in every file.... */
+void
+cfe_dummy(void)
+{
+}
diff --git a/pstoraster/scfx.h b/pstoraster/scfx.h
new file mode 100644
index 000000000..b0771d0bd
--- /dev/null
+++ b/pstoraster/scfx.h
@@ -0,0 +1,122 @@
+/* Copyright (C) 1993, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* scfx.h */
+/* CCITTFax filter state definition */
+/* Requires strimpl.h */
+#include "shc.h"
+
+/* Common state */
+#define stream_CF_state_common\
+ stream_hc_state_common;\
+ /* The client sets the following before initialization. */\
+ bool Uncompressed;\
+ int K;\
+ bool EndOfLine;\
+ bool EncodedByteAlign;\
+ int Columns;\
+ int Rows;\
+ bool EndOfBlock;\
+ bool BlackIs1;\
+ int DamagedRowsBeforeError; /* (Decode only) */\
+ /*bool FirstBitLowOrder;*/ /* in stream_hc_state_common */\
+ int DecodedByteAlign;\
+ /* The init procedure sets the following. */\
+ uint raster;\
+ byte *lbuf; /* current scan line buffer */\
+ /* (only if decoding or 2-D encoding) */\
+ byte *lprev; /* previous scan line buffer (only if 2-D) */\
+ /* The following are updated dynamically. */\
+ int k_left; /* number of next rows to encode in 2-D */\
+ /* (only if K > 0) */\
+ int run_color; /* -1 if processing white run, */\
+ /* 0 if between runs but white is next, */\
+ /* 1 if between runs and black is next, */\
+ /* 2 if processing black run */\
+ int damaged_rows; /* # of consecutive damaged rows preceding */\
+ /* the current row */\
+ bool skipping_damage /* true if skipping a damaged row looking */\
+ /* for EOL */
+typedef struct stream_CF_state_s {
+ stream_CF_state_common;
+} stream_CF_state;
+/* Define common default parameter setting. */
+#define s_CF_set_defaults_inline(ss)\
+ ((ss)->Uncompressed = false,\
+ (ss)->K = 0,\
+ (ss)->EndOfLine = false,\
+ (ss)->EncodedByteAlign = false,\
+ (ss)->Columns = 1728,\
+ (ss)->Rows = 0,\
+ (ss)->EndOfBlock = true,\
+ (ss)->BlackIs1 = false,\
+ /* Added by Adobe since the Red Book */\
+ (ss)->DamagedRowsBeforeError = 0,\
+ (ss)->FirstBitLowOrder = false,\
+ /* Added by us */\
+ (ss)->DecodedByteAlign = 1)
+
+/* CCITTFaxEncode */
+typedef struct stream_CFE_state_s {
+ stream_CF_state_common;
+ int count; /* # of source bits left to scan, */
+ /* padded to a byte boundary */
+ int run_count; /* count at start of run begin scanned */
+ int copy_count; /* # of bytes to copy into lbuf */
+ bool new_line; /* false if processing a line, */
+ /* true if need to start new line */
+} stream_CFE_state;
+#define private_st_CFE_state() /* in scfe.c */\
+ gs_private_st_ptrs2(st_CFE_state, stream_CFE_state, "CCITTFaxEncode state",\
+ cfe_enum_ptrs, cfe_reloc_ptrs, lbuf, lprev)
+#define s_CFE_set_defaults_inline(ss)\
+ s_CF_set_defaults_inline(ss)
+extern const stream_template s_CFE_template;
+
+/* CCITTFaxDecode */
+typedef struct stream_CFD_state_s {
+ stream_CF_state_common;
+ int cbit; /* bits left to fill in current decoded */
+ /* byte at lbuf[wpos] (0..7) */
+ int rows_left; /* number of rows left */
+ int rpos; /* rptr for copying lbuf to client */
+ int wpos; /* rlimit/wptr for filling lbuf or */
+ /* copying to client */
+ int eol_count; /* number of EOLs seen so far */
+ byte invert; /* current value of 'white' */
+ /* for 2-D decoding */
+ /* The following are not used yet. */
+ int uncomp_run; /* non-0 iff we are in an uncompressed */
+ /* run straddling a scan line (-1 if white, */
+ /* 1 if black) */
+ int uncomp_left; /* # of bits left in the run */
+ int uncomp_exit; /* non-0 iff this is an exit run */
+ /* (-1 if next run white, 1 if black) */
+} stream_CFD_state;
+#define private_st_CFD_state() /* in scfd.c */\
+ gs_private_st_ptrs2(st_CFD_state, stream_CFD_state, "CCITTFaxDecode state",\
+ cfd_enum_ptrs, cfd_reloc_ptrs, lbuf, lprev)
+#define s_CFD_set_defaults_inline(ss)\
+ (s_CF_set_defaults_inline(ss),\
+ (ss)->DamagedRowsBeforeError = 0)
+extern const stream_template s_CFD_template;
diff --git a/pstoraster/scommon.h b/pstoraster/scommon.h
new file mode 100644
index 000000000..81b14a699
--- /dev/null
+++ b/pstoraster/scommon.h
@@ -0,0 +1,157 @@
+/* Copyright (C) 1994, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* scommon.h */
+/* Definitions common to stream clients and implementors */
+
+#ifndef scommon_DEFINED
+# define scommon_DEFINED
+
+#include "gsmemory.h"
+#include "gstypes.h" /* for gs_string */
+#include "gsstruct.h" /* for extern_st */
+
+/*
+ * There are three major structures involved in the stream package.
+ *
+ * A stream is an "object" that owns a buffer, which it uses to implement
+ * byte-oriented sequential access in a standard way, and a set of
+ * procedures that handle things like buffer refilling. See stream.h
+ * for more information about streams.
+ */
+#ifndef stream_DEFINED
+# define stream_DEFINED
+typedef struct stream_s stream;
+#endif
+/*
+ * A stream_state records the state specific to a given variety of stream.
+ * The buffer processing function of a stream maintains this state.
+ */
+typedef struct stream_state_s stream_state;
+/*
+ * A stream_template provides the information needed to create a stream.
+ * The client must fill in any needed setup parameters in the appropriate
+ * variety of stream_state, and then call the initialization function
+ * provided by the template. See strimpl.h for more information about
+ * stream_templates.
+ */
+typedef struct stream_template_s stream_template;
+
+/*
+ * The stream package works with bytes, not chars.
+ * This is to ensure unsigned representation on all systems.
+ * A stream currently can only be read or written, not both.
+ * Note also that the read procedure returns an int, not a char or a byte;
+ * we use negative values to indicate exceptional conditions.
+ * (We cast these values to int explicitly, because some compilers
+ * don't do this if the other arm of a conditional is a byte.)
+ */
+/* End of data */
+#define EOFC ((int)(-1))
+/* Error */
+#define ERRC ((int)(-2))
+/* Interrupt */
+#define INTC ((int)(-3))
+/****** INTC IS NOT USED YET ******/
+/* Callout */
+#define CALLC ((int)(-4))
+#define max_stream_exception 4
+/* The following hack is needed for initializing scan_char_array in iscan.c. */
+#define stream_exception_repeat(x) x, x, x, x
+
+/*
+ * Define cursors for reading from or writing into a buffer.
+ * We lay them out this way so that we can alias
+ * the write pointer and the read limit.
+ */
+typedef struct stream_cursor_read_s {
+ const byte *ptr;
+ const byte *limit;
+ byte *_skip;
+} stream_cursor_read;
+typedef struct stream_cursor_write_s {
+ const byte *_skip;
+ byte *ptr;
+ byte *limit;
+} stream_cursor_write;
+typedef union stream_cursor_s {
+ stream_cursor_read r;
+ stream_cursor_write w;
+} stream_cursor;
+
+/*
+ * Define the prototype for the procedures known to both the generic
+ * stream code and the stream implementations.
+ */
+
+/* Initialize the stream state (after the client parameters are set). */
+#define stream_proc_init(proc)\
+ int proc(P1(stream_state *))
+
+/* Process a buffer. See strimpl.h for details. */
+#define stream_proc_process(proc)\
+ int proc(P4(stream_state *, stream_cursor_read *,\
+ stream_cursor_write *, bool))
+
+/* Release the stream state when closing. */
+#define stream_proc_release(proc)\
+ void proc(P1(stream_state *))
+
+/* Initialize the client parameters to default values. */
+#define stream_proc_set_defaults(proc)\
+ void proc(P1(stream_state *))
+
+/* Reinitialize any internal stream state. Note that this does not */
+/* affect buffered data. We declare this as returning an int so that */
+/* it can be the same as the init procedure; however, reinit cannot fail. */
+#define stream_proc_reinit(proc)\
+ int proc(P1(stream_state *))
+
+/* Report an error. Note that this procedure is stored in the state, */
+/* not in the main stream structure. */
+#define stream_proc_report_error(proc)\
+ int proc(P2(stream_state *, const char *))
+stream_proc_report_error(s_no_report_error);
+
+/*
+ * Define a generic stream state. If a processing procedure has no
+ * state of its own, it can use stream_state; otherwise, it must
+ * create a "subclass". There is a hack in stream.h to allow the stream
+ * itself to serve as the "state" of a couple of heavily used stream types.
+ *
+ * In order to simplify the structure descriptors for concrete streams,
+ * we require that the generic stream state not contain any pointers
+ * to garbage-collectable storage.
+ */
+#define stream_state_common\
+ const stream_template *template;\
+ gs_memory_t *memory;\
+ stream_proc_report_error((*report_error))
+struct stream_state_s {
+ stream_state_common;
+};
+extern_st(st_stream_state);
+#define public_st_stream_state() /* in stream.c */\
+ gs_public_st_simple(st_stream_state, stream_state, "stream_state")
+
+#endif /* scommon_INCLUDED */
diff --git a/pstoraster/sdct.h b/pstoraster/sdct.h
new file mode 100644
index 000000000..342103bf8
--- /dev/null
+++ b/pstoraster/sdct.h
@@ -0,0 +1,97 @@
+/* Copyright (C) 1994 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* sdct.h */
+/* Definitions for DCT filters for Ghostscript streams */
+/* Requires stream.h, strimpl.h, jpeg/jpeglib.h */
+#include <setjmp.h> /* for jmp_buf */
+
+/* ------ DCT filters ------ */
+
+/*
+ * Define the stream state.
+ * The jpeg_xxx_data structs are allocated in immovable memory
+ * to simplify use of the IJG library.
+ */
+#define jpeg_stream_data_common\
+ /* We put a copy of the stream template here, because */\
+ /* the minimum buffer sizes depend on the image parameters. */\
+ stream_template template;\
+ struct jpeg_error_mgr err;\
+ jmp_buf exit_jmpbuf;\
+ /* The following are documented in Adobe TN 5116. */\
+ int Picky; /* 0 or 1 */\
+ int Relax /* 0 or 1 */
+typedef struct jpeg_stream_data_s {
+ jpeg_stream_data_common;
+} jpeg_stream_data;
+typedef struct jpeg_compress_data_s {
+ jpeg_stream_data_common;
+ /* cinfo must immediately follow the common fields */
+ struct jpeg_compress_struct cinfo;
+ struct jpeg_destination_mgr destination;
+} jpeg_compress_data;
+typedef struct jpeg_decompress_data_s {
+ jpeg_stream_data_common;
+ /* dinfo must immediately follow the common fields, */
+ /* so that it has same offset as cinfo. */
+ struct jpeg_decompress_struct dinfo;
+ struct jpeg_source_mgr source;
+ long skip; /* # of bytes remaining to skip in input */
+ bool input_eod; /* true when no more input data available */
+ bool faked_eoi; /* true when fill_input_buffer inserted EOI */
+ byte * scanline_buffer; /* buffer for oversize scanline, or NULL */
+ uint bytes_in_scanline; /* # of bytes remaining to output from same */
+} jpeg_decompress_data;
+
+/* The stream state itself. This is kept in garbage-collectable memory. */
+typedef struct stream_DCT_state_s {
+ stream_state_common;
+ /* The following are set before initialization. */
+ /* Note that most JPEG parameters go straight into */
+ /* the IJG data structures, not into this struct. */
+ gs_const_string Markers; /* NULL if no Markers parameter */
+ float QFactor;
+ int ColorTransform; /* -1 if not specified */
+ bool NoMarker; /* DCTEncode only */
+ /* This is a pointer to immovable storage. */
+ union _jd {
+ jpeg_stream_data *common;
+ jpeg_compress_data *compress;
+ jpeg_decompress_data *decompress;
+ } data;
+ /* DCTEncode sets this before initialization;
+ * DCTDecode cannot set it until the JPEG headers are read.
+ */
+ uint scan_line_size;
+ /* The following are updated dynamically. */
+ int phase;
+} stream_DCT_state;
+/* The state descriptor is public only to allow us to split up */
+/* the encoding and decoding filters. */
+extern_st(st_DCT_state);
+#define public_st_DCT_state() /* in sdctc.c */\
+ gs_public_st_composite(st_DCT_state, stream_DCT_state,\
+ "DCTEncode/Decode state", dct_enum_ptrs, dct_reloc_ptrs)
+extern const stream_template s_DCTD_template;
+extern const stream_template s_DCTE_template;
diff --git a/pstoraster/sdctc.c b/pstoraster/sdctc.c
new file mode 100644
index 000000000..cf7f31159
--- /dev/null
+++ b/pstoraster/sdctc.c
@@ -0,0 +1,41 @@
+/* Copyright (C) 1994 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+#include <config.h>
+#ifdef HAVE_LIBJPEG
+
+/* sdctc.c */
+/* Code common to DCT encoding and decoding streams */
+#include "stdio_.h"
+#include "jpeglib.h"
+#include "strimpl.h"
+#include "sdct.h"
+
+public_st_DCT_state();
+/* GC procedures */
+private ENUM_PTRS_BEGIN(dct_enum_ptrs) return 0;
+ ENUM_CONST_STRING_PTR(0, stream_DCT_state, Markers);
+} }
+private RELOC_PTRS_BEGIN(dct_reloc_ptrs) {
+ RELOC_CONST_STRING_PTR(stream_DCT_state, Markers);
+} RELOC_PTRS_END
+#endif /* HAVE_LIBJPEG */
diff --git a/pstoraster/sdctd.c b/pstoraster/sdctd.c
new file mode 100644
index 000000000..3679e4862
--- /dev/null
+++ b/pstoraster/sdctd.c
@@ -0,0 +1,282 @@
+/* Copyright (C) 1994, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+#include <config.h>
+#ifdef HAVE_LIBJPEG
+
+/* sdctd.c */
+/* DCT decoding filter stream */
+#include "memory_.h"
+#include "stdio_.h"
+#include "jpeglib.h"
+#include "jerror.h"
+#include "gdebug.h"
+#include "strimpl.h"
+#include "sdct.h"
+#include "sjpeg.h"
+
+#define ss ((stream_DCT_state *)st)
+
+/* ------ DCTDecode ------ */
+
+/* JPEG source manager procedures */
+private void
+dctd_init_source(j_decompress_ptr dinfo)
+{
+}
+static const JOCTET fake_eoi[2] = { 0xFF , JPEG_EOI };
+private boolean
+dctd_fill_input_buffer(j_decompress_ptr dinfo)
+{ jpeg_decompress_data *jddp =
+ (jpeg_decompress_data *)((char *)dinfo -
+ offset_of(jpeg_decompress_data, dinfo));
+ if (! jddp->input_eod)
+ return FALSE; /* normal case: suspend processing */
+ /* Reached end of source data without finding EOI */
+ WARNMS(dinfo, JWRN_JPEG_EOF);
+ /* Insert a fake EOI marker */
+ dinfo->src->next_input_byte = fake_eoi;
+ dinfo->src->bytes_in_buffer = 2;
+ jddp->faked_eoi = true; /* so process routine doesn't use next_input_byte */
+ return TRUE;
+}
+private void
+dctd_skip_input_data(j_decompress_ptr dinfo, long num_bytes)
+{ struct jpeg_source_mgr *src = dinfo->src;
+ jpeg_decompress_data *jddp =
+ (jpeg_decompress_data *)((char *)dinfo -
+ offset_of(jpeg_decompress_data, dinfo));
+ if ( num_bytes > 0 )
+ { if ( num_bytes > src->bytes_in_buffer )
+ { jddp->skip += num_bytes - src->bytes_in_buffer;
+ src->next_input_byte += src->bytes_in_buffer;
+ src->bytes_in_buffer = 0;
+ return;
+ }
+ src->next_input_byte += num_bytes;
+ src->bytes_in_buffer -= num_bytes;
+ }
+}
+private void
+dctd_term_source(j_decompress_ptr dinfo)
+{
+}
+
+/* Initialize DCTDecode filter */
+private int
+s_DCTD_init(stream_state *st)
+{ struct jpeg_source_mgr *src = &ss->data.decompress->source;
+ src->init_source = dctd_init_source;
+ src->fill_input_buffer = dctd_fill_input_buffer;
+ src->skip_input_data = dctd_skip_input_data;
+ src->term_source = dctd_term_source;
+ src->resync_to_restart = jpeg_resync_to_restart; /* use default method */
+ ss->data.decompress->dinfo.src = src;
+ ss->data.decompress->skip = 0;
+ ss->data.decompress->input_eod = false;
+ ss->data.decompress->faked_eoi = false;
+ ss->phase = 0;
+ return 0;
+}
+
+/* Process a buffer */
+private int
+s_DCTD_process(stream_state *st, stream_cursor_read *pr,
+ stream_cursor_write *pw, bool last)
+{ jpeg_decompress_data *jddp = ss->data.decompress;
+ struct jpeg_source_mgr *src = jddp->dinfo.src;
+ int code;
+
+ if_debug3('w', "[wdd]process avail=%u, skip=%u, last=%d\n",
+ (uint)(pr->limit - pr->ptr), (uint)jddp->skip, last);
+ if ( jddp->skip != 0 )
+ { long avail = pr->limit - pr->ptr;
+ if ( avail < jddp->skip )
+ { jddp->skip -= avail;
+ pr->ptr = pr->limit;
+ if (!last)
+ return 0; /* need more data */
+ jddp->skip = 0; /* don't skip past input EOD */
+ }
+ pr->ptr += jddp->skip;
+ jddp->skip = 0;
+ }
+ src->next_input_byte = pr->ptr + 1;
+ src->bytes_in_buffer = pr->limit - pr->ptr;
+ jddp->input_eod = last;
+ switch ( ss->phase )
+ {
+ case 0: /* not initialized yet */
+ /*
+ * Adobe implementations seem to ignore leading garbage bytes,
+ * even though neither the standard nor Adobe's own
+ * documentation mention this.
+ */
+ while ( pr->ptr < pr->limit && pr->ptr[1] != 0xff )
+ pr->ptr++;
+ if ( pr->ptr == pr->limit )
+ return 0;
+ src->next_input_byte = pr->ptr + 1;
+ src->bytes_in_buffer = pr->limit - pr->ptr;
+ ss->phase = 1;
+ /* falls through */
+ case 1: /* reading header markers */
+ if ( (code = gs_jpeg_read_header(ss, TRUE)) < 0 )
+ return ERRC;
+ pr->ptr =
+ (jddp->faked_eoi ? pr->limit : src->next_input_byte - 1);
+ switch ( code )
+ {
+ case JPEG_SUSPENDED:
+ return 0;
+ /*case JPEG_HEADER_OK: */
+ }
+ /* If we have a ColorTransform parameter, and it's not
+ * overridden by an Adobe marker in the data, set colorspace.
+ */
+ if ( ss->ColorTransform >= 0 &&
+ ! jddp->dinfo.saw_Adobe_marker )
+ { switch ( jddp->dinfo.num_components )
+ {
+ case 3:
+ jddp->dinfo.jpeg_color_space =
+ (ss->ColorTransform ? JCS_YCbCr : JCS_RGB);
+ /* out_color_space will default to JCS_RGB */
+ break;
+ case 4:
+ jddp->dinfo.jpeg_color_space =
+ (ss->ColorTransform ? JCS_YCCK : JCS_CMYK);
+ /* out_color_space will default to JCS_CMYK */
+ break;
+ }
+ }
+ ss->phase = 2;
+ /* falls through */
+ case 2: /* start_decompress */
+ if ( (code = gs_jpeg_start_decompress(ss)) < 0 )
+ return ERRC;
+ pr->ptr =
+ (jddp->faked_eoi ? pr->limit : src->next_input_byte - 1);
+ if ( code == 0 )
+ return 0;
+ ss->scan_line_size =
+ jddp->dinfo.output_width * jddp->dinfo.output_components;
+ if_debug4('w', "[wdd]width=%u, components=%d, scan_line_size=%u, min_out_size=%u\n",
+ jddp->dinfo.output_width,
+ jddp->dinfo.output_components,
+ ss->scan_line_size, jddp->template.min_out_size);
+ if ( ss->scan_line_size > (uint) jddp->template.min_out_size )
+ {
+ /* Create a spare buffer for oversize scanline */
+ jddp->scanline_buffer = (byte *)
+ gs_malloc(ss->scan_line_size, sizeof(byte),
+ "s_DCTD_process(scanline_buffer)");
+ if ( jddp->scanline_buffer == NULL )
+ return ERRC;
+ }
+ jddp->bytes_in_scanline = 0;
+ ss->phase = 3;
+ /* falls through */
+ case 3: /* reading data */
+dumpbuffer:
+ if ( jddp->bytes_in_scanline != 0 )
+ { uint avail = pw->limit - pw->ptr;
+ uint tomove = min(jddp->bytes_in_scanline,
+ avail);
+ if_debug2('w', "[wdd]moving %u/%u\n",
+ tomove, avail);
+ memcpy(pw->ptr + 1, jddp->scanline_buffer +
+ (ss->scan_line_size - jddp->bytes_in_scanline),
+ tomove);
+ pw->ptr += tomove;
+ jddp->bytes_in_scanline -= tomove;
+ if ( jddp->bytes_in_scanline != 0 )
+ return 1; /* need more room */
+ }
+ while ( jddp->dinfo.output_height > jddp->dinfo.output_scanline )
+ { int read;
+ byte *samples;
+ if ( jddp->scanline_buffer != NULL )
+ samples = jddp->scanline_buffer;
+ else
+ { if ( (uint)(pw->limit - pw->ptr) < ss->scan_line_size )
+ return 1; /* need more room */
+ samples = pw->ptr + 1;
+ }
+ read = gs_jpeg_read_scanlines(ss, &samples, 1);
+ if ( read < 0 )
+ return ERRC;
+ if_debug3('w', "[wdd]read returns %d, used=%u, faked_eoi=%d\n",
+ read,
+ (uint)(src->next_input_byte - 1 - pr->ptr),
+ (int)jddp->faked_eoi);
+ pr->ptr =
+ (jddp->faked_eoi ? pr->limit : src->next_input_byte - 1);
+ if ( !read )
+ return 0; /* need more data */
+ if ( jddp->scanline_buffer != NULL )
+ { jddp->bytes_in_scanline = ss->scan_line_size;
+ goto dumpbuffer;
+ }
+ pw->ptr += ss->scan_line_size;
+ }
+ ss->phase = 4;
+ /* falls through */
+ case 4: /* end of image; scan for EOI */
+ if ( (code = gs_jpeg_finish_decompress(ss)) < 0 )
+ return ERRC;
+ pr->ptr =
+ (jddp->faked_eoi ? pr->limit : src->next_input_byte - 1);
+ if ( code == 0 )
+ return 0;
+ ss->phase = 5;
+ /* falls through */
+ case 5: /* we are DONE */
+ return EOFC;
+ }
+ /* Default case can't happen.... */
+ return ERRC;
+}
+
+/* Release the stream */
+private void
+s_DCTD_release(stream_state *st)
+{ gs_jpeg_destroy(ss);
+ if ( ss->data.decompress->scanline_buffer != NULL )
+ {
+ gs_free(ss->data.decompress->scanline_buffer,
+ ss->scan_line_size, sizeof(byte),
+ "s_DCTD_release(scanline_buffer)");
+ }
+ gs_free(ss->data.decompress, 1, sizeof(jpeg_decompress_data),
+ "s_DCTD_release");
+ /* Switch the template pointer back in case we still need it. */
+ st->template = &s_DCTD_template;
+}
+
+/* Stream template */
+const stream_template s_DCTD_template =
+{ &st_DCT_state, s_DCTD_init, s_DCTD_process, 2000, 4000, s_DCTD_release
+};
+
+#undef ss
+#endif /* HAVE_LIBJPEG */
diff --git a/pstoraster/sdcte.c b/pstoraster/sdcte.c
new file mode 100644
index 000000000..f085f77c4
--- /dev/null
+++ b/pstoraster/sdcte.c
@@ -0,0 +1,173 @@
+/* Copyright (C) 1994 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+#include <config.h>
+#ifdef HAVE_LIBJPEG
+
+/* sdcte.c */
+/* DCT encoding filter stream */
+#include "memory_.h"
+#include "stdio_.h"
+#include "jpeglib.h"
+#include "jerror.h"
+#include "gdebug.h"
+#include "strimpl.h"
+#include "sdct.h"
+#include "sjpeg.h"
+
+#define ss ((stream_DCT_state *)st)
+
+/* ------ DCTEncode ------ */
+
+/* JPEG destination manager procedures */
+private void
+dcte_init_destination(j_compress_ptr cinfo)
+{
+}
+private boolean
+dcte_empty_output_buffer(j_compress_ptr cinfo)
+{ return FALSE;
+}
+private void
+dcte_term_destination(j_compress_ptr cinfo)
+{
+}
+
+/* Initialize DCTEncode filter */
+private int
+s_DCTE_init(stream_state *st)
+{ struct jpeg_destination_mgr *dest = &ss->data.compress->destination;
+ dest->init_destination = dcte_init_destination;
+ dest->empty_output_buffer = dcte_empty_output_buffer;
+ dest->term_destination = dcte_term_destination;
+ ss->data.compress->cinfo.dest = dest;
+ ss->phase = 0;
+ return 0;
+}
+
+/* Process a buffer */
+private int
+s_DCTE_process(stream_state *st, stream_cursor_read *pr,
+ stream_cursor_write *pw, bool last)
+{ jpeg_compress_data *jcdp = ss->data.compress;
+ struct jpeg_destination_mgr *dest = jcdp->cinfo.dest;
+ dest->next_output_byte = pw->ptr + 1;
+ dest->free_in_buffer = pw->limit - pw->ptr;
+ switch ( ss->phase )
+ {
+ case 0: /* not initialized yet */
+ if ( gs_jpeg_start_compress(ss, TRUE) < 0 )
+ return ERRC;
+ pw->ptr = dest->next_output_byte - 1;
+ ss->phase = 1;
+ /* falls through */
+ case 1: /* initialized, Markers not written */
+ if ( pw->limit - pw->ptr < ss->Markers.size )
+ return 1;
+ memcpy(pw->ptr + 1, ss->Markers.data, ss->Markers.size);
+ pw->ptr += ss->Markers.size;
+ ss->phase = 2;
+ /* falls through */
+ case 2: /* still need to write Adobe marker */
+ if ( ! ss->NoMarker )
+ { static const byte Adobe[] = {
+ 0xFF, JPEG_APP0+14, 0, 14, /* parameter length */
+ 'A', 'd', 'o', 'b', 'e',
+ 0, 100, /* Version */
+ 0, 0, /* Flags0 */
+ 0, 0, /* Flags1 */
+ 0 /* ColorTransform */
+ };
+#define ADOBE_MARKER_LEN sizeof(Adobe)
+ if ( pw->limit - pw->ptr < ADOBE_MARKER_LEN )
+ return 1;
+ memcpy(pw->ptr + 1, Adobe, ADOBE_MARKER_LEN);
+ pw->ptr += ADOBE_MARKER_LEN;
+ *pw->ptr = ss->ColorTransform;
+#undef ADOBE_MARKER_LEN
+ }
+ dest->next_output_byte = pw->ptr + 1;
+ dest->free_in_buffer = pw->limit - pw->ptr;
+ ss->phase = 3;
+ /* falls through */
+ case 3: /* markers written, processing data */
+ while ( jcdp->cinfo.image_height > jcdp->cinfo.next_scanline )
+ { int written;
+ /*
+ * The data argument for jpeg_write_scanlines is
+ * declared as a JSAMPARRAY. There is no corresponding
+ * const type, so we must remove const from the
+ * argument that we are passing here. (Tom Lane of IJG
+ * judges that providing const analogues of the
+ * interface types wouldn't be worth the trouble.)
+ */
+ /*const*/ byte *samples = (byte *)(pr->ptr + 1);
+ if ( (uint)(pr->limit - pr->ptr) < ss->scan_line_size )
+ { if ( last )
+ return ERRC; /* premature EOD */
+ return 0; /* need more data */
+ }
+ written = gs_jpeg_write_scanlines(ss, &samples, 1);
+ if ( written < 0 )
+ return ERRC;
+ pw->ptr = dest->next_output_byte - 1;
+ if ( !written )
+ return 1; /* output full */
+ pr->ptr += ss->scan_line_size;
+ }
+ ss->phase = 4;
+ /* falls through */
+ case 4: /* all data processed, finishing */
+ /* jpeg_finish_compress can't suspend, so make sure
+ * it has plenty of room to write the last few bytes.
+ */
+ if ( pw->limit - pw->ptr < 100 )
+ return 1;
+ if ( gs_jpeg_finish_compress(ss) < 0 )
+ return ERRC;
+ pw->ptr = dest->next_output_byte - 1;
+ ss->phase = 5;
+ /* falls through */
+ case 5: /* we are DONE */
+ return EOFC;
+ }
+ /* Default case can't happen.... */
+ return ERRC;
+}
+
+/* Release the stream */
+private void
+s_DCTE_release(stream_state *st)
+{ gs_jpeg_destroy(ss);
+ gs_free(ss->data.compress, 1, sizeof(jpeg_compress_data),
+ "s_DCTE_release");
+ /* Switch the template pointer back in case we still need it. */
+ st->template = &s_DCTE_template;
+}
+
+/* Stream template */
+const stream_template s_DCTE_template =
+{ &st_DCT_state, s_DCTE_init, s_DCTE_process, 1000, 4000, s_DCTE_release
+};
+
+#undef ss
+#endif /* HAVE_LIBJPEG */
diff --git a/pstoraster/seexec.c b/pstoraster/seexec.c
new file mode 100644
index 000000000..213105294
--- /dev/null
+++ b/pstoraster/seexec.c
@@ -0,0 +1,175 @@
+/* Copyright (C) 1994 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* seexec.c */
+/* eexec filters */
+#include "stdio_.h" /* includes std.h */
+#include "strimpl.h"
+#include "sfilter.h"
+#include "gscrypt1.h"
+#include "scanchar.h"
+
+/* ------ eexecEncode ------ */
+
+/* Encoding is much simpler than decoding, because we don't */
+/* worry about initial characters or hex vs. binary (the client */
+/* has to take care of these aspects). */
+
+private_st_exE_state();
+
+#define ss ((stream_exE_state *)st)
+
+/* Process a buffer */
+private int
+s_exE_process(stream_state *st, stream_cursor_read *pr,
+ stream_cursor_write *pw, bool last)
+{ const byte *p = pr->ptr;
+ byte *q = pw->ptr;
+ uint rcount = pr->limit - p;
+ uint wcount = pw->limit - q;
+ uint count;
+ int status;
+ if ( rcount <= wcount )
+ count = rcount, status = 0;
+ else
+ count = wcount, status = 1;
+ gs_type1_encrypt(q + 1, p + 1, count, (crypt_state *)&ss->cstate);
+ pr->ptr += count;
+ pw->ptr += count;
+ return status;
+}
+
+#undef ss
+
+/* Stream template */
+const stream_template s_exE_template =
+{ &st_exE_state, NULL, s_exE_process, 1, 2
+};
+
+/* ------ eexecDecode ------ */
+
+private_st_exD_state();
+
+#define ss ((stream_exD_state *)st)
+
+/* Initialize the state for reading and decrypting. */
+/* Decrypting streams are not positionable. */
+private int
+s_exD_init(stream_state *st)
+{ ss->odd = -1;
+ ss->binary = -1; /* unknown */
+ ss->record_left = max_long;
+ ss->skip = 4;
+ return 0;
+}
+
+/* Process a buffer. */
+private int
+s_exD_process(stream_state *st, stream_cursor_read *pr,
+ stream_cursor_write *pw, bool last)
+{ register const byte *p = pr->ptr;
+ register byte *q = pw->ptr;
+ int skip = ss->skip;
+ int rcount = pr->limit - p;
+ int wcount = pw->limit - q;
+ int status = 0;
+ int count = (wcount < rcount ? (status = 1, wcount) : rcount);
+
+ if ( ss->binary < 0 )
+ { /* This is the very first time we're filling the buffer. */
+ /* Determine whether this is ASCII or hex encoding. */
+ register const byte _ds *decoder = scan_char_decoder;
+ int i;
+
+ if ( rcount < 8 )
+ return 0;
+ /* Adobe's documentation doesn't actually specify the test */
+ /* that eexec should use, but we believe the following */
+ /* gives correct answers even on certain non-conforming */
+ /* PostScript files encountered in practice: */
+ ss->binary = 0;
+ for ( i = 1; i <= 8; i++ )
+ if ( !(decoder[p[i]] <= 0xf ||
+ decoder[p[i]] == ctype_space)
+ )
+ { ss->binary = 1;
+ if ( ss->pfb_state != 0 )
+ { /* Stop at the end of the .PFB binary data. */
+ ss->record_left = ss->pfb_state->record_left;
+ }
+ break;
+ }
+ }
+ if ( ss->binary )
+ { if ( count > ss->record_left )
+ { count = ss->record_left;
+ status = 0;
+ }
+ /* We pause at the end of the .PFB binary data, */
+ /* in an attempt to keep from reading beyond the end of */
+ /* the encrypted data. */
+ if ( (ss->record_left -= count) == 0 )
+ ss->record_left = max_long;
+ pr->ptr = p + count;
+ }
+ else
+ { /* We only ignore leading whitespace, in an attempt to */
+ /* keep from reading beyond the end of the encrypted data. */
+ status = s_hex_process(pr, pw, &ss->odd,
+ hex_ignore_leading_whitespace);
+ p = q;
+ count = pw->ptr - q;
+ }
+ if ( skip >= count && skip != 0 )
+ { gs_type1_decrypt(q + 1, p + 1, count,
+ (crypt_state *)&ss->cstate);
+ ss->skip -= count;
+ count = 0;
+ status = 0;
+ }
+ else
+ { gs_type1_decrypt(q + 1, p + 1, skip,
+ (crypt_state *)&ss->cstate);
+ count -= skip;
+ gs_type1_decrypt(q + 1, p + 1 + skip, count,
+ (crypt_state *)&ss->cstate);
+ ss->skip = 0;
+ }
+ pw->ptr = q + count;
+ return status;
+}
+
+#undef ss
+
+/* Stream template */
+/*
+ * The specification of eexec decoding requires that it never read more than
+ * 512 source bytes ahead. The only reliable way to ensure this is to
+ * limit the size of the output buffer to 256. We set it a little smaller
+ * so that it will stay under the limit even after adding min_in_size
+ * for a subsequent filter in a pipeline. Note that we have to specify
+ * a size of at least 128 so that filter_read won't round it up.
+ */
+const stream_template s_exD_template =
+{ &st_exD_state, s_exD_init, s_exD_process, 8, 200
+};
diff --git a/pstoraster/sfile.c b/pstoraster/sfile.c
new file mode 100644
index 000000000..2198cc5ed
--- /dev/null
+++ b/pstoraster/sfile.c
@@ -0,0 +1,246 @@
+/* Copyright (C) 1993, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* sfile.c */
+/* File stream implementation */
+#include "stdio_.h" /* includes std.h */
+#include "memory_.h"
+#include "gdebug.h"
+#include "gpcheck.h"
+#include "stream.h"
+#include "strimpl.h"
+
+/* Forward references for file stream procedures */
+private int
+ s_file_available(P2(stream *, long *)),
+ s_file_read_seek(P2(stream *, long)),
+ s_file_read_close(P1(stream *)),
+ s_file_read_process(P4(stream_state *, stream_cursor_read *,
+ stream_cursor_write *, bool));
+private int
+ s_file_write_seek(P2(stream *, long)),
+ s_file_write_flush(P1(stream *)),
+ s_file_write_close(P1(stream *)),
+ s_file_write_process(P4(stream_state *, stream_cursor_read *,
+ stream_cursor_write *, bool));
+private int
+ s_file_switch(P2(stream *, bool));
+
+/* ------ File streams ------ */
+
+/* Initialize a stream for reading an OS file. */
+void
+sread_file(register stream *s, FILE *file, byte *buf, uint len)
+{ static const stream_procs p =
+ { s_file_available, s_file_read_seek, s_std_read_reset,
+ s_std_read_flush, s_file_read_close, s_file_read_process,
+ s_file_switch
+ };
+ /*
+ * There is no really portable way to test seekability,
+ * but this should work on most systems.
+ * Note that if our probe sets the ferror bit for the stream,
+ * we have to clear it again to avoid trouble later.
+ */
+ int had_error = ferror(file);
+ long curpos = ftell(file);
+ bool seekable = (curpos != -1L && fseek(file, curpos, SEEK_SET) == 0);
+
+ if ( !had_error )
+ clearerr(file);
+ s_std_init(s, buf, len, &p,
+ (seekable ? s_mode_read + s_mode_seek : s_mode_read));
+ if_debug1('s', "[s]read file=0x%lx\n", (ulong)file);
+ s->file = file;
+ s->file_modes = s->modes;
+}
+/* Procedures for reading from a file */
+private int
+s_file_available(register stream *s, long *pl)
+{ *pl = sbufavailable(s);
+ if ( sseekable(s) )
+ { long pos, end;
+ pos = ftell(s->file);
+ if ( fseek(s->file, 0L, SEEK_END) ) return ERRC;
+ end = ftell(s->file);
+ if ( fseek(s->file, pos, SEEK_SET) ) return ERRC;
+ *pl += end - pos;
+ if ( *pl == 0 ) *pl = -1; /* EOF */
+ }
+ else
+ { if ( *pl == 0 && feof(s->file) ) *pl = -1; /* EOF */
+ }
+ return 0;
+}
+private int
+s_file_read_seek(register stream *s, long pos)
+{ uint end = s->srlimit - s->cbuf + 1;
+ long offset = pos - s->position;
+ if ( offset >= 0 && offset <= end )
+ { /* Staying within the same buffer */
+ s->srptr = s->cbuf + offset - 1;
+ return 0;
+ }
+ if ( fseek(s->file, pos, SEEK_SET) != 0 )
+ return ERRC;
+ s->srptr = s->srlimit = s->cbuf - 1;
+ s->end_status = 0;
+ s->position = pos;
+ return 0;
+}
+private int
+s_file_read_close(stream *s)
+{ FILE *file = s->file;
+ if ( file != 0 )
+ { s->file = 0;
+ return fclose(file);
+ }
+ return 0;
+}
+
+/* Initialize a stream for writing an OS file. */
+void
+swrite_file(register stream *s, FILE *file, byte *buf, uint len)
+{ static const stream_procs p =
+ { s_std_noavailable, s_file_write_seek, s_std_write_reset,
+ s_file_write_flush, s_file_write_close, s_file_write_process,
+ s_file_switch
+ };
+ s_std_init(s, buf, len, &p,
+ (file == stdout ? s_mode_write : s_mode_write + s_mode_seek));
+ if_debug1('s', "[s]write file=0x%lx\n", (ulong)file);
+ s->file = file;
+ s->file_modes = s->modes;
+}
+/* Initialize for appending to an OS file. */
+void
+sappend_file(register stream *s, FILE *file, byte *buf, uint len)
+{ swrite_file(s, file, buf, len);
+ s->modes = s_mode_write + s_mode_append; /* no seek */
+ s->file_modes = s->modes;
+ fseek(file, 0L, SEEK_END);
+ s->position = ftell(file);
+}
+/* Procedures for writing on a file */
+private int
+s_file_write_seek(stream *s, long pos)
+{ /* We must flush the buffer to reposition. */
+ int code = sflush(s);
+ if ( code < 0 )
+ return code;
+ if ( fseek(s->file, pos, SEEK_SET) != 0 )
+ return ERRC;
+ s->position = pos;
+ return 0;
+}
+private int
+s_file_write_flush(register stream *s)
+{ int result = s_process_write_buf(s, false);
+ fflush(s->file);
+ return result;
+}
+private int
+s_file_write_close(register stream *s)
+{ s_process_write_buf(s, true);
+ return s_file_read_close(s);
+}
+
+#define ss ((stream *)st)
+
+/* Process a buffer for a file reading stream. */
+/* This is the first stream in the pipeline, so pr is irrelevant. */
+private int
+s_file_read_process(stream_state *st, stream_cursor_read *ignore_pr,
+ stream_cursor_write *pw, bool last)
+{ FILE *file = ss->file;
+ int count = fread(pw->ptr + 1, 1, (uint)(pw->limit - pw->ptr), file);
+ if ( count < 0 )
+ count = 0;
+ pw->ptr += count;
+ process_interrupts();
+ return (ferror(file) ? ERRC : feof(file) ? EOFC : 1);
+}
+
+/* Process a buffer for a file writing stream. */
+/* This is the last stream in the pipeline, so pw is irrelevant. */
+private int
+s_file_write_process(stream_state *st, stream_cursor_read *pr,
+ stream_cursor_write *ignore_pw, bool last)
+{ /* The DEC C library on AXP architectures gives an error on */
+ /* fwrite if the count is zero! */
+ uint count = pr->limit - pr->ptr;
+ if ( count != 0 )
+ { FILE *file = ss->file;
+ int written = fwrite(pr->ptr + 1, 1, count, file);
+ if ( written < 0 )
+ written = 0;
+ pr->ptr += written;
+ process_interrupts();
+ return (ferror(file) ? ERRC : 0);
+ }
+ else
+ { process_interrupts();
+ return 0;
+ }
+}
+
+#undef ss
+
+/* Switch a file stream to reading or writing. */
+private int
+s_file_switch(stream *s, bool writing)
+{ uint modes = s->file_modes;
+ FILE *file = s->file;
+ long pos;
+ if ( writing )
+ { if ( !(s->file_modes & s_mode_write) )
+ return ERRC;
+ pos = stell(s);
+ if_debug2('s', "[s]switch 0x%lx to write at %ld\n",
+ (ulong)s, pos);
+ fseek(file, pos, SEEK_SET);
+ if ( modes & s_mode_append )
+ { sappend_file(s, file, s->cbuf, s->cbsize); /* sets position */
+ }
+ else
+ { swrite_file(s, file, s->cbuf, s->cbsize);
+ s->position = pos;
+ }
+ s->modes = modes;
+ }
+ else
+ { if ( !(s->file_modes & s_mode_read) )
+ return ERRC;
+ pos = stell(s);
+ if_debug2('s', "[s]switch 0x%lx to read at %ld\n",
+ (ulong)s, pos);
+ if ( sflush(s) < 0 )
+ return ERRC;
+ fseek(file, 0L, SEEK_CUR); /* pacify C library */
+ sread_file(s, file, s->cbuf, s->cbsize);
+ s->modes |= modes & s_mode_append; /* don't lose append info */
+ s->position = pos;
+ }
+ s->file_modes = modes;
+ return 0;
+}
diff --git a/pstoraster/sfilter.h b/pstoraster/sfilter.h
new file mode 100644
index 000000000..a98742cd7
--- /dev/null
+++ b/pstoraster/sfilter.h
@@ -0,0 +1,129 @@
+/* Copyright (C) 1993, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* sfilter.h */
+/* Definitions for simple Ghostscript streams */
+/* Requires scommon.h; should require strimpl.h only if any templates */
+/* are referenced, but some compilers always require strimpl.h. */
+
+#include "gstypes.h" /* for gs_[const_]string */
+
+/*
+ * Define the processing states of the simplest Ghostscript streams.
+ * We use abbreviations for the stream names so as not to exceed the
+ * 31-character limit that some compilers put on identifiers.
+ *
+ * The processing state of a stream has three logical sections:
+ * parameters set by the client before the stream is opened,
+ * values computed from the parameters at initialization time,
+ * and values that change dynamically. Unless otherwise indicated,
+ * all structure members change dynamically.
+ */
+
+/* (T)BCPEncode */
+/* (no state) */
+extern const stream_template s_BCPE_template;
+extern const stream_template s_TBCPE_template;
+
+/* (T)BCPDecode */
+typedef struct stream_BCPD_state_s {
+ stream_state_common;
+ /* The client sets the following before initialization. */
+ int (*signal_interrupt)(P1(stream_state *));
+ int (*request_status)(P1(stream_state *));
+ /* The following are updated dynamically. */
+ bool escaped;
+ int matched; /* TBCP only */
+ int copy_count; /* TBCP only */
+ const byte _ds *copy_ptr; /* TBCP only */
+} stream_BCPD_state;
+#define private_st_BCPD_state() /* in sbcp.c */\
+ gs_private_st_simple(st_BCPD_state, stream_BCPD_state, "(T)BCPDecode state")
+extern const stream_template s_BCPD_template;
+extern const stream_template s_TBCPD_template;
+
+/* eexecEncode */
+typedef struct stream_exE_state_s {
+ stream_state_common;
+ /* The following parameters are set by the client. */
+ ushort cstate; /* encryption state */
+} stream_exE_state;
+#define private_st_exE_state() /* in sfilter1.c */\
+ gs_private_st_simple(st_exE_state, stream_exE_state, "eexecEncode state")
+extern const stream_template s_exE_template;
+
+/* eexecDecode */
+typedef struct stream_PFBD_state_s stream_PFBD_state;
+typedef struct stream_exD_state_s {
+ stream_state_common;
+ /* The following parameters are set by the client. */
+ ushort cstate; /* encryption state */
+ stream_PFBD_state *pfb_state; /* state of underlying */
+ /* PFBDecode stream, if any */
+ int binary; /* 1=binary, 0=hex, -1=don't know yet */
+ /* The following change dynamically. */
+ int odd; /* odd digit */
+ long record_left; /* data left in binary record in .PFB file, */
+ /* max_long if not reading a .PFB file */
+ int skip; /* # of decoded bytes to skip */
+} stream_exD_state;
+#define private_st_exD_state() /* in seexec.c */\
+ gs_private_st_ptrs1(st_exD_state, stream_exD_state, "eexecDecode state",\
+ exd_enum_ptrs, exd_reloc_ptrs, pfb_state)
+extern const stream_template s_exD_template;
+
+/* NullEncode/Decode */
+/* (no state) */
+extern const stream_template s_Null_template;
+#define s_NullE_template s_Null_template
+#define s_NullD_template s_Null_template
+
+/* PFBDecode */
+/* The typedef for the state appears under eexecDecode above. */
+/*typedef*/ struct stream_PFBD_state_s {
+ stream_state_common;
+ /* The following parameters are set by the client. */
+ int binary_to_hex;
+ /* The following change dynamically. */
+ int record_type;
+ ulong record_left; /* bytes left in current record */
+} /*stream_PFBD_state*/;
+#define private_st_PFBD_state() /* in sfilter1.c */\
+ gs_private_st_simple(st_PFBD_state, stream_PFBD_state, "PFBDecode state")
+extern const stream_template s_PFBD_template;
+
+/* SubFileDecode */
+typedef struct stream_SFD_state_s {
+ stream_state_common;
+ /* The following parameters are set by the client. */
+ long count; /* # of EODs to scan over */
+ gs_const_string eod;
+ /* The following change dynamically. */
+ uint match; /* # of matched chars not copied to output */
+ uint copy_count; /* # of matched characters left to copy */
+ uint copy_ptr; /* index of next character to copy */
+} stream_SFD_state;
+#define private_st_SFD_state() /* in sfilter1.c */\
+ gs_private_st_composite(st_SFD_state, stream_SFD_state,\
+ "SubFileDecode state", sfd_enum_ptrs, sfd_reloc_ptrs)
+extern const stream_template s_SFD_template;
diff --git a/pstoraster/sfilter1.c b/pstoraster/sfilter1.c
new file mode 100644
index 000000000..cb74318cb
--- /dev/null
+++ b/pstoraster/sfilter1.c
@@ -0,0 +1,292 @@
+/* Copyright (C) 1993, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* sfilter1.c */
+/* Filters included in Level 1 systems: NullEncode/Decode, PFBDecode, */
+/* SubFileDecode, RunLengthEncode/Decode. */
+#include "stdio_.h" /* includes std.h */
+#include "memory_.h"
+#include "strimpl.h"
+#include "sfilter.h"
+
+/* ------ NullEncode/Decode ------ */
+
+/* Process a buffer */
+private int
+s_Null_process(stream_state *st, stream_cursor_read *pr,
+ stream_cursor_write *pw, bool last)
+{ return stream_move(pr, pw);
+}
+
+/* Stream template */
+const stream_template s_Null_template =
+{ &st_stream_state, NULL, s_Null_process, 1, 1
+};
+
+/* ------ PFBDecode ------ */
+
+private_st_PFBD_state();
+
+#define ss ((stream_PFBD_state *)st)
+
+/* Initialize the state */
+private int
+s_PFBD_init(stream_state *st)
+{ ss->record_type = -1;
+ return 0;
+}
+
+/* Process a buffer */
+private int
+s_PFBD_process(stream_state *st, stream_cursor_read *pr,
+ stream_cursor_write *pw, bool last)
+{ register const byte *p = pr->ptr;
+ register byte *q = pw->ptr;
+ int rcount, wcount;
+ int c;
+ int status = 0;
+top: rcount = pr->limit - p;
+ wcount = pw->limit - q;
+ switch ( ss->record_type )
+ {
+ case -1: /* new record */
+ if ( rcount < 2 )
+ goto out;
+ if ( p[1] != 0x80 )
+ goto err;
+ c = p[2];
+ switch ( c )
+ {
+ case 1: case 2:
+ break;
+ case 3:
+ status = EOFC;
+ p += 2;
+ goto out;
+ default:
+ p += 2;
+ goto err;
+ }
+ if ( rcount < 6 )
+ goto out;
+ ss->record_type = c;
+ ss->record_left = p[3] + ((uint)p[4] << 8) +
+ ((ulong)p[5] << 16) +
+ ((ulong)p[6] << 24);
+ p += 6;
+ goto top;
+ case 1: /* text data */
+ /* Translate \r to \n. */
+ { int count = (wcount < rcount ? (status = 1, wcount) : rcount);
+ if ( count > ss->record_left )
+ count = ss->record_left,
+ status = 0;
+ ss->record_left -= count;
+ for ( ; count != 0; count-- )
+ { c = *++p;
+ *++q = (c == '\r' ? '\n' : c);
+ }
+ } break;
+ case 2: /* binary data */
+ if ( ss->binary_to_hex )
+ { /* Translate binary to hex. */
+ int count;
+ register const char _ds *hex_digits =
+ "0123456789abcdef";
+ wcount >>= 1; /* 2 chars per input byte */
+ count = (wcount < rcount ? (status = 1, wcount) : rcount);
+ if ( count > ss->record_left )
+ count = ss->record_left,
+ status = 0;
+ ss->record_left -= count;
+ for ( ; count != 0; count-- )
+ { c = *++p;
+ q[1] = hex_digits[c >> 4];
+ q[2] = hex_digits[c & 0xf];
+ q += 2;
+ }
+ }
+ else
+ { /* Just read binary data. */
+ int count = (wcount < rcount ? (status = 1, wcount) : rcount);
+ if ( count > ss->record_left )
+ count = ss->record_left,
+ status = 0;
+ ss->record_left -= count;
+ memcpy(q + 1, p + 1, count);
+ p += count;
+ q += count;
+ }
+ break;
+ }
+ if ( ss->record_left == 0 )
+ { ss->record_type = -1;
+ goto top;
+ }
+out: pr->ptr = p;
+ pw->ptr = q;
+ return status;
+err: pr->ptr = p;
+ pw->ptr = q;
+ return ERRC;
+}
+
+#undef ss
+
+/* Stream template */
+const stream_template s_PFBD_template =
+{ &st_PFBD_state, s_PFBD_init, s_PFBD_process, 6, 2
+};
+
+/* ------ SubFileDecode ------ */
+
+private_st_SFD_state();
+/* GC procedures */
+private ENUM_PTRS_BEGIN(sfd_enum_ptrs) return 0;
+ ENUM_CONST_STRING_PTR(0, stream_SFD_state, eod);
+} }
+private RELOC_PTRS_BEGIN(sfd_reloc_ptrs) ;
+ RELOC_CONST_STRING_PTR(stream_SFD_state, eod);
+RELOC_PTRS_END
+
+#define ss ((stream_SFD_state *)st)
+
+/* Initialize the stream */
+private int
+s_SFD_init(stream_state *st)
+{ ss->match = 0;
+ ss->copy_count = 0;
+ return 0;
+}
+
+/* Refill the buffer */
+private int
+s_SFD_process(stream_state *st, stream_cursor_read *pr,
+ stream_cursor_write *pw, bool last)
+{ register const byte *p = pr->ptr;
+ register byte *q = pw->ptr;
+ const byte *rlimit = pr->limit;
+ byte *wlimit = pw->limit;
+ int status = 0;
+ if ( ss->eod.size == 0 )
+ { /* Just read, with no EOD pattern. */
+ int rcount = rlimit - p;
+ int wcount = wlimit - q;
+ int count = min(rcount, wcount);
+ if ( ss->count == 0 ) /* no EOD limit */
+ return stream_move(pr, pw);
+ else if ( ss->count > count ) /* not EOD yet */
+ { ss->count -= count;
+ return stream_move(pr, pw);
+ }
+ else
+ { /* We're going to reach EOD. */
+ count = ss->count;
+ memcpy(q + 1, p + 1, count);
+ pr->ptr = p + count;
+ pw->ptr = q + count;
+ return EOFC;
+ }
+ }
+ else
+ { /* Read looking for an EOD pattern. */
+ const byte *pattern = ss->eod.data;
+ uint match = ss->match;
+cp: /* Check whether we're still copying a partial match. */
+ if ( ss->copy_count )
+ { int count = min(wlimit - q, ss->copy_count);
+ memcpy(q + 1, ss->eod.data + ss->copy_ptr, count);
+ ss->copy_count -= count;
+ ss->copy_ptr += count;
+ q += count;
+ if ( ss->copy_count != 0 ) /* hit wlimit */
+ { status = 1;
+ goto xit;
+ }
+ else if ( ss->count < 0 )
+ { status = EOFC;
+ goto xit;
+ }
+ }
+ while ( p < rlimit )
+ { int c = *++p;
+ if ( c == pattern[match] )
+ { if ( ++match == ss->eod.size )
+ { switch ( ss->count )
+ {
+ case 0:
+ status = EOFC;
+ goto xit;
+ case 1:
+ ss->count = -1;
+ break;
+ default:
+ ss->count--;
+ }
+ ss->copy_ptr = 0;
+ ss->copy_count = match;
+ match = 0;
+ goto cp;
+ }
+ continue;
+ }
+ /* No match here, back up to find the longest one. */
+ /* This may be quadratic in string_size, but */
+ /* we don't expect this to be a real problem. */
+ if ( match > 0 )
+ { int end = match;
+ while ( match > 0 )
+ { match--;
+ if ( !memcmp(pattern,
+ pattern + end - match,
+ match)
+ )
+ break;
+ }
+ /* Copy the unmatched initial portion of */
+ /* the EOD string to the output. */
+ p--;
+ ss->copy_ptr = 0;
+ ss->copy_count = end - match;
+ goto cp;
+ }
+ if ( q == wlimit )
+ { p--;
+ status = 1;
+ break;
+ }
+ *++q = c;
+ }
+xit: pr->ptr = p;
+ pw->ptr = q;
+ ss->match = match;
+ }
+ return status;
+}
+
+#undef ss
+
+/* Stream template */
+const stream_template s_SFD_template =
+{ &st_SFD_state, s_SFD_init, s_SFD_process, 1, 1
+};
diff --git a/pstoraster/sfilter2.c b/pstoraster/sfilter2.c
new file mode 100644
index 000000000..c25d1fc28
--- /dev/null
+++ b/pstoraster/sfilter2.c
@@ -0,0 +1,284 @@
+/* Copyright (C) 1991, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* sfilter2.c */
+/* Simple Level 2 filters */
+#include "stdio_.h" /* includes std.h */
+#include "memory_.h"
+#include "strimpl.h"
+#include "sa85x.h"
+#include "sbtx.h"
+#include "sfilter.h"
+#include "scanchar.h"
+
+/* ------ ASCII85Encode ------ */
+
+/* Process a buffer */
+private int
+s_A85E_process(stream_state *st, stream_cursor_read *pr,
+ stream_cursor_write *pw, bool last)
+{ register const byte *p = pr->ptr;
+ register byte *q = pw->ptr;
+ const byte *rlimit = pr->limit;
+ byte *wlimit = pw->limit;
+ int status = 0;
+ int count;
+ for ( ; (count = rlimit - p) >= 4; p += 4 )
+ { ulong word =
+ ((ulong)(((uint)p[1] << 8) + p[2]) << 16) +
+ (((uint)p[3] << 8) + p[4]);
+ if ( word == 0 )
+ { if ( wlimit - q < 2 )
+ { status = 1;
+ break;
+ }
+ *++q = 'z';
+ }
+ else
+ { ulong v4 = word / 85; /* max 85^4 */
+ ulong v3 = v4 / 85; /* max 85^3 */
+ uint v2 = v3 / 85; /* max 85^2 */
+ uint v1 = v2 / 85; /* max 85 */
+
+ if ( wlimit - q < 6 )
+ { status = 1;
+ break;
+ }
+ q[1] = (byte)v1 + '!';
+ q[2] = (byte)(v2 - v1 * 85) + '!';
+ q[3] = (byte)((uint)v3 - v2 * 85) + '!';
+ q[4] = (byte)((uint)v4 - (uint)v3 * 85) + '!';
+ q[5] = (byte)((uint)word - (uint)v4 * 85) + '!';
+ q += 5;
+ }
+ if ( !(count & 60) )
+ *++q = '\n';
+ }
+ /* Check for final partial word. */
+ if ( last && status == 0 && count < 4 )
+ { if ( wlimit - q < (count == 0 ? 2 : count + 3) )
+ status = 1;
+ else
+ { ulong word = 0;
+ ulong divisor = 85L*85*85*85;
+ switch ( count )
+ {
+ case 3:
+ word += (uint)p[3] << 8;
+ case 2:
+ word += (ulong)p[2] << 16;
+ case 1:
+ word += (ulong)p[1] << 24;
+ p += count;
+ while ( count-- >= 0 )
+ { ulong v = word / divisor; /* actually only a byte */
+ *++q = (byte)v + '!';
+ word -= v * divisor;
+ divisor /= 85;
+ }
+ /*case 0:*/
+ }
+ *++q = '~';
+ *++q = '>';
+ }
+ }
+ pr->ptr = p;
+ pw->ptr = q;
+ return status;
+}
+
+/* Stream template */
+const stream_template s_A85E_template =
+{ &st_stream_state, NULL, s_A85E_process, 4, 6
+};
+
+/* ------ ASCII85Decode ------ */
+
+private_st_A85D_state();
+
+#define ss ((stream_A85D_state *)st)
+
+/* Initialize the state */
+private int
+s_A85D_init(stream_state *st)
+{ return s_A85D_init_inline(ss);
+}
+
+/* Process a buffer */
+private int a85d_finish(P3(int, ulong, stream_cursor_write *));
+private int
+s_A85D_process(stream_state *st, stream_cursor_read *pr,
+ stream_cursor_write *pw, bool last)
+{ register const byte *p = pr->ptr;
+ register byte *q = pw->ptr;
+ const byte *rlimit = pr->limit;
+ byte *wlimit = pw->limit;
+ int ccount = ss->odd;
+ ulong word = ss->word;
+ int status = 0;
+ while ( p < rlimit )
+ { int ch = *++p;
+ uint ccode = ch - '!';
+ if ( ccode < 85 ) /* catches ch < '!' as well */
+ { if ( wlimit - q < 4 )
+ { p--;
+ status = 1;
+ break;
+ }
+ word = word * 85 + ccode;
+ if ( ++ccount == 5 )
+ { q[1] = word >> 24;
+ q[2] = (byte)(word >> 16);
+ q[3] = (byte)((uint)word >> 8);
+ q[4] = (byte)word;
+ q += 4;
+ word = 0;
+ ccount = 0;
+ }
+ }
+ else if ( ch == 'z' && ccount == 0 )
+ { if ( wlimit - q < 4 )
+ { p--;
+ status = 1;
+ break;
+ }
+ q[1] = q[2] = q[3] = q[4] = 0,
+ q += 4;
+ }
+ else if ( scan_char_decoder[ch] == ctype_space )
+ ;
+ else if ( ch == '~' )
+ { /* Handle odd bytes. */
+ if ( p == rlimit )
+ { if ( last )
+ status = ERRC;
+ else
+ p--;
+ break;
+ }
+ if ( (int)(wlimit - q) < ccount - 1 )
+ { status = 1;
+ p--;
+ break;
+ }
+ if ( *++p != '>' )
+ { status = ERRC;
+ break;
+ }
+ pw->ptr = q;
+ status = a85d_finish(ccount, word, pw);
+ q = pw->ptr;
+ break;
+ }
+ else /* syntax error or exception */
+ { status = ERRC;
+ break;
+ }
+ }
+ pw->ptr = q;
+ if ( status == 0 && last )
+ { if ( (int)(wlimit - q) < ccount - 1 )
+ status = 1;
+ else
+ status = a85d_finish(ccount, word, pw);
+ }
+ pr->ptr = p;
+ ss->odd = ccount;
+ ss->word = word;
+ return status;
+}
+/* Handle the end of input data. */
+private int
+a85d_finish(int ccount, ulong word, stream_cursor_write *pw)
+{ /* Assume there is enough room in the output buffer! */
+ byte *q = pw->ptr;
+ int status = EOFC;
+ switch ( ccount )
+ {
+ case 0:
+ break;
+ case 1: /* syntax error */
+ status = ERRC;
+ break;
+ case 2: /* 1 odd byte */
+ word = word * (85L*85*85) + 0xffffffL;
+ goto o1;
+ case 3: /* 2 odd bytes */
+ word = word * (85L*85) + 0xffffL;
+ goto o2;
+ case 4: /* 3 odd bytes */
+ word = word * 85 + 0xffL;
+ q[3] = (byte)(word >> 8);
+o2: q[2] = (byte)(word >> 16);
+o1: q[1] = (byte)(word >> 24);
+ q += ccount - 1;
+ pw->ptr = q;
+ }
+ return status;
+}
+
+#undef ss
+
+/* Stream template */
+const stream_template s_A85D_template =
+{ &st_A85D_state, s_A85D_init, s_A85D_process, 2, 4
+};
+
+/* ------ ByteTranslateEncode/Decode ------ */
+
+private_st_BT_state();
+
+#define ss ((stream_BT_state *)st)
+
+/* Process a buffer. Note that the same code serves for both streams. */
+
+private int
+s_BT_process(stream_state *st, stream_cursor_read *pr,
+ stream_cursor_write *pw, bool last)
+{ const byte *p = pr->ptr;
+ byte *q = pw->ptr;
+ uint rcount = pr->limit - p;
+ uint wcount = pw->limit - q;
+ uint count;
+ int status;
+
+ if ( rcount <= wcount )
+ count = rcount, status = 0;
+ else
+ count = wcount, status = 1;
+
+ while ( count-- )
+ *++q = ss->table[*++p];
+
+ pr->ptr = p;
+ pw->ptr = q;
+ return status;
+}
+
+#undef ss
+
+/* Stream template */
+
+const stream_template s_BT_template =
+{ &st_BT_state, NULL, s_BT_process, 1, 1
+};
diff --git a/pstoraster/shc.c b/pstoraster/shc.c
new file mode 100644
index 000000000..df4529bfa
--- /dev/null
+++ b/pstoraster/shc.c
@@ -0,0 +1,74 @@
+/* Copyright (C) 1992, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* shc.c */
+/* Support code for shc.h */
+#include "std.h"
+#include "scommon.h"
+#include "shc.h"
+
+/* ------ Encoding ------ */
+
+/* Empty the 1-word buffer onto the output stream. */
+/* q has already been incremented. */
+void
+hc_put_code_proc(bool reverse_bits, byte *q, uint cw)
+{
+#define cb(n) ((byte)(cw >> (n * 8)))
+ if ( reverse_bits )
+ {
+#if hc_bits_size > 16
+ q[-3] = byte_reverse_bits[cb(3)];
+ q[-2] = byte_reverse_bits[cb(2)];
+#endif
+ q[-1] = byte_reverse_bits[cb(1)];
+ q[0] = byte_reverse_bits[cb(0)];
+ }
+ else
+ {
+#if hc_bits_size > 16
+ q[-3] = cb(3);
+ q[-2] = cb(2);
+#endif
+ q[-1] = cb(1);
+ q[0] = cb(0);
+ }
+#undef cb
+}
+
+/* Put out any final bytes. */
+/* Note that this does a store_state, but not a load_state. */
+byte *
+hc_put_last_bits_proc(stream_hc_state *ss, byte *q, uint bits, int bits_left)
+{ while ( bits_left < hc_bits_size )
+ { byte c = (byte)(bits >> (hc_bits_size - 8));
+ if ( ss->FirstBitLowOrder )
+ c = byte_reverse_bits[c];
+ *++q = c;
+ bits <<= 8;
+ bits_left += 8;
+ }
+ ss->bits = bits;
+ ss->bits_left = bits_left;
+ return q;
+}
diff --git a/pstoraster/shc.h b/pstoraster/shc.h
new file mode 100644
index 000000000..5d0ab25c2
--- /dev/null
+++ b/pstoraster/shc.h
@@ -0,0 +1,248 @@
+/* Copyright (C) 1992, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* shc.h */
+/* Common definitions for filters using Huffman coding */
+/* Requires scommon.h */
+
+#ifndef shc_INCLUDED
+# define shc_INCLUDED
+
+#include "gsbittab.h"
+
+/*
+ * These definitions are valid for code lengths up to 16 bits
+ * and non-negative decoded values up to 15 bits.
+ *
+ * We define 3 different representations of the code: encoding tables,
+ * decoding tables, and a definition table which can be generated easily
+ * from frequency information and which in turn can easily generate
+ * the encoding and decoding tables.
+ *
+ * The definition table has two parts: a list of the number of i-bit
+ * codes for each i >= 1, and the decoded values corresponding to
+ * the code values in increasing lexicographic order (which will also
+ * normally be decreasing code frequency). Calling these two lists
+ * L[1..M] and V[0..N-1] respectively, we have the following invariants:
+ * - 1 <= M <= max_hc_length, N >= 2.
+ * - L[0] = 0.
+ * - for i=1..M, L[i] >= 0.
+ * - sum(i=1..M: L[i]) = N.
+ * - sum(i=1..M: L[i] * 2^-i) = 1.
+ * - V[0..N-1] are a permutation of the integers 0..N-1.
+ */
+#define max_hc_length 16
+typedef struct hc_definition_s {
+ ushort *counts; /* [0..M] */
+ uint num_counts; /* M */
+ ushort *values; /* [0..N-1] */
+ uint num_values; /* N */
+} hc_definition;
+
+/* ------ Common state ------ */
+
+/*
+ * Define the common stream state for Huffman-coded filters.
+ * Invariants when writing:
+ * 0 <= bits_left <= hc_bits_size;
+ * Only the leftmost (hc_bits_size - bits_left) bits of bits
+ * contain valid data.
+ */
+#define stream_hc_state_common\
+ stream_state_common;\
+ /* The client sets the following before initialization. */\
+ bool FirstBitLowOrder;\
+ /* The following are updated dynamically. */\
+ uint bits; /* most recent bits of input or */\
+ /* current bits of output */\
+ int bits_left /* # of valid low bits (input) or */\
+ /* unused low bits (output) in above, */\
+ /* 0 <= bits_left <= 7 */
+typedef struct stream_hc_state_s {
+ stream_hc_state_common;
+} stream_hc_state;
+
+#define hc_bits_size (arch_sizeof_int * 8)
+#define s_hce_init_inline(ss)\
+ ((ss)->bits = 0, (ss)->bits_left = hc_bits_size)
+#define s_hcd_init_inline(ss)\
+ ((ss)->bits = 0, (ss)->bits_left = 0)
+
+/* ------ Encoding tables ------ */
+
+/* Define the structure for the encoding tables. */
+typedef struct hce_code_s {
+ ushort code;
+ ushort code_length;
+} hce_code;
+#define hce_entry(c, len) { c, len }
+
+typedef struct hce_table_s {
+ uint count;
+ hce_code *codes;
+} hce_table;
+
+#define hce_bits_available(n)\
+ (ss->bits_left >= (n) || wlimit - q > ((n) - ss->bits_left - 1) >> 3)
+
+/* ------ Encoding utilities ------ */
+
+/*
+ * Put a code on the output. The client is responsible for ensuring
+ * that q does not exceed pw->limit.
+ */
+
+#ifdef DEBUG
+# define hc_print_value(code, clen)\
+ (gs_debug_c('W') ?\
+ (dprintf2("[W]0x%x,%d\n", code, clen), 0) : 0)
+#else
+# define hc_print_value(code, clen) 0
+#endif
+#define hc_print_code(rp) hc_print_value((rp)->code, (rp)->code_length)
+
+/* Declare variables that hold the encoder state. */
+#define hce_declare_state\
+ register uint bits;\
+ register int bits_left
+
+/* Load the state from the stream. */
+/* Free variables: ss, bits, bits_left. */
+#define hce_load_state()\
+ bits = ss->bits, bits_left = ss->bits_left
+
+/* Store the state back in the stream. */
+/* Free variables: ss, bits, bits_left. */
+#define hce_store_state()\
+ ss->bits = bits, ss->bits_left = bits_left
+
+/* Put a code on the stream. */
+void hc_put_code_proc(P3(bool, byte *, uint));
+#define hc_put_value(ss, q, code, clen)\
+ (hc_print_value(code, clen),\
+ ((bits_left -= (clen)) >= 0 ?\
+ (bits += (code) << bits_left) :\
+ (hc_put_code_proc((ss)->FirstBitLowOrder,\
+ q += hc_bits_size >> 3,\
+ (bits + ((code) >> -bits_left))),\
+ bits = (code) << (bits_left += hc_bits_size))))
+#define hc_put_code(ss, q, cp)\
+ hc_put_value(ss, q, (cp)->code, (cp)->code_length)
+
+/*
+ * Force out the final bits to the output.
+ * Note that this does a store_state, but not a load_state.
+ */
+byte *hc_put_last_bits_proc(P4(stream_hc_state *, byte *, uint, int));
+#define hc_put_last_bits(ss, q)\
+ hc_put_last_bits_proc(ss, q, bits, bits_left)
+
+/* ------ Decoding tables ------ */
+
+/*
+ * Define the structure for the decoding tables.
+ * First-level nodes are either leaves, which have
+ * value = decoded value
+ * code_length <= initial_bits
+ * or non-leaves, which have
+ * value = the index of a sub-table
+ * code_length = initial_bits + the number of additional dispatch bits
+ * Second-level nodes are always leaves, with
+ * code_length = the actual number of bits in the code - initial_bits.
+ */
+
+typedef struct hcd_code_s {
+ short value;
+ ushort code_length;
+} hcd_code;
+
+typedef struct hcd_table_s {
+ uint count;
+ uint initial_bits;
+ hcd_code *codes;
+} hcd_table;
+
+/* Declare variables that hold the decoder state. */
+#define hcd_declare_state\
+ register const byte *p;\
+ const byte *rlimit;\
+ uint bits;\
+ int bits_left
+
+/* Load the state from the stream. */
+/* Free variables: pr, ss, p, rlimit, bits, bits_left. */
+#define hcd_load_state()\
+ p = pr->ptr,\
+ rlimit = pr->limit,\
+ bits = ss->bits,\
+ bits_left = ss->bits_left
+
+/* Store the state back in the stream. */
+/* Put back any complete bytes into the input buffer. */
+/* Free variables: pr, ss, p, bits, bits_left. */
+#define hcd_store_state()\
+ pr->ptr = p -= (bits_left >> 3),\
+ ss->bits = bits >>= (bits_left & ~7),\
+ ss->bits_left = bits_left &= 7
+
+/* Macros to get blocks of bits from the input stream. */
+/* Invariants: 0 <= bits_left <= bits_size; */
+/* bits [bits_left-1..0] contain valid data. */
+
+#define hcd_bits_available(n)\
+ (bits_left >= (n) || rlimit - p > ((n) - bits_left - 1) >> 3)
+/* For hcd_ensure_bits, n must not be greater than 8. */
+#define hcd_ensure_bits(n, outl)\
+ if ( bits_left < n ) hcd_more_bits(outl)
+
+/* Load more bits into the buffer. */
+#define hcd_more_bits_1(outl)\
+ { int c;\
+ if ( p < rlimit ) c = *++p;\
+ else goto outl;\
+ if ( ss->FirstBitLowOrder ) c = byte_reverse_bits[c];\
+ bits = (bits << 8) + c, bits_left += 8;\
+ }
+#if hc_bits_size == 16
+# define hcd_more_bits(outl) hcd_more_bits_1(outl)
+#else /* hc_bits_size >= 32 */
+# define hcd_more_bits(outl)\
+ { if ( rlimit - p < 3 ) hcd_more_bits_1(outl)\
+ else\
+ { if ( ss->FirstBitLowOrder )\
+ bits = (bits << 24) + ((uint)byte_reverse_bits[p[1]] << 16) + ((uint)byte_reverse_bits[p[2]] << 8) + byte_reverse_bits[p[3]];\
+ else\
+ bits = (bits << 24) + ((uint)p[1] << 16) + ((uint)p[2] << 8) + p[3];\
+ bits_left += 24, p += 3;\
+ }\
+ }
+#endif
+
+#define hcd_peek_bits(n) ((bits >> (bits_left - (n))) & ((1 << (n)) - 1))
+
+#define hcd_peek_var_bits(n)\
+ ((bits >> (bits_left - (n))) & byte_right_mask[n])
+
+#define hcd_skip_bits(n) (bits_left -= (n))
+
+#endif /* shc_INCLUDED */
diff --git a/pstoraster/shcgen.c b/pstoraster/shcgen.c
new file mode 100644
index 000000000..dd0902649
--- /dev/null
+++ b/pstoraster/shcgen.c
@@ -0,0 +1,467 @@
+/* Copyright (C) 1994 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* shcgen.c */
+/* Generate (bounded) Huffman code definitions from frequencies, */
+/* and tables from definitions. */
+#include "memory_.h"
+#include "stdio_.h"
+#include <stdlib.h> /* for qsort */
+#include "gdebug.h"
+#include "gserror.h"
+#include "gserrors.h"
+#include "gsmemory.h"
+#include "scommon.h"
+#include "shc.h"
+#include "shcgen.h"
+
+/* ------ Frequency -> definition procedure ------ */
+
+/* Define a node for the Huffman code tree. */
+typedef struct count_node_s count_node;
+struct count_node_s {
+ long freq; /* frequency of value */
+ uint value; /* data value being encoded */
+ uint code_length; /* length of Huffman code */
+ count_node *next; /* next node in freq-sorted list */
+ count_node *left; /* left child in tree (smaller code_length) */
+ count_node *right; /* right child in tree (greater code_length) */
+};
+
+#ifdef DEBUG
+# define debug_print_nodes(nodes, n, tag, lengths)\
+ if ( gs_debug_c('W') ) print_nodes_proc(nodes, n, tag, lengths);
+private void
+print_nodes_proc(const count_node *nodes, int n, const char *tag, int lengths)
+{ int i;
+
+ dprintf1("[w]---------------- %s ----------------\n", tag);
+ for ( i = 0; i < n; ++i )
+ dprintf7("[w]node %d: f=%ld v=%d len=%d N=%d L=%d R=%d\n",
+ i, nodes[i].freq, nodes[i].value, nodes[i].code_length,
+ (nodes[i].next == 0 ? -1 : (int)(nodes[i].next - nodes)),
+ (nodes[i].left == 0 ? -1 : (int)(nodes[i].left - nodes)),
+ (nodes[i].right == 0 ? -1 : (int)(nodes[i].right - nodes)));
+ for ( i = lengths; i > 0; )
+ { int j = i;
+ int len = nodes[--j].code_length;
+
+ while ( j > 0 && nodes[j-1].code_length == len )
+ --j;
+ dprintf2("[w]%d codes of length %d\n", i - j, len);
+ i = j;
+ }
+}
+#else
+# define debug_print_nodes(nodes, n, tag, lengths) DO_NOTHING
+#endif
+
+/* Node comparison procedures for sorting. */
+#define pn1 ((const count_node *)p1)
+#define pn2 ((const count_node *)p2)
+/* Sort by decreasing frequency. */
+private int
+compare_freqs(const void *p1, const void *p2)
+{ long diff = pn2->freq - pn1->freq;
+ return (diff < 0 ? -1 : diff > 0 ? 1 : 0);
+}
+/* Sort by increasing code length, and secondarily by decreasing frequency. */
+private int
+compare_code_lengths(const void *p1, const void *p2)
+{ int diff = pn1->code_length - pn2->code_length;
+ return (diff < 0 ? -1 : diff > 0 ? 1 : compare_freqs(p1, p2));
+}
+/* Sort by increasing code value. */
+private int
+compare_values(const void *p1, const void *p2)
+{ return (pn1->value < pn2->value ? -1 :
+ pn1->value > pn2->value ? 1 : 0);
+}
+#undef pn1
+#undef pn2
+
+/* Adjust code lengths so that none of them exceeds max_length. */
+/* We break this out just to help organize the code; it's only called */
+/* from one place in hc_compute. */
+private void
+hc_limit_code_lengths(count_node *nodes, uint num_values, int max_length)
+{ int needed; /* # of max_length codes we need to free up */
+ count_node *longest = nodes + num_values;
+
+ { /* Compute the number of additional max_length codes */
+ /* we need to make available. */
+ int length = longest[-1].code_length;
+ int next_length;
+ int avail = 0;
+ while ( (next_length = longest[-1].code_length) > max_length )
+ { avail >>= length - next_length;
+ length = next_length;
+ (--longest)->code_length = max_length;
+ ++avail;
+ }
+ needed = (nodes + num_values - longest) -
+ (avail >>= (length - max_length));
+ if_debug2('W', "[w]avail=%d, needed=%d\n",
+ avail, needed);
+ }
+ /* Skip over all max_length codes. */
+ while ( longest[-1].code_length == max_length )
+ --longest;
+
+ /*
+ * To make available a code of length N, suppose that the next
+ * shortest used code is of length M.
+ * We take the lowest-frequency code of length M and change it
+ * to M+1; we then have to compensate by reducing the length of
+ * some of the highest-frequency codes of length N, as follows:
+ * M new lengths for codes of length N
+ * --- -----------
+ * N-1 (none)
+ * N-2 N-1
+ * <N-2 M+2, M+2, N-1
+ * In the present situation, N = max_length.
+ */
+ for ( ; needed > 0; --needed )
+ { /* longest points to the first code of length max_length. */
+ /* Since codes are sorted by increasing code length, */
+ /* longest-1 is the desired code of length M. */
+ int M1 = ++(longest[-1].code_length);
+ switch ( max_length - M1 )
+ {
+ case 0: /* M == N-1 */
+ --longest;
+ break;
+ case 1: /* M == N-2 */
+ longest++->code_length = M1;
+ break;
+ default:
+ longest->code_length = M1 + 1;
+ longest[1].code_length = M1 + 1;
+ longest[2].code_length--;
+ longest += 3;
+ }
+ }
+}
+
+/* Compute an optimal Huffman code from an input data set. */
+/* The client must have set all the elements of *def. */
+int
+hc_compute(hc_definition *def, const long *freqs, gs_memory_t *mem)
+{ uint num_values = def->num_values;
+ count_node *nodes =
+ (count_node *)gs_alloc_byte_array(mem, num_values * 2 - 1,
+ sizeof(count_node), "hc_compute");
+ int i;
+ count_node *lowest;
+ count_node *comb;
+
+ if ( nodes == 0 )
+ return_error(gs_error_VMerror);
+
+ /* Create leaf nodes for the input data. */
+ for ( i = 0; i < num_values; ++i )
+ nodes[i].freq = freqs[i], nodes[i].value = i;
+
+ /* Create a list sorted by increasing frequency. */
+ /* Also initialize the tree structure. */
+ qsort(nodes, num_values, sizeof(count_node), compare_freqs);
+ for ( i = 0; i < num_values; ++i )
+ nodes[i].next = &nodes[i - 1],
+ nodes[i].code_length = 0,
+ nodes[i].left = nodes[i].right = 0;
+ nodes[0].next = 0;
+ debug_print_nodes(nodes, num_values, "after sort", 0);
+
+ /* Construct the Huffman code tree. */
+ for ( lowest = &nodes[num_values - 1], comb = &nodes[num_values]; ;
+ ++comb
+ )
+ { count_node *pn1 = lowest;
+ count_node *pn2 = pn1->next;
+ long freq = pn1->freq + pn2->freq;
+
+ /* Create a parent for the two lowest-frequency nodes. */
+ lowest = pn2->next;
+ comb->freq = freq;
+ if ( pn1->code_length <= pn2->code_length )
+ comb->left = pn1, comb->right = pn2,
+ comb->code_length = pn2->code_length + 1;
+ else
+ comb->left = pn2, comb->right = pn1,
+ comb->code_length = pn1->code_length + 1;
+ if ( lowest == 0 ) /* no nodes left to combine */
+ break;
+ /* Insert comb in the sorted list. */
+ if ( freq < lowest->freq )
+ comb->next = lowest, lowest = comb;
+ else
+ { count_node *here = lowest;
+ while ( here->next != 0 && freq >= here->next->freq )
+ here = here->next;
+ comb->next = here->next;
+ here->next = comb;
+ }
+ }
+
+ /* comb (i.e., &nodes[num_values * 2 - 2] is the root of the tree. */
+ /* Note that the left and right children of an interior node */
+ /* were constructed before, and therefore have lower indices */
+ /* in the nodes array than, the parent node. Thus we can assign */
+ /* the code lengths (node depths) in a single descending-order */
+ /* sweep. */
+ comb++->code_length = 0;
+ while ( comb > nodes + num_values )
+ { --comb;
+ comb->left->code_length = comb->right->code_length =
+ comb->code_length + 1;
+ }
+ debug_print_nodes(nodes, num_values * 2 - 1, "after combine", 0);
+
+ /* Sort the leaves again by code length. */
+ qsort(nodes, num_values, sizeof(count_node), compare_code_lengths);
+ debug_print_nodes(nodes, num_values, "after re-sort", num_values);
+
+ /* Limit the code length to def->num_counts. */
+ hc_limit_code_lengths(nodes, num_values, def->num_counts);
+ debug_print_nodes(nodes, num_values, "after limit", num_values);
+
+ /* Sort within each code length by increasing code value. */
+ /* This doesn't affect data compression, but it makes */
+ /* the code definition itself compress better using our */
+ /* incremental encoding. */
+ for ( i = num_values; i > 0; )
+ { int j = i;
+ int len = nodes[--j].code_length;
+ while ( j > 0 && nodes[j-1].code_length == len )
+ --j;
+ qsort(&nodes[j], i - j, sizeof(count_node), compare_values);
+ i = j;
+ }
+
+ /* Extract the definition from the nodes. */
+ memset(def->counts, 0, sizeof(*def->counts) * (def->num_counts + 1));
+ for ( i = 0; i < num_values; ++i )
+ { def->values[i] = nodes[i].value;
+ def->counts[nodes[i].code_length]++;
+ }
+
+ /* All done, release working storage. */
+ gs_free_object(mem, nodes, "hc_compute");
+ return 0;
+}
+
+/* ------ Byte string <-> definition procedures ------ */
+
+/*
+ * We define a compressed representation for (well-behaved) definitions
+ * as a byte string. A "well-behaved" definition is one where if
+ * code values A and B have the same code length and A < B,
+ * A precedes B in the values table of the definition, and hence
+ * A's encoding lexicographically precedes B's.
+ *
+ * The successive bytes in the compressed string give the code lengths for
+ * runs of decoded values, in the form nnnnllll where nnnn is the number of
+ * consecutive values -1 and llll is the code length -1.
+ */
+
+/* Convert a definition to a byte string. */
+/* The caller must provide the byte string, of length def->num_values. */
+/* Assume (do not check) that the definition is well-behaved. */
+/* Return the actual length of the string. */
+int
+hc_bytes_from_definition(byte *dbytes, const hc_definition *def)
+{ int i, j;
+ byte *bp = dbytes;
+ const byte *lp = dbytes;
+ const byte *end = dbytes + def->num_values;
+ const ushort *values = def->values;
+
+ /* Temporarily use the output string as a map from */
+ /* values to code lengths. */
+ for ( i = 1; i <= def->num_counts; i++ )
+ for ( j = 0; j < def->counts[i]; j++ )
+ bp[*values++] = i;
+
+ /* Now construct the actual string. */
+ while ( lp < end )
+ { const byte *vp;
+ byte len = *lp;
+ for ( vp = lp + 1; vp < end && vp < lp + 16 && *vp == len; )
+ vp++;
+ *bp++ = ((vp - lp - 1) << 4) + (len - 1);
+ lp = vp;
+ }
+
+ return bp - dbytes;
+}
+
+/* Extract num_counts and num_values from a byte string. */
+void
+hc_sizes_from_bytes(hc_definition *def, const byte *dbytes, int num_bytes)
+{ uint num_counts = 0, num_values = 0;
+ int i;
+
+ for ( i = 0; i < num_bytes; i++ )
+ { int n = (dbytes[i] >> 4) + 1;
+ int l = (dbytes[i] & 15) + 1;
+ if ( l > num_counts )
+ num_counts = l;
+ num_values += n;
+ }
+ def->num_counts = num_counts;
+ def->num_values = num_values;
+}
+
+/* Convert a byte string back to a definition. */
+/* The caller must initialize *def, including allocating counts and values. */
+void
+hc_definition_from_bytes(hc_definition *def, const byte *dbytes)
+{ int v, i;
+ ushort counts[max_hc_length + 1];
+
+ /* Make a first pass to set the counts for each code length. */
+ memset(counts, 0, sizeof(counts[0]) * (def->num_counts + 1));
+ for ( i = 0, v = 0; v < def->num_values; i++ )
+ { int n = (dbytes[i] >> 4) + 1;
+ int l = (dbytes[i] & 15) + 1;
+ counts[l] += n;
+ v += n;
+ }
+
+ /* Now fill in the definition. */
+ memcpy(def->counts, counts, sizeof(counts[0]) * (def->num_counts + 1));
+ for ( i = 1, v = 0; i <= def->num_counts; i++ )
+ { uint prev = counts[i];
+ counts[i] = v;
+ v += prev;
+ }
+ for ( i = 0, v = 0; v < def->num_values; i++ )
+ { int n = (dbytes[i] >> 4) + 1;
+ int l = (dbytes[i] & 15) + 1;
+ int j;
+ for ( j = 0; j < n; n++ )
+ def->values[counts[l]++] = v++;
+ }
+}
+
+/* ------ Definition -> table procedures ------ */
+
+/* Generate the encoding table from the definition. */
+/* The size of the encode array is def->num_values. */
+void
+hc_make_encoding(hce_code *encode, const hc_definition *def)
+{ uint next = 0;
+ const ushort *pvalue = def->values;
+ uint i, k;
+
+ for ( i = 1; i <= def->num_counts; i++ )
+ { for ( k = 0; k < def->counts[i]; k++, pvalue++, next++ )
+ { hce_code *pce = encode + *pvalue;
+ pce->code = next;
+ pce->code_length = i;
+ }
+ next <<= 1;
+ }
+}
+
+/* We decode in two steps, first indexing into a table with */
+/* a fixed number of bits from the source, and then indexing into */
+/* an auxiliary table if necessary. (See shc.h for details.) */
+
+/* Calculate the size of the decoding table. */
+uint
+hc_sizeof_decoding(const hc_definition *def, int initial_bits)
+{ uint size = 1 << initial_bits;
+ uint carry = 0, mask = ~1;
+ uint i;
+
+ for ( i = initial_bits + 1; i <= def->num_counts;
+ i++, carry <<= 1, mask <<= 1
+ )
+ { carry += def->counts[i];
+ size += carry & mask;
+ carry &= ~mask;
+ }
+ return size;
+}
+
+/* Generate the decoding tables. */
+void
+hc_make_decoding(hcd_code *decode, const hc_definition *def,
+ int initial_bits)
+{ /* Make entries for single-dispatch codes. */
+ { hcd_code *pcd = decode;
+ const ushort *pvalue = def->values;
+ uint i, k, d;
+
+ for ( i = 0; i <= initial_bits; i++ )
+ { for ( k = 0; k < def->counts[i]; k++, pvalue++ )
+ { for ( d = 1 << (initial_bits - i); d > 0;
+ d--, pcd++
+ )
+ pcd->value = *pvalue,
+ pcd->code_length = i;
+ }
+ }
+ }
+ /* Make entries for two-dispatch codes. */
+ /* By working backward, we can do this more easily */
+ /* in a single pass. */
+ { uint dsize = hc_sizeof_decoding(def, initial_bits);
+ hcd_code *pcd = decode + (1 << initial_bits);
+ hcd_code *pcd2 = decode + dsize;
+ const ushort *pvalue = def->values + def->num_values;
+ uint entries_left = 0, slots_left = 0, mult_shift = 0;
+ uint i = def->num_counts + 1, j;
+
+ for ( ; ; )
+ { if ( slots_left == 0 )
+ { if ( entries_left != 0 )
+ { slots_left = 1 << (i - initial_bits);
+ mult_shift = 0;
+ continue;
+ }
+ if ( --i <= initial_bits )
+ break;
+ entries_left = def->counts[i];
+ continue;
+ }
+ if ( entries_left == 0 )
+ { entries_left = def->counts[--i];
+ mult_shift++;
+ continue;
+ }
+ --entries_left, --pvalue;
+ for ( j = 1 << mult_shift; j > 0; j-- )
+ { --pcd2;
+ pcd2->value = *pvalue;
+ pcd2->code_length = i - initial_bits;
+ }
+ if ( (slots_left -= 1 << mult_shift) == 0 )
+ { --pcd;
+ pcd->value = pcd2 - decode;
+ pcd->code_length = i + mult_shift;
+ }
+ }
+ }
+}
diff --git a/pstoraster/shcgen.h b/pstoraster/shcgen.h
new file mode 100644
index 000000000..dcc9f351b
--- /dev/null
+++ b/pstoraster/shcgen.h
@@ -0,0 +1,55 @@
+/* Copyright (C) 1994 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* shcgen.h */
+/* Interface to shcgen.c */
+/* Requires shc.h */
+
+/* Compute an optimal Huffman code from an input data set. */
+/* The client must have set all the elements of *def. */
+/* The definition is guaranteed to be well-behaved. */
+int hc_compute(P3(hc_definition *def, const long *freqs, gs_memory_t *mem));
+
+/* Convert a definition to a byte string. */
+/* The caller must provide the byte string, of length def->num_values. */
+/* Assume (do not check) that the definition is well-behaved. */
+/* Return the actual length of the string. */
+int hc_bytes_from_definition(P2(byte *dbytes, const hc_definition *def));
+
+/* Extract num_counts and num_values from a byte string. */
+void hc_sizes_from_bytes(P3(hc_definition *def, const byte *dbytes, int num_bytes));
+
+/* Convert a byte string back to a definition. */
+/* The caller must initialize *def, including allocating counts and values. */
+void hc_definition_from_bytes(P2(hc_definition *def, const byte *dbytes));
+
+/* Generate the encoding table from the definition. */
+/* The size of the encode array is def->num_values. */
+void hc_make_encoding(P2(hce_code *encode, const hc_definition *def));
+
+/* Calculate the size of the decoding table. */
+uint hc_sizeof_decoding(P2(const hc_definition *def, int initial_bits));
+
+/* Generate the decoding tables. */
+void hc_make_decoding(P3(hcd_code *decode, const hc_definition *def,
+ int initial_bits));
diff --git a/pstoraster/siscale.c b/pstoraster/siscale.c
new file mode 100644
index 000000000..9b0af9078
--- /dev/null
+++ b/pstoraster/siscale.c
@@ -0,0 +1,486 @@
+/* Copyright (C) 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* siscale.c */
+/* Image scaling filters */
+#include "math_.h"
+#include "memory_.h"
+#include "stdio_.h"
+#include <assert.h>
+#include "strimpl.h"
+#include "siscale.h"
+
+/*
+ * Image scaling code is based on public domain code from
+ * Graphics Gems III (pp. 414-424), Academic Press, 1992.
+ */
+
+/* ---------------- ImageScaleEncode/Decode ---------------- */
+
+public_st_IScale_state(); /* public so clients can allocate */
+
+/* ------ Digital filter definition ------ */
+
+/* Mitchell filter definition */
+#define Mitchell_support 2.0
+#define B (1.0 / 3.0)
+#define C (1.0 / 3.0)
+private double
+Mitchell_filter(double t)
+{ double t2 = t * t;
+
+ if ( t < 0 )
+ t = -t;
+
+ if ( t < 1 )
+ return
+ ((12 - 9 * B - 6 * C) * (t * t2) +
+ (-18 + 12 * B + 6 * C) * t2 +
+ (6 - 2 * B)) / 6;
+ else if ( t < 2 )
+ return
+ ((-1 * B - 6 * C) * (t * t2) +
+ (6 * B + 30 * C) * t2 +
+ (-12 * B - 48 * C) * t +
+ (8 * B + 24 * C)) / 6;
+ else
+ return 0;
+}
+
+#define filter_support Mitchell_support
+#define filter_proc Mitchell_filter
+#define fproc(t) filter_proc(t)
+#define fWidthIn filter_support
+
+/*
+ * The environment provides the following definitions:
+ * typedef PixelTmp, PixelTmp2
+ * double fproc(double t)
+ * double fWidthIn
+ * PixelTmp {min,max,unit}PixelTmp
+ */
+#define CLAMP(v, mn, mx)\
+ (v < mn ? mn : v > mx ? mx : v)
+
+/* ------ Auxiliary procedures ------ */
+
+/* Define the minimum scale. */
+#define min_scale ((fWidthIn * 2) / (max_support - 1.01))
+
+/* Calculate the support for a given scale. */
+/* The value is always in the range 1 .. max_support. */
+private int
+contrib_pixels(double scale)
+{ return (int)(fWidthIn / (scale >= 1.0 ? 1.0 : max(scale, min_scale))
+ * 2 + 1);
+}
+
+/* Pre-calculate filter contributions for a row or a column. */
+/* Return the highest input pixel index used. */
+private int
+calculate_contrib(
+ /* Return weight list parameters in contrib[0 .. size-1]. */
+ CLIST *contrib,
+ /* Store weights in items[0 .. contrib_pixels(scale)*size-1]. */
+ /* (Less space than this may actually be needed.) */
+ CONTRIB *items,
+ /* The output image is scaled by 'scale' relative to the input. */
+ double scale,
+ /* Start generating weights for input pixel 'input_index'. */
+ int input_index,
+ /* Generate 'size' weight lists. */
+ int size,
+ /* Limit pixel indices to 'limit', for clamping at the edges */
+ /* of the image. */
+ int limit,
+ /* Wrap pixel indices modulo 'modulus'. */
+ int modulus,
+ /* Successive pixel values are 'stride' distance apart -- */
+ /* normally, the number of color components. */
+ int stride,
+ /* The unit of output is 'rescale_factor' times the unit of input. */
+ double rescale_factor
+ )
+{
+ double scaled_factor = scale_PixelWeight(rescale_factor);
+ double WidthIn, fscale;
+ bool squeeze;
+ int npixels;
+ int i, j;
+ int last_index = -1;
+
+ if ( scale < 1.0 )
+ { double clamped_scale = max(scale, min_scale);
+ WidthIn = fWidthIn / clamped_scale;
+ fscale = 1.0 / clamped_scale;
+ squeeze = true;
+ }
+ else
+ { WidthIn = fWidthIn;
+ fscale = 1.0;
+ squeeze = false;
+ }
+ npixels = (int)(WidthIn * 2 + 1);
+
+ for ( i = 0; i < size; ++i )
+ { double center = (input_index + i) / scale;
+ int left = (int)ceil(center - WidthIn);
+ int right = (int)floor(center + WidthIn);
+#define clamp_pixel(j)\
+ (j < 0 ? -j : j >= limit ? (limit - j) + limit - 1 : j)
+ int lmin = (left < 0 ? 0 : left);
+ int lmax = (left < 0 ? -left : left);
+ int rmin =
+ (right >= limit ? (limit - right) + limit - 1 : right);
+ int rmax = (right >= limit ? limit - 1 : right);
+ int first_pixel = min(lmin, rmin);
+ int last_pixel = max(lmax, rmax);
+ CONTRIB *p;
+
+ if ( last_pixel > last_index )
+ last_index = last_pixel;
+ contrib[i].first_pixel = (first_pixel % modulus) * stride;
+ contrib[i].n = last_pixel - first_pixel + 1;
+ contrib[i].index = i * npixels;
+ p = items + contrib[i].index;
+ for ( j = 0; j < npixels; ++j )
+ p[j].weight = 0;
+ if ( squeeze )
+ { for ( j = left; j <= right; ++j )
+ { double weight =
+ fproc((center - j) / fscale) / fscale;
+ int n = clamp_pixel(j);
+ int k = n - first_pixel;
+ p[k].weight +=
+ (PixelWeight)(weight * scaled_factor);
+ }
+ }
+ else
+ { for ( j = left; j <= right; ++j )
+ { double weight = fproc(center - j);
+ int n = clamp_pixel(j);
+ int k = n - first_pixel;
+ p[k].weight +=
+ (PixelWeight)(weight * scaled_factor);
+ }
+ }
+ }
+ return last_index;
+}
+
+
+/* Apply filter to zoom horizontally from src to tmp. */
+private void
+zoom_x(PixelTmp *tmp, const void /*PixelIn*/ *src, int sizeofPixelIn,
+ int tmp_width, int WidthIn, int Colors, const CLIST *contrib,
+ const CONTRIB *items)
+{ int c, i;
+ for ( c = 0; c < Colors; ++c )
+ { PixelTmp *tp = tmp + c;
+ const CLIST *clp = contrib;
+#define zoom_x_loop(PixelIn, PixelIn2)\
+ const PixelIn *raster = (const PixelIn *)src + c;\
+ for ( i = 0; i < tmp_width; tp += Colors, ++clp, ++i )\
+ { AccumTmp weight = 0;\
+ { int j = clp->n;\
+ const PixelIn *pp = raster + clp->first_pixel;\
+ const CONTRIB *cp = items + clp->index;\
+ switch ( Colors )\
+ {\
+ case 1:\
+ for ( ; j > 0; pp += 1, ++cp, --j )\
+ weight += *pp * cp->weight;\
+ break;\
+ case 3:\
+ for ( ; j > 0; pp += 3, ++cp, --j )\
+ weight += *pp * cp->weight;\
+ break;\
+ default:\
+ for ( ; j > 0; pp += Colors, ++cp, --j )\
+ weight += *pp * cp->weight;\
+ }\
+ }\
+ { PixelIn2 pixel = unscale_AccumTmp(weight);\
+ *tp =\
+ (PixelTmp)CLAMP(pixel, minPixelTmp, maxPixelTmp);\
+ }\
+ }
+
+ if ( sizeofPixelIn == 1 )
+ { zoom_x_loop(byte, int)
+ }
+ else /* sizeofPixelIn == 2 */
+ {
+#if arch_ints_are_short
+ zoom_x_loop(bits16, long)
+#else
+ zoom_x_loop(bits16, int)
+#endif
+ }
+ }
+}
+
+/* Apply filter to zoom vertically from tmp to dst. */
+/* This is simpler because we can treat all columns identically */
+/* without regard to the number of samples per pixel. */
+private void
+zoom_y(void /*PixelOut*/ *dst, int sizeofPixelOut, uint MaxValueOut,
+ const PixelTmp *tmp, int WidthOut, int tmp_width,
+ int Colors, const CLIST *contrib, const CONTRIB *items)
+{ int kn = WidthOut * Colors;
+ int cn = contrib->n;
+ int first_pixel = contrib->first_pixel;
+ const CONTRIB *cbp = items + contrib->index;
+ int kc;
+ PixelTmp2 max_weight = MaxValueOut;
+
+#define zoom_y_loop(PixelOut)\
+ for ( kc = 0; kc < kn; ++kc )\
+ { AccumTmp weight = 0;\
+ { const PixelTmp *pp = &tmp[kc + first_pixel];\
+ int j = cn;\
+ const CONTRIB *cp = cbp;\
+ for ( ; j > 0; pp += kn, ++cp, --j )\
+ weight += *pp * cp->weight;\
+ }\
+ { PixelTmp2 pixel = unscale_AccumTmp(weight);\
+ ((PixelOut *)dst)[kc] =\
+ (PixelOut)CLAMP(pixel, 0, max_weight);\
+ }\
+ }
+
+ if ( sizeofPixelOut == 1 )
+ { zoom_y_loop(byte)
+ }
+ else /* sizeofPixelOut == 2 */
+ { zoom_y_loop(bits16)
+ }
+}
+
+/* ------ Stream implementation ------ */
+
+#define tmp_width WidthOut
+#define tmp_height HeightIn
+
+/* Forward references */
+private void s_IScale_release(P1(stream_state *st));
+
+/* Calculate the weights for an output row. */
+private void
+calculate_dst_contrib(stream_IScale_state *ss, int y)
+{ uint row_size = ss->WidthOut * ss->Colors;
+ int last_index =
+ calculate_contrib(&ss->dst_next_list, ss->dst_items, ss->yscale,
+ y, 1, ss->HeightIn, max_support, row_size,
+ (double)ss->MaxValueOut / unitPixelTmp);
+ int first_index_mod = ss->dst_next_list.first_pixel / row_size;
+
+ ss->dst_last_index = last_index;
+ last_index %= max_support;
+ if ( last_index < first_index_mod )
+ { /* Shuffle the indices to account for wraparound. */
+ CONTRIB shuffle[max_support];
+ int i;
+
+ for ( i = 0; i < max_support; ++i )
+ shuffle[i].weight =
+ (i <= last_index ?
+ ss->dst_items[i + max_support - first_index_mod].weight :
+ i >= first_index_mod ?
+ ss->dst_items[i - first_index_mod].weight :
+ 0);
+ memcpy(ss->dst_items, shuffle, max_support * sizeof(CONTRIB));
+ ss->dst_next_list.n = max_support;
+ ss->dst_next_list.first_pixel = 0;
+ }
+}
+
+#define ss ((stream_IScale_state *)st)
+
+/* Initialize the filter. */
+private int
+s_IScale_init(stream_state *st)
+{ gs_memory_t *mem = ss->memory;
+
+ ss->sizeofPixelIn = ss->BitsPerComponentIn / 8;
+ ss->sizeofPixelOut = ss->BitsPerComponentOut / 8;
+ ss->xscale = (double)ss->WidthOut / (double)ss->WidthIn;
+ ss->yscale = (double)ss->HeightOut / (double)ss->HeightIn;
+
+ ss->src_y = 0;
+ ss->src_size = ss->WidthIn * ss->sizeofPixelIn * ss->Colors;
+ ss->src_offset = 0;
+ ss->dst_y = 0;
+ ss->dst_size = ss->WidthOut * ss->sizeofPixelOut * ss->Colors;
+ ss->dst_offset = 0;
+
+ /* create intermediate image to hold horizontal zoom */
+ ss->tmp = (PixelTmp *)gs_alloc_byte_array(mem,
+ min(ss->tmp_height, max_support),
+ ss->tmp_width * ss->Colors * sizeof(PixelTmp),
+ "image_scale tmp");
+ ss->contrib = (CLIST *)gs_alloc_byte_array(mem,
+ max(ss->WidthOut, ss->HeightOut),
+ sizeof(CLIST), "image_scale contrib");
+ ss->items = (CONTRIB *)gs_alloc_byte_array(mem,
+ contrib_pixels(ss->xscale) * ss->WidthOut,
+ sizeof(CONTRIB), "image_scale contrib[*]");
+ /* Allocate buffers for 1 row of source and destination. */
+ ss->dst = gs_alloc_byte_array(mem, ss->WidthOut * ss->Colors,
+ ss->sizeofPixelOut, "image_scale dst");
+ ss->src = gs_alloc_byte_array(mem, ss->WidthIn * ss->Colors,
+ ss->sizeofPixelIn, "image_scale src");
+ if ( ss->tmp == 0 || ss->contrib == 0 || ss->items == 0 ||
+ ss->dst == 0 || ss->src == 0
+ )
+ { s_IScale_release(st);
+ return ERRC; /****** WRONG ******/
+ }
+
+ /* Pre-calculate filter contributions for a row. */
+ calculate_contrib(ss->contrib, ss->items, ss->xscale,
+ 0, ss->WidthOut, ss->WidthIn, ss->WidthIn,
+ ss->Colors, (double)unitPixelTmp / ss->MaxValueIn);
+
+ /* Prepare the weights for the first output row. */
+ calculate_dst_contrib(ss, 0);
+
+ return 0;
+
+}
+
+/* Process a buffer. Note that this handles Encode and Decode identically. */
+private int
+s_IScale_process(stream_state *st, stream_cursor_read *pr,
+ stream_cursor_write *pw, bool last)
+{
+ /* Check whether we need to deliver any output. */
+
+top: while ( ss->src_y > ss->dst_last_index )
+ { /* We have enough horizontally scaled temporary rows */
+ /* to generate a vertically scaled output row. */
+ uint wleft = pw->limit - pw->ptr;
+ if ( ss->dst_y == ss->HeightOut )
+ return EOFC;
+ if ( wleft == 0 )
+ return 1;
+ if ( ss->dst_offset == 0 )
+ { byte *row;
+ if ( wleft >= ss->dst_size )
+ { /* We can scale the row directly into the output. */
+ row = pw->ptr + 1;
+ pw->ptr += ss->dst_size;
+ }
+ else
+ { /* We'll have to buffer the row. */
+ row = ss->dst;
+ }
+ /* Apply filter to zoom vertically from tmp to dst. */
+ zoom_y(row, ss->sizeofPixelOut, ss->MaxValueOut, ss->tmp,
+ ss->WidthOut, ss->tmp_width, ss->Colors,
+ &ss->dst_next_list, ss->dst_items);
+ if ( row != (byte *)ss->dst ) /* no buffering */
+ goto adv;
+ }
+ { /* We're delivering a buffered output row. */
+ uint wcount = ss->dst_size - ss->dst_offset;
+ uint ncopy = min(wleft, wcount);
+ memcpy(pw->ptr + 1, (byte *)ss->dst + ss->dst_offset, ncopy);
+ pw->ptr += ncopy;
+ ss->dst_offset += ncopy;
+ if ( ncopy != wcount )
+ return 1;
+ ss->dst_offset = 0;
+ }
+ /* Advance to the next output row. */
+adv: ++(ss->dst_y);
+ if ( ss->dst_y != ss->HeightOut )
+ calculate_dst_contrib(ss, ss->dst_y);
+ }
+
+ /* Read input data and scale horizontally into tmp. */
+
+#ifdef DEBUG
+ assert(ss->src_y < ss->HeightIn);
+#endif
+ { uint rleft = pr->limit - pr->ptr;
+ uint rcount = ss->src_size - ss->src_offset;
+ if ( rleft == 0 )
+ return 0; /* need more input */
+ if ( rleft >= rcount )
+ { /* We're going to fill up a row. */
+ const byte *row;
+ if ( ss->src_offset == 0 )
+ { /* We have a complete row. Read the data */
+ /* directly from the input. */
+ row = pr->ptr + 1;
+ }
+ else
+ { /* We're buffering a row in src. */
+ row = ss->src;
+ memcpy((byte *)ss->src + ss->src_offset, pr->ptr + 1,
+ rcount);
+ ss->src_offset = 0;
+ }
+ /* Apply filter to zoom horizontally from src to tmp. */
+ zoom_x(ss->tmp + (ss->src_y % max_support) *
+ ss->tmp_width * ss->Colors, row,
+ ss->sizeofPixelIn, ss->tmp_width, ss->WidthIn,
+ ss->Colors, ss->contrib, ss->items);
+ pr->ptr += rcount;
+ ++(ss->src_y);
+ goto top;
+ }
+ else
+ { /* We don't have a complete row. Copy data to src buffer. */
+ memcpy((byte *)ss->src + ss->src_offset, pr->ptr + 1, rleft);
+ ss->src_offset += rleft;
+ pr->ptr += rleft;
+ return 0;
+ }
+ }
+}
+
+/* Release the filter's storage. */
+private void
+s_IScale_release(stream_state *st)
+{ gs_memory_t *mem = ss->memory;
+ gs_free_object(mem, (void *)ss->src, "image_scale src"); /* no longer const */
+ ss->src = 0;
+ gs_free_object(mem, ss->dst, "image_scale dst");
+ ss->dst = 0;
+ gs_free_object(mem, ss->items, "image_scale contrib[*]");
+ ss->items = 0;
+ gs_free_object(mem, ss->contrib, "image_scale contrib");
+ ss->contrib = 0;
+ gs_free_object(mem, ss->tmp, "image_scale tmp");
+ ss->tmp = 0;
+}
+
+#undef ss
+
+/* Stream template */
+const stream_template s_IScale_template =
+{ &st_IScale_state, s_IScale_init, s_IScale_process, 1, 1,
+ s_IScale_release
+};
diff --git a/pstoraster/siscale.h b/pstoraster/siscale.h
new file mode 100644
index 000000000..58edba73b
--- /dev/null
+++ b/pstoraster/siscale.h
@@ -0,0 +1,136 @@
+/* Copyright (C) 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* siscale.h */
+/* Image scaling filter state definition */
+/* Requires strimpl.h */
+#include "gconfigv.h"
+
+/* Define whether to accumulate pixels in fixed or floating point. */
+#if USE_FPU <= 0
+
+ /* Accumulate pixels in fixed point. */
+
+typedef int PixelWeight;
+# if arch_ints_are_short
+typedef long AccumTmp;
+# else
+typedef int AccumTmp;
+# endif
+#define num_weight_bits\
+ ((sizeof(AccumTmp) - maxSizeofPixel) * 8 - (log2_max_support + 1))
+#define scale_PixelWeight(factor) ((int)((factor) * (1 << num_weight_bits)))
+#define unscale_AccumTmp(atemp) arith_rshift(atemp, num_weight_bits)
+
+#else /* USE_FPU > 0 */
+
+ /* Accumulate pixels in floating point. */
+
+typedef float PixelWeight;
+typedef double AccumTmp;
+#define scale_PixelWeight(factor) (factor)
+#define unscale_AccumTmp(atemp) ((int)(atemp))
+
+#endif /* USE_FPU */
+
+/* Input values */
+/*typedef byte PixelIn;*/ /* see sizeofPixelIn below */
+/*#define MaxValueIn 255*/ /* see MaxValueIn below */
+
+/* Temporary intermediate values */
+typedef byte PixelTmp;
+typedef int PixelTmp2; /* extra width for clamping sum */
+#define minPixelTmp 0
+#define maxPixelTmp 255
+#define unitPixelTmp 255
+
+/* Max of all pixel sizes */
+#define maxSizeofPixel 2
+
+/* Output values */
+/*typedef byte PixelOut;*/ /* see sizeofPixelOut below */
+/*#define MaxValueOut 255*/ /* see MaxValueOut below */
+
+/*
+ * The 'support' S of the digital filter is the value such that the filter
+ * is guaranteed to be zero for all arguments outside the range [-S..S].
+ * We artificially limit the support so that we can put an upper bound
+ * on the time required to compute an output value and on the amount of
+ * storage required for the X-filtered input data; this also allows us
+ * to use pre-scaled fixed-point values for the weights if we wish.
+ *
+ * 8x8 pixels should be enough for any reasonable application....
+ */
+#define log2_max_support 3
+#define max_support (1 << log2_max_support)
+
+/* Auxiliary structures. */
+
+typedef struct {
+ PixelWeight weight; /* float or scaled fraction */
+} CONTRIB;
+
+typedef struct {
+ int index; /* index of first element in list of */
+ /* contributors */
+ int n; /* number of contributors */
+ /* (not multiplied by stride) */
+ int first_pixel; /* offset of first value in source data */
+} CLIST;
+
+/* ImageScaleEncode / ImageScaleDecode */
+typedef struct stream_IScale_state_s {
+ stream_state_common;
+ /* The client sets the following before initialization. */
+ int Colors; /* any number >= 1 */
+ int BitsPerComponentIn; /* bits per input value, 8 or 16 */
+ uint MaxValueIn; /* max value of input component */
+ int WidthIn, HeightIn;
+ int BitsPerComponentOut; /* bits per output value, 8 or 16 */
+ uint MaxValueOut; /* max value of output component */
+ int WidthOut, HeightOut;
+ /* The init procedure sets the following. */
+ int sizeofPixelIn; /* bytes per input value, 1 or 2 */
+ int sizeofPixelOut; /* bytes per output value, 1 or 2 */
+ double xscale, yscale;
+ void /*PixelIn*/ *src;
+ void /*PixelOut*/ *dst;
+ PixelTmp *tmp;
+ CLIST *contrib;
+ CONTRIB *items;
+ /* The following are updated dynamically. */
+ int src_y;
+ uint src_offset, src_size;
+ int dst_y;
+ uint dst_offset, dst_size;
+ CLIST dst_next_list; /* for next output value */
+ int dst_last_index; /* highest index used in list */
+ CONTRIB dst_items[max_support]; /* ditto */
+} stream_IScale_state;
+extern_st(st_IScale_state); /* so clients can allocate */
+#define public_st_IScale_state() /* in siscale.c */\
+ gs_public_st_ptrs5(st_IScale_state, stream_IScale_state,\
+ "ImageScaleEncode/Decode state",\
+ iscale_state_enum_ptrs, iscale_state_reloc_ptrs,\
+ dst, src, tmp, contrib, items)
+extern const stream_template s_IScale_template;
diff --git a/pstoraster/sjpeg.h b/pstoraster/sjpeg.h
new file mode 100644
index 000000000..a4b01c7fc
--- /dev/null
+++ b/pstoraster/sjpeg.h
@@ -0,0 +1,72 @@
+/* Copyright (C) 1994 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* sjpeg.h */
+/* Definitions for Ghostscript's IJG library interface routines */
+/* Requires sdct.h, jpeg/jpeglib.h */
+
+/*
+ * Each routine gs_jpeg_xxx is equivalent to the IJG entry point jpeg_xxx,
+ * except that
+ * (a) it takes a pointer to stream_DCT_state instead of just the IJG
+ * jpeg_(de)compress_data struct;
+ * (b) it catches any error exit from the IJG code and converts it into
+ * an error return value per Ghostscript custom. A negative return
+ * value is an error code, except for gs_jpeg_alloc_xxx which return
+ * NULL (indicating e_VMerror).
+ */
+
+/* Common to encode/decode */
+
+void gs_jpeg_error_setup (P1(stream_DCT_state *st));
+int gs_jpeg_log_error (P1(stream_DCT_state *st));
+JQUANT_TBL * gs_jpeg_alloc_quant_table (P1(stream_DCT_state *st));
+JHUFF_TBL * gs_jpeg_alloc_huff_table (P1(stream_DCT_state *st));
+int gs_jpeg_destroy (P1(stream_DCT_state *st));
+
+/* Encode */
+
+int gs_jpeg_create_compress (P1(stream_DCT_state *st));
+int gs_jpeg_set_defaults (P1(stream_DCT_state *st));
+int gs_jpeg_set_colorspace (P2(stream_DCT_state *st,
+ J_COLOR_SPACE colorspace));
+int gs_jpeg_set_linear_quality (P3(stream_DCT_state *st,
+ int scale_factor,
+ boolean force_baseline));
+int gs_jpeg_start_compress (P2(stream_DCT_state *st,
+ boolean write_all_tables));
+int gs_jpeg_write_scanlines (P3(stream_DCT_state *st,
+ JSAMPARRAY scanlines,
+ int num_lines));
+int gs_jpeg_finish_compress (P1(stream_DCT_state *st));
+
+/* Decode */
+
+int gs_jpeg_create_decompress (P1(stream_DCT_state *st));
+int gs_jpeg_read_header (P2(stream_DCT_state *st,
+ boolean require_image));
+int gs_jpeg_start_decompress (P1(stream_DCT_state *st));
+int gs_jpeg_read_scanlines (P3(stream_DCT_state *st,
+ JSAMPARRAY scanlines,
+ int max_lines));
+int gs_jpeg_finish_decompress (P1(stream_DCT_state *st));
diff --git a/pstoraster/sjpegc.c b/pstoraster/sjpegc.c
new file mode 100644
index 000000000..eba8794a4
--- /dev/null
+++ b/pstoraster/sjpegc.c
@@ -0,0 +1,274 @@
+/*
+ Copyright 1993-1999 by Easy Software Products.
+ Copyright (C) 1994 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+#include <config.h>
+#ifdef HAVE_LIBJPEG
+/* sjpegc.c */
+/* Interface routines for IJG code, common to encode/decode. */
+#include "stdio_.h"
+#include "string_.h"
+#include "jpeglib.h"
+#include "jerror.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "strimpl.h"
+#include "sdct.h"
+#include "sjpeg.h"
+
+/* gs_jpeg_message_table() is kept in a separate file for arcane reasons.
+ * See sjpegerr.c.
+ */
+const char * const * gs_jpeg_message_table (P1(void));
+
+/*
+ * Error handling routines (these replace corresponding IJG routines from
+ * jpeg/jerror.c). These are used for both compression and decompression.
+ * We assume
+ * offset_of(jpeg_compress_data, cinfo)==offset_of(jpeg_decompress_data, dinfo)
+ */
+
+private void
+gs_jpeg_error_exit (j_common_ptr cinfo)
+{ jpeg_stream_data *jcomdp =
+ (jpeg_stream_data *)((char *)cinfo -
+ offset_of(jpeg_compress_data, cinfo));
+ longjmp(jcomdp->exit_jmpbuf, 1);
+}
+
+private void
+gs_jpeg_emit_message (j_common_ptr cinfo, int msg_level)
+{ if ( msg_level < 0 )
+ { /* GS policy is to ignore IJG warnings when Picky=0,
+ * treat them as errors when Picky=1.
+ */
+ jpeg_stream_data *jcomdp =
+ (jpeg_stream_data *)((char *)cinfo -
+ offset_of(jpeg_compress_data, cinfo));
+ if ( jcomdp->Picky )
+ gs_jpeg_error_exit(cinfo);
+ }
+ /* Trace messages are always ignored. */
+}
+
+/*
+ * This is an exact copy of format_message from jpeg/jerror.c.
+ * We do not use jerror.c in Ghostscript, so we have to duplicate this routine.
+ */
+
+private void
+gs_jpeg_format_message (j_common_ptr cinfo, char * buffer)
+{
+ struct jpeg_error_mgr * err = cinfo->err;
+ int msg_code = err->msg_code;
+ const char * msgtext = NULL;
+ const char * msgptr;
+ char ch;
+ boolean isstring;
+
+ /* Look up message string in proper table */
+ if (msg_code > 0 && msg_code <= err->last_jpeg_message) {
+ msgtext = err->jpeg_message_table[msg_code];
+ } else if (err->addon_message_table != NULL &&
+ msg_code >= err->first_addon_message &&
+ msg_code <= err->last_addon_message) {
+ msgtext = err->addon_message_table[msg_code - err->first_addon_message];
+ }
+
+ /* Defend against bogus message number */
+ if (msgtext == NULL) {
+ err->msg_parm.i[0] = msg_code;
+ msgtext = err->jpeg_message_table[0];
+ }
+
+ /* Check for string parameter, as indicated by %s in the message text */
+ isstring = FALSE;
+ msgptr = msgtext;
+ while ((ch = *msgptr++) != '\0') {
+ if (ch == '%') {
+ if (*msgptr == 's') isstring = TRUE;
+ break;
+ }
+ }
+
+ /* Format the message into the passed buffer */
+ if (isstring)
+ sprintf(buffer, msgtext, err->msg_parm.s);
+ else
+ sprintf(buffer, msgtext,
+ err->msg_parm.i[0], err->msg_parm.i[1],
+ err->msg_parm.i[2], err->msg_parm.i[3],
+ err->msg_parm.i[4], err->msg_parm.i[5],
+ err->msg_parm.i[6], err->msg_parm.i[7]);
+}
+
+/* And this is an exact copy of another routine from jpeg/jerror.c. */
+
+private void
+gs_jpeg_reset_error_mgr (j_common_ptr cinfo)
+{
+ cinfo->err->num_warnings = 0;
+ /* trace_level is not reset since it is an application-supplied parameter */
+ cinfo->err->msg_code = 0; /* may be useful as a flag for "no error" */
+}
+
+/*
+ * This routine initializes the error manager fields in the JPEG object.
+ * It is based on jpeg_std_error from jpeg/jerror.c.
+ */
+
+void
+gs_jpeg_error_setup (stream_DCT_state *st)
+{
+ struct jpeg_error_mgr * err = &st->data.common->err;
+
+ err->error_exit = gs_jpeg_error_exit;
+ err->emit_message = gs_jpeg_emit_message;
+ /* We need not set the output_message field since gs_jpeg_emit_message
+ * doesn't call it, and the IJG library never calls output_message directly.
+ * Setting the format_message field isn't strictly necessary either,
+ * since gs_jpeg_log_error calls gs_jpeg_format_message directly.
+ */
+ err->format_message = gs_jpeg_format_message;
+ err->reset_error_mgr = gs_jpeg_reset_error_mgr;
+
+ err->trace_level = 0; /* default = no tracing */
+ err->num_warnings = 0; /* no warnings emitted yet */
+ err->msg_code = 0; /* may be useful as a flag for "no error" */
+
+ /* Initialize message table pointers */
+ err->jpeg_message_table = gs_jpeg_message_table();
+ err->last_jpeg_message = (int) JMSG_LASTMSGCODE - 1;
+
+ err->addon_message_table = NULL;
+ err->first_addon_message = 0; /* for safety */
+ err->last_addon_message = 0;
+
+ st->data.compress->cinfo.err = err; /* works for decompress case too */
+}
+
+/* Stuff the IJG error message into errorinfo after an error exit. */
+
+int
+gs_jpeg_log_error (stream_DCT_state *st)
+{ j_common_ptr cinfo = (j_common_ptr) &st->data.compress->cinfo;
+ char buffer[JMSG_LENGTH_MAX];
+ /* Format the error message */
+ gs_jpeg_format_message(cinfo, buffer);
+ (*st->report_error)((stream_state *)st, buffer);
+ return gs_error_ioerror; /* caller will do return_error() */
+}
+
+
+/*
+ * Interface routines. This layer of routines exists solely to limit
+ * side-effects from using setjmp.
+ */
+
+
+JQUANT_TBL *
+gs_jpeg_alloc_quant_table (stream_DCT_state *st)
+{ if (setjmp(st->data.common->exit_jmpbuf))
+ { gs_jpeg_log_error(st);
+ return NULL;
+ }
+ return jpeg_alloc_quant_table((j_common_ptr)
+ &st->data.compress->cinfo);
+}
+
+JHUFF_TBL *
+gs_jpeg_alloc_huff_table (stream_DCT_state *st)
+{ if (setjmp(st->data.common->exit_jmpbuf))
+ { gs_jpeg_log_error(st);
+ return NULL;
+ }
+ return jpeg_alloc_huff_table((j_common_ptr)
+ &st->data.compress->cinfo);
+}
+
+int
+gs_jpeg_destroy (stream_DCT_state *st)
+{ if (setjmp(st->data.common->exit_jmpbuf))
+ return_error(gs_jpeg_log_error(st));
+ jpeg_destroy((j_common_ptr) &st->data.compress->cinfo);
+ return 0;
+}
+
+#if 0
+/*
+ * These routines replace the low-level memory manager of the IJG library.
+ * They pass malloc/free calls to the Ghostscript memory manager.
+ * Note we do not need these to be declared in any GS header file.
+ */
+
+void *
+jpeg_get_small (j_common_ptr cinfo, size_t sizeofobject)
+{
+ return gs_malloc(1, sizeofobject, "JPEG small internal data allocation");
+}
+
+void
+jpeg_free_small (j_common_ptr cinfo, void * object, size_t sizeofobject)
+{
+ gs_free(object, 1, sizeofobject, "Freeing JPEG small internal data");
+}
+
+void FAR *
+jpeg_get_large (j_common_ptr cinfo, size_t sizeofobject)
+{
+ return gs_malloc(1, sizeofobject, "JPEG large internal data allocation");
+}
+
+void
+jpeg_free_large (j_common_ptr cinfo, void FAR * object, size_t sizeofobject)
+{
+ gs_free(object, 1, sizeofobject, "Freeing JPEG large internal data");
+}
+
+long
+jpeg_mem_available (j_common_ptr cinfo, long min_bytes_needed,
+ long max_bytes_needed, long already_allocated)
+{
+ return max_bytes_needed;
+}
+
+void
+jpeg_open_backing_store (j_common_ptr cinfo, void * info,
+ long total_bytes_needed)
+{
+ ERREXIT(cinfo, JERR_NO_BACKING_STORE);
+}
+
+long
+jpeg_mem_init (j_common_ptr cinfo)
+{
+ return 0; /* just set max_memory_to_use to 0 */
+}
+
+void
+jpeg_mem_term (j_common_ptr cinfo)
+{
+ /* no work */
+}
+#endif /* 0 */
+#endif
diff --git a/pstoraster/sjpegd.c b/pstoraster/sjpegd.c
new file mode 100644
index 000000000..21f50f8f3
--- /dev/null
+++ b/pstoraster/sjpegd.c
@@ -0,0 +1,91 @@
+/* Copyright (C) 1994 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+#include <config.h>
+#ifdef HAVE_LIBJPEG
+/* sjpegd.c */
+/* Interface routines for IJG decoding code. */
+#include "stdio_.h"
+#include "string_.h"
+#include "jpeglib.h"
+#include "jerror.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "strimpl.h"
+#include "sdct.h"
+#include "sjpeg.h"
+
+/*
+ * Interface routines. This layer of routines exists solely to limit
+ * side-effects from using setjmp.
+ */
+
+int
+gs_jpeg_create_decompress (stream_DCT_state *st)
+{ /* Initialize error handling */
+ gs_jpeg_error_setup(st);
+ /* Establish the setjmp return context for gs_jpeg_error_exit to use. */
+ if (setjmp(st->data.common->exit_jmpbuf))
+ return_error(gs_jpeg_log_error(st));
+
+ jpeg_create_decompress(&st->data.decompress->dinfo);
+ return 0;
+}
+
+int
+gs_jpeg_read_header (stream_DCT_state *st,
+ boolean require_image)
+{ if (setjmp(st->data.common->exit_jmpbuf))
+ return_error(gs_jpeg_log_error(st));
+ return jpeg_read_header(&st->data.decompress->dinfo, require_image);
+}
+
+int
+gs_jpeg_start_decompress (stream_DCT_state *st)
+{ if (setjmp(st->data.common->exit_jmpbuf))
+ return_error(gs_jpeg_log_error(st));
+#if JPEG_LIB_VERSION > 55
+ return (int) jpeg_start_decompress(&st->data.decompress->dinfo);
+#else
+ /* in IJG version 5, jpeg_start_decompress had no return value */
+ jpeg_start_decompress(&st->data.decompress->dinfo);
+ return 1;
+#endif
+}
+
+int
+gs_jpeg_read_scanlines (stream_DCT_state *st,
+ JSAMPARRAY scanlines,
+ int max_lines)
+{ if (setjmp(st->data.common->exit_jmpbuf))
+ return_error(gs_jpeg_log_error(st));
+ return (int) jpeg_read_scanlines(&st->data.decompress->dinfo,
+ scanlines, (JDIMENSION) max_lines);
+}
+
+int
+gs_jpeg_finish_decompress (stream_DCT_state *st)
+{ if (setjmp(st->data.common->exit_jmpbuf))
+ return_error(gs_jpeg_log_error(st));
+ return (int) jpeg_finish_decompress(&st->data.decompress->dinfo);
+}
+#endif
diff --git a/pstoraster/sjpege.c b/pstoraster/sjpege.c
new file mode 100644
index 000000000..b14c8ecf7
--- /dev/null
+++ b/pstoraster/sjpege.c
@@ -0,0 +1,107 @@
+/* Copyright (C) 1994 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+#include <config.h>
+#ifdef HAVE_LIBJPEG
+/* sjpege.c */
+/* Interface routines for IJG encoding code. */
+#include "stdio_.h"
+#include "string_.h"
+#include "jpeglib.h"
+#include "jerror.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "strimpl.h"
+#include "sdct.h"
+#include "sjpeg.h"
+
+/*
+ * Interface routines. This layer of routines exists solely to limit
+ * side-effects from using setjmp.
+ */
+
+int
+gs_jpeg_create_compress (stream_DCT_state *st)
+{ /* Initialize error handling */
+ gs_jpeg_error_setup(st);
+ /* Establish the setjmp return context for gs_jpeg_error_exit to use. */
+ if (setjmp(st->data.common->exit_jmpbuf))
+ return_error(gs_jpeg_log_error(st));
+
+ jpeg_create_compress(&st->data.compress->cinfo);
+ return 0;
+}
+
+int
+gs_jpeg_set_defaults (stream_DCT_state *st)
+{ if (setjmp(st->data.common->exit_jmpbuf))
+ return_error(gs_jpeg_log_error(st));
+ jpeg_set_defaults(&st->data.compress->cinfo);
+ return 0;
+}
+
+int
+gs_jpeg_set_colorspace (stream_DCT_state *st,
+ J_COLOR_SPACE colorspace)
+{ if (setjmp(st->data.common->exit_jmpbuf))
+ return_error(gs_jpeg_log_error(st));
+ jpeg_set_colorspace(&st->data.compress->cinfo, colorspace);
+ return 0;
+}
+
+int
+gs_jpeg_set_linear_quality (stream_DCT_state *st,
+ int scale_factor, boolean force_baseline)
+{ if (setjmp(st->data.common->exit_jmpbuf))
+ return_error(gs_jpeg_log_error(st));
+ jpeg_set_linear_quality(&st->data.compress->cinfo,
+ scale_factor, force_baseline);
+ return 0;
+}
+
+int
+gs_jpeg_start_compress (stream_DCT_state *st,
+ boolean write_all_tables)
+{ if (setjmp(st->data.common->exit_jmpbuf))
+ return_error(gs_jpeg_log_error(st));
+ jpeg_start_compress(&st->data.compress->cinfo, write_all_tables);
+ return 0;
+}
+
+int
+gs_jpeg_write_scanlines (stream_DCT_state *st,
+ JSAMPARRAY scanlines,
+ int num_lines)
+{ if (setjmp(st->data.common->exit_jmpbuf))
+ return_error(gs_jpeg_log_error(st));
+ return (int) jpeg_write_scanlines(&st->data.compress->cinfo,
+ scanlines, (JDIMENSION) num_lines);
+}
+
+int
+gs_jpeg_finish_compress (stream_DCT_state *st)
+{ if (setjmp(st->data.common->exit_jmpbuf))
+ return_error(gs_jpeg_log_error(st));
+ jpeg_finish_compress(&st->data.compress->cinfo);
+ return 0;
+}
+#endif
diff --git a/pstoraster/sjpegerr.c b/pstoraster/sjpegerr.c
new file mode 100644
index 000000000..01c52dbb7
--- /dev/null
+++ b/pstoraster/sjpegerr.c
@@ -0,0 +1,97 @@
+/* Copyright (C) 1994, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+#include <config.h>
+#ifdef HAVE_LIBJPEG
+/* sjpegerr.c */
+/* IJG error message table for Ghostscript. */
+#include "stdio_.h"
+
+/*
+ * MRS - these are normally found in jversion.h, however it seems that
+ * many vendors (Red Hat, SGI, etc.) do not distribute it...
+ *
+ * The following definitions come from the 6B distribution...
+ */
+
+#define JVERSION "6b 27-Mar-1998"
+#define JCOPYRIGHT "Copyright (C) 1998, Thomas G. Lane"
+
+#include "jpeglib.h"
+
+/*
+ * This file exists solely to hold the rather large IJG error message string
+ * table (now about 4K, and likely to grow in future releases). The table
+ * is large enough that we don't want it to be in the default data segment
+ * in a 16-bit executable.
+ *
+ * In IJG version 5 and earlier, under Borland C, this is accomplished simply
+ * by compiling this one file in "huge" memory model rather than "large".
+ * The string constants will then go into a private far data segment.
+ * In less brain-damaged architectures, this file is simply compiled normally,
+ * and we pay only the price of one extra function call.
+ *
+ * In IJG version 5a and later, under Borland C, this is accomplished by making
+ * each string be a separate variable that's explicitly declared "far".
+ * (What a crock...)
+ *
+ * This must be a separate file to avoid duplicate-symbol errors, since we
+ * use the IJG message code names as variables rather than as enum constants.
+ */
+
+#if JPEG_LIB_VERSION <= 50 /**************** ****************/
+
+#include "jerror.h" /* get error codes */
+#define JMAKE_MSG_TABLE
+#include "jerror.h" /* create message string table */
+
+#define jpeg_std_message_table jpeg_message_table
+
+#else /* JPEG_LIB_VERSION >= 51 */ /**************** ****************/
+
+/* Create a static const char[] variable for each message string. */
+
+#define JMESSAGE(code,string) static const char far_data code[] = string;
+
+#include "jerror.h"
+
+/* Now build an array of pointers to same. */
+
+#define JMESSAGE(code,string) code ,
+
+static const char far_data * const far_data jpeg_std_message_table[] = {
+#include "jerror.h"
+ NULL
+};
+
+#endif /* JPEG_LIB_VERSION */ /**************** ****************/
+
+/*
+ * Return a pointer to the message table.
+ * It is unsafe to do much more than this within the "huge" environment.
+ */
+
+const char * const *
+gs_jpeg_message_table (void)
+{ return jpeg_std_message_table;
+}
+#endif
diff --git a/pstoraster/slzwc.c b/pstoraster/slzwc.c
new file mode 100644
index 000000000..9e1edcaf1
--- /dev/null
+++ b/pstoraster/slzwc.c
@@ -0,0 +1,47 @@
+/* Copyright (C) 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* slzwc.c */
+/* Code common to LZW encoding and decoding streams */
+#include "std.h"
+#include "strimpl.h"
+#include "slzwx.h"
+
+/* Define the structure for the GC. */
+public_st_LZW_state();
+
+#define ss ((stream_LZW_state *)st)
+
+/* Set defaults */
+void
+s_LZW_set_defaults(stream_state *st)
+{ s_LZW_set_defaults_inline(ss);
+}
+
+/* Release a LZW filter. */
+void
+s_LZW_release(stream_state *st)
+{ gs_free_object(st->memory, ss->table.decode, "LZW(close)");
+}
+
+#undef ss
diff --git a/pstoraster/slzwce.c b/pstoraster/slzwce.c
new file mode 100644
index 000000000..6583559c6
--- /dev/null
+++ b/pstoraster/slzwce.c
@@ -0,0 +1,160 @@
+/* Copyright (C) 1994, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* slzwce.c */
+/* Simple encoder compatible with LZW decoding filter */
+#include "stdio_.h" /* includes std.h */
+#include "gdebug.h"
+#include "strimpl.h"
+#include "slzwx.h"
+
+/* ------ Alternate LZWEncode filter implementation ------ */
+
+/*
+
+The encoded data stream produced by this implementation of the LZWEncode
+filter consists of a sequence of 9-bit data elements. These elements are
+packed into bytes in big-endian order, e.g. the elements
+
+ 100000000 001100001
+
+occurring at the very beginning of the data stream would be packed into
+bytes as
+
+ 10000000 00011000 01......
+
+The first bit of each data element is a control bit. If the control bit is
+0, the remaining 8 bits of the data element are a data byte. If the control
+bit is 1, the remaining 8 bits of the data element define a control
+function:
+
+ 1 00000000 synchronization mark, see below
+ 1 00000001 end of data
+ 1 xxxxxxxx not used (all other values)
+
+The synchronization mark occurs at the beginning of the data stream, and at
+least once every 254 data bytes thereafter.
+
+This format is derived from basic principles of data encoding (the use of a
+separate flag bit to distinguish out-of-band control information from data
+per se, and the use of a periodic synchronization mark to help verify the
+validity of a data stream); it has no relationship to data compression. It
+is, however, compatible with LZW decompressors. It produces output that is
+approximately 9/8 times the size of the input.
+
+*/
+
+/* Define the special codes, relative to 1 << InitialCodeLength. */
+#define code_reset 0
+#define code_eod 1
+#define code_0 2 /* first assignable code */
+
+/* Internal routine to put a code into the output buffer. */
+/* Let S = ss->code_size. */
+/* Relevant invariants: 9 <= S <= 15, 0 <= code < 1 << S; */
+/* 1 <= ss->bits_left <= 8; only the rightmost (8 - ss->bits_left) */
+/* bits of ss->bits contain valid data. */
+private byte *
+lzw_put_code(register stream_LZW_state *ss, byte *q, uint code)
+{ uint size = ss->code_size;
+ byte cb = (ss->bits << ss->bits_left) +
+ (code >> (size - ss->bits_left));
+ if_debug2('W', "[w]writing 0x%x,%d\n", code, ss->code_size);
+ *++q = cb;
+ if ( (ss->bits_left += 8 - size) <= 0 )
+ { *++q = code >> -ss->bits_left;
+ ss->bits_left += 8;
+ }
+ ss->bits = code;
+ return q;
+}
+
+#define ss ((stream_LZW_state *)st)
+
+/* Initialize LZW-compatible encoding filter. */
+int
+s_LZWE_reset(stream_state *st)
+{ ss->code_size = ss->InitialCodeLength + 1;
+ ss->bits_left = 8;
+ ss->next_code = (1 << ss->InitialCodeLength) + code_0;
+ return 0;
+}
+private int
+s_LZWE_init(stream_state *st)
+{ ss->InitialCodeLength = 8;
+ ss->table.encode = 0;
+ return s_LZWE_reset(st);
+}
+
+/* Process a buffer */
+private int
+s_LZWE_process(stream_state *st, stream_cursor_read *pr,
+ stream_cursor_write *pw, bool last)
+{ register const byte *p = pr->ptr;
+ const byte *rlimit = pr->limit;
+ register byte *q = pw->ptr;
+ byte *wlimit = pw->limit;
+ int status = 0;
+ int signal = 1 << (ss->code_size - 1);
+ uint limit_code = (1 << ss->code_size) - 1;
+ uint next_code = ss->next_code;
+
+ while ( p < rlimit )
+ { if ( next_code == limit_code )
+ { /* Emit a reset code. */
+ if ( wlimit - q < 2 )
+ { status = 1;
+ break;
+ }
+ q = lzw_put_code(ss, q, signal + code_reset);
+ next_code = signal + code_0;
+ }
+ if ( wlimit - q < 2 )
+ { status = 1;
+ break;
+ }
+ q = lzw_put_code(ss, q, *++p);
+ next_code++;
+ }
+ if ( last && status == 0 )
+ { if ( wlimit - q < 2 )
+ status = 1;
+ else
+ { q = lzw_put_code(ss, q, signal + code_eod);
+ if ( ss->bits_left < 8 )
+ *++q = ss->bits << ss->bits_left; /* final byte */
+ }
+ }
+ ss->next_code = next_code;
+ pr->ptr = p;
+ pw->ptr = q;
+ return status;
+}
+
+#undef ss
+
+/* Stream template */
+const stream_template s_LZWE_template =
+{ &st_LZW_state, s_LZWE_init, s_LZWE_process, 1, 2, NULL,
+ s_LZW_set_defaults, s_LZWE_reset
+};
diff --git a/pstoraster/slzwd.c b/pstoraster/slzwd.c
new file mode 100644
index 000000000..16f4057b7
--- /dev/null
+++ b/pstoraster/slzwd.c
@@ -0,0 +1,373 @@
+/* Copyright (C) 1993, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* slzwd.c */
+/* LZW decoding filter */
+#include "stdio_.h" /* includes std.h */
+#include "gdebug.h"
+#include "strimpl.h"
+#include "slzwx.h"
+
+/* ------ LZWDecode ------ */
+
+/********************************************************/
+/* LZW routines are based on: */
+/* Dr. Dobbs Journal --- Oct. 1989. */
+/* Article on LZW Data Compression by Mark R. Nelson */
+/********************************************************/
+
+/* Define the special codes in terms of code_escape, which is */
+/* 1 << InitialCodeLength. */
+#define code_reset (code_escape + 0)
+#define code_eod (code_escape + 1)
+#define code_0 (code_escape + 2) /* first assignable code */
+
+struct lzw_decode_s {
+ byte datum;
+ byte len; /* length of code */
+ ushort prefix; /* code to be prefixed */
+};
+gs_private_st_simple(st_lzw_decode, lzw_decode, "lzw_decode");
+/* We can use a simple type as the element type, */
+/* because there are no pointers to enumerate or relocate. */
+#define st_lzw_decode_element st_lzw_decode
+#define lzw_decode_max 4096 /* must be 4096 */
+
+#define ss ((stream_LZW_state *)st)
+
+/* Initialize LZWDecode filter */
+/* We separate out the reset function for some non-stream clients. */
+int
+s_LZWD_reset(stream_state *st)
+{ register lzw_decode *dc = ss->table.decode;
+ register int i;
+ uint code_escape = 1 << ss->InitialCodeLength;
+
+ ss->bits_left = 0;
+ ss->bytes_left = 0;
+ ss->next_code = code_0;
+ ss->code_size = ss->InitialCodeLength + 1;
+ ss->prev_code = -1;
+ ss->copy_code = -1;
+ dc[code_reset].len = 255;
+ dc[code_eod].len = 255;
+ for ( i = 0; i < code_escape; i++, dc++ )
+ dc->datum = i, dc->len = 1, dc->prefix = code_eod;
+ return 0;
+}
+private int
+s_LZWD_init(stream_state *st)
+{ lzw_decode *dc =
+ gs_alloc_struct_array(st->memory, lzw_decode_max + 1,
+ lzw_decode, &st_lzw_decode_element,
+ "LZWDecode(init)");
+
+ if ( dc == 0 )
+ return ERRC; /****** WRONG ******/
+ ss->table.decode = dc;
+ return s_LZWD_reset(st);
+}
+
+/* Process a buffer */
+private int
+s_LZWD_process(stream_state *st, stream_cursor_read *pr,
+ stream_cursor_write *pw, bool last)
+{ register const byte *p = pr->ptr;
+ register byte *q = pw->ptr;
+#ifdef DEBUG
+ byte *q0 = q;
+#endif
+ const byte *rlimit = pr->limit; /* constant pointer */
+ byte *wlimit = pw->limit; /* constant pointer */
+ int status = 0;
+ int code = ss->copy_code;
+ int prev_code = ss->prev_code;
+ uint prev_len = ss->prev_len;
+ byte bits = ss->bits;
+ int bits_left = ss->bits_left;
+ int bytes_left = ss->bytes_left;
+ int code_size = ss->code_size;
+ int code_mask;
+ int switch_code;
+ int next_code = ss->next_code;
+ lzw_decode *table = ss->table.decode; /* constant pointer */
+ lzw_decode *dc_next = table + next_code; /* invariant */
+ lzw_decode *dc;
+ int code_escape = 1 << ss->InitialCodeLength;
+ int eod = code_eod;
+ bool low_order = ss->FirstBitLowOrder;
+ uint len;
+ int c;
+ byte b;
+ byte *q1;
+ if_debug2('w', "[w]process decode: code_size=%d next_code=%d\n",
+ code_size, next_code);
+#define set_code_size()\
+ code_mask = (1 << code_size) - 1,\
+ switch_code = code_mask + 1 - ss->EarlyChange
+ set_code_size();
+ if ( !ss->BlockData )
+ bytes_left = rlimit - p + 2; /* never stop for bytes_left */
+ /* If we are in the middle of copying a string, */
+ /* do some more now. */
+ if ( code >= 0 )
+ { int rlen = ss->copy_left;
+ int wlen = wlimit - q;
+ int n = len = min(rlen, wlen);
+ c = code;
+ ss->copy_left = rlen -= len;
+ if_debug3('W', "[W]copying 0x%x, %d byte(s) out of %d left\n",
+ code, len, rlen + len);
+ while ( rlen )
+ c = table[c].prefix,
+ rlen--;
+ q1 = q += len;
+ n = len;
+ while ( --n >= 0 )
+ { *q1-- = (dc = &table[c])->datum;
+ c = dc->prefix;
+ }
+ if ( ss->copy_left ) /* more to do */
+ { pw->ptr = q;
+ return 1;
+ }
+ ss->copy_code = -1;
+ len = ss->copy_len;
+ /* Retrieve the first byte of the code just copied. */
+ if ( c == eod )
+ { /* We just copied the entire code, */
+ /* so the byte we want is immediately available. */
+ b = q1[1];
+ }
+ else
+ { /* We have to scan to the beginning of the code. */
+ for ( ; c != eod; c = table[c].prefix )
+ b = (byte)c;
+ }
+ goto add;
+ }
+top: if ( code_size > bits_left )
+ { if ( bytes_left == 0 )
+ { if ( p == rlimit )
+ goto out;
+ bytes_left = *++p;
+ if_debug1('W', "[W]block count %d\n", bytes_left);
+ if ( bytes_left == 0 )
+ { status = EOFC;
+ goto out;
+ }
+ goto top;
+ }
+ if ( low_order )
+ code = bits >> (8 - bits_left);
+ else
+ code = (uint)bits << (code_size - bits_left);
+ if ( bits_left + 8 < code_size )
+ { /* Need 2 more data bytes */
+ if ( bytes_left == 1 )
+ { if ( rlimit - p < 3 )
+ goto out;
+ bytes_left = p[2];
+ if_debug1('W', "[W]block count %d\n",
+ bytes_left);
+ if ( bytes_left == 0 )
+ { status = EOFC;
+ goto out;
+ }
+ bytes_left++;
+ bits = p[1];
+ p++;
+ }
+ else
+ { if ( rlimit - p < 2 )
+ goto out;
+ bits = p[1];
+ }
+ if ( low_order )
+ code += (uint)bits << bits_left;
+ else
+ code += (uint)bits << (code_size - 8 - bits_left);
+ bits_left += 8;
+ bits = p[2];
+ p += 2;
+ bytes_left -= 2;
+ }
+ else
+ { if ( p == rlimit )
+ goto out;
+ bits = *++p;
+ bytes_left--;
+ }
+ if ( low_order )
+ code += (uint)bits << bits_left,
+ bits_left += 8 - code_size;
+ else
+ bits_left += 8 - code_size,
+ code += bits >> bits_left;
+ }
+ else
+ { if ( low_order )
+ code = bits >> (8 - bits_left),
+ bits_left -= code_size;
+ else
+ bits_left -= code_size,
+ code = bits >> bits_left;
+ }
+ code &= code_mask;
+ if_debug2('W', "[W]reading 0x%x,%d\n", code, code_size);
+ /*
+ * There is an anomalous case where a code S is followed
+ * immediately by another occurrence of the S string.
+ * In this case, the next available code will be defined as
+ * S followed by the first character of S, and will be
+ * emitted immediately after the code S. We have to
+ * recognize this case specially, by noting that the code is
+ * equal to next_code.
+ */
+ if ( code >= next_code )
+ { if ( code > next_code )
+ {
+#ifdef DEBUG
+ lprintf2("[W]code = %d > next_code = %d\n",
+ code, next_code);
+#endif
+ status = ERRC;
+ goto out;
+ }
+ /* Fabricate the entry for the code. It will be */
+ /* overwritten immediately, of course. */
+ for ( c = prev_code; c != eod; c = table[c].prefix )
+ dc_next->datum = c;
+ len = prev_len + 1;
+ dc_next->len = min(len, 255);
+ dc_next->prefix = prev_code;
+ if_debug3('w', "[w]decoding anomalous 0x%x=0x%x+%c\n",
+ next_code, prev_code, dc_next->datum);
+ }
+ /* See if there is enough room for the code. */
+ len = table[code].len;
+ if ( len == 255 )
+ { /* Check for special code (reset or end). */
+ /* We set their lengths to 255 to avoid doing */
+ /* an extra check in the normal case. */
+ if ( code == code_reset )
+ { if_debug1('w', "[w]reset: next_code was %d\n",
+ next_code);
+ next_code = code_0;
+ dc_next = table + code_0;
+ code_size = ss->InitialCodeLength + 1;
+ set_code_size();
+ prev_code = -1;
+ goto top;
+ }
+ else if ( code == eod )
+ { status = EOFC;
+ goto out;
+ }
+ /* The code length won't fit in a byte, */
+ /* compute it the hard way. */
+ for ( c = code, len = 0; c != eod; len++ )
+ c = table[c].prefix;
+ if_debug2('w', "[w]long code %d, length=%d\n", code, len);
+ }
+ if ( wlimit - q < len )
+ { ss->copy_code = code;
+ ss->copy_left = ss->copy_len = len;
+ status = 1;
+ goto out;
+ }
+ /* Copy the string to the buffer (back to front). */
+ /* Optimize for short codes, which are the most frequent. */
+ dc = &table[code];
+ switch ( len )
+ {
+ default:
+ { byte *q1 = q += len;
+ c = code;
+ do
+ { *q1-- = (dc = &table[c])->datum;
+ }
+ while ( (c = dc->prefix) != eod );
+ b = q1[1];
+ } break;
+ case 3:
+ q[3] = dc->datum;
+ dc = &table[dc->prefix];
+ case 2:
+ q[2] = dc->datum;
+ dc = &table[dc->prefix];
+ case 1:
+ q[1] = b = dc->datum;
+ q += len;
+ }
+add: /* Add a new entry to the table */
+ if ( prev_code >= 0 )
+ { /* Unfortunately, we have to check for next_code == */
+ /* lzw_decode_max every time: just checking at power */
+ /* of 2 boundaries stops us one code too soon. */
+ if ( next_code == lzw_decode_max )
+ { status = ERRC;
+ goto out;
+ }
+ dc_next->datum = b; /* added char of string */
+ dc_next->len = min(prev_len, 254) + 1;
+ dc_next->prefix = prev_code;
+ dc_next++;
+ if_debug4('W', "[W]adding 0x%x=0x%x+%c(%d)\n",
+ next_code, prev_code, b, min(len, 255));
+ if ( ++next_code == switch_code )
+ { /* Crossed a power of 2. */
+ /* We have to make a strange special check for */
+ /* reaching the end of the code space. */
+ if ( next_code < lzw_decode_max - 1 )
+ { code_size++;
+ set_code_size();
+ if_debug2('w', "[w]crossed power of 2: new code_size=%d, next_code=%d\n",
+ code_size, next_code);
+ }
+ }
+ }
+ prev_code = code;
+ prev_len = len;
+ goto top;
+out: pr->ptr = p;
+ pw->ptr = q;
+ ss->code_size = code_size;
+ ss->prev_code = prev_code;
+ ss->prev_len = prev_len;
+ ss->bits = bits;
+ ss->bits_left = bits_left;
+ ss->bytes_left = bytes_left;
+ ss->next_code = next_code;
+ if_debug3('w', "[w]decoded %d bytes, prev_code=%d, next_code=%d\n",
+ (int)(q - q0), prev_code, next_code);
+ return status;
+}
+
+#undef ss
+
+/* Stream template */
+const stream_template s_LZWD_template =
+{ &st_LZW_state, s_LZWD_init, s_LZWD_process, 3, 1, s_LZW_release,
+ s_LZW_set_defaults, s_LZWD_reset
+};
diff --git a/pstoraster/slzwx.h b/pstoraster/slzwx.h
new file mode 100644
index 000000000..de204e74b
--- /dev/null
+++ b/pstoraster/slzwx.h
@@ -0,0 +1,71 @@
+/* Copyright (C) 1993, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* slzwx.h */
+/* LZW filter state definition */
+/* Requires strimpl.h */
+
+typedef struct lzw_decode_s lzw_decode;
+typedef struct lzw_encode_table_s lzw_encode_table;
+typedef struct stream_LZW_state_s {
+ stream_state_common;
+ /* The following are set before initialization. */
+ int InitialCodeLength; /* decoding only */
+ bool FirstBitLowOrder; /* decoding only */
+ bool BlockData; /* decoding only */
+ int EarlyChange; /* decoding only */
+ /* The following are updated dynamically. */
+ uint bits; /* buffer for input bits */
+ int bits_left; /* # of valid low bits left */
+ int bytes_left; /* # of bytes left in current block */
+ /* (arbitrary large # if not GIF) */
+ union _lzt {
+ lzw_decode *decode;
+ lzw_encode_table *encode;
+ } table;
+ uint next_code; /* next code to be assigned */
+ int code_size; /* current # of bits per code */
+ int prev_code; /* previous code recognized */
+ /* or assigned */
+ uint prev_len; /* length of prev_code */
+ int copy_code; /* code whose string is being */
+ /* copied, -1 if none */
+ uint copy_len; /* length of copy_code */
+ int copy_left; /* amount of string left to copy */
+ bool first; /* true if no output yet */
+} stream_LZW_state;
+extern_st(st_LZW_state);
+#define public_st_LZW_state() /* in slzwc.c */\
+ gs_public_st_ptrs1(st_LZW_state, stream_LZW_state,\
+ "LZWDecode state", lzwd_enum_ptrs, lzwd_reloc_ptrs, table.decode)
+#define s_LZW_set_defaults_inline(ss)\
+ ((ss)->InitialCodeLength = 8,\
+ (ss)->FirstBitLowOrder = false,\
+ (ss)->BlockData = false,\
+ (ss)->EarlyChange = 1)
+extern const stream_template s_LZWD_template;
+extern const stream_template s_LZWE_template;
+
+/* Shared procedures */
+void s_LZW_set_defaults(P1(stream_state *));
+void s_LZW_release(P1(stream_state *));
diff --git a/pstoraster/smtf.c b/pstoraster/smtf.c
new file mode 100644
index 000000000..e42914f80
--- /dev/null
+++ b/pstoraster/smtf.c
@@ -0,0 +1,171 @@
+/* Copyright (C) 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* smtf.c */
+/* MoveToFront filters */
+#include "stdio_.h"
+#include "strimpl.h"
+#include "smtf.h"
+
+/* ------ MoveToFrontEncode/Decode ------ */
+
+private_st_MTF_state();
+
+#define ss ((stream_MTF_state *)st)
+
+/* Initialize */
+private int
+s_MTF_init(stream_state *st)
+{ int i;
+ for ( i = 0; i < 256; i++ )
+ ss->prev.b[i] = (byte)i;
+ return 0;
+}
+
+/* Encode a buffer */
+private int
+s_MTFE_process(stream_state *st, stream_cursor_read *pr,
+ stream_cursor_write *pw, bool last)
+{ register const byte *p = pr->ptr;
+ register byte *q = pw->ptr;
+ const byte *rlimit = pr->limit;
+ uint count = rlimit - p;
+ uint wcount = pw->limit - q;
+ int status =
+ (count < wcount ? 0 :
+ (rlimit = p + wcount, 1));
+ while ( p < rlimit )
+ { byte b = *++p;
+ int i;
+ byte prev = b, repl;
+ for ( i = 0; (repl = ss->prev.b[i]) != b; i++ )
+ ss->prev.b[i] = prev, prev = repl;
+ ss->prev.b[i] = prev;
+ *++q = (byte)i;
+ }
+ pr->ptr = p;
+ pw->ptr = q;
+ return status;
+}
+
+/* Stream template */
+const stream_template s_MTFE_template =
+{ &st_MTF_state, s_MTF_init, s_MTFE_process, 1, 1
+};
+
+/* Decode a buffer */
+private int
+s_MTFD_process(stream_state *st, stream_cursor_read *pr,
+ stream_cursor_write *pw, bool last)
+{ register const byte *p = pr->ptr;
+ register byte *q = pw->ptr;
+ const byte *rlimit = pr->limit;
+ uint count = rlimit - p;
+ uint wcount = pw->limit - q;
+ int status = (count <= wcount ? 0 : (rlimit = p + wcount, 1));
+ /* Cache the first few entries in local variables. */
+ byte
+ v0 = ss->prev.b[0], v1 = ss->prev.b[1],
+ v2 = ss->prev.b[2], v3 = ss->prev.b[3];
+ while ( p < rlimit )
+ { byte first;
+ /* Zeros far outnumber all other bytes in the BWBS */
+ /* code; check for them first. */
+ if ( *++p == 0 )
+ { *++q = v0;
+ continue;
+ }
+ switch ( *p )
+ {
+ default:
+ { uint b = *p;
+ byte *bp = &ss->prev.b[b];
+ *++q = first = *bp;
+#if arch_sizeof_long == 4
+ ss->prev.b[3] = v3;
+#endif
+ /* Move trailing entries individually. */
+ for ( ; ; bp--, b-- )
+ { *bp = bp[-1];
+ if ( !(b & (sizeof(long) - 1)) )
+ break;
+ }
+ /* Move in long-size chunks. */
+ for ( ; (b -= sizeof(long)) != 0; )
+ { bp -= sizeof(long);
+#if arch_is_big_endian
+ *(ulong *)bp =
+ (*(ulong *)bp >> 8) |
+ (bp[-1] << ((sizeof(long) - 1) * 8));
+#else
+ *(ulong *)bp = (*(ulong *)bp << 8) | bp[-1];
+#endif
+ }
+ }
+#if arch_sizeof_long > 4 /* better be 8! */
+ goto m7;
+ case 7:
+ *++q = first = ss->prev.b[7];
+m7: ss->prev.b[7] = ss->prev.b[6];
+ goto m6;
+ case 6:
+ *++q = first = ss->prev.b[6];
+m6: ss->prev.b[6] = ss->prev.b[5];
+ goto m5;
+ case 5:
+ *++q = first = ss->prev.b[5];
+m5: ss->prev.b[5] = ss->prev.b[4];
+ goto m4;
+ case 4:
+ *++q = first = ss->prev.b[4];
+m4: ss->prev.b[4] = v3;
+#endif
+ goto m3;
+ case 3:
+ *++q = first = v3;
+m3: v3 = v2, v2 = v1, v1 = v0, v0 = first;
+ break;
+ case 2:
+ *++q = first = v2;
+ v2 = v1, v1 = v0, v0 = first;
+ break;
+ case 1:
+ *++q = first = v1;
+ v1 = v0, v0 = first;
+ break;
+ }
+ }
+ ss->prev.b[0] = v0; ss->prev.b[1] = v1;
+ ss->prev.b[2] = v2; ss->prev.b[3] = v3;
+ pr->ptr = p;
+ pw->ptr = q;
+ return status;
+}
+
+/* Stream template */
+const stream_template s_MTFD_template =
+{ &st_MTF_state, s_MTF_init, s_MTFD_process, 1, 1,
+ NULL, NULL, s_MTF_init
+};
+
+#undef ss
diff --git a/pstoraster/smtf.h b/pstoraster/smtf.h
new file mode 100644
index 000000000..d55eeaa73
--- /dev/null
+++ b/pstoraster/smtf.h
@@ -0,0 +1,43 @@
+/* Copyright (C) 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* smtf.h */
+/* Definitions for MoveToFront streams */
+/* Requires scommon.h; strimpl.h if any templates are referenced */
+
+/* MoveToFrontEncode/Decode */
+typedef struct stream_MTF_state_s {
+ stream_state_common;
+ /* The following change dynamically. */
+ union _p {
+ ulong l[256 / sizeof(long)];
+ byte b[256];
+ } prev;
+} stream_MTF_state;
+typedef stream_MTF_state stream_MTFE_state;
+typedef stream_MTF_state stream_MTFD_state;
+#define private_st_MTF_state() /* in sbwbs.c */\
+ gs_private_st_simple(st_MTF_state, stream_MTF_state,\
+ "MoveToFrontEncode/Decode state")
+extern const stream_template s_MTFE_template;
+extern const stream_template s_MTFD_template;
diff --git a/pstoraster/spcxd.c b/pstoraster/spcxd.c
new file mode 100644
index 000000000..1ec649492
--- /dev/null
+++ b/pstoraster/spcxd.c
@@ -0,0 +1,77 @@
+/* Copyright (C) 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* spcxd.c */
+/* PCXDecode filter */
+#include "stdio_.h" /* includes std.h */
+#include "memory_.h"
+#include "strimpl.h"
+#include "spcxx.h"
+
+/* ------ PCXDecode ------ */
+
+/* Refill the buffer */
+private int
+s_PCXD_process(stream_state *st, stream_cursor_read *pr,
+ stream_cursor_write *pw, bool last)
+{ register const byte *p = pr->ptr;
+ register byte *q = pw->ptr;
+ const byte *rlimit = pr->limit;
+ byte *wlimit = pw->limit;
+ int status = 0;
+
+ while ( p < rlimit )
+ { int b = *++p;
+ if ( b < 0xc0 )
+ { if ( q >= wlimit )
+ { --p;
+ status = 1;
+ break;
+ }
+ *++q = b;
+ }
+ else if ( p >= rlimit )
+ { --p;
+ break;
+ }
+ else if ( (b -= 0xc0) > wlimit - q )
+ { --p;
+ status = 1;
+ break;
+ }
+ else
+ { memset(q + 1, *++p, b);
+ q += b;
+ }
+ }
+ pr->ptr = p;
+ pw->ptr = q;
+ return status;
+}
+
+#undef ss
+
+/* Stream template */
+const stream_template s_PCXD_template =
+{ &st_stream_state, NULL, s_PCXD_process, 2, 63
+};
diff --git a/pstoraster/spcxx.h b/pstoraster/spcxx.h
new file mode 100644
index 000000000..55a0a36fe
--- /dev/null
+++ b/pstoraster/spcxx.h
@@ -0,0 +1,30 @@
+/* Copyright (C) 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* spcxx.h */
+/* Definitions for PCXDecode streams */
+/* Requires scommon.h; strimpl.h if any templates are referenced */
+
+/* PCXDecode */
+/* (no state) */
+extern const stream_template s_PCXD_template;
diff --git a/pstoraster/spdiff.c b/pstoraster/spdiff.c
new file mode 100644
index 000000000..ff69a55f1
--- /dev/null
+++ b/pstoraster/spdiff.c
@@ -0,0 +1,302 @@
+/* Copyright (C) 1994, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* spdiff.c */
+/* Pixel differencing filters */
+#include "stdio_.h" /* should be std.h, but needs NULL */
+#include "strimpl.h"
+#include "spdiffx.h"
+
+/* ------ PixelDifferenceEncode/Decode ------ */
+
+private_st_PDiff_state();
+
+#define ss ((stream_PDiff_state *)st)
+
+/* Define values for case dispatch. */
+#define cBits1 0
+#define cBits2 4
+#define cBits4 8
+#define cBits8 12
+#define cEncode -1
+#define cDecode 15
+
+/* Set defaults */
+private void
+s_PDiff_set_defaults(stream_state *st)
+{ s_PDiff_set_defaults_inline(ss);
+}
+
+/* Common (re)initialization. */
+private int
+s_PDiff_reinit(stream_state *st)
+{ ss->row_left = 0;
+ return 0;
+}
+
+/* Initialize PixelDifferenceEncode filter. */
+private int
+s_PDiffE_init(stream_state *st)
+{ long bits_per_row =
+ ss->Colors * ss->BitsPerComponent * (long)ss->Columns;
+ static const byte cb_values[] =
+ { 0, cBits1, cBits2, 0, cBits4, 0, 0, 0, cBits8 };
+ ss->row_count = (uint)((bits_per_row + 7) >> 3);
+ ss->end_mask = (1 << (-bits_per_row & 7)) - 1;
+ ss->case_index =
+ cb_values[ss->BitsPerComponent] + ss->Colors + cEncode;
+ return s_PDiff_reinit(st);
+}
+
+/* Initialize PixelDifferenceDecode filter. */
+private int
+s_PDiffD_init(stream_state *st)
+{ s_PDiffE_init(st);
+ ss->case_index += cDecode - cEncode;
+ return 0;
+}
+
+/* Process a buffer. Note that this handles both Encode and Decode. */
+private int
+s_PDiff_process(stream_state *st, stream_cursor_read *pr,
+ stream_cursor_write *pw, bool last)
+{ register const byte *p = pr->ptr;
+ register byte *q = pw->ptr;
+ int rcount, wcount;
+ register int count;
+ int status = 0;
+ register byte s0 = ss->s0;
+ register byte t;
+ byte save_last;
+ const byte end_mask = ss->end_mask;
+
+row: if ( ss->row_left == 0 )
+ ss->row_left = ss->row_count,
+ s0 = ss->s1 = ss->s2 = ss->s3 = 0;
+ rcount = pr->limit - p;
+ wcount = pw->limit - q;
+ if ( ss->row_left < rcount )
+ rcount = ss->row_left;
+ count = (wcount < rcount ? (status = 1, wcount) : rcount);
+ ss->row_left -= count;
+ if ( ss->row_left == count )
+ save_last = p[count];
+
+ /*
+ * Encoding and decoding are fundamentally different.
+ * Encoding computes E[i] = D[i] - D[i-1];
+ * decoding computes D[i] = E[i] + D[i-1].
+ * Nevertheless, the loop structures are similar enough that
+ * we put the code for both functions in the same place.
+ */
+
+#define loopn(n, body)\
+ while ( count >= n ) p += n, q += n, body, count -= n
+
+ switch ( ss->case_index )
+ {
+
+ /* 1 bit per component */
+
+#define eloop1(ee)\
+ loopn(1, (t = *p, *q = ee, s0 = t))
+
+ case cEncode+cBits1+1:
+ eloop1(t ^ ((s0 << 7) | (t >> 1)));
+ case cEncode+cBits1+2:
+ eloop1(t ^ ((s0 << 6) | (t >> 2)));
+ case cEncode+cBits1+3:
+ eloop1(t ^ ((s0 << 5) | (t >> 3)));
+ case cEncode+cBits1+4:
+ eloop1(t ^ ((s0 << 4) | (t >> 4)));
+
+#define dloop1(te, de)\
+ loopn(1, (t = te, s0 = *q = de)); break
+
+ case cDecode+cBits1+1:
+ dloop1(*p ^ (s0 << 7),
+ (t ^= t >> 1, t ^= t >> 2, t ^ (t >> 4)));
+ case cDecode+cBits1+2:
+ dloop1(*p ^ (s0 << 6),
+ (t ^= (t >> 2), t ^ (t >> 4)));
+ case cDecode+cBits1+3:
+ dloop1(*p ^ (s0 << 5),
+ t ^ (t >> 3) ^ (t >> 6));
+ case cDecode+cBits1+4:
+ dloop1(*p ^ (s0 << 4),
+ t ^ (t >> 4));
+
+ /* 2 bits per component */
+
+#define add4x2(a, b) ( (((a) & (b) & 0x55) << 1) ^ (a) ^ (b) )
+#define sub4x2(a, b) ( ((~(a) & (b) & 0x55) << 1) ^ (a) ^ (b) )
+
+ case cEncode+cBits2+1:
+ eloop1((s0 = (s0 << 6) | (t >> 2), sub4x2(t, s0)));
+ case cEncode+cBits2+2:
+ eloop1((s0 = (s0 << 4) | (t >> 4), sub4x2(t, s0)));
+ case cEncode+cBits2+3:
+ eloop1((s0 = (s0 << 2) | (t >> 6), sub4x2(t, s0)));
+ case cEncode+cBits2+4:
+ eloop1(sub4x2(t, s0));
+
+ case cDecode+cBits2+1:
+ dloop1(*p + (s0 << 6),
+ (t = add4x2(t >> 2, t),
+ add4x2(t >> 4, t)));
+ case cDecode+cBits2+2:
+ dloop1(*p, (t = add4x2(t, s0 << 4),
+ add4x2(t >> 4, t)));
+ case cDecode+cBits2+3:
+ dloop1(*p, (t = add4x2(t, s0 << 2),
+ add4x2(t >> 6, t)));
+ case cDecode+cBits2+4:
+ dloop1(*p, add4x2(t, s0));
+
+#undef add4x2
+#undef sub4x2
+
+ /* 4 bits per component */
+
+#define add2x4(a, b) ( (((a) + (b)) & 0xf) + ((a) & 0xf0) + ((b) & 0xf0) )
+#define add2x4r4(a) ( (((a) + ((a) >> 4)) & 0xf) + ((a) & 0xf0) )
+#define sub2x4(a, b) ( (((a) - (b)) & 0xf) + ((a) & 0xf0) - ((b) & 0xf0) )
+#define sub2x4r4(a) ( (((a) - ((a) >> 4)) & 0xf) + ((a) & 0xf0) )
+
+ case cEncode+cBits4+1:
+ eloop1( ((t - (s0 << 4)) & 0xf0) | ((t - (t >> 4)) & 0xf) );
+ case cEncode+cBits4+2:
+ eloop1(sub2x4(t, s0));
+ case cEncode+cBits4+3:
+ { register byte s1 = ss->s1;
+ loopn(1, (t = *p,
+ *q =
+ ((t - (s0 << 4)) & 0xf0) | ((t - (s1 >> 4)) & 0xf),
+ s0 = s1, s1 = t));
+ ss->s1 = s1;
+ } break;
+ case cEncode+cBits4+4:
+ { register byte s1 = ss->s1;
+ loopn(2,
+ (t = p[-1], q[-1] = sub2x4(t, s0), s0 = t,
+ t = *p, *q = sub2x4(t, s1), s1 = t));
+ ss->s1 = s1;
+ } break;
+
+ case cDecode+cBits4+1:
+ dloop1(*p + (s0 << 4), add2x4r4(t));
+ case cDecode+cBits4+2:
+ dloop1(*p, add2x4(t, s0));
+ case cDecode+cBits4+3:
+ { register byte s1 = ss->s1;
+ loopn(1, (t = (s0 << 4) + (s1 >> 4),
+ s0 = s1, s1 = *q = add2x4(*p, t)));
+ ss->s1 = s1;
+ } break;
+ case cDecode+cBits4+4:
+ { register byte s1 = ss->s1;
+ loopn(2,
+ (t = p[-1], s0 = q[-1] = add2x4(s0, t),
+ t = *p, s1 = *q = add2x4(s1, t)));
+ ss->s1 = s1;
+ } break;
+
+#undef add2x4
+#undef add2x4r4
+#undef sub2x4
+#undef sub2x4r4
+
+ /* 8 bits per component */
+
+#define encode8(s, d) (q[d] = p[d] - s, s = p[d])
+#define decode8(s, d) s = q[d] = s + p[d]
+
+ case cEncode+cBits8+1:
+ loopn(1, encode8(s0, 0));
+ break;
+ case cDecode+cBits8+1:
+ loopn(1, decode8(s0, 0));
+ break;
+ case cEncode+cBits8+2:
+ { register byte s1 = ss->s1;
+ loopn(2, (encode8(s0, -1), encode8(s1, 0)));
+ ss->s1 = s1;
+ } break;
+ case cDecode+cBits8+2:
+ { register byte s1 = ss->s1;
+ loopn(2, (decode8(s0, -1), decode8(s1, 0)));
+ ss->s1 = s1;
+ } break;
+ case cEncode+cBits8+3:
+ { register byte s1 = ss->s1, s2 = ss->s2;
+ loopn(3, (encode8(s0, -2), encode8(s1, -1),
+ encode8(s2, 0)));
+ ss->s1 = s1, ss->s2 = s2;
+ } break;
+ case cDecode+cBits8+3:
+ { register byte s1 = ss->s1, s2 = ss->s2;
+ loopn(3, (decode8(s0, -2), decode8(s1, -1),
+ decode8(s2, 0)));
+ ss->s1 = s1, ss->s2 = s2;
+ } break;
+ case cEncode+cBits8+4:
+ { register byte s1 = ss->s1, s2 = ss->s2, s3 = ss->s3;
+ loopn(4, (encode8(s0, -3), encode8(s1, -2),
+ encode8(s2, -1), encode8(s3, 0)));
+ ss->s1 = s1, ss->s2 = s2, ss->s3 = s3;
+ } break;
+ case cDecode+cBits8+4:
+ { register byte s1 = ss->s1, s2 = ss->s2, s3 = ss->s3;
+ loopn(4, (decode8(s0, -3), decode8(s1, -2),
+ decode8(s2, -1), decode8(s3, 0)));
+ ss->s1 = s1, ss->s2 = s2, ss->s3 = s3;
+ } break;
+
+#undef encode8
+#undef decode8
+
+ }
+#undef loopn
+#undef dloop1
+ ss->row_left += count; /* leftover bytes are possible */
+ if ( ss->row_left == 0 )
+ { if ( end_mask != 0 )
+ *q = (*q & ~end_mask) | (save_last & end_mask);
+ if ( p < pr->limit && q < pw->limit )
+ goto row;
+ }
+ ss->s0 = s0;
+ pr->ptr = p;
+ pw->ptr = q;
+ return status;
+}
+
+/* Stream templates */
+const stream_template s_PDiffE_template =
+{ &st_PDiff_state, s_PDiffE_init, s_PDiff_process, 1, 1, NULL,
+ s_PDiff_set_defaults, s_PDiff_reinit
+};
+const stream_template s_PDiffD_template =
+{ &st_PDiff_state, s_PDiffD_init, s_PDiff_process, 1, 1, NULL,
+ s_PDiff_set_defaults, s_PDiff_reinit
+};
diff --git a/pstoraster/spdiffx.h b/pstoraster/spdiffx.h
new file mode 100644
index 000000000..d65f49173
--- /dev/null
+++ b/pstoraster/spdiffx.h
@@ -0,0 +1,49 @@
+/* Copyright (C) 1994, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* spdiffx.h */
+/* Pixel differencing filter state definition */
+/* Requires strimpl.h */
+
+/* PixelDifferenceDecode / PixelDifferenceEncode */
+typedef struct stream_PDiff_state_s {
+ stream_state_common;
+ /* The client sets the following before initialization. */
+ int Colors; /* # of colors, 1..4 */
+ int BitsPerComponent; /* 1, 2, 4, 8 */
+ int Columns;
+ /* The init procedure computes the following. */
+ uint row_count; /* # of bytes per row */
+ byte end_mask; /* mask for left-over bits in last byte */
+ int case_index; /* switch index for case dispatch */
+ /* The following are updated dynamically. */
+ uint row_left; /* # of bytes left in row */
+ byte s0, s1, s2, s3; /* previous sample */
+} stream_PDiff_state;
+#define private_st_PDiff_state() /* in spdiff.c */\
+ gs_private_st_simple(st_PDiff_state, stream_PDiff_state,\
+ "PixelDifferenceEncode/Decode state")
+#define s_PDiff_set_defaults_inline(ss)\
+ ((ss)->Colors = 1, (ss)->BitsPerComponent = 8, (ss)->Columns = 1)
+extern const stream_template s_PDiffD_template;
+extern const stream_template s_PDiffE_template;
diff --git a/pstoraster/spngp.c b/pstoraster/spngp.c
new file mode 100644
index 000000000..99da61530
--- /dev/null
+++ b/pstoraster/spngp.c
@@ -0,0 +1,338 @@
+/* Copyright (C) 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* spngp.c */
+/* PNG pixel prediction filters */
+#include "memory_.h"
+#include "strimpl.h"
+#include "spngpx.h"
+
+/* ------ PNGPredictorEncode/Decode ------ */
+
+private_st_PNGP_state();
+
+#define ss ((stream_PNGP_state *)st)
+#define ss_const ((const stream_PNGP_state *)st_const)
+
+/* Define values for case dispatch. */
+#define cNone 10
+#define cSub 11
+#define cUp 12
+#define cAverage 13
+#define cPaeth 14
+#define cOptimum 15
+#define cEncode -10
+#define cDecode -4
+private const byte pngp_case_needs_prev[] = { 0, 0, 1, 1, 1, 1 };
+
+/* Set defaults */
+private void
+s_PNGP_set_defaults(stream_state *st)
+{ s_PNGP_set_defaults_inline(ss);
+}
+
+/* Common (re)initialization. */
+private int
+s_PNGP_reinit(stream_state *st)
+{ if ( ss->prev_row != 0 )
+ memset(ss->prev_row + ss->bpp, 0, ss->row_count);
+ ss->row_left = 0;
+ return 0;
+}
+
+/* Initialize PNGPredictorEncode filter. */
+private int
+s_pngp_init(stream_state *st, bool need_prev)
+{ int bits_per_pixel = ss->Colors * ss->BitsPerComponent;
+ long bits_per_row = (long)bits_per_pixel * ss->Columns;
+ byte *prev_row = 0;
+
+#if arch_sizeof_long > arch_sizeof_int
+ if ( bits_per_row > max_uint * 7L )
+ return ERRC; /****** WRONG ******/
+#endif
+ ss->row_count = (uint)((bits_per_row + 7) >> 3);
+ ss->end_mask = (1 << (-bits_per_row & 7)) - 1;
+ ss->bpp = (bits_per_pixel + 7) >> 3;
+ if ( need_prev )
+ { prev_row = gs_alloc_bytes(st->memory, ss->bpp + ss->row_count,
+ "PNGPredictor prev row");
+ if ( prev_row == 0 )
+ return ERRC; /****** WRONG ******/
+ memset(prev_row, 0, ss->bpp);
+ }
+ ss->prev_row = prev_row;
+ /* case_index is only preset for encoding */
+ return s_PNGP_reinit(st);
+}
+
+/* Initialize PNGPredictorEncode filter. */
+private int
+s_PNGPE_init(stream_state *st)
+{ return s_pngp_init(st, pngp_case_needs_prev[ss->Predictor - cNone]);
+}
+
+/* Initialize PNGPredictorDecode filter. */
+private int
+s_PNGPD_init(stream_state *st)
+{ return s_pngp_init(st, true);
+}
+
+/*
+ * Process a partial buffer. We pass in current and previous pointers
+ * to both the current and preceding scan line. Note that dprev is
+ * p - bpp for encoding, q - bpp for decoding; similarly, the 'up' row
+ * is the raw data for encoding, the filtered data for decoding.
+ * Note also that the case_index cannot be cOptimum.
+ */
+private int
+paeth_predictor(int a, int b, int c)
+{
+#define any_abs(u) ((u) < 0 ? -(u) : (u))
+ int px = a + b - c;
+ int pa = any_abs(px - a), pb = any_abs(px - b), pc = any_abs(px - c);
+ return (pa <= pb && pa <= pc ? a : pb <= pc ? b : c);
+#undef any_abs
+}
+private void
+s_pngp_process(stream_state *st, stream_cursor_write *pw,
+ const byte *dprev, stream_cursor_read *pr,
+ const byte *upprev, const byte *up, uint count)
+{ byte *q = pw->ptr + 1;
+ const byte *p = pr->ptr + 1;
+
+ pr->ptr += count;
+ pw->ptr += count;
+ ss->row_left -= count;
+ switch ( ss->case_index )
+ {
+ case cEncode + cNone:
+ case cDecode + cNone:
+ memcpy(q, p, count);
+ break;
+ case cEncode + cSub:
+ for ( ; count; ++q, ++dprev, ++p, --count )
+ *q = (byte)(*p - *dprev);
+ break;
+ case cDecode + cSub:
+ for ( ; count; ++q, ++dprev, ++p, --count )
+ *q = (byte)(*p + *dprev);
+ break;
+ case cEncode + cUp:
+ for ( ; count; ++q, ++up, ++p, --count )
+ *q = (byte)(*p - *up);
+ break;
+ case cDecode + cUp:
+ for ( ; count; ++q, ++up, ++p, --count )
+ *q = (byte)(*p + *up);
+ break;
+ case cEncode + cAverage:
+ for ( ; count; ++q, ++dprev, ++up, ++p, --count )
+ *q = (byte)(*p - arith_rshift_1((int)*dprev + (int)*up));
+ break;
+ case cDecode + cAverage:
+ for ( ; count; ++q, ++dprev, ++up, ++p, --count )
+ *q = (byte)(*p + arith_rshift_1((int)*dprev + (int)*up));
+ break;
+ case cEncode + cPaeth:
+ for ( ; count; ++q, ++dprev, ++up, ++upprev, ++p, --count )
+ *q = (byte)(*p - paeth_predictor(*dprev, *up, *upprev));
+ break;
+ case cDecode + cPaeth:
+ for ( ; count; ++q, ++dprev, ++up, ++upprev, ++p, --count )
+ *q = (byte)(*p + paeth_predictor(*dprev, *up, *upprev));
+ break;
+ }
+}
+
+/* Calculate the number of bytes for the next processing step, */
+/* the min of (input data, output data, remaining row length). */
+private uint near
+s_pngp_count(const stream_state *st_const, const stream_cursor_read *pr,
+ const stream_cursor_write *pw)
+{ uint rcount = pr->limit - pr->ptr;
+ uint wcount = pw->limit - pw->ptr;
+ uint row_left = ss_const->row_left;
+
+ if ( rcount < row_left )
+ row_left = rcount;
+ if ( wcount < row_left )
+ row_left = wcount;
+ return row_left;
+}
+
+/*
+ * Encode a buffer. Let N = ss->row_count, P = N - ss->row_left,
+ * and B = ss->bpp. Consider that bytes [-B .. -1] of every row are zero.
+ * Then:
+ * prev_row[0 .. P - 1] contain bytes -B .. P - B - 1
+ * of the current input row.
+ * ss->prev[0 .. B - 1] contain bytes P - B .. P - 1
+ * of the current input row.
+ * prev_row[P .. N + B - 1] contain bytes P - B .. N - 1
+ * of the previous input row.
+ */
+private int
+optimum_predictor(const stream_state *st, const stream_cursor_read *pr)
+{ return cSub;
+}
+private int
+s_PNGPE_process(stream_state *st, stream_cursor_read *pr,
+ stream_cursor_write *pw, bool last)
+{ int bpp = ss->bpp;
+ int code = 0;
+ while ( pr->ptr < pr->limit )
+ { uint count;
+ if ( ss->row_left == 0 )
+ { /* Beginning of row, write algorithm byte. */
+ int predictor;
+ if ( pw->ptr >= pw->limit )
+ { code = 1;
+ break;
+ }
+ predictor =
+ (ss->Predictor == cOptimum ?
+ optimum_predictor(st, pr) :
+ ss->Predictor);
+ *++(pw->ptr) = (byte)predictor - cNone;
+ ss->case_index = predictor + cEncode;
+ ss->row_left = ss->row_count;
+ memset(ss->prev, 0, bpp);
+ continue;
+ }
+ count = s_pngp_count(st, pr, pw);
+ if ( count == 0 )
+ { /* We know we have input, so output must be full. */
+ code = 1;
+ break;
+ }
+ { byte *up = ss->prev_row + bpp + ss->row_count - ss->row_left;
+ uint n = min(count, bpp);
+
+ /* Process bytes whose predecessors are in prev. */
+ s_pngp_process(st, pw, ss->prev, pr, up - bpp, up, n);
+ if ( ss->prev_row )
+ memcpy(up - bpp, ss->prev, n);
+ if ( n < bpp )
+ { /* We didn't have enough data to use up all of prev. */
+ /* Shift more data into prev and exit. */
+ int prev_left = bpp - n;
+ memmove(ss->prev, ss->prev + n, prev_left);
+ memcpy(ss->prev + prev_left, pr->ptr - (n - 1), n);
+ break;
+ }
+ /* Process bytes whose predecessors are in the input. */
+ /* We know we have at least bpp input and output bytes, */
+ /* and that n = bpp. */
+ count -= bpp;
+ s_pngp_process(st, pw, pr->ptr - (bpp - 1), pr,
+ up, up + bpp, count);
+ memcpy(ss->prev, pr->ptr - (bpp - 1), bpp);
+ if ( ss->prev_row )
+ { memcpy(up, pr->ptr - (bpp + count - 1), count);
+ if ( ss->row_left == 0 )
+ memcpy(up + count, ss->prev, bpp);
+ }
+ }
+ }
+ return code;
+}
+
+/*
+ * Decode a buffer. Let N = ss->row_count, P = N - ss->row_left,
+ * and B = ss->bpp. Consider that bytes [-B .. -1] of every row are zero.
+ * Then:
+ * prev_row[0 .. P - 1] contain bytes -B .. P - B - 1
+ * of the current output row.
+ * ss->prev[0 .. B - 1] contain bytes P - B .. P - 1
+ * of the current output row.
+ * prev_row[P .. N + B - 1] contain bytes P - B .. N - 1
+ * of the previous output row.
+ */
+private int
+s_PNGPD_process(stream_state *st, stream_cursor_read *pr,
+ stream_cursor_write *pw, bool last)
+{ int bpp = ss->bpp;
+ int code = 0;
+ while ( pr->ptr < pr->limit )
+ { uint count;
+ if ( ss->row_left == 0 )
+ { /* Beginning of row, read algorithm byte. */
+ int predictor = pr->ptr[1];
+ if ( predictor >= cOptimum - cNone )
+ { code = ERRC;
+ break;
+ }
+ pr->ptr++;
+ ss->case_index = predictor + cNone + cDecode;
+ ss->row_left = ss->row_count;
+ memset(ss->prev, 0, bpp);
+ continue;
+ }
+ count = s_pngp_count(st, pr, pw);
+ if ( count == 0 )
+ { /* We know we have input, so output must be full. */
+ code = 1;
+ break;
+ }
+ { byte *up = ss->prev_row + bpp + ss->row_count - ss->row_left;
+ uint n = min(count, bpp);
+
+ /* Process bytes whose predecessors are in prev. */
+ s_pngp_process(st, pw, ss->prev, pr, up - bpp, up, n);
+ if ( ss->prev_row )
+ memcpy(up - bpp, ss->prev, n);
+ if ( n < bpp )
+ { /* We didn't have enough data to use up all of prev. */
+ /* Shift more data into prev and exit. */
+ int prev_left = bpp - n;
+ memmove(ss->prev, ss->prev + n, prev_left);
+ memcpy(ss->prev + prev_left, pw->ptr - (n - 1), n);
+ break;
+ }
+ /* Process bytes whose predecessors are in the output. */
+ /* We know we have at least bpp input and output bytes, */
+ /* and that n = bpp. */
+ count -= bpp;
+ s_pngp_process(st, pw, pw->ptr - (bpp - 1), pr,
+ up, up + bpp, count);
+ memcpy(ss->prev, pw->ptr - (bpp - 1), bpp);
+ if ( ss->prev_row )
+ { memcpy(up, pw->ptr - (bpp + count - 1), count);
+ if ( ss->row_left == 0 )
+ memcpy(up + count, ss->prev, bpp);
+ }
+ }
+ }
+ return code;
+}
+
+/* Stream templates */
+const stream_template s_PNGPE_template =
+{ &st_PNGP_state, s_PNGPE_init, s_PNGPE_process, 1, 1, 0/*NULL*/,
+ s_PNGP_set_defaults, s_PNGP_reinit
+};
+const stream_template s_PNGPD_template =
+{ &st_PNGP_state, s_PNGPD_init, s_PNGPD_process, 1, 1, 0/*NULL*/,
+ s_PNGP_set_defaults, s_PNGP_reinit
+};
diff --git a/pstoraster/spngpx.h b/pstoraster/spngpx.h
new file mode 100644
index 000000000..363ab0c61
--- /dev/null
+++ b/pstoraster/spngpx.h
@@ -0,0 +1,55 @@
+/* Copyright (C) 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* spngpx.h */
+/* PNG pixel prediction filter state definition */
+/* Requires strimpl.h */
+
+/* PNGPredictorDecode / PNGPredictorEncode */
+typedef struct stream_PNGP_state_s {
+ stream_state_common;
+ /* The client sets the following before initialization. */
+ int Colors; /* # of colors, 1..16 */
+ int BitsPerComponent; /* 1, 2, 4, 8, 16 */
+ uint Columns; /* >0 */
+ int Predictor; /* 10-15, only relevant for Encode */
+ /* The init procedure computes the following. */
+ uint row_count; /* # of bytes per row */
+ byte end_mask; /* mask for left-over bits in last byte */
+ int bpp; /* bytes per pixel */
+ byte *prev_row; /* previous row */
+ int case_index; /* switch index for case dispatch, */
+ /* set dynamically when decoding */
+ /* The following are updated dynamically. */
+ long row_left; /* # of bytes left in row */
+ byte prev[32]; /* previous samples */
+} stream_PNGP_state;
+#define private_st_PNGP_state() /* in sPNGP.c */\
+ gs_private_st_ptrs1(st_PNGP_state, stream_PNGP_state,\
+ "PNGPredictorEncode/Decode state", pngp_enum_ptrs, pngp_reloc_ptrs,\
+ prev_row)
+#define s_PNGP_set_defaults_inline(ss)\
+ ((ss)->Colors = 1, (ss)->BitsPerComponent = 8, (ss)->Columns = 1,\
+ (ss)->Predictor = 15)
+extern const stream_template s_PNGPD_template;
+extern const stream_template s_PNGPE_template;
diff --git a/pstoraster/srld.c b/pstoraster/srld.c
new file mode 100644
index 000000000..3988d8cc4
--- /dev/null
+++ b/pstoraster/srld.c
@@ -0,0 +1,100 @@
+/* Copyright (C) 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* srld.c */
+/* RunLengthDecode filter */
+#include "stdio_.h" /* includes std.h */
+#include "memory_.h"
+#include "strimpl.h"
+#include "srlx.h"
+
+/* ------ RunLengthDecode ------ */
+
+private_st_RLD_state();
+
+#define ss ((stream_RLD_state *)st)
+
+/* Set defaults */
+private void
+s_RLD_set_defaults(stream_state *st)
+{ s_RLD_set_defaults_inline(ss);
+}
+
+/* Refill the buffer */
+private int
+s_RLD_process(stream_state *st, stream_cursor_read *pr,
+ stream_cursor_write *pw, bool last)
+{ register const byte *p = pr->ptr;
+ register byte *q = pw->ptr;
+ const byte *rlimit = pr->limit;
+ byte *wlimit = pw->limit;
+ int status = 0;
+
+ while ( p < rlimit )
+ { int b = *++p;
+ if ( b < 128 )
+ { if ( b >= rlimit - p )
+ { p--;
+ break;
+ }
+ else if ( b >= wlimit - q )
+ { p--;
+ status = 1;
+ break;
+ }
+ memcpy(q + 1, p + 1, ++b);
+ p += b;
+ q += b;
+ }
+ else if ( b == 128 ) /* end of data */
+ { if ( ss->EndOfData )
+ { status = EOFC;
+ break;
+ }
+ }
+ else if ( p == rlimit )
+ { p--;
+ break;
+ }
+ else if ( (b = 257 - b) > wlimit - q )
+ { p--;
+ status = 1;
+ break; /* won't fit */
+ }
+ else
+ { memset(q + 1, *++p, b);
+ q += b;
+ }
+ }
+ pr->ptr = p;
+ pw->ptr = q;
+ return status;
+}
+
+#undef ss
+
+/* Stream template */
+const stream_template s_RLD_template =
+{ &st_RLD_state, NULL, s_RLD_process, 129, 128, NULL,
+ s_RLD_set_defaults
+};
diff --git a/pstoraster/srle.c b/pstoraster/srle.c
new file mode 100644
index 000000000..a5e56927d
--- /dev/null
+++ b/pstoraster/srle.c
@@ -0,0 +1,120 @@
+/* Copyright (C) 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* srle.c */
+/* RunLengthEncode filter */
+#include "stdio_.h" /* includes std.h */
+#include "memory_.h"
+#include "strimpl.h"
+#include "srlx.h"
+
+/* ------ RunLengthEncode ------ */
+
+private_st_RLE_state();
+
+#define ss ((stream_RLE_state *)st)
+
+/* Set defaults */
+private void
+s_RLE_set_defaults(stream_state *st)
+{ s_RLE_set_defaults_inline(ss);
+}
+
+/* Initialize */
+private int
+s_RLE_init(stream_state *st)
+{ return s_RLE_init_inline(ss);
+}
+
+/* Process a buffer */
+private int
+s_RLE_process(stream_state *st, stream_cursor_read *pr,
+ stream_cursor_write *pw, bool last)
+{ register const byte *p = pr->ptr;
+ register byte *q = pw->ptr;
+ const byte *rlimit = pr->limit;
+ byte *wlimit = pw->limit;
+ int status = 0;
+ ulong rleft = ss->record_left;
+ while ( p < rlimit )
+ { const byte *beg = p;
+ const byte *p1;
+ uint count = rlimit - p;
+ byte next;
+ if ( count > rleft )
+ count = rleft;
+ if ( count > 127 )
+ count = 127;
+ p1 = p + count;
+ if ( count > 2 && (next = p[1]) == p[2] && next == p[3] )
+ { if ( wlimit - q < 2 )
+ { status = 1;
+ break;
+ }
+ /* Recognize leading repeated byte */
+ p1--;
+ do { p++; }
+ while ( p < p1 && p[2] == next );
+ p++;
+ *++q = (byte)(257 - (p - beg));
+ *++q = next;
+ }
+ else
+ { p1 -= 2;
+ while ( p < p1 && (p[2] != p[1] || p[3] != p[1]) )
+ p++;
+ if ( p >= p1 )
+ p = p1 + 2;
+ count = p - beg;
+ if ( wlimit - q < count + 1 )
+ { p = beg;
+ status = 1;
+ break;
+ }
+ *++q = count - 1;
+ memcpy(q + 1, beg + 1, count);
+ q += count;
+ }
+ rleft -= p - beg;
+ if ( rleft == 0 )
+ rleft = ss->record_size;
+ }
+ if ( last && status == 0 && ss->EndOfData )
+ { if ( q < wlimit )
+ *++q = 128;
+ else
+ status = 1;
+ }
+ pr->ptr = p;
+ pw->ptr = q;
+ ss->record_left = rleft;
+ return status;
+}
+
+#undef ss
+
+/* Stream template */
+const stream_template s_RLE_template =
+{ &st_RLE_state, s_RLE_init, s_RLE_process, 128, 129, NULL,
+ s_RLE_set_defaults, s_RLE_init
+};
diff --git a/pstoraster/srlx.h b/pstoraster/srlx.h
new file mode 100644
index 000000000..35f3165a1
--- /dev/null
+++ b/pstoraster/srlx.h
@@ -0,0 +1,64 @@
+/* Copyright (C) 1994, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* srlx.h */
+/* Definitions for RLE/RLD streams */
+/* Requires scommon.h; strimpl.h if any templates are referenced */
+
+/* Common state */
+#define stream_RL_state_common\
+ stream_state_common;\
+ bool EndOfData /* true if 128 = EOD */
+
+/* RunLengthEncode */
+typedef struct stream_RLE_state_s {
+ stream_RL_state_common;
+ /* The following parameters are set by the client. */
+ ulong record_size;
+ /* The following change dynamically. */
+ ulong record_left; /* bytes left in current record */
+} stream_RLE_state;
+#define private_st_RLE_state() /* in srle.c */\
+ gs_private_st_simple(st_RLE_state, stream_RLE_state, "RunLengthEncode state")
+/* We define the initialization procedure here, so that clients */
+/* can avoid a procedure call. */
+#define s_RLE_set_defaults_inline(ss)\
+ ((ss)->EndOfData = true, (ss)->record_size = 0)
+#define s_RLE_init_inline(ss)\
+ ((ss)->record_left = ((ss)->record_size == 0 ?\
+ ((ss)->record_size = max_uint) :\
+ ((ss)->record_size)), 0)
+extern const stream_template s_RLE_template;
+
+/* RunLengthDecode */
+typedef struct stream_RLD_state_s {
+ stream_RL_state_common;
+} stream_RLD_state;
+#define private_st_RLD_state() /* in srld.c */\
+ gs_private_st_simple(st_RLD_state, stream_RLD_state, "RunLengthDecode state")
+/* We define the initialization procedure here, so that clients */
+/* can avoid a procedure call. */
+#define s_RLD_set_defaults_inline(ss)\
+ ((ss)->EndOfData = true)
+#define s_RLD_init_inline(ss) DO_NOTHING
+extern const stream_template s_RLD_template;
diff --git a/pstoraster/sstring.c b/pstoraster/sstring.c
new file mode 100644
index 000000000..8659f5daf
--- /dev/null
+++ b/pstoraster/sstring.c
@@ -0,0 +1,434 @@
+/* Copyright (C) 1993, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* sstring.c */
+/* String and hexstring streams (filters) */
+#include "stdio_.h" /* includes std.h */
+#include "memory_.h"
+#include "string_.h"
+#include "strimpl.h"
+#include "sstring.h"
+#include "scanchar.h"
+
+/* ------ ASCIIHexEncode ------ */
+
+/* Process a buffer */
+private int
+s_AXE_process(stream_state *st, stream_cursor_read *pr,
+ stream_cursor_write *pw, bool last)
+{ register const byte *p = pr->ptr;
+ register byte *q = pw->ptr;
+ int rcount = pr->limit - p;
+ int wcount = pw->limit - q;
+ register int count;
+ register const char _ds *hex_digits = "0123456789abcdef";
+ int status = 0;
+
+ if ( last )
+ wcount--; /* leave room for '>' */
+ wcount -= (wcount + 64) / 65; /* leave room for \n */
+ wcount >>= 1; /* 2 chars per input byte */
+ count = (wcount < rcount ? (status = 1, wcount) : rcount);
+ while ( --count >= 0 )
+ { *++q = hex_digits[*++p >> 4];
+ *++q = hex_digits[*p & 0xf];
+ if ( !(count & 31) && (count != 0 || !last) )
+ *++q = '\n';
+ }
+ if ( last && status == 0 )
+ *++q = '>';
+ pr->ptr = p;
+ pw->ptr = q;
+ return status;
+}
+
+/* Stream template */
+const stream_template s_AXE_template =
+{ &st_stream_state, NULL, s_AXE_process, 1, 3
+};
+
+/* ------ ASCIIHexDecode ------ */
+
+private_st_AXD_state();
+
+#define ss ((stream_AXD_state *)st)
+
+/* Initialize the state */
+private int
+s_AXD_init(stream_state *st)
+{ return s_AXD_init_inline(ss);
+}
+
+/* Process a buffer */
+private int
+s_AXD_process(stream_state *st, stream_cursor_read *pr,
+ stream_cursor_write *pw, bool last)
+{ int code = s_hex_process(pr, pw, &ss->odd, hex_ignore_whitespace);
+ switch ( code )
+ {
+ case 0:
+ if ( ss->odd >= 0 && last )
+ { if ( pw->ptr == pw->limit )
+ return 1;
+ *++(pw->ptr) = ss->odd << 4;
+ }
+ /* falls through */
+ case 1:
+ /* We still need to read ahead and check for EOD. */
+ for ( ; pr->ptr < pr->limit; pr->ptr++ )
+ if ( scan_char_decoder[pr->ptr[1]] != ctype_space )
+ { if ( pr->ptr[1] == '>' )
+ { pr->ptr++;
+ goto eod;
+ }
+ return 1;
+ }
+ return 0; /* still need to scan ahead */
+ default:
+ return code;
+ case ERRC:
+ ;
+ }
+ /* Check for EOD. ERRC implies at least one more character */
+ /* was read. */
+ if ( *pr->ptr != '>' ) /* EOD */
+ return ERRC;
+eod: if ( ss->odd >= 0 )
+ { if ( pw->ptr == pw->limit )
+ return 1;
+ *++(pw->ptr) = ss->odd << 4;
+ }
+ return EOFC;
+}
+
+#undef ss
+
+/* Stream template */
+const stream_template s_AXD_template =
+{ &st_AXD_state, s_AXD_init, s_AXD_process, 2, 1
+};
+
+/* ------ PSStringEncode ------ */
+
+/* Process a buffer */
+private int
+s_PSSE_process(stream_state *st, stream_cursor_read *pr,
+ stream_cursor_write *pw, bool last)
+{ register const byte *p = pr->ptr;
+ const byte *rlimit = pr->limit;
+ register byte *q = pw->ptr;
+ byte *wlimit = pw->limit;
+ int status = 0;
+ /* This doesn't have to be very efficient. */
+ while ( p < rlimit )
+ { register int c = *++p;
+ if ( c < 32 || c >= 127 )
+ { const char *pesc;
+ const char *esc = "\n\r\t\b\f";
+ if ( c < 32 && c != 0 && (pesc = strchr(esc, c)) != 0 )
+ { if ( wlimit - q < 2 )
+ { --p;
+ status = 1;
+ break;
+ }
+ *++q = '\\';
+ *++q = "nrtbf"[pesc - esc];
+ continue;
+ }
+ if ( wlimit - q < 4 )
+ { --p;
+ status = 1;
+ break;
+ }
+ q[1] = '\\';
+ q[2] = (c >> 6) + '0';
+ q[3] = ((c >> 3) & 7) + '0';
+ q[4] = (c & 7) + '0';
+ q += 4;
+ continue;
+ }
+ else if ( c == '(' || c == ')' || c == '\\' )
+ { if ( wlimit - q < 2 )
+ { --p;
+ status = 1;
+ break;
+ }
+ *++q = '\\';
+ }
+ else
+ { if ( q == wlimit )
+ { --p;
+ status = 1;
+ break;
+ }
+ }
+ *++q = c;
+ }
+ if ( last && status == 0 )
+ { if ( q == wlimit )
+ status = 1;
+ else
+ *++q = ')';
+ }
+ pr->ptr = p;
+ pw->ptr = q;
+ return status;
+}
+
+/* Stream template */
+const stream_template s_PSSE_template =
+{ &st_stream_state, NULL, s_PSSE_process, 1, 4
+};
+
+/* ------ PSStringDecode ------ */
+
+private_st_PSSD_state();
+
+#define ss ((stream_PSSD_state *)st)
+
+/* Initialize the state */
+private int
+s_PSSD_init(stream_state *st)
+{ return s_PSSD_init_inline(ss);
+}
+
+/* Process a buffer */
+private int
+s_PSSD_process(stream_state *st, stream_cursor_read *pr,
+ stream_cursor_write *pw, bool last)
+{ register const byte *p = pr->ptr;
+ const byte *rlimit = pr->limit;
+ register byte *q = pw->ptr;
+ byte *wlimit = pw->limit;
+ int status = 0;
+ register int c;
+#define check_p(n)\
+ if ( p == rlimit ) { p -= n; goto out; }
+#define check_q(n)\
+ if ( q == wlimit ) { p -= n; status = 1; goto out; }
+ while ( p < rlimit )
+ { c = *++p;
+ if ( c == '\\' && !ss->from_string )
+ { check_p(1);
+ switch ( (c = *++p) )
+ {
+ case 'n':
+ c = '\n';
+ goto put;
+ case 'r':
+ c = '\r';
+ goto put;
+ case 't':
+ c = '\t';
+ goto put;
+ case 'b':
+ c = '\b';
+ goto put;
+ case 'f':
+ c = '\f';
+ goto put;
+ default: /* ignore the \ */
+put: check_q(2);
+ *++q = c;
+ continue;
+ case char_CR: /* ignore, check for following \n */
+ check_p(2);
+ if ( p[1] == char_EOL )
+ p++;
+ continue;
+ case char_EOL: /* ignore */
+ continue;
+ case '0': case '1': case '2': case '3':
+ case '4': case '5': case '6': case '7':
+ { int d;
+ check_p(2);
+ d = p[1];
+ c -= '0';
+ if ( d >= '0' && d <= '7' )
+ { if ( p + 1 == rlimit )
+ { p -= 2;
+ goto out;
+ }
+ check_q(2);
+ c = (c << 3) + d - '0';
+ d = p[2];
+ if ( d >= '0' && d <= '7' )
+ { c = (c << 3) + d - '0';
+ p += 2;
+ }
+ else
+ p++;
+ }
+ else
+ check_q(2);
+ *++q = c;
+ continue;
+ }
+ }
+ }
+ else
+ switch ( c )
+ {
+ case '(':
+ check_q(1);
+ ss->depth++;
+ break;
+ case ')':
+ if ( ss->depth == 0 )
+ { status = EOFC;
+ goto out;
+ }
+ check_q(1);
+ ss->depth--;
+ break;
+ case char_CR: /* convert to \n */
+ check_p(1);
+ check_q(1);
+ if ( p[1] == char_EOL )
+ p++;
+ *++q = '\n';
+ continue;
+ case char_EOL:
+ c = '\n';
+ default:
+ check_q(1);
+ break;
+ }
+ *++q = c;
+ }
+#undef check_p
+#undef check_q
+out: pr->ptr = p;
+ pw->ptr = q;
+ if ( last && status == 0 && p != rlimit )
+ status = ERRC;
+ return status;
+}
+
+#undef ss
+
+/* Stream template */
+const stream_template s_PSSD_template =
+{ &st_PSSD_state, s_PSSD_init, s_PSSD_process, 4, 1
+};
+
+/* ------ Utilities ------ */
+
+/*
+ * Convert hex data to binary. Return 1 if we filled the string, 0 if
+ * we ran out of input data before filling the string, or ERRC on error.
+ * The caller must set *odd_digit to -1 before the first call;
+ * after each call, if an odd number of hex digits has been read (total),
+ * *odd_digit is the odd digit value, otherwise *odd_digit = -1.
+ * See strimpl.h for the definition of syntax.
+ */
+int
+s_hex_process(stream_cursor_read *pr, stream_cursor_write *pw,
+ int *odd_digit, hex_syntax syntax)
+{ const byte *p = pr->ptr;
+ const byte *rlimit = pr->limit;
+ byte *q = pw->ptr;
+ byte *wlimit = pw->limit;
+ byte *q0 = q;
+ byte val1 = (byte)*odd_digit;
+ byte val2;
+ uint rcount;
+ byte *flimit;
+ register const byte _ds *decoder = scan_char_decoder;
+ int code = 0;
+ if ( q >= wlimit )
+ return 1;
+ if ( val1 <= 0xf )
+ goto d2;
+d1: if ( (rcount = (rlimit - p) >> 1) == 0 )
+ goto x1;
+ /* Set up a fast end-of-loop check, so we don't have to test */
+ /* both p and q against their respective limits. */
+ flimit = (rcount < wlimit - q ? q + rcount : wlimit);
+f1: if ( (val1 = decoder[p[1]]) <= 0xf &&
+ (val2 = decoder[p[2]]) <= 0xf
+ )
+ { p += 2;
+ *++q = (val1 << 4) + val2;
+ if ( q < flimit )
+ goto f1;
+ if ( q >= wlimit )
+ goto px;
+ }
+x1: if ( p >= rlimit )
+ goto end1;
+ if ( (val1 = decoder[*++p]) > 0xf )
+ { if ( val1 == ctype_space )
+ { switch ( syntax )
+ {
+ case hex_ignore_whitespace:
+ goto x1;
+ case hex_ignore_leading_whitespace:
+ if ( q == q0 && *odd_digit < 0 )
+ goto x1;
+ --p;
+ code = 1;
+ goto end1;
+ case hex_ignore_garbage:
+ goto x1;
+ }
+ }
+ else if ( syntax == hex_ignore_garbage )
+ goto x1;
+ code = ERRC;
+ goto end1;
+ }
+d2: if ( p >= rlimit )
+ { *odd_digit = val1;
+ goto ended;
+ }
+ if ( (val2 = decoder[*++p]) > 0xf )
+ { if ( val2 == ctype_space )
+ switch ( syntax )
+ {
+ case hex_ignore_whitespace:
+ goto d2;
+ case hex_ignore_leading_whitespace:
+ if ( q == q0 )
+ goto d2;
+ --p;
+ *odd_digit = val1;
+ code = 1;
+ goto ended;
+ case hex_ignore_garbage: /* pacify compilers */
+ ;
+ }
+ if ( syntax == hex_ignore_garbage )
+ goto d2;
+ *odd_digit = val1;
+ code = ERRC;
+ goto ended;
+ }
+ *++q = (val1 << 4) + val2;
+ if ( q < wlimit ) goto d1;
+px: code = 1;
+end1: *odd_digit = -1;
+ended: pr->ptr = p;
+ pw->ptr = q;
+ return code;
+}
diff --git a/pstoraster/sstring.h b/pstoraster/sstring.h
new file mode 100644
index 000000000..d4cd9ca31
--- /dev/null
+++ b/pstoraster/sstring.h
@@ -0,0 +1,66 @@
+/* Copyright (C) 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* sstring.h */
+/* Definitions for string encoding/decoding streams */
+/* Requires scommon.h; should require strimpl.h only if any templates */
+/* are referenced, but some compilers always require strimpl.h. */
+
+/* ASCIIHexEncode */
+/* (no state) */
+extern const stream_template s_AXE_template;
+
+/* ASCIIHexDecode */
+typedef struct stream_AXD_state_s {
+ stream_state_common;
+ int odd; /* odd digit */
+} stream_AXD_state;
+#define private_st_AXD_state() /* in sstring.c */\
+ gs_private_st_simple(st_AXD_state, stream_AXD_state,\
+ "ASCIIHexDecode state")
+/* We define the initialization procedure here, so that the scanner */
+/* can avoid a procedure call. */
+#define s_AXD_init_inline(ss)\
+ ((ss)->odd = -1, 0)
+extern const stream_template s_AXD_template;
+
+/* PSStringDecode */
+typedef struct stream_PSSD_state_s {
+ stream_state_common;
+ /* The following are set by the client. */
+ bool from_string; /* true if using Level 1 \ convention */
+ /* The following change dynamically. */
+ int depth;
+} stream_PSSD_state;
+#define private_st_PSSD_state() /* in sstring.c */\
+ gs_private_st_simple(st_PSSD_state, stream_PSSD_state, "PSStringDecode state")
+/* We define the initialization procedure here, so that the scanner */
+/* can avoid a procedure call. */
+#define s_PSSD_init_inline(ss)\
+ ((ss)->depth = 0)
+extern const stream_template s_PSSD_template;
+
+/* PSStringEncode */
+/* (no state) */
+extern const stream_template s_PSSE_template;
+
diff --git a/pstoraster/stat_.h b/pstoraster/stat_.h
new file mode 100644
index 000000000..5a676b46a
--- /dev/null
+++ b/pstoraster/stat_.h
@@ -0,0 +1,51 @@
+/* Copyright (C) 1991, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* stat_.h */
+/* Generic substitute for Unix sys/stat.h */
+
+/* We must include std.h before any file that includes sys/types.h. */
+#include "std.h"
+#include <sys/stat.h>
+
+/* Many environments, including the MS-DOS compilers, don't define */
+/* the st_blocks member of a stat structure. */
+#if defined(__SVR3) || defined(__EMX__) || defined(__DVX__) || defined(OSK) || defined(__MSDOS__) || defined(__QNX__) || defined(VMS) || defined(__WIN32__) || defined(__IBMC__)
+# define stat_blocks(psbuf) (((psbuf)->st_size + 1023) >> 10)
+#else
+# define stat_blocks(psbuf) ((psbuf)->st_blocks)
+#endif
+
+/* Microsoft C uses _stat instead of stat, */
+/* for both the function name and the structure name. */
+#ifdef _MSC_VER
+# define stat _stat
+#endif
+
+/* Some (System V?) systems test for directories */
+/* in a slightly different way. */
+#if defined(OSK) || !defined(S_ISDIR)
+# define stat_is_dir(stbuf) ((stbuf).st_mode & S_IFDIR)
+#else
+# define stat_is_dir(stbuf) S_ISDIR((stbuf).st_mode)
+#endif
diff --git a/pstoraster/std.h b/pstoraster/std.h
new file mode 100644
index 000000000..f76b74b5c
--- /dev/null
+++ b/pstoraster/std.h
@@ -0,0 +1,225 @@
+/* Copyright (C) 1989, 1992, 1993, 1994, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* std.h */
+/* Standard definitions for Aladdin Enterprises code */
+
+#ifndef std_INCLUDED
+# define std_INCLUDED
+
+#include "stdpre.h"
+
+/* Include the architecture definitions. */
+#include "arch.h"
+
+/* Define integer data type sizes in terms of log2s. */
+#define arch_sizeof_short (1 << arch_log2_sizeof_short)
+#define arch_sizeof_int (1 << arch_log2_sizeof_int)
+#define arch_sizeof_long (1 << arch_log2_sizeof_long)
+#define arch_ints_are_short (arch_sizeof_int == arch_sizeof_short)
+
+/* Define whether we are on a large- or small-memory machine. */
+/* Currently, we assume small memory and 16-bit ints are synonymous. */
+#define arch_small_memory (arch_sizeof_int <= 2)
+
+/* Define unsigned 16- and 32-bit types. These are needed in */
+/* a surprising number of places that do bit manipulation. */
+#if arch_sizeof_short == 2 /* no plausible alternative! */
+typedef ushort bits16;
+#endif
+#if arch_sizeof_int == 4
+typedef uint bits32;
+#else
+# if arch_sizeof_long == 4
+typedef ulong bits32;
+# endif
+#endif
+
+/* Minimum and maximum values for the signed types. */
+/* Avoid casts, to make them acceptable to strict ANSI compilers. */
+#define min_short (-1 << (arch_sizeof_short * 8 - 1))
+#define max_short (~min_short)
+#define min_int (-1 << (arch_sizeof_int * 8 - 1))
+#define max_int (~min_int)
+#define min_long (-1L << (arch_sizeof_long * 8 - 1))
+#define max_long (~min_long)
+
+/*
+ * The maximum values for the unsigned types are defined in arch.h,
+ * because so many compilers handle unsigned constants wrong.
+ * In particular, most of the DEC VMS compilers incorrectly sign-extend
+ * short unsigned constants (but not short unsigned variables) when
+ * widening them to longs. We program around this on a case-by-case basis.
+ * Some compilers don't truncate constants when they are cast down.
+ * The UTek compiler does special weird things of its own.
+ * All the rest (including gcc on all platforms) do the right thing.
+ */
+#define max_uchar arch_max_uchar
+#define max_ushort arch_max_ushort
+#define max_uint arch_max_uint
+#define max_ulong arch_max_ulong
+
+/* Minimum and maximum values for pointers. */
+#if arch_ptrs_are_signed
+# define min_ptr min_long
+# define max_ptr max_long
+#else
+# define min_ptr ((ulong)0)
+# define max_ptr max_ulong
+#endif
+
+/*
+ * Define whether pointers are segmented. If they are, we assume that
+ * the compiler can't do reasonable register assignment for pointers,
+ * so sometimes we use in-line casts instead of assignment to
+ * a logically redundant pointer of the proper type.
+ */
+#define arch_ptrs_are_segmented (arch_sizeof_ds_ptr < arch_sizeof_ptr)
+
+/* Define a reliable arithmetic right shift. */
+/* Must use arith_rshift_1 for a shift by a literal 1. */
+#define arith_rshift_slow(x,n) ((x) < 0 ? ~(~(x) >> (n)) : (x) >> (n))
+#if arch_arith_rshift == 2
+# define arith_rshift(x,n) ((x) >> (n))
+# define arith_rshift_1(x) ((x) >> 1)
+#else
+#if arch_arith_rshift == 1 /* OK except for n=1 */
+# define arith_rshift(x,n) ((x) >> (n))
+# define arith_rshift_1(x) arith_rshift_slow(x,1)
+#else
+# define arith_rshift(x,n) arith_rshift_slow(x,n)
+# define arith_rshift_1(x) arith_rshift_slow(x,1)
+#endif
+#endif
+
+/*
+ * Standard error printing macros.
+ * Use dprintf for messages that just go to dstderr,
+ * eprintf for error messages to estderr that include the program name,
+ * lprintf for debugging messages that should include line number info.
+ * Since we intercept fprintf to redirect output under MS Windows,
+ * we have to define dputc and dputs in terms of fprintf also.
+ * We also define eprintf and lprintf in a way that allows us to
+ * intercept all calls to them by redefining eprintf_program_name
+ * and lprintf_file_and_line.
+ */
+
+/* dstderr and estderr may be redefined. */
+#define dstderr stderr
+#define estderr stderr
+
+#define dputc(chr) dprintf1("%c", chr)
+#define dputs(str) dprintf1("%s", str)
+#define dprintf(str)\
+ fprintf(dstderr, str)
+#define dprintf1(str,arg1)\
+ fprintf(dstderr, str, arg1)
+#define dprintf2(str,arg1,arg2)\
+ fprintf(dstderr, str, arg1, arg2)
+#define dprintf3(str,arg1,arg2,arg3)\
+ fprintf(dstderr, str, arg1, arg2, arg3)
+#define dprintf4(str,arg1,arg2,arg3,arg4)\
+ fprintf(dstderr, str, arg1, arg2, arg3, arg4)
+#define dprintf5(str,arg1,arg2,arg3,arg4,arg5)\
+ fprintf(dstderr, str, arg1, arg2, arg3, arg4, arg5)
+#define dprintf6(str,arg1,arg2,arg3,arg4,arg5,arg6)\
+ fprintf(dstderr, str, arg1, arg2, arg3, arg4, arg5, arg6)
+#define dprintf7(str,arg1,arg2,arg3,arg4,arg5,arg6,arg7)\
+ fprintf(dstderr, str, arg1, arg2, arg3, arg4, arg5, arg6, arg7)
+#define dprintf8(str,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8)\
+ fprintf(dstderr, str, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8)
+#define dprintf9(str,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9)\
+ fprintf(dstderr, str, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9)
+#define dprintf10(str,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9,arg10)\
+ fprintf(dstderr, str, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10)
+#define dprintf11(str,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9,arg10,arg11)\
+ fprintf(dstderr, str, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11)
+#define dprintf12(str,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9,arg10,arg11,arg12)\
+ fprintf(dstderr, str, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12)
+
+/* eprintf_program_name may be redefined. */
+#define eprintf_program_name(f, program_name)\
+ fprintf(f, "%s: ", program_name)
+#ifdef PROGRAM_NAME
+extern const char *PROGRAM_NAME;
+# define _epn eprintf_program_name(estderr, PROGRAM_NAME),
+#else
+# define _epn /* */
+#endif
+
+#define eprintf(str)\
+ (_epn fprintf(estderr, str))
+#define eprintf1(str,arg1)\
+ (_epn fprintf(estderr, str, arg1))
+#define eprintf2(str,arg1,arg2)\
+ (_epn fprintf(estderr, str, arg1, arg2))
+#define eprintf3(str,arg1,arg2,arg3)\
+ (_epn fprintf(estderr, str, arg1, arg2, arg3))
+#define eprintf4(str,arg1,arg2,arg3,arg4)\
+ (_epn fprintf(estderr, str, arg1, arg2, arg3, arg4))
+#define eprintf5(str,arg1,arg2,arg3,arg4,arg5)\
+ (_epn fprintf(estderr, str, arg1, arg2, arg3, arg4, arg5))
+#define eprintf6(str,arg1,arg2,arg3,arg4,arg5,arg6)\
+ (_epn fprintf(estderr, str, arg1, arg2, arg3, arg4, arg5, arg6))
+#define eprintf7(str,arg1,arg2,arg3,arg4,arg5,arg6,arg7)\
+ (_epn fprintf(estderr, str, arg1, arg2, arg3, arg4, arg5, arg6, arg7))
+#define eprintf8(str,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8)\
+ (_epn fprintf(estderr, str, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8))
+#define eprintf9(str,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9)\
+ (_epn fprintf(estderr, str, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9))
+#define eprintf10(str,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9,arg10)\
+ (_epn fprintf(estderr, str, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10))
+
+/* lprintf_file_and_line may be redefined. */
+#define lprintf_file_and_line(f, file, line)\
+ fprintf(f, "%s(%d): ", file, line)
+#if __LINE__ /* compiler provides it */
+# define _epl _epn lprintf_file_and_line(estderr, __FILE__, __LINE__),
+#else
+# define _epl _epn
+#endif
+
+#define lprintf(str)\
+ (_epl fprintf(estderr, str))
+#define lprintf1(str,arg1)\
+ (_epl fprintf(estderr, str, arg1))
+#define lprintf2(str,arg1,arg2)\
+ (_epl fprintf(estderr, str, arg1, arg2))
+#define lprintf3(str,arg1,arg2,arg3)\
+ (_epl fprintf(estderr, str, arg1, arg2, arg3))
+#define lprintf4(str,arg1,arg2,arg3,arg4)\
+ (_epl fprintf(estderr, str, arg1, arg2, arg3, arg4))
+#define lprintf5(str,arg1,arg2,arg3,arg4,arg5)\
+ (_epl fprintf(estderr, str, arg1, arg2, arg3, arg4, arg5))
+#define lprintf6(str,arg1,arg2,arg3,arg4,arg5,arg6)\
+ (_epl fprintf(estderr, str, arg1, arg2, arg3, arg4, arg5, arg6))
+#define lprintf7(str,arg1,arg2,arg3,arg4,arg5,arg6,arg7)\
+ (_epl fprintf(estderr, str, arg1, arg2, arg3, arg4, arg5, arg6, arg7))
+#define lprintf8(str,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8)\
+ (_epl fprintf(estderr, str, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8))
+#define lprintf9(str,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9)\
+ (_epl fprintf(estderr, str, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9))
+#define lprintf10(str,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9,arg10)\
+ (_epl fprintf(estderr, str, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10))
+
+#endif /* std_INCLUDED */
diff --git a/pstoraster/stdio_.h b/pstoraster/stdio_.h
new file mode 100644
index 000000000..9d2fb5ed5
--- /dev/null
+++ b/pstoraster/stdio_.h
@@ -0,0 +1,53 @@
+/* Copyright (C) 1992, 1993, 1994 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* stdio_.h */
+/* Generic substitute for stdio.h */
+
+/* This is here primarily because we must include std.h before */
+/* any file that includes sys/types.h. */
+#include "std.h"
+#include <stdio.h>
+
+#ifdef VMS
+/* VMS doesn't have the unlink system call. Use delete instead. */
+# ifdef __DECC
+# include <unixio.h>
+# endif
+# define unlink(fname) delete(fname)
+#else
+/* Other systems may or may not declare unlink in stdio.h; */
+/* if they do, the declaration will be compatible with this one. */
+int unlink(P1(const char *));
+#endif
+
+/* Patch a couple of things possibly missing from stdio.h. */
+#ifndef SEEK_SET
+# define SEEK_SET 0
+#endif
+#ifndef SEEK_CUR
+# define SEEK_CUR 1
+#endif
+#ifndef SEEK_END
+# define SEEK_END 2
+#endif
diff --git a/pstoraster/stdpre.h b/pstoraster/stdpre.h
new file mode 100644
index 000000000..efa8e4398
--- /dev/null
+++ b/pstoraster/stdpre.h
@@ -0,0 +1,336 @@
+/* Copyright (C) 1993, 1994 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* stdpre.h */
+/* Standard definitions for Aladdin Enterprises code not needing arch.h */
+
+#ifndef stdpre_INCLUDED
+# define stdpre_INCLUDED
+
+/*
+ * Here we deal with the vagaries of various C compilers. We assume that:
+ * ANSI-standard Unix compilers define __STDC__.
+ * Borland Turbo C and Turbo C++ define __MSDOS__ and __TURBOC__.
+ * Borland C++ defines __BORLANDC__, __MSDOS__, and __TURBOC__.
+ * Microsoft C/C++ defines _MSC_VER and _MSDOS.
+ * Watcom C defines __WATCOMC__ and MSDOS.
+ *
+ * We arrange to define __MSDOS__ on all the MS-DOS platforms.
+ */
+#if (defined(MSDOS) || defined(_MSDOS)) && !defined(__MSDOS__)
+# define __MSDOS__
+#endif
+/*
+ * Also, not used much here, but used in other header files, we assume:
+ * Unix System V environments define SYSV.
+ * The SCO ODT compiler defines M_SYSV and M_SYS3.
+ * VMS systems define VMS.
+ * OSF/1 compilers define __osf__ or __OSF__.
+ * (The VMS and OSF/1 C compilers handle prototypes and const,
+ * but do not define __STDC__.)
+ * bsd 4.2 or 4.3 systems define BSD4_2.
+ * POSIX-compliant environments define _POSIX_SOURCE.
+ * Motorola 88K BCS/OCS systems defined m88k.
+ *
+ * We make fairly heroic efforts to confine all uses of these flags to
+ * header files, and never to use them in code.
+ */
+#if defined(__osf__) && !defined(__OSF__)
+# define __OSF__ /* */
+#endif
+#if defined(M_SYSV) && !defined(SYSV)
+# define SYSV /* */
+#endif
+#if defined(M_SYS3) && !defined(__SVR3)
+# define __SVR3 /* */
+#endif
+
+#if defined(__STDC__) || defined(__MSDOS__) || defined(__convex__) || defined(VMS) || defined(__OSF__) || defined(__WIN32__) || defined(__IBMC__) || defined(M_UNIX) || defined(__GNUC__) || defined(__BORLANDC__)
+# if !(defined(M_XENIX) && !defined(__GNUC__)) /* SCO Xenix cc is broken */
+# define __PROTOTYPES__ /* */
+# endif
+#endif
+
+/* Define dummy values for __FILE__ and __LINE__ if the compiler */
+/* doesn't provide these. Note that places that use __FILE__ */
+/* must check explicitly for a null pointer. */
+#ifndef __FILE__
+# define __FILE__ NULL
+#endif
+#ifndef __LINE__
+# define __LINE__ 0
+#endif
+
+/* Disable 'const' and 'volatile' if the compiler can't handle them. */
+#ifndef __PROTOTYPES__
+# undef const
+# define const /* */
+# undef volatile
+# define volatile /* */
+#endif
+
+/*
+ * Some compilers give a warning if a function call that returns a value
+ * is used as a statement; a few compilers give an error for the construct
+ * (void)0, which is contrary to the ANSI standard. Since we don't know of
+ * any compilers that do both, we define a macro here for discarding
+ * the value of an expression statement, which can be defined as either
+ * including or not including the cast. (We don't conditionalize this here,
+ * because no commercial compiler gives the error on (void)0, although
+ * some give warnings.)
+ */
+#define discard(expr) ((void)(expr))
+
+/*
+ * The SVR4.2 C compiler incorrectly considers the result of << and >>
+ * to be unsigned if the left operand is signed and the right operand is
+ * unsigned. We believe this only causes trouble in Ghostscript code when
+ * the right operand is a sizeof(...), which is unsigned for this compiler.
+ * Therefore, we replace the relevant uses of sizeof with size_of:
+ */
+#define size_of(x) ((int)(sizeof(x)))
+
+/*
+ * Disable MS-DOS specialized pointer types on non-MS-DOS systems.
+ * Watcom C defines near, far, and huge as macros, so we must undef them.
+ * far_data is used for static data that must get its own segment.
+ * This is supported in Borland C++, but none of the others.
+ */
+#undef far_data
+#if defined(__TURBOC__) && !defined(__WIN32__)
+# ifdef __BORLANDC__
+# define far_data far
+# else
+# define far_data /* */
+# endif
+#else
+# undef near
+# define near /* */
+# undef far
+# define far /* */
+# define far_data /* */
+# undef huge
+# define huge /* */
+# define _cs /* */
+# define _ds /* */
+/* _es is never safe to use */
+# define _ss /* */
+#endif
+
+/* Get the size of a statically declared array. */
+#define countof(a) (sizeof(a) / sizeof((a)[0]))
+#define count_of(a) (size_of(a) / size_of((a)[0]))
+
+/*
+ * Get the offset of a structure member.
+ * Amazingly enough, this appears to work on all compilers
+ * (except for one broken MIPS compiler).
+ */
+#define offset_of(type, memb) ((int) &((type *) 0)->memb)
+
+/*
+ * Define a Boolean type. Even though we would like it to be
+ * unsigned char, it pretty well has to be int, because
+ * that's what all the relational operators and && and || produce.
+ * We can't make it an enumerated type, because ints don't coerce
+ * freely to enums (although the opposite is true).
+ */
+typedef int bool;
+#define false ((bool)0)
+#define true ((bool)1)
+
+/* Define short names for the unsigned types. */
+typedef unsigned char byte;
+typedef unsigned char uchar;
+typedef unsigned short ushort;
+typedef unsigned int uint;
+typedef unsigned long ulong;
+
+/* Since sys/types.h often defines one or more of these (depending on */
+/* the platform), we have to take steps to prevent name clashes. */
+/*** NOTE: This requires that you include std.h *before* any other ***/
+/*** header file that includes sys/types.h. ***/
+#define bool bool_ /* (maybe not needed) */
+#define uchar uchar_
+#define uint uint_
+#define ushort ushort_
+#define ulong ulong_
+#include <sys/types.h>
+#undef bool
+#undef uchar
+#undef uint
+#undef ushort
+#undef ulong
+
+/*
+ * Compilers disagree as to whether macros used in macro arguments
+ * should be expanded at the time of the call, or at the time of
+ * final expansion. Even authoritative documents disagree: the ANSI
+ * standard says the former, but Harbison and Steele's book says the latter.
+ * In order to work around this discrepancy, we have to do some very
+ * ugly things in a couple of places. We mention it here because
+ * it might well trip up future developers.
+ */
+
+/*
+ * Define the type to be used for ordering pointers (<, >=, etc.).
+ * The Borland and Microsoft large models only compare the offset part
+ * of segmented pointers. Semantically, the right type to use for the
+ * comparison is char huge *, but we have no idea how expensive comparing
+ * such pointers is, and any type that compares all the bits of the pointer,
+ * gives the right result for pointers in the same segment, and keeps
+ * different segments disjoint will do.
+ */
+#if defined(__TURBOC__) || defined(_MSC_VER)
+typedef unsigned long ptr_ord_t;
+#else
+typedef const char *ptr_ord_t;
+#endif
+/* Define all the pointer comparison operations. */
+#define _ptr_cmp(p1, rel, p2) ((ptr_ord_t)(p1) rel (ptr_ord_t)(p2))
+#define ptr_le(p1, p2) _ptr_cmp(p1, <=, p2)
+#define ptr_lt(p1, p2) _ptr_cmp(p1, <, p2)
+#define ptr_ge(p1, p2) _ptr_cmp(p1, >=, p2)
+#define ptr_gt(p1, p2) _ptr_cmp(p1, >, p2)
+#define ptr_between(ptr, lo, hi)\
+ (ptr_ge(ptr, lo) && ptr_lt(ptr, hi))
+
+/* Define min and max, but make sure to use the identical definition */
+/* to the one that all the compilers seem to have.... */
+#ifndef min
+# define min(a, b) (((a) < (b)) ? (a) : (b))
+#endif
+#ifndef max
+# define max(a, b) (((a) > (b)) ? (a) : (b))
+#endif
+
+/* Define a standard way to round values to a (constant) modulus. */
+#define round_down(value, modulus)\
+ ( (modulus) & ((modulus) - 1) ? /* not a power of 2 */\
+ (value) - (value) % (modulus) :\
+ (value) & -(modulus) )
+#define round_up(value, modulus)\
+ ( (modulus) & ((modulus) - 1) ? /* not a power of 2 */\
+ ((value) + ((modulus) - 1)) / (modulus) * (modulus) :\
+ ((value) + ((modulus) - 1)) & -(modulus) )
+
+/*
+ * In pre-ANSI C, float parameters get converted to double.
+ * However, if we pass a float to a function that has been declared
+ * with a prototype, and the parameter has been declared as float,
+ * the ANSI standard specifies that the parameter is left as float.
+ * To avoid problems caused by missing prototypes,
+ * we declare almost all float parameters as double.
+ */
+typedef double floatp;
+
+/*
+ * Define a handy macro for a statement that does nothing.
+ * We can't just use an empty body, since this upsets some compilers.
+ * We can't use the obvious
+ * if (0)
+ * since that could "capture" a following statement if used incorrectly.
+ */
+#ifndef DO_NOTHING
+# define DO_NOTHING do {} while (0)
+#endif
+
+/*
+ * For accountability, debugging, and error messages,
+ * we pass a client identification string to alloc and free,
+ * and possibly other places as well.
+ * Define the type for these strings. Note that because of the _ds,
+ * we must coerce them explicitly when passing them to printf et al.
+ */
+typedef const char _ds *client_name_t;
+#define client_name_string(cname) ((const char *)(cname))
+
+/*
+ * If we are debugging, make all static variables and procedures public
+ * so they get passed through the linker.
+ */
+#define public /* */
+/*
+ * We separate out the definition of private this way so that
+ * we can temporarily #undef it to handle the X Windows headers,
+ * which define a member named private.
+ */
+#ifdef NOPRIVATE
+# define private_ /* */
+#else
+# define private_ static
+#endif
+#define private private_
+
+/*
+ * Macros for argument templates. ANSI C has these, as does Turbo C,
+ * but older pcc-derived (K&R) Unix compilers don't. The syntax is
+ * resulttype func(Pn(arg1, ..., argn));
+ */
+
+#ifdef __PROTOTYPES__
+# define P0() void
+# define P1(t1) t1
+# define P2(t1,t2) t1,t2
+# define P3(t1,t2,t3) t1,t2,t3
+# define P4(t1,t2,t3,t4) t1,t2,t3,t4
+# define P5(t1,t2,t3,t4,t5) t1,t2,t3,t4,t5
+# define P6(t1,t2,t3,t4,t5,t6) t1,t2,t3,t4,t5,t6
+# define P7(t1,t2,t3,t4,t5,t6,t7) t1,t2,t3,t4,t5,t6,t7
+# define P8(t1,t2,t3,t4,t5,t6,t7,t8) t1,t2,t3,t4,t5,t6,t7,t8
+# define P9(t1,t2,t3,t4,t5,t6,t7,t8,t9) t1,t2,t3,t4,t5,t6,t7,t8,t9
+# define P10(t1,t2,t3,t4,t5,t6,t7,t8,t9,t10) t1,t2,t3,t4,t5,t6,t7,t8,t9,t10
+# define P11(t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11) t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11
+# define P12(t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12) t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12
+# define P13(t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13) t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13
+# define P14(t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14) t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14
+# define P15(t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14,t15) t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14,t15
+#else
+# define P0() /* */
+# define P1(t1) /* */
+# define P2(t1,t2) /* */
+# define P3(t1,t2,t3) /* */
+# define P4(t1,t2,t3,t4) /* */
+# define P5(t1,t2,t3,t4,t5) /* */
+# define P6(t1,t2,t3,t4,t5,t6) /* */
+# define P7(t1,t2,t3,t4,t5,t6,t7) /* */
+# define P8(t1,t2,t3,t4,t5,t6,t7,t8) /* */
+# define P9(t1,t2,t3,t4,t5,t6,t7,t8,t9) /* */
+# define P10(t1,t2,t3,t4,t5,t6,t7,t8,t9,t10) /* */
+# define P11(t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11) /* */
+# define P12(t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12) /* */
+# define P13(t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13) /* */
+# define P14(t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14) /* */
+# define P15(t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14,t15) /* */
+#endif
+
+/* Define success and failure codes for 'exit'. */
+#ifdef VMS
+# define exit_OK 1
+# define exit_FAILED 18
+#else
+# define exit_OK 0
+# define exit_FAILED 1
+#endif
+
+#endif /* stdpre_INCLUDED */
diff --git a/pstoraster/store.h b/pstoraster/store.h
new file mode 100644
index 000000000..8f8b41135
--- /dev/null
+++ b/pstoraster/store.h
@@ -0,0 +1,241 @@
+/* Copyright (C) 1989, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* store.h */
+/* Assignment-related macros */
+#include "ialloc.h" /* for imemory masks & checks */
+
+/*
+ * Macros for storing a ref. We use macros for storing into objects,
+ * since the storage manager needs to be able to track stores for
+ * save/restore and also for global/local checking.
+ * We also use macros for other ref assignments, because (as it happens)
+ * Turbo C generates pretty awful code for doing this.
+ *
+ * There are three cases that we need to distinguish:
+ * - Storing to a stack (no special action);
+ * - Storing into a newly created object (set l_new);
+ * - Storing into a slot of an existing object (check l_new in
+ * old value, set in new value).
+ * The macros are called
+ * <make/store><new_type><case>(place_to_store, new_value)
+ * where <case> is nothing for storing to the stack, _new for storing into
+ * a new object, and _old for storing into an existing object.
+ * (The _old macros also take a client name for tracing and debugging.)
+ * <new_type> and new_value are chosen from the following alternatives:
+ * ref_assign POINTER TO arbitrary ref
+ * make_t type (only for null and mark)
+ * make_tv type, value field name, value
+ * (only for scalars, which don't have attributes)
+ * make_tav type, attributes, value field name, value
+ * make_tasv type, attributes, size, value field name, value
+ * There are also specialized make_ macros for specific types:
+ * make_array, make_int, make_real, make_bool, make_false, make_true,
+ * make_mark, make_null, make_oper, make_[const_]string, make_struct.
+ * Not all of the specialized make_ macros have _new and _old variants.
+ *
+ * For _tav and _tasv, we must store the value first, because sometimes
+ * it depends on the contents of the place being stored into.
+ *
+ * Note that for composite objects (dictionary, file, array, string, device,
+ * struct), we must set a_foreign if the contents are allocated statically
+ * (e.g., for constant C strings) or not by the Ghostscript allocator
+ * (e.g., with malloc).
+ */
+
+/*
+ * Define the most efficient ref assignment macro for the platform.
+ */
+/*
+ * Assigning the components individually is fastest on Turbo C,
+ * and on Watcom C when one or both of the addresses are
+ * already known or in a register.
+ */
+#define ref_assign_inline(pto,pfrom)\
+ ((pto)->value = (pfrom)->value,\
+ (pto)->tas = (pfrom)->tas)
+#ifdef __TURBOC__
+ /*
+ * Move the data in two 32-bit chunks, because
+ * otherwise the compiler calls SCOPY@.
+ * The cast to void is to discourage the compiler from
+ * wanting to deliver the value of the expression.
+ */
+# define ref_assign(pto,pfrom)\
+ discard(ref_assign_inline(pto, pfrom))
+#else
+ /*
+ * Trust the compiler and hope for the best.
+ * The MIPS compiler doesn't like the cast to void.
+ */
+# define ref_assign(pto,pfrom)\
+ (*(pto) = *(pfrom))
+#endif
+
+/****** NOTE: the following declarations are duplicated from isave.h. ******/
+
+int alloc_save_change(P4(gs_dual_memory_t *, const ref *pcont,
+ ref_packed *ptr, client_name_t cname));
+int alloc_save_level(P1(const gs_dual_memory_t *));
+
+/****** END duplicated declarations. ******/
+
+#define ialloc_save_change(pcont, ptr, cname)\
+ alloc_save_change(idmemory, pcont, ptr, cname)
+#define ialloc_is_in_save() (idmemory->save_level > 0)
+#define ialloc_test_mask (idmemory->test_mask)
+#define ialloc_new_mask (idmemory->new_mask)
+#define ref_must_save(pto)\
+ ((r_type_attrs(pto) & ialloc_test_mask) == 0)
+#define ref_do_save(pcont, pto, cname)\
+ ialloc_save_change(pcont, (ref_packed *)(pto), cname)
+#define ref_save(pcont, pto, cname)\
+ discard((ref_must_save(pto) ? ref_do_save(pcont, pto, cname) : 0))
+#define ref_mark_new(pto) ((pto)->tas.type_attrs |= ialloc_new_mask)
+#define ref_assign_new(pto,pfrom)\
+ discard((ref_assign(pto,pfrom), ref_mark_new(pto)))
+#define ref_assign_new_inline(pto,pfrom)\
+ discard((ref_assign_inline(pto,pfrom), ref_mark_new(pto)))
+#define ref_assign_old(pcont,pto,pfrom,cname)\
+ (ref_save(pcont,pto,cname), ref_assign_new(pto,pfrom))
+#define ref_assign_old_inline(pcont,pto,pfrom,cname)\
+ (ref_save(pcont,pto,cname), ref_assign_new_inline(pto,pfrom))
+/* ref_mark_old is only needed in very unusual situations, namely, */
+/* when we want to do a ref_save just before a save instead of */
+/* when the actual assignment occurs. */
+#define ref_mark_old(pto) ((pto)->tas.type_attrs &= ~ialloc_new_mask)
+
+/* Define macros for conditionally clearing the parts of a ref */
+/* that aren't being set to anything useful. */
+
+#ifdef DEBUG
+# define and_fill_s(pref)\
+ , (gs_debug['$'] ? r_set_size(pref, 0xfeed) : 0)
+# define and_fill_sv(pref)\
+ , (gs_debug['$'] ? (r_set_size(pref, 0xfeed),\
+ (pref)->value.intval = 0xdeadbeef) : 0)
+#else /* !DEBUG */
+# define and_fill_s(pref) /* */
+# define and_fill_sv(pref) /* */
+#endif
+
+/* make_t must set the attributes to 0 to clear a_local! */
+#define make_t(pref,newtype)\
+ (r_set_type_attrs(pref, newtype, 0) and_fill_sv(pref))
+#define make_t_new(pref,newtype)\
+ (r_set_type_attrs(pref, newtype, ialloc_new_mask) and_fill_sv(pref))
+#define make_t_old(pcont,pref,newtype,cname)\
+ (ref_save(pcont,pref,cname), make_t_new(pref,newtype))
+
+#define make_tav(pref,newtype,newattrs,valfield,newvalue)\
+ ((pref)->value.valfield = (newvalue),\
+ r_set_type_attrs(pref, newtype, newattrs)\
+ and_fill_s(pref))
+#define make_tav_new(pref,t,a,vf,v)\
+ make_tav(pref,t,(a)|ialloc_new_mask,vf,v)
+#define make_tav_old(pcont,pref,t,a,vf,v,cname)\
+ (ref_save(pcont,pref,cname), make_tav_new(pref,t,a,vf,v))
+
+#define make_tv(pref,newtype,valfield,newvalue)\
+ make_tav(pref,newtype,0,valfield,newvalue)
+#define make_tv_new(pref,t,vf,v)\
+ make_tav_new(pref,t,0,vf,v)
+#define make_tv_old(pcont,pref,t,vf,v,cname)\
+ make_tav_old(pcont,pref,t,0,vf,v,cname)
+
+#define make_tasv(pref,newtype,newattrs,newsize,valfield,newvalue)\
+ ((pref)->value.valfield = (newvalue),\
+ r_set_type_attrs(pref, newtype, newattrs),\
+ r_set_size(pref, newsize))
+#define make_tasv_new(pref,t,a,s,vf,v)\
+ make_tasv(pref,t,(a)|ialloc_new_mask,s,vf,v)
+#define make_tasv_old(pcont,pref,t,a,s,vf,v,cname)\
+ (ref_save(pcont,pref,cname), make_tasv_new(pref,t,a,s,vf,v))
+
+/* Type-specific constructor macros for scalar (non-composite) types */
+
+#define make_bool(pref,bval)\
+ make_tv(pref, t_boolean, boolval, bval)
+#define make_false(pref)\
+ make_bool(pref, 0)
+#define make_true(pref)\
+ make_bool(pref, 1)
+
+#define make_int(pref,ival)\
+ make_tv(pref, t_integer, intval, ival)
+#define make_int_new(pref,ival)\
+ make_tv_new(pref, t_integer, intval, ival)
+
+#define make_mark(pref)\
+ make_t(pref, t_mark)
+
+#define make_null(pref)\
+ make_t(pref, t_null)
+#define make_null_new(pref)\
+ make_t_new(pref, t_null)
+#define make_null_old(pcont,pref,cname)\
+ make_t_old(pcont, pref, t_null, cname)
+
+#define make_oper(pref,opidx,proc)\
+ make_tasv(pref, t_operator, a_executable, opidx, opproc, proc)
+
+#define make_real(pref,rval)\
+ make_tv(pref, t_real, realval, rval)
+#define make_real_new(pref,rval)\
+ make_tv_new(pref, t_real, realval, rval)
+
+/* Type-specific constructor macros for composite types */
+
+/* For composite types, the a_space field is relevant; however, */
+/* as noted in ivmspace.h, a value of 0 designates the most static space, */
+/* so for making empty composites, a space value of 0 is appropriate. */
+
+#define make_array(pref,attrs,size,elts)\
+ make_tasv(pref, t_array, attrs, size, refs, elts)
+#define make_array_new(pref,attrs,size,elts)\
+ make_tasv_new(pref, t_array, attrs, size, refs, elts)
+#define make_const_array(pref,attrs,size,elts)\
+ make_tasv(pref, t_array, attrs, size, const_refs, elts)
+#define make_empty_array(pref,attrs)\
+ make_array(pref, attrs, 0, (ref *)NULL)
+#define make_empty_const_array(pref,attrs)\
+ make_const_array(pref, attrs, 0, (const ref *)NULL)
+
+#define make_string(pref,attrs,size,chars)\
+ make_tasv(pref, t_string, attrs, size, bytes, chars)
+#define make_const_string(pref,attrs,size,chars)\
+ make_tasv(pref, t_string, attrs, size, const_bytes, chars)
+#define make_empty_string(pref,attrs)\
+ make_string(pref, attrs, 0, (byte *)NULL)
+#define make_empty_const_string(pref,attrs)\
+ make_const_string(pref, attrs, 0, (const byte *)NULL)
+
+#define make_struct(pref,attrs,ptr)\
+ make_tav(pref, t_struct, attrs, pstruct, (obj_header_t *)(ptr))
+#define make_struct_new(pref,attrs,ptr)\
+ make_tav_new(pref, t_struct, attrs, pstruct, (obj_header_t *)(ptr))
+
+#define make_astruct(pref,attrs,ptr)\
+ make_tav(pref, t_astruct, attrs, pstruct, (obj_header_t *)(ptr))
+#define make_astruct_new(pref,attrs,ptr)\
+ make_tav_new(pref, t_astruct, attrs, pstruct, (obj_header_t *)(ptr))
diff --git a/pstoraster/stream.c b/pstoraster/stream.c
new file mode 100644
index 000000000..52d87d724
--- /dev/null
+++ b/pstoraster/stream.c
@@ -0,0 +1,792 @@
+/* Copyright (C) 1989, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* stream.c */
+/* Stream package for Ghostscript interpreter */
+#include "stdio_.h" /* includes std.h */
+#include "memory_.h"
+#include "gdebug.h"
+#include "gpcheck.h"
+#include "stream.h"
+#include "strimpl.h"
+
+/* Forward declarations */
+private int sreadbuf(P2(stream *, stream_cursor_write *));
+private int swritebuf(P3(stream *, stream_cursor_read *, bool));
+private void stream_compact(P2(stream *, bool));
+
+/* Structure types for allocating streams. */
+private_st_stream();
+public_st_stream_state(); /* default */
+/* GC procedures */
+#define st ((stream *)vptr)
+private ENUM_PTRS_BEGIN(stream_enum_ptrs) return 0;
+ case 0:
+ if ( st->foreign )
+ *pep = NULL;
+ else if ( st->cbuf_string.data != 0 )
+ { ENUM_RETURN_STRING_PTR(stream, cbuf_string);
+ }
+ else
+ *pep = st->cbuf;
+ break;
+ ENUM_PTR(1, stream, strm);
+ ENUM_PTR(2, stream, prev);
+ ENUM_PTR(3, stream, next);
+ ENUM_PTR(4, stream, state);
+ENUM_PTRS_END
+private RELOC_PTRS_BEGIN(stream_reloc_ptrs) {
+ byte *cbuf_old = st->cbuf;
+ if ( cbuf_old != 0 && !st->foreign )
+ { long reloc;
+ if ( st->cbuf_string.data != 0 )
+ { RELOC_STRING_PTR(stream, cbuf_string);
+ st->cbuf = st->cbuf_string.data;
+ }
+ else
+ RELOC_PTR(stream, cbuf);
+ reloc = cbuf_old - st->cbuf;
+ /* Relocate the other buffer pointers. */
+ st->srptr -= reloc;
+ st->srlimit -= reloc; /* same as swptr */
+ st->swlimit -= reloc;
+ }
+ RELOC_PTR(stream, strm);
+ RELOC_PTR(stream, prev);
+ RELOC_PTR(stream, next);
+ RELOC_PTR(stream, state);
+} RELOC_PTRS_END
+/* Finalize a stream by closing it. */
+/* We only do this for file streams, because other kinds of streams */
+/* may attempt to free storage when closing. */
+private void
+stream_finalize(void *vptr)
+{ if_debug2('u', "[u]%s 0x%lx\n",
+ (!s_is_valid(st) ? "already closed:" :
+ st->is_temp ? "is_temp set:" :
+ st->file == 0 ? "not file:" :
+ "closing file:"), (ulong)st);
+ if ( s_is_valid(st) && !st->is_temp && st->file != 0 )
+ { /* Prevent any attempt to free the buffer. */
+ st->cbuf = 0;
+ st->cbuf_string.data = 0;
+ sclose(st); /* ignore errors */
+ }
+}
+#undef st
+
+/* Dummy template for streams that don't have a separate state. */
+private const stream_template s_no_template =
+{ &st_stream_state, 0, 0, 1, 1, 0
+};
+
+/* ------ Generic procedures ------ */
+
+/* Allocate a stream and initialize it minimally. */
+stream *
+s_alloc(gs_memory_t *mem, client_name_t cname)
+{ stream *s = gs_alloc_struct(mem, stream, &st_stream, cname);
+ if_debug2('s', "[s]alloc(%s) = 0x%lx\n",
+ client_name_string(cname), (ulong)s);
+ if ( s == 0 )
+ return 0;
+ s->memory = mem;
+ s->report_error = s_no_report_error;
+ s->prev = s->next = 0; /* clean for GC */
+ return s;
+}
+
+/* Allocate a stream state and initialize it minimally. */
+stream_state *
+s_alloc_state(gs_memory_t *mem, gs_memory_type_ptr_t stype,
+ client_name_t cname)
+{ stream_state *st = gs_alloc_struct(mem, stream_state, stype, cname);
+ if_debug3('s', "[s]alloc_state %s(%s) = 0x%lx\n",
+ client_name_string(cname),
+ client_name_string(stype->sname),
+ (ulong)st);
+ if ( st == 0 )
+ return 0;
+ st->memory = mem;
+ st->report_error = s_no_report_error;
+ return st;
+}
+
+/* Vacuous stream initialization */
+int
+s_no_init(stream_state *st)
+{ return 0;
+}
+
+/* Standard stream initialization */
+void
+s_std_init(register stream *s, byte *ptr, uint len, const stream_procs *pp,
+ int modes)
+{ s->template = &s_no_template;
+ s->cbuf = ptr;
+ s->srptr = s->srlimit = s->swptr = ptr - 1;
+ s->swlimit = ptr - 1 + len;
+ s->end_status = 0;
+ s->foreign = 0;
+ s->modes = modes;
+ s->cbuf_string.data = 0;
+ s->position = 0;
+ s->bsize = s->cbsize = len;
+ s->strm = 0; /* not a filter */
+ s->is_temp = 0;
+ s->procs = *pp;
+ s->state = (stream_state *)s; /* hack to avoid separate state */
+ s->file = 0;
+ if_debug4('s', "[s]init 0x%lx, buf=0x%lx, len=%u, modes=%d\n",
+ (ulong)s, (ulong)ptr, len, modes);
+}
+
+/* Implement a stream procedure as a no-op. */
+int
+s_std_null(stream *s)
+{ return 0;
+}
+
+/* Discard the contents of the buffer when reading. */
+void
+s_std_read_reset(stream *s)
+{ s->srptr = s->srlimit = s->cbuf - 1;
+}
+
+/* Discard the contents of the buffer when writing. */
+void
+s_std_write_reset(stream *s)
+{ s->swptr = s->cbuf- 1;
+}
+
+/* Flush data to end-of-file when reading. */
+int
+s_std_read_flush(stream *s)
+{ while ( 1 )
+ { s->srptr = s->srlimit = s->cbuf - 1;
+ if ( s->end_status ) break;
+ s_process_read_buf(s);
+ }
+ return (s->end_status == EOFC ? 0 : s->end_status);
+}
+
+/* Flush buffered data when writing. */
+int
+s_std_write_flush(stream *s)
+{ return s_process_write_buf(s, false);
+}
+
+/* Indicate that the number of available input bytes is unknown. */
+int
+s_std_noavailable(stream *s, long *pl)
+{ *pl = -1;
+ return 0;
+}
+
+/* Indicate an error when asked to seek. */
+int
+s_std_noseek(stream *s, long pos)
+{ return ERRC;
+}
+
+/* Standard stream closing. */
+int
+s_std_close(stream *s)
+{ return 0;
+}
+
+/* Standard stream mode switching. */
+int
+s_std_switch_mode(stream *s, bool writing)
+{ return ERRC;
+}
+
+/* Standard stream finalization. Disable the stream. */
+void
+s_disable(register stream *s)
+{ s->cbuf = 0;
+ s->bsize = 0;
+ s->end_status = EOFC;
+ s->modes = 0;
+ s->cbuf_string.data = 0;
+ s->cursor.r.ptr = s->cursor.r.limit = (const byte *)0 - 1;
+ s->cursor.w.limit = (byte *)0 - 1;
+ s->procs.close = s_std_null;
+ /* Clear pointers for GC */
+ s->strm = 0;
+ s->state = (stream_state *)s;
+ s->template = &s_no_template;
+ /****** SHOULD DO MORE THAN THIS ******/
+ if_debug1('s', "[s]disable 0x%lx\n", (ulong)s);
+}
+
+/* Implement flushing for encoding filters. */
+int
+s_filter_write_flush(register stream *s)
+{ int status = s_process_write_buf(s, false);
+ if ( status != 0 )
+ return status;
+ return sflush(s->strm);
+}
+
+/* Close a filter. If this is an encoding filter, flush it first. */
+int
+s_filter_close(register stream *s)
+{ if ( s_is_writing(s) )
+ { int status = s_process_write_buf(s, true);
+ if ( status != 0 && status != EOFC )
+ return status;
+ }
+ return s_std_close(s);
+}
+
+/* Disregard a stream error message. */
+int
+s_no_report_error(stream_state *st, const char *str)
+{ return 0;
+}
+
+/* ------ Implementation-independent procedures ------ */
+
+/* Store the amount of available data in a(n input) stream. */
+int
+savailable(stream *s, long *pl)
+{ return (*(s)->procs.available)(s, pl);
+}
+
+/* Return the current position of a stream. */
+long
+stell(stream *s)
+{ /* The stream might have been closed, but the position */
+ /* is still meaningful in this case. */
+ const byte *ptr = (s_is_writing(s) ? s->swptr : s->srptr);
+ return (ptr == 0 ? 0 : ptr + 1 - s->cbuf) + s->position;
+}
+
+/* Set the position of a stream. */
+int
+spseek(stream *s, long pos)
+{ if_debug3('s', "[s]seek 0x%lx to %ld, position was %ld\n",
+ (ulong)s, pos, stell(s));
+ return (*(s)->procs.seek)(s, pos);
+}
+
+/* Switch a stream to read or write mode. */
+/* Return 0 or ERRC. */
+int
+sswitch(register stream *s, bool writing)
+{ if ( s->procs.switch_mode == 0 )
+ return ERRC;
+ return (*s->procs.switch_mode)(s, writing);
+}
+
+/* Close a stream, disabling it if successful. */
+/* (The stream may already be closed.) */
+int
+sclose(register stream *s)
+{ stream_state *st;
+ int code = (*s->procs.close)(s);
+ if ( code < 0 )
+ return code;
+ st = s->state;
+ if ( st != 0 )
+ { stream_proc_release((*release)) = st->template->release;
+ if ( release != 0 )
+ (*release)(st);
+ if ( st != (stream_state *)s && st->memory != 0 )
+ gs_free_object(st->memory, st, "s_std_close");
+ s->state = (stream_state *)s;
+ }
+ s_disable(s);
+ return code;
+}
+
+/*
+ * Implement sgetc when the buffer may be empty.
+ * If the buffer really is empty, refill it and then read a byte.
+ * Note that filters must read one byte ahead, so that they can close immediately
+ * after the client reads the last data byte if the next thing is an EOD.
+ */
+int
+spgetcc(register stream *s, bool close_on_eof)
+{ int status, left;
+ int min_left = sbuf_min_left(s);
+
+ while ( status = s->end_status,
+ left = s->srlimit - s->srptr,
+ left <= min_left && status >= 0
+ )
+ s_process_read_buf(s);
+ if ( left <= min_left && (left == 0 || (status != EOFC && status != ERRC)) )
+ { /* Compact the stream so stell will return the right result. */
+ stream_compact(s, true);
+ if ( status == EOFC && close_on_eof )
+ { status = sclose(s);
+ if ( status == 0 )
+ status = EOFC;
+ s->end_status = status;
+ }
+ return status;
+ }
+ return *++(s->srptr);
+}
+
+/* Implementing sputc when the buffer is full, */
+/* by flushing the buffer and then writing the byte. */
+int
+spputc(register stream *s, byte b)
+{ for ( ; ; )
+ { if ( s->end_status )
+ return s->end_status;
+ if ( !sendwp(s) )
+ { *++(s->swptr) = b;
+ return b;
+ }
+ s_process_write_buf(s, false);
+ }
+}
+
+/* Push back a character onto a (read) stream. */
+/* The character must be the same as the last one read. */
+/* Return 0 on success, ERRC on failure. */
+int
+sungetc(register stream *s, byte c)
+{ if ( !s_is_reading(s) || s->srptr < s->cbuf || *(s->srptr) != c )
+ return ERRC;
+ s->srptr--;
+ return 0;
+}
+
+/* Get a string from a stream. */
+/* Return 0 if the string was filled, or an exception status. */
+int
+sgets(stream *s, byte *buf, uint nmax, uint *pn)
+{ stream_cursor_write cw;
+ int status = 0;
+ int min_left = sbuf_min_left(s);
+
+ cw.ptr = buf - 1;
+ cw.limit = cw.ptr + nmax;
+ while ( cw.ptr < cw.limit )
+ { int left;
+ if ( (left = s->srlimit - s->srptr) > min_left )
+ { s->srlimit -= min_left;
+ stream_move(&s->cursor.r, &cw);
+ s->srlimit += min_left;
+ }
+ else
+ { uint wanted = cw.limit - cw.ptr;
+ int c;
+ stream_state *st;
+ if ( wanted >= s->bsize >> 2 &&
+ (st = s->state) != 0 &&
+ wanted >= st->template->min_out_size &&
+ s->end_status == 0 &&
+ left == 0
+ )
+ { byte *wptr = cw.ptr;
+ cw.limit -= min_left;
+ status = sreadbuf(s, &cw);
+ cw.limit += min_left;
+ /* We know the stream buffer is empty, */
+ /* so it's safe to update position. */
+ s->position += cw.ptr - wptr;
+ if ( status != 1 || cw.ptr == cw.limit )
+ break;
+ }
+ c = spgetc(s);
+ if ( c < 0 )
+ { status = c;
+ break;
+ }
+ *++(cw.ptr) = c;
+ }
+ }
+ *pn = cw.ptr + 1 - buf;
+ return (status >= 0 ? 0 : status);
+}
+
+/* Write a string on a stream. */
+/* Return 0 if the entire string was written, or an exception status. */
+int
+sputs(register stream *s, const byte *str, uint wlen, uint *pn)
+{ uint len = wlen;
+ int status = s->end_status;
+ if ( status >= 0 )
+ while ( len > 0 )
+ { uint count = s->swlimit - s->swptr;
+ if ( count > 0 )
+ { if ( count > len ) count = len;
+ memcpy(s->swptr + 1, str, count);
+ s->swptr += count;
+ str += count;
+ len -= count;
+ }
+ else
+ { byte ch = *str++;
+ status = sputc(s, ch);
+ if ( status < 0 )
+ break;
+ len--;
+ }
+ }
+ *pn = wlen - len;
+ return (status >= 0 ? 0 : status);
+}
+
+/* Skip ahead a specified distance in a read stream. */
+/* Return 0 or an exception code. */
+/* Store the number of bytes skipped in *pskipped. */
+int
+spskip(register stream *s, long nskip, long *pskipped)
+{ long n = nskip;
+ int min_left;
+
+ if ( nskip < 0 || !s_is_reading(s) )
+ { *pskipped = 0;
+ return ERRC;
+ }
+ if ( s_can_seek(s) )
+ { long pos = stell(s);
+ int code = sseek(s, pos + n);
+ *pskipped = stell(s) - pos;
+ return code;
+ }
+ min_left = sbuf_min_left(s);
+ while ( sbufavailable(s) < n + min_left )
+ { int code;
+ n -= sbufavailable(s);
+ s->srptr = s->srlimit;
+ if ( s->end_status )
+ { *pskipped = nskip - n;
+ return s->end_status;
+ }
+ code = sgetc(s);
+ if ( code < 0 )
+ { *pskipped = nskip - n;
+ return code;
+ }
+ --n;
+ }
+ /* Note that if min_left > 0, n < 0 is possible; this is harmless. */
+ s->srptr += n;
+ *pskipped = nskip;
+ return 0;
+}
+
+/* ------ Utilities ------ */
+
+/*
+ * Attempt to refill the buffer of a read stream. Only call this if the
+ * end_status is not EOFC, and if the buffer is (nearly) empty.
+ */
+int
+s_process_read_buf(stream *s)
+{ int status;
+ stream_compact(s, false);
+ status = sreadbuf(s, &s->cursor.w);
+ s->end_status = (status >= 0 ? 0 : status);
+ return 0;
+}
+
+/*
+ * Attempt to empty the buffer of a write stream. Only call this if the
+ * end_status is not EOFC.
+ */
+int
+s_process_write_buf(stream *s, bool last)
+{ int status = swritebuf(s, &s->cursor.r, last);
+ stream_compact(s, false);
+ if ( status >= 0 )
+ status = 0;
+ s->end_status = status;
+ return status;
+}
+
+/* Move forward or backward in a pipeline. We temporarily reverse */
+/* the direction of the pointers while doing this. */
+/* (Cf the Deutsch-Schorr-Waite graph marking algorithm.) */
+#define move_back(curr, prev)\
+{ stream *back = prev->strm;\
+ prev->strm = curr; curr = prev; prev = back;\
+}
+#define move_ahead(curr, prev)\
+{ stream *ahead = curr->strm;\
+ curr->strm = prev; prev = curr; curr = ahead;\
+}
+
+/* Read from a pipeline. */
+private int
+sreadbuf(stream *s, stream_cursor_write *pbuf)
+{ stream *prev = 0;
+ stream *curr = s;
+ int status;
+ for ( ; ; )
+ { stream *strm;
+ for ( ; ; )
+ { /* Descend into the recursion. */
+ stream_cursor_read cr;
+ stream_cursor_read *pr;
+ stream_cursor_write *pw;
+ bool eof;
+
+ strm = curr->strm;
+ if ( strm == 0 )
+ { cr.ptr = 0, cr.limit = 0;
+ pr = &cr;
+ eof = false;
+ }
+ else
+ { pr = &strm->cursor.r;
+ eof = strm->end_status == EOFC;
+ }
+ pw = (prev == 0 ? pbuf : &curr->cursor.w);
+ if_debug4('s', "[s]read process 0x%lx, nr=%u, nw=%u, eof=%d\n",
+ (ulong)curr, (uint)(pr->limit - pr->ptr),
+ (uint)(pw->limit - pw->ptr), eof);
+ status = (*curr->procs.process)(curr->state, pr, pw, eof);
+ if_debug4('s', "[s]after read 0x%lx, nr=%u, nw=%u, status=%d\n",
+ (ulong)curr, (uint)(pr->limit - pr->ptr),
+ (uint)(pw->limit - pw->ptr), status);
+ if ( strm == 0 || status != 0 )
+ break;
+ status = strm->end_status;
+ if ( status < 0 )
+ break;
+ move_ahead(curr, prev);
+ stream_compact(curr, false);
+ }
+ /* If curr reached EOD and is a filter stream, close it. */
+ if ( strm != 0 && status == EOFC &&
+ curr->cursor.r.ptr >= curr->cursor.r.limit
+ )
+ { int cstat = sclose(curr);
+ if ( cstat != 0 )
+ status = cstat;
+ }
+#if 0
+ /* If we need to do a callout, unwind all the way now. */
+ if ( status == CALLC )
+ { while ( (curr->end_status = status), prev != 0 )
+ { move_back(curr, prev);
+ }
+ return status;
+ }
+#endif
+ /* Unwind from the recursion. */
+ curr->end_status = (status >= 0 ? 0 : status);
+ if ( prev == 0 )
+ return status;
+ move_back(curr, prev);
+ }
+}
+
+/* Write to a pipeline. */
+private int
+swritebuf(stream *s, stream_cursor_read *pbuf, bool last)
+{ stream *prev = 0;
+ stream *curr = s;
+ int depth = 1; /* depth of nesting in non-temp streams */
+ int level = 0; /* depth of recursion */
+ int top_level = 0; /* level below which all streams have */
+ /* returned 0 when called with last = true */
+ int status;
+
+ for ( ; ; )
+ { for ( ; ; )
+ { /* Descend into the recursion. */
+ stream *strm = curr->strm;
+ stream_cursor_write cw;
+ stream_cursor_read *pr;
+ stream_cursor_write *pw;
+ /*
+ * We only want to set the last/end flag for
+ * the top-level stream and any temporary streams
+ * immediately below it.
+ */
+ bool end = last && depth <= 1 && level == top_level;
+
+ if ( strm == 0 )
+ cw.ptr = 0, cw.limit = 0, pw = &cw;
+ else
+ pw = &strm->cursor.w;
+ if ( prev == 0 )
+ pr = pbuf;
+ else
+ pr = &curr->cursor.r;
+ if_debug4('s', "[s]write process 0x%lx, nr=%u, nw=%u, end=%d\n",
+ (ulong)curr, (uint)(pr->limit - pr->ptr),
+ (uint)(pw->limit - pw->ptr), end);
+ status = (*curr->procs.process)(curr->state, pr, pw,
+ end);
+ if_debug4('s', "[s]after write 0x%lx, nr=%u, nw=%u, status=%d\n",
+ (ulong)curr, (uint)(pr->limit - pr->ptr),
+ (uint)(pw->limit - pw->ptr), status);
+ if ( strm == 0 || status < 0 )
+ break;
+ if ( status == 1 )
+ end = false;
+ else
+ { /* Keep going if we are closing */
+ /* a filter with a sub-stream. */
+ /* We know status == 0. */
+ if ( !end || !strm->is_temp )
+ break;
+ /* This level is finished, don't come back. */
+ top_level = level + 1;
+ }
+ status = strm->end_status;
+ if ( status < 0 )
+ break;
+ move_ahead(curr, prev);
+ stream_compact(curr, false);
+ ++level;
+ if ( !curr->is_temp )
+ ++depth;
+ }
+ /* Unwind from the recursion. */
+ curr->end_status = (status >= 0 ? 0 : status);
+ if ( level <= top_level )
+ { /* All streams above here were called with last = true */
+ /* and returned 0: finish unwinding and then return. */
+ while ( prev )
+ { move_back(curr, prev);
+ curr->end_status = (status >= 0 ? 0 : status);
+ }
+ return status;
+ }
+ move_back(curr, prev);
+ --level;
+ if ( !curr->is_temp )
+ --depth;
+ }
+}
+
+/* Move as much data as possible from one buffer to another. */
+/* Return 0 if the input became empty, 1 if the output became full. */
+int
+stream_move(stream_cursor_read *pr, stream_cursor_write *pw)
+{ uint rcount = pr->limit - pr->ptr;
+ uint wcount = pw->limit - pw->ptr;
+ uint count;
+ int status;
+ if ( rcount <= wcount )
+ count = rcount, status = 0;
+ else
+ count = wcount, status = 1;
+ memmove(pw->ptr + 1, pr->ptr + 1, count);
+ pr->ptr += count;
+ pw->ptr += count;
+ return status;
+}
+
+/* If possible, compact the information in a stream buffer to the bottom. */
+private void
+stream_compact(stream *s, bool always)
+{ if ( s->cursor.r.ptr >= s->cbuf && (always || s->end_status >= 0) )
+ { uint dist = s->cursor.r.ptr + 1 - s->cbuf;
+ memmove(s->cbuf, s->cursor.r.ptr + 1,
+ (uint)(s->cursor.r.limit - s->cursor.r.ptr));
+ s->cursor.r.ptr = s->cbuf - 1;
+ s->cursor.r.limit -= dist; /* same as w.ptr */
+ s->position += dist;
+ }
+}
+
+/* ------ String streams ------ */
+
+/* String stream procedures */
+private int
+ s_string_available(P2(stream *, long *)),
+ s_string_read_seek(P2(stream *, long)),
+ s_string_write_seek(P2(stream *, long)),
+ s_string_read_process(P4(stream_state *, stream_cursor_read *,
+ stream_cursor_write *, bool)),
+ s_string_write_process(P4(stream_state *, stream_cursor_read *,
+ stream_cursor_write *, bool));
+
+/* Initialize a stream for reading a string. */
+void
+sread_string(register stream *s, const byte *ptr, uint len)
+{ static const stream_procs p =
+ { s_string_available, s_string_read_seek, s_std_read_reset,
+ s_std_read_flush, s_std_null, s_string_read_process
+ };
+ s_std_init(s, (byte *)ptr, len, &p, s_mode_read + s_mode_seek);
+ s->cbuf_string.data = (byte *)ptr;
+ s->cbuf_string.size = len;
+ s->end_status = EOFC;
+ s->srlimit = s->swlimit;
+}
+/* Return the number of available bytes when reading from a string. */
+private int
+s_string_available(stream *s, long *pl)
+{ *pl = sbufavailable(s);
+ if ( *pl == 0 ) /* EOF */
+ *pl = -1;
+ return 0;
+}
+
+/* Seek in a string being read. Return 0 if OK, ERRC if not. */
+private int
+s_string_read_seek(register stream *s, long pos)
+{ if ( pos < 0 || pos > s->bsize ) return ERRC;
+ s->srptr = s->cbuf + pos - 1;
+ return 0;
+}
+
+/* Initialize a stream for writing a string. */
+void
+swrite_string(register stream *s, byte *ptr, uint len)
+{ static const stream_procs p =
+ { s_std_noavailable, s_string_write_seek, s_std_write_reset,
+ s_std_null, s_std_null, s_string_write_process
+ };
+ s_std_init(s, ptr, len, &p, s_mode_write + s_mode_seek);
+ s->cbuf_string.data = ptr;
+ s->cbuf_string.size = len;
+}
+
+/* Seek in a string being written. Return 0 if OK, ERRC if not. */
+private int
+s_string_write_seek(register stream *s, long pos)
+{ if ( pos < 0 || pos > s->bsize ) return ERRC;
+ s->swptr = s->cbuf + pos - 1;
+ return 0;
+}
+
+/* Since we initialize the input buffer of a string read stream */
+/* to contain all of the data in the string, if we are ever asked */
+/* to refill the buffer, we should signal EOF. */
+private int
+s_string_read_process(stream_state *st, stream_cursor_read *ignore_pr,
+ stream_cursor_write *pw, bool last)
+{ return EOFC;
+}
+/* Similarly, if we are ever asked to empty the buffer, it means that */
+/* there has been an overrun (unless we are closing the stream). */
+private int
+s_string_write_process(stream_state *st, stream_cursor_read *pr,
+ stream_cursor_write *ignore_pw, bool last)
+{ return (last ? EOFC : ERRC);
+}
diff --git a/pstoraster/stream.h b/pstoraster/stream.h
new file mode 100644
index 000000000..2901f928b
--- /dev/null
+++ b/pstoraster/stream.h
@@ -0,0 +1,314 @@
+/* Copyright (C) 1989, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* stream.h */
+/* Definitions for Ghostscript stream package */
+/* Requires stdio.h */
+#include "scommon.h"
+
+/* See scommon.h for documentation on the design of streams. */
+
+/* ------ Stream structure definition ------ */
+
+/*
+ * We expose the stream structure definition to clients so that
+ * they can get reasonable performance out of the basic operations.
+ */
+
+/* Define the "virtual" stream procedures. */
+
+typedef struct {
+
+ /* Store # available for reading. */
+ /* Return 0 if OK, ERRC if error or not implemented. */
+#define stream_proc_available(proc)\
+ int proc(P2(stream *, long *))
+ stream_proc_available((*available));
+
+ /* Set position. */
+ /* Return 0 if OK, ERRC if error or not implemented. */
+#define stream_proc_seek(proc)\
+ int proc(P2(stream *, long))
+ stream_proc_seek((*seek));
+
+ /* Clear buffer and, if relevant, unblock channel. */
+ /* Cannot cause an error. */
+#define stream_proc_reset(proc)\
+ void proc(P1(stream *))
+ stream_proc_reset((*reset));
+
+ /* Flush buffered data to output, or drain input. */
+ /* Return 0 if OK, ERRC if error. */
+#define stream_proc_flush(proc)\
+ int proc(P1(stream *))
+ stream_proc_flush((*flush));
+
+ /* Flush data (if writing) & close stream. */
+ /* Return 0 if OK, ERRC if error. */
+#define stream_proc_close(proc)\
+ int proc(P1(stream *))
+ stream_proc_close((*close));
+
+ /* Process a buffer, updating the cursor pointers. */
+ /* See strimpl.h for details. */
+ stream_proc_process((*process));
+
+ /* Switch the stream to read or write mode. */
+ /* false = read, true = write. */
+ /* If the procedure is 0, switching is not allowed. */
+#define stream_proc_switch_mode(proc)\
+ int proc(P2(stream *, bool))
+ stream_proc_switch_mode((*switch_mode));
+
+} stream_procs;
+
+/* ------ The actual stream structure ------ */
+
+struct stream_s {
+ /*
+ * To allow the stream itself to serve as the "state"
+ * of a couple of heavily used types, we start its
+ * definition with the common stream state.
+ */
+ stream_state_common;
+ stream_cursor cursor; /* cursor for reading/writing data */
+ byte *cbuf; /* base of buffer */
+ uint bsize; /* size of buffer, 0 if closed */
+ uint cbsize; /* size of buffer */
+ /*
+ * end_status indicates what should happen when the client
+ * reaches the end of the buffer: 0 in the normal case, ERRC
+ * if an error terminated the last read or write operation
+ * from or to the underlying data source or sink, CALLC if
+ * a callout is required.
+ *
+ * When reading, end_status can also have the value EOFC,
+ * indicating that the stream has reached EOD.
+ */
+ short end_status; /* status at end of buffer (when */
+ /* reading) or now (when writing) */
+ byte foreign; /* true if buffer is outside heap */
+ byte modes; /* access modes allowed for this */
+ /* stream */
+#define s_mode_read 1
+#define s_mode_write 2
+#define s_mode_seek 4
+#define s_mode_append 8 /* (s_mode_write also set) */
+#define s_is_valid(s) ((s)->modes != 0)
+#define s_is_reading(s) (((s)->modes & s_mode_read) != 0)
+#define s_is_writing(s) (((s)->modes & s_mode_write) != 0)
+#define s_can_seek(s) (((s)->modes & s_mode_seek) != 0)
+ gs_string cbuf_string; /* cbuf/cbsize if cbuf is a string, */
+ /* 0/? if not */
+ long position; /* file position of beginning of */
+ /* buffer */
+ stream_procs procs;
+ stream *strm; /* the underlying stream, non-zero */
+ /* iff this is a filter stream */
+ int is_temp; /* if >0, this is a temporary */
+ /* stream and should be freed */
+ /* when its source/sink is closed; */
+ /* if >1, the buffer is also */
+ /* temporary */
+ int inline_temp; /* temporary for inline access */
+ /* (see spgetc_inline below) */
+ stream_state *state; /* state of process */
+ /*
+ * The following are for the use of the interpreter.
+ * See files.h for more information on read_id and write_id,
+ * zfile.c for more information on prev, next, and save_count.
+ */
+ ushort read_id; /* "unique" serial # for detecting */
+ /* references to closed streams */
+ /* and for validating read access */
+ ushort write_id; /* ditto to validate write access */
+ stream *prev, *next; /* keep track of all files */
+ int save_count; /* # of saves for which this stream */
+ /* was the head of the file list */
+ /*
+ * In order to avoid allocating a separate stream_state for
+ * file streams, which are the most heavily used stream type,
+ * we put their state here.
+ */
+ FILE *file; /* file handle for C library */
+ uint file_modes; /* access modes for the file, */
+ /* may be a superset of modes */
+ int (*save_close)(P1(stream *)); /* save original close proc */
+};
+#define private_st_stream() /* in stream.c */\
+ gs_private_st_composite_final(st_stream, stream, "stream",\
+ stream_enum_ptrs, stream_reloc_ptrs, stream_finalize)
+
+/* Initialize the checking IDs of a stream. */
+#define s_init_ids(s) ((s)->read_id = (s)->write_id = 1)
+#define s_init_read_id(s) ((s)->read_id = 1, (s)->write_id = 0)
+#define s_init_write_id(s) ((s)->read_id = 0, (s)->write_id = 1)
+#define s_init_no_id(s) ((s)->read_id = (s)->write_id = 0)
+
+/* ------ Stream functions ------ */
+
+#define srptr cursor.r.ptr
+#define srlimit cursor.r.limit
+#define swptr cursor.w.ptr
+#define swlimit cursor.w.limit
+
+/* Some of these are macros -- beware. */
+/* Note that unlike the C stream library, */
+/* ALL stream procedures take the stream as the first argument. */
+#define sendrp(s) ((s)->srptr >= (s)->srlimit) /* NOT FOR CLIENTS */
+#define sendwp(s) ((s)->swptr >= (s)->swlimit) /* NOT FOR CLIENTS */
+
+/*
+ * Following are valid for all streams.
+ */
+/* flush is NOT a no-op for read streams -- it discards data until EOF. */
+/* close is NOT a no-op for non-file streams -- */
+/* it actively disables them. */
+/* The close routine must do a flush if needed. */
+#define sseekable(s) s_can_seek(s)
+int savailable(P2(stream *, long *));
+#define sreset(s) (*(s)->procs.reset)(s)
+#define sflush(s) (*(s)->procs.flush)(s)
+int sclose(P1(stream *));
+int sswitch(P2(stream *, bool));
+
+/*
+ * Following are only valid for read streams.
+ */
+int spgetcc(P2(stream *, bool)); /* bool indicates close-on-EOD */
+#define spgetc(s) spgetcc(s, true) /* a procedure equivalent of sgetc */
+/*
+ * Note that sgetc must call spgetc one byte early, because filter must read ahead
+ * to detect EOD.
+ *
+ * In the definition of sgetc, the first alternative should read
+ * (int)(*++((s)->srptr))
+ * but the Borland compiler generates truly atrocious code for this.
+ * The SCO ODT compiler requires the first, pointless cast to int.
+ */
+#define sgetc(s)\
+ ((int)((s)->srlimit - (s)->srptr > 1 ? (++((s)->srptr), (int)*(s)->srptr) : spgetc(s)))
+int sgets(P4(stream *, byte *, uint, uint *));
+int sungetc(P2(stream *, byte)); /* ERRC on error, 0 if OK */
+#define sputback(s) ((s)->srptr--) /* can only do this once! */
+#define seofp(s) (sendrp(s) && (s)->end_status == EOFC)
+#define serrorp(s) (sendrp(s) && (s)->end_status == ERRC)
+int spskip(P3(stream *, long, long *));
+#define sskip(s,nskip,pskipped) spskip(s, (long)(nskip), pskipped)
+/*
+ * Attempt to refill the buffer of a read stream.
+ * Only call this if the end_status is not EOFC,
+ * and if the buffer is (nearly) empty.
+ */
+int s_process_read_buf(P1(stream *));
+
+/*
+ * Following are only valid for write streams.
+ */
+int spputc(P2(stream *, byte)); /* a procedure equivalent of sputc */
+/*
+ * The first alternative should read
+ * ((int)(*++((s)->swptr)=(c)))
+ * but the Borland compiler generates truly atrocious code for this.
+ */
+#define sputc(s,c)\
+ (!sendwp(s) ? (++((s)->swptr), *(s)->swptr=(c), 0) : spputc((s),(c)))
+int sputs(P4(stream *, const byte *, uint, uint *));
+/*
+ * Attempt to empty the buffer of a write stream.
+ * Only call this if the end_status is not EOFC.
+ */
+int s_process_write_buf(P2(stream *, bool));
+
+/* Following are only valid for positionable streams. */
+long stell(P1(stream *));
+int spseek(P2(stream *, long));
+#define sseek(s,pos) spseek(s, (long)(pos))
+
+/* Following are for high-performance reading clients. */
+/* bufptr points to the next item. */
+#define sbufptr(s) ((s)->srptr + 1)
+#define sbufavailable(s) ((s)->srlimit - (s)->srptr)
+#define sbufskip(s, n) ((s)->srptr += (n), 0)
+/*
+ * Define the minimum amount of data that must be left in an input buffer
+ * after a read operation to handle filter read-ahead. This is 1 byte for
+ * filters (including procedure data sources), 0 for files.
+ */
+#define max_min_left 1
+#define sbuf_min_left(s) (s->strm == 0 && s->end_status != CALLC ? 0 : 1)
+
+/* The following are for very high-performance clients of read streams, */
+/* who unpack the stream state into local variables. */
+/* Note that any non-inline operations must do a s_end_inline before, */
+/* and a s_begin_inline after. */
+#define s_declare_inline(s, cp, ep)\
+ register const byte *cp;\
+ const byte *ep
+#define s_begin_inline(s, cp, ep)\
+ cp = (s)->srptr, ep = (s)->srlimit
+#define s_end_inline(s, cp, ep)\
+ (s)->srptr = cp
+#define sbufavailable_inline(s, cp, ep)\
+ (ep - cp)
+#define sendbufp_inline(s, cp, ep)\
+ (cp >= ep)
+/* The (int) is needed to pacify the SCO ODT compiler. */
+#define sgetc_inline(s, cp, ep)\
+ ((int)(sendbufp_inline(s, cp, ep) ? spgetc_inline(s, cp, ep) : *++cp))
+#define spgetc_inline(s, cp, ep)\
+ (s_end_inline(s, cp, ep), (s)->inline_temp = spgetc(s),\
+ s_begin_inline(s, cp, ep), (s)->inline_temp)
+#define sputback_inline(s, cp, ep)\
+ --cp
+
+/* Allocate a stream or a stream state. */
+stream *s_alloc(P2(gs_memory_t *, client_name_t));
+stream_state *s_alloc_state(P3(gs_memory_t *, gs_memory_type_ptr_t, client_name_t));
+
+/* Stream creation procedures */
+void sread_string(P3(stream *, const byte *, uint)),
+ swrite_string(P3(stream *, byte *, uint));
+void sread_file(P4(stream *, FILE *, byte *, uint)),
+ swrite_file(P4(stream *, FILE *, byte *, uint)),
+ sappend_file(P4(stream *, FILE *, byte *, uint));
+
+/* Standard stream initialization */
+void s_std_init(P5(stream *, byte *, uint, const stream_procs *, int /*mode*/));
+
+/* Standard stream finalization */
+void s_disable(P1(stream *));
+
+/* Generic stream procedures exported for templates */
+int s_std_null(P1(stream *));
+void s_std_read_reset(P1(stream *)),
+ s_std_write_reset(P1(stream *));
+int s_std_read_flush(P1(stream *)),
+ s_std_write_flush(P1(stream *)),
+ s_std_noavailable(P2(stream *, long *)),
+ s_std_noseek(P2(stream *, long)),
+ s_std_close(P1(stream *)),
+ s_std_switch_mode(P2(stream *, bool));
+/* Generic procedures for filters. */
+int s_filter_write_flush(P1(stream *)),
+ s_filter_close(P1(stream *));
diff --git a/pstoraster/strimpl.h b/pstoraster/strimpl.h
new file mode 100644
index 000000000..f6b01c80c
--- /dev/null
+++ b/pstoraster/strimpl.h
@@ -0,0 +1,146 @@
+/* Copyright (C) 1993, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* strimpl.h */
+/* Definitions for stream implementors */
+/* Requires stdio.h */
+
+#ifndef strimpl_INCLUDED
+# define strimpl_INCLUDED
+
+#include "scommon.h"
+#include "gstypes.h" /* for gsstruct.h */
+#include "gsstruct.h"
+
+/*
+ * The 'process' procedure does the real work of the stream.
+ * It must process as much input information (from pr->ptr + 1 through
+ * pr->limit) as it can, subject to space available for output
+ * (pw->ptr + 1 through pw->limit), updating pr->ptr and pw->ptr.
+ *
+ * The procedure return value must be one of:
+ * EOFC - an end-of-data pattern was detected in the input.
+ * ERRC - a syntactic error was detected in the input.
+ * 0 - more input data is needed.
+ * 1 - more output space is needed.
+ * If the procedure returns EOFC, it can assume it will never be called
+ * again for that stream.
+ *
+ * If the procedure is called with last = 1, this is an indication that
+ * no more input will ever be supplied (after the input in the current
+ * buffer defined by *pr); the procedure should produce as much output
+ * as possible, including an end-of-data marker if applicable.
+ * If the procedure is called with last = 1 and returns 1, it may be
+ * called again (with last = 1); if it is called with last = 1 and returns
+ * any other value, it will never be called again for that stream.
+ * If the procedure is called with last = 1 and returns 0, this is taken
+ * as equivalent to returning EOFC.
+ *
+ * Note that these specifications do not distinguish input from output
+ * streams. This is deliberate: The processing procedures should work
+ * regardless of which way they are oriented in a stream pipeline.
+ * (The PostScript language does take a position as whether any given
+ * filter may be used for input or output, but this occurs at a higher level.)
+ *
+ * The value returned by the process procedure of a stream whose data source
+ * or sink is external (i.e., not another stream) is interpreted slightly
+ * differently. For an external data source, a return value of 0 means
+ * "no more input data are available now, but more might become available
+ * later." For an external data sink, a return value of 1 means "there is
+ * no more room for output data now, but there might be room later."
+ *
+ * It appears that the Adobe specifications, read correctly, require that when
+ * the process procedure of a decoding filter has filled up the output
+ * buffer, it must still peek ahead in the input to determine whether or not
+ * the next thing in the input stream is EOD. If the next thing is an EOD (or
+ * end-of-data, indicated by running out of input data with last = true), the
+ * process procedure must return EOFC; if the next thing is definitely not
+ * an EOD, the process procedure must return 1 (output full) (without, of
+ * course, consuming the non-EOD datum); if the procedure cannot determine
+ * whether or not the next thing is an EOD, it must return 0 (need more input).
+ * Decoding filters that don't have EOD (for example, NullDecode) can use
+ * a simpler algorithm: if the output buffer is full, then if there is more
+ * input, return 1, otherwise return 0 (which is taken as EOFC if last
+ * is true). All this may seem a little awkward, but it is needed in order
+ * to have consistent behavior regardless of where buffer boundaries fall --
+ * in particular, if a buffer boundary falls just before an EOD. It is
+ * actually quite easy to implement if the main loop of the process
+ * procedure tests for running out of input rather than for filling the
+ * output: with this structure, exhausting the input always returns 0,
+ * and discovering that the output buffer is full when attempting to store
+ * more output always returns 1.
+ *
+ * Even this algorithm for handling end-of-buffer is not sufficient if an
+ * EOD falls just after a buffer boundary, but the generic stream code
+ * handles this case: the process procedures need only do what was just
+ * described.
+ */
+
+/*
+ * Define a template for creating a stream.
+ *
+ * The meaning of min_in_size and min_out_size is the following:
+ * If the amount of input information is at least min_in_size,
+ * and the available output space is at least min_out_size,
+ * the process procedure guarantees that it will make some progress.
+ * (It may make progress even if this condition is not met, but this is
+ * not guaranteed.)
+ */
+struct stream_template_s {
+
+ /* Define the structure type for the stream state. */
+ gs_memory_type_ptr_t stype;
+
+ /* Define an optional initialization procedure. */
+ stream_proc_init((*init));
+
+ /* Define the processing procedure. */
+ /* (The init procedure can reset other procs if it wants.) */
+ stream_proc_process((*process));
+
+ /* Define the minimum buffer sizes. */
+ uint min_in_size; /* minimum size for process input */
+ uint min_out_size; /* minimum size for process output */
+
+ /* Define an optional releasing procedure. */
+ stream_proc_release((*release));
+
+ /* Define an optional parameter defaulting procedure. */
+ stream_proc_set_defaults((*set_defaults));
+
+ /* Define an optional reinitialization procedure. */
+ stream_proc_reinit((*reinit));
+
+};
+
+/* Utility procedures */
+int stream_move(P2(stream_cursor_read *, stream_cursor_write *)); /* in stream.c */
+/* Hex decoding utility procedure */
+typedef enum {
+ hex_ignore_garbage = 0,
+ hex_ignore_whitespace = 1,
+ hex_ignore_leading_whitespace = 2
+} hex_syntax;
+int s_hex_process(P4(stream_cursor_read *, stream_cursor_write *, int *, hex_syntax)); /* in sstring.c */
+
+#endif /* strimpl_INCLUDED */
diff --git a/pstoraster/string_.h b/pstoraster/string_.h
new file mode 100644
index 000000000..950e7190d
--- /dev/null
+++ b/pstoraster/string_.h
@@ -0,0 +1,53 @@
+/* Copyright (C) 1989, 1992, 1993, 1994 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* string.h */
+/* Generic substitute for Unix string.h */
+
+/* We must include std.h before any file that includes sys/types.h. */
+#include "std.h"
+
+#ifdef BSD4_2
+# include <strings.h>
+# define strchr index
+#else
+# ifdef __TURBOC__
+# undef memset /* just in case */
+# include <string.h>
+# ifndef memset /* Borland C++ can inline this */
+# define memset(dest,chr,cnt) setmem(dest,cnt,chr)
+# endif
+# else
+# ifdef memory__need_memmove
+# undef memmove /* This is disgusting, but so is GCC */
+# endif
+# include <string.h>
+# if defined(THINK_C)
+ /* Patch strlen to return a uint rather than a size_t. */
+# define strlen (uint)strlen
+# endif
+# ifdef memory__need_memmove
+# define memmove(dest,src,len) gs_memmove(dest,src,len)
+# endif
+# endif
+#endif
diff --git a/pstoraster/szlibc.c b/pstoraster/szlibc.c
new file mode 100644
index 000000000..100f64f42
--- /dev/null
+++ b/pstoraster/szlibc.c
@@ -0,0 +1,48 @@
+/* Copyright (C) 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+#include <config.h>
+#ifdef HAVE_LIBZ
+
+/* szlibc.c */
+/* Code common to zlib encoding and decoding streams */
+#include "std.h"
+#include "gstypes.h"
+#include "gsmemory.h"
+#include "gsstruct.h"
+#include "strimpl.h"
+#include "szlibx.h"
+
+public_st_zlib_state();
+
+/* Provide zlib-compatible allocation and freeing functions. */
+void *
+s_zlib_alloc(void *mem, uint items, uint size)
+{ void *address =
+ gs_alloc_byte_array((gs_memory_t *)mem, items, size, "zlib");
+ return (address == 0 ? Z_NULL : address);
+}
+void
+s_zlib_free(void *mem, void *address)
+{ gs_free_object((gs_memory_t *)mem, address, "zlib");
+}
+#endif
diff --git a/pstoraster/szlibd.c b/pstoraster/szlibd.c
new file mode 100644
index 000000000..ec641342d
--- /dev/null
+++ b/pstoraster/szlibd.c
@@ -0,0 +1,90 @@
+/* Copyright (C) 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+#include <config.h>
+#ifdef HAVE_LIBZ
+
+/* szlibd.c */
+/* zlib decoding (decompression) filter stream */
+#include "std.h"
+#include "gsmemory.h"
+#include "strimpl.h"
+#include "szlibx.h"
+
+#define ss ((stream_zlib_state *)st)
+#define szs (&ss->zstate)
+
+/* Initialize the filter. */
+private int
+s_zlibD_init(stream_state *st)
+{ szs->zalloc = s_zlib_alloc;
+ szs->zfree = s_zlib_free;
+ szs->opaque = &gs_memory_default;
+ if ( inflateInit(szs) != Z_OK )
+ return ERRC; /****** WRONG ******/
+ return 0;
+}
+
+/* Process a buffer */
+private int
+s_zlibD_process(stream_state *st, stream_cursor_read *pr,
+ stream_cursor_write *pw, bool ignore_last)
+{ const byte *p = pr->ptr;
+ int status;
+
+ /* Detect no input or full output so that we don't get */
+ /* a Z_BUF_ERROR return. */
+ if ( pw->ptr == pw->limit )
+ return 1;
+ if ( pr->ptr == pr->limit )
+ return 0;
+ szs->next_in = (byte *)p + 1;
+ szs->avail_in = pr->limit - p;
+ szs->next_out = pw->ptr + 1;
+ szs->avail_out = pw->limit - pw->ptr;
+ status = inflate(szs, Z_PARTIAL_FLUSH);
+ pr->ptr = szs->next_in - 1;
+ pw->ptr = szs->next_out - 1;
+ switch ( status )
+ {
+ case Z_OK:
+ return (pw->ptr == pw->limit ? 1 : pr->ptr > p ? 0 : 1);
+ case Z_STREAM_END:
+ return EOFC;
+ default:
+ return ERRC;
+ }
+}
+
+/* Release the stream */
+private void
+s_zlibD_release(stream_state *st)
+{ inflateEnd(szs);
+}
+
+/* Stream template */
+const stream_template s_zlibD_template =
+{ &st_zlib_state, s_zlibD_init, s_zlibD_process, 1, 1, s_zlibD_release
+};
+
+#undef ss
+#endif
diff --git a/pstoraster/szlibe.c b/pstoraster/szlibe.c
new file mode 100644
index 000000000..8e4567cfc
--- /dev/null
+++ b/pstoraster/szlibe.c
@@ -0,0 +1,90 @@
+/* Copyright (C) 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+#include <config.h>
+#ifdef HAVE_LIBZ
+
+/* szlibe.c */
+/* zlib encoding (compression) filter stream */
+#include "std.h"
+#include "gsmemory.h"
+#include "strimpl.h"
+#include "szlibx.h"
+
+#define ss ((stream_zlib_state *)st)
+#define szs (&ss->zstate)
+
+/* Initialize the filter. */
+private int
+s_zlibE_init(stream_state *st)
+{ szs->zalloc = s_zlib_alloc;
+ szs->zfree = s_zlib_free;
+ szs->opaque = &gs_memory_default;
+ if ( deflateInit(szs, Z_DEFAULT_COMPRESSION) != Z_OK )
+ return ERRC; /****** WRONG ******/
+ return 0;
+}
+
+/* Process a buffer */
+private int
+s_zlibE_process(stream_state *st, stream_cursor_read *pr,
+ stream_cursor_write *pw, bool last)
+{ const byte *p = pr->ptr;
+ int status;
+
+ /* Detect no input or full output so that we don't get */
+ /* a Z_BUF_ERROR return. */
+ if ( pw->ptr == pw->limit )
+ return 1;
+ if ( pr->ptr == pr->limit && !last )
+ return 0;
+ szs->next_in = (byte *)p + 1;
+ szs->avail_in = pr->limit - p;
+ szs->next_out = pw->ptr + 1;
+ szs->avail_out = pw->limit - pw->ptr;
+ status = deflate(szs, (last ? Z_FINISH : Z_NO_FLUSH));
+ pr->ptr = szs->next_in - 1;
+ pw->ptr = szs->next_out - 1;
+ switch ( status )
+ {
+ case Z_OK:
+ return (pw->ptr == pw->limit ? 1 : pr->ptr > p && !last ? 0 : 1);
+ case Z_STREAM_END:
+ return EOFC;
+ default:
+ return ERRC;
+ }
+}
+
+/* Release the stream */
+private void
+s_zlibE_release(stream_state *st)
+{ deflateEnd(szs);
+}
+
+/* Stream template */
+const stream_template s_zlibE_template =
+{ &st_zlib_state, s_zlibE_init, s_zlibE_process, 1, 1, s_zlibE_release
+};
+
+#undef ss
+#endif
diff --git a/pstoraster/szlibx.h b/pstoraster/szlibx.h
new file mode 100644
index 000000000..887a84ca4
--- /dev/null
+++ b/pstoraster/szlibx.h
@@ -0,0 +1,47 @@
+/* Copyright (C) 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* szlibx.h */
+/* Definitions for zlib filters */
+/* Requires strimpl.h */
+/* Must be compiled with -I$(ZSRCDIR) */
+#include "zlib.h"
+
+/* Provide zlib-compatible allocation and freeing functions. */
+void *s_zlib_alloc(P3(void *mem, uint items, uint size));
+void s_zlib_free(P2(void *mem, void *address));
+
+typedef struct stream_zlib_state_s {
+ stream_state_common;
+ z_stream zstate;
+} stream_zlib_state;
+/* The state descriptor is public only to allow us to split up */
+/* the encoding and decoding filters. */
+/* Note that we allocate all of zlib's private data directly from */
+/* the C heap, to avoid garbage collection issues. */
+extern_st(st_zlib_state);
+#define public_st_zlib_state() /* in szlibc.c */\
+ gs_public_st_simple(st_zlib_state, stream_zlib_state,\
+ "zlibEncode/Decode state")
+extern const stream_template s_zlibD_template;
+extern const stream_template s_zlibE_template;
diff --git a/pstoraster/time_.h b/pstoraster/time_.h
new file mode 100644
index 000000000..d68bd2da0
--- /dev/null
+++ b/pstoraster/time_.h
@@ -0,0 +1,65 @@
+/*
+ Copyright 1993-1999 by Easy Software Products.
+ Copyright (C) 1991, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* time_.h */
+/* Generic substitute for Unix sys/time.h */
+
+/* We must include std.h before any file that includes sys/types.h. */
+#include "std.h"
+
+/* Some System V environments don't include sys/time.h. */
+/* The SYSTIME_H switch in gconfig_.h reflects this. */
+#include <sys/time.h>
+#include <time.h>
+
+#if defined(ultrix) && defined(mips)
+/* Apparently some versions of Ultrix for the DECstation include */
+/* time_t in sys/time.h, and some don't. If you get errors */
+/* compiling gp_unix.c, uncomment the next line. */
+/* typedef int time_t; */
+#endif
+
+/* In SVR4.0 (but not other System V implementations), */
+/* gettimeofday doesn't take a timezone argument. */
+#ifdef SVR4_0
+# define gettimeofday_no_timezone 1
+#else
+# define gettimeofday_no_timezone 0
+#endif
+
+/* Some System V environments, and Posix environments, need <sys/times.h>. */
+#if defined(SYSV) || defined(SVR4)
+# include <sys/times.h>
+# define use_times_for_usertime 1
+ /* Posix 1003.1b-1993 section 4.8.1.5 says that
+ CLK_TCK is obsolescent and that sysconf(_SC_CLK_TCK)
+ should be used instead, but this requires including
+ <unistd.h>, which is too painful to configure. */
+# ifndef CLK_TCK
+# define CLK_TCK 100 /* guess for older hosts */
+# endif
+#else
+# define use_times_for_usertime 0
+#endif
diff --git a/pstoraster/vmsmath.h b/pstoraster/vmsmath.h
new file mode 100644
index 000000000..9b309535c
--- /dev/null
+++ b/pstoraster/vmsmath.h
@@ -0,0 +1,42 @@
+/* Copyright (C) 1989 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* vmsmath.h */
+/* Substitute for math.h on VAX/VMS systems */
+
+/* DEC VAX/VMS C comes with a math.h file but GNU VAX/VMS C does not. */
+/* This file substitutes for math.h when using GNU C. */
+# ifndef __MATH
+# define __MATH
+# if CC$gfloat
+# define HUGE_VAL 8.988465674311578540726371186585e+307
+# else
+# define HUGE_VAL 1.70141183460469229e+38
+# endif
+ extern double acos(), asin(), atan(), atan2();
+ extern double sin(), tan(), cos();
+ extern double cosh(), sinh(), tanh();
+ extern double exp(), frexp(), ldexp(), log(), log10(), pow();
+ extern double modf(), fmod(), sqrt(), ceil(), floor();
+ extern double fabs(), cabs(), hypot();
+# endif
diff --git a/pstoraster/zarith.c b/pstoraster/zarith.c
new file mode 100644
index 000000000..489c3a7c1
--- /dev/null
+++ b/pstoraster/zarith.c
@@ -0,0 +1,296 @@
+/* Copyright (C) 1989, 1992, 1993, 1994 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* zarith.c */
+/* Arithmetic operators */
+#include "math_.h"
+#include "ghost.h"
+#include "errors.h"
+#include "oper.h"
+#include "store.h"
+
+/****** NOTE: none of the arithmetic operators ******/
+/****** currently check for floating exceptions ******/
+
+/* Define max and min values for what will fit in value.intval. */
+#define min_intval min_long
+#define max_intval max_long
+#define max_half_intval ((1 << (size_of(long) / 2 - 1)) - 1)
+
+/* Macros for generating non-integer cases for arithmetic operations. */
+/* 'frob' is one of the arithmetic operators, +, -, or *. */
+#define non_int_cases(frob,frob_equals)\
+ switch ( r_type(op) ) {\
+ default: return_op_typecheck(op);\
+ case t_real: switch ( r_type(op - 1) ) {\
+ default: return_op_typecheck(op - 1);\
+ case t_real: op[-1].value.realval frob_equals op->value.realval; break;\
+ case t_integer: make_real(op - 1, op[-1].value.intval frob op->value.realval);\
+ } break;\
+ case t_integer: switch ( r_type(op - 1) ) {\
+ default: return_op_typecheck(op - 1);\
+ case t_real: op[-1].value.realval frob_equals op->value.intval; break;\
+ case t_integer:
+#define end_cases()\
+ } }
+
+/* <num1> <num2> add <sum> */
+/* We make this into a separate procedure because */
+/* the interpreter will almost always call it directly. */
+int
+zop_add(register os_ptr op)
+{ non_int_cases(+, +=)
+ { long int2 = op->value.intval;
+ if ( ((op[-1].value.intval += int2) ^ int2) < 0 &&
+ ((op[-1].value.intval - int2) ^ int2) >= 0
+ )
+ { /* Overflow, convert to real */
+ make_real(op - 1, (float)(op[-1].value.intval - int2) + int2);
+ }
+ }
+ end_cases()
+ return 0;
+}
+int
+zadd(os_ptr op)
+{ int code = zop_add(op);
+ if ( code == 0 ) { pop(1); }
+ return code;
+}
+
+/* <num1> <num2> div <real_quotient> */
+private int
+zdiv(register os_ptr op)
+{ register os_ptr op1 = op - 1;
+ /* We can't use the non_int_cases macro, */
+ /* because we have to check explicitly for op == 0. */
+ switch ( r_type(op) )
+ {
+ default:
+ return_op_typecheck(op);
+ case t_real:
+ if ( op->value.realval == 0 )
+ return_error(e_undefinedresult);
+ switch ( r_type(op1) )
+ {
+ default:
+ return_op_typecheck(op1);
+ case t_real:
+ op1->value.realval /= op->value.realval;
+ break;
+ case t_integer:
+ make_real(op1, op1->value.intval / op->value.realval);
+ }
+ break;
+ case t_integer:
+ if ( op->value.intval == 0 )
+ return_error(e_undefinedresult);
+ switch ( r_type(op1) )
+ {
+ default:
+ return_op_typecheck(op1);
+ case t_real:
+ op1->value.realval /= op->value.intval; break;
+ case t_integer:
+ make_real(op1, (float)op1->value.intval / op->value.intval);
+ }
+ }
+ pop(1);
+ return 0;
+}
+
+/* <num1> <num2> mul <product> */
+private int
+zmul(register os_ptr op)
+{ non_int_cases(*, *=)
+ { long int1 = op[-1].value.intval;
+ long int2 = op->value.intval;
+ long abs1 = (int1 >= 0 ? int1 : - int1);
+ long abs2 = (int2 >= 0 ? int2 : - int2);
+ float fprod;
+ if ( (abs1 > max_half_intval || abs2 > max_half_intval) &&
+ /* At least one of the operands is very large. */
+ /* Check for integer overflow. */
+ abs1 != 0 &&
+ abs2 > max_intval / abs1 &&
+ /* Check for the boundary case */
+ (fprod = (float)int1 * int2,
+ (int1 * int2 != min_intval ||
+ fprod != (float)min_intval))
+ )
+ make_real(op - 1, fprod);
+ else
+ op[-1].value.intval = int1 * int2;
+ }
+ end_cases()
+ pop(1);
+ return 0;
+}
+
+/* <num1> <num2> sub <difference> */
+/* We make this into a separate procedure because */
+/* the interpreter will almost always call it directly. */
+int
+zop_sub(register os_ptr op)
+{ non_int_cases(-, -=)
+ { long int1 = op[-1].value.intval;
+ if ( (int1 ^ (op[-1].value.intval = int1 - op->value.intval)) < 0 &&
+ (int1 ^ op->value.intval) < 0
+ )
+ { /* Overflow, convert to real */
+ make_real(op - 1, (float)int1 - op->value.intval);
+ }
+ }
+ end_cases()
+ return 0;
+}
+int
+zsub(os_ptr op)
+{ int code = zop_sub(op);
+ if ( code == 0 ) { pop(1); }
+ return code;
+}
+
+/* <num1> <num2> idiv <int_quotient> */
+private int
+zidiv(register os_ptr op)
+{ register os_ptr op1 = op - 1;
+ check_type(*op, t_integer);
+ check_type(*op1, t_integer);
+ if ( op->value.intval == 0 )
+ return_error(e_undefinedresult);
+ if ( (op1->value.intval /= op->value.intval) ==
+ min_intval && op->value.intval == -1
+ )
+ { /* Anomalous boundary case, fail. */
+ return_error(e_rangecheck);
+ }
+ pop(1);
+ return 0;
+}
+
+/* <int1> <int2> mod <remainder> */
+private int
+zmod(register os_ptr op)
+{ check_type(*op, t_integer);
+ check_type(op[-1], t_integer);
+ if ( op->value.intval == 0 )
+ return_error(e_undefinedresult);
+ op[-1].value.intval %= op->value.intval;
+ pop(1);
+ return 0;
+}
+
+/* <num1> neg <num2> */
+private int
+zneg(register os_ptr op)
+{ switch ( r_type(op) )
+ {
+ default:
+ return_op_typecheck(op);
+ case t_real:
+ op->value.realval = -op->value.realval;
+ break;
+ case t_integer:
+ if ( op->value.intval == min_intval )
+ make_real(op, -(float)min_intval);
+ else
+ op->value.intval = -op->value.intval;
+ }
+ return 0;
+}
+
+/* <num1> ceiling <num2> */
+private int
+zceiling(register os_ptr op)
+{ switch ( r_type(op) )
+ {
+ default:
+ return_op_typecheck(op);
+ case t_real:
+ op->value.realval = ceil(op->value.realval);
+ case t_integer: ;
+ }
+ return 0;
+}
+
+/* <num1> floor <num2> */
+private int
+zfloor(register os_ptr op)
+{ switch ( r_type(op) )
+ {
+ default:
+ return_op_typecheck(op);
+ case t_real:
+ op->value.realval = floor(op->value.realval);
+ case t_integer: ;
+ }
+ return 0;
+}
+
+/* <num1> round <num2> */
+private int
+zround(register os_ptr op)
+{ switch ( r_type(op) )
+ {
+ default:
+ return_op_typecheck(op);
+ case t_real:
+ op->value.realval = floor(op->value.realval + 0.5);
+ case t_integer: ;
+ }
+ return 0;
+}
+
+/* <num1> truncate <num2> */
+private int
+ztruncate(register os_ptr op)
+{ switch ( r_type(op) )
+ {
+ default:
+ return_op_typecheck(op);
+ case t_real:
+ op->value.realval =
+ (op->value.realval < 0.0 ?
+ ceil(op->value.realval) :
+ floor(op->value.realval));
+ case t_integer: ;
+ }
+ return 0;
+}
+
+/* ------ Initialization table ------ */
+
+BEGIN_OP_DEFS(zarith_op_defs) {
+ {"2add", zadd},
+ {"1ceiling", zceiling},
+ {"2div", zdiv},
+ {"2idiv", zidiv},
+ {"1floor", zfloor},
+ {"2mod", zmod},
+ {"2mul", zmul},
+ {"1neg", zneg},
+ {"1round", zround},
+ {"2sub", zsub},
+ {"1truncate", ztruncate},
+END_OP_DEFS(0) }
diff --git a/pstoraster/zarray.c b/pstoraster/zarray.c
new file mode 100644
index 000000000..731281a51
--- /dev/null
+++ b/pstoraster/zarray.c
@@ -0,0 +1,125 @@
+/* Copyright (C) 1989, 1992, 1993 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* zarray.c */
+/* Array operators */
+#include "memory_.h"
+#include "ghost.h"
+#include "errors.h"
+#include "ialloc.h"
+#include "ipacked.h"
+#include "oper.h"
+#include "store.h"
+
+/* The generic operators (copy, get, put, getinterval, putinterval, */
+/* length, and forall) are implemented in zgeneric.c. */
+
+/* <int> array <array> */
+int
+zarray(register os_ptr op)
+{ uint size;
+ int code;
+ check_int_leu(*op, max_array_size);
+ size = op->value.intval;
+ code = ialloc_ref_array((ref *)op, a_all, size, "array");
+ if ( code < 0 )
+ return code;
+ refset_null(op->value.refs, size);
+ return 0;
+}
+
+/* <array> aload <obj_0> ... <obj_n-1> <array> */
+private int
+zaload(register os_ptr op)
+{ ref aref;
+ ref_assign(&aref, op);
+ if ( !r_is_array(&aref) )
+ return_op_typecheck(op);
+ check_read(aref);
+#define asize r_size(&aref)
+ if ( asize > ostop - op )
+ { /* Use the slow, general algorithm. */
+ int code = ref_stack_push(&o_stack, asize);
+ uint i;
+ const ref_packed *packed = aref.value.packed;
+ if ( code < 0 )
+ return code;
+ for ( i = asize; i > 0; i--, packed = packed_next(packed) )
+ packed_get(packed, ref_stack_index(&o_stack, i));
+ *osp = aref;
+ return 0;
+ }
+ if ( r_has_type(&aref, t_array) )
+ memcpy((char *)op, (const char *)aref.value.refs,
+ asize * sizeof(ref));
+ else
+ { register ushort i;
+ const ref_packed *packed = aref.value.packed;
+ os_ptr pdest = op;
+ for ( i = 0; i < asize; i++, pdest++ )
+ packed_get(packed, pdest),
+ packed = packed_next(packed);
+ }
+ push(asize);
+#undef asize
+ ref_assign(op, &aref);
+ return 0;
+}
+
+/* <obj_0> ... <obj_n-1> <array> astore <array> */
+private int
+zastore(register os_ptr op)
+{ uint size;
+ int code;
+ check_write_type(*op, t_array);
+ size = r_size(op);
+ if ( size > op - osbot )
+ { /* The store operation might involve other stack segments. */
+ ref arr;
+ if ( size >= ref_stack_count(&o_stack) )
+ return_error(e_stackunderflow);
+ arr = *op;
+ code = ref_stack_store(&o_stack, &arr, size, 1, 0, true,
+ "astore");
+ if ( code < 0 )
+ return code;
+ ref_stack_pop(&o_stack, size);
+ *ref_stack_index(&o_stack, 0) = arr;
+ }
+ else
+ { code = refcpy_to_old(op, 0, op - size, size, "astore");
+ if ( code < 0 )
+ return code;
+ op[-(int)size] = *op;
+ pop(size);
+ }
+ return 0;
+}
+
+/* ------ Initialization procedure ------ */
+
+BEGIN_OP_DEFS(zarray_op_defs) {
+ {"1aload", zaload},
+ {"1array", zarray},
+ {"1astore", zastore},
+END_OP_DEFS(0) }
diff --git a/pstoraster/zbseq.c b/pstoraster/zbseq.c
new file mode 100644
index 000000000..68236660e
--- /dev/null
+++ b/pstoraster/zbseq.c
@@ -0,0 +1,271 @@
+/* Copyright (C) 1990, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* zbseq.c */
+/* Level 2 binary object sequence operators */
+#include "memory_.h"
+#include "ghost.h"
+#include "errors.h"
+#include "oper.h"
+#include "stream.h"
+#include "strimpl.h"
+#include "sfilter.h"
+#include "ialloc.h" /* for isave.h */
+#include "idict.h"
+#include "iname.h"
+#include "isave.h"
+#include "ibnum.h"
+#include "btoken.h"
+#include "bseq.h"
+#include "store.h"
+
+/* Current binary format (in iscan.c) */
+extern ref ref_binary_object_format;
+
+/* System and user name arrays. */
+ref system_names, user_names;
+private ref *system_names_p = &system_names;
+private ref *user_names_p = &user_names;
+private gs_gc_root_t system_names_root, user_names_root;
+
+/* Import the Level 2 scanner extensions. */
+typedef struct scanner_state_s scanner_state;
+extern int scan_binary_token(P3(stream *, ref *, scanner_state *));
+extern int (*scan_btoken_proc)(P3(stream *, ref *, scanner_state *));
+
+/* Forward references */
+private void store_short(P3(byte *, short, int));
+private void store_long(P3(byte *, long, int));
+
+/* Initialize the binary token machinery. */
+private void
+zbseq_init(void)
+{ /* Initialize fake system and user name tables. */
+ /* PostScript code will install the real ones. */
+ make_empty_array(&system_names, a_readonly);
+ gs_register_ref_root(imemory, &system_names_root,
+ (void **)&system_names_p, "system_names");
+ make_empty_array(&user_names, a_all);
+ gs_register_ref_root(imemory, &user_names_root,
+ (void **)&user_names_p, "user_names");
+ /* Set up Level 2 scanning constants. */
+ scan_btoken_proc = scan_binary_token;
+}
+
+/* <system_names> <user_names> .installnames - */
+private int
+zinstallnames(register os_ptr op)
+{ check_read_type(op[-1], t_shortarray);
+ check_type(*op, t_array);
+ ref_assign_old(NULL, &system_names, op - 1, ".installnames");
+ ref_assign_old(NULL, &user_names, op, ".installnames");
+ pop(2);
+ return 0;
+}
+
+/* - currentobjectformat <int> */
+private int
+zcurrentobjectformat(register os_ptr op)
+{ push(1);
+ *op = ref_binary_object_format;
+ return 0;
+}
+
+/* <int> setobjectformat - */
+private int
+zsetobjectformat(register os_ptr op)
+{ check_type(*op, t_integer);
+ if ( op->value.intval < 0 || op->value.intval > 4 )
+ return_error(e_rangecheck);
+ ref_assign_old(NULL, &ref_binary_object_format, op, "setobjectformat");
+ pop(1);
+ return 0;
+}
+
+/*
+ * The remaining operators in this file are conversion operators
+ * that do the dirty work of printobject and writeobject.
+ * The main control is in PostScript code, so that we don't have to worry
+ * about interrupts or callouts in the middle of writing the various data
+ * items.
+ */
+
+/* <top_length> <total_length> <string8> .bosheader <string4|8> */
+private int
+zbosheader(register os_ptr op)
+{ int order = (int)ref_binary_object_format.value.intval - 1;
+ long top, total;
+ byte *p;
+ if ( order < 0 )
+ return_error(e_undefined);
+ check_type(op[-2], t_integer);
+ check_type(op[-1], t_integer);
+ check_write_type(*op, t_string);
+ if ( r_size(op) < 8 )
+ return_error(e_rangecheck);
+ top = op[-2].value.intval;
+ total = op[-1].value.intval;
+ p = op->value.bytes;
+ p[0] = bt_seq + order;
+ if ( top > 255 || total > 0xffff - 4 ) /* use long format */
+ { p[1] = 0;
+ store_short(p + 2, top, order);
+ store_long(p + 4, total + 8, order);
+ r_set_size(op, 8);
+ }
+ else /* use short format */
+ { p[1] = top;
+ store_short(p + 2, total + 4, order);
+ r_set_size(op, 4);
+ }
+ op[-2] = *op;
+ pop(2);
+ return 0;
+}
+
+/* <ref_offset> <char_offset> <obj> <string8> .bosobject */
+/* <ref_offset'> <char_offset'> <string8> */
+/* This converts a single object to its binary object sequence */
+/* representation. Note that this may or may not modify the 'unused' field. */
+private int
+zbosobject(os_ptr op)
+{ register os_ptr op1 = op - 1;
+ int order = (int)ref_binary_object_format.value.intval - 1;
+ bin_seq_obj ob;
+ ref nstr;
+ check_type(op[-3], t_integer);
+ check_type(op[-2], t_integer);
+ check_write_type(*op, t_string);
+ if ( r_size(op) < 8 )
+ return_error(e_rangecheck);
+#define swap_t(a, b) t = a, a = b, b = t
+#if arch_is_big_endian
+# define must_swap() (order & 1)
+#else
+# define must_swap() (!(order & 1))
+#endif
+ switch ( r_type(op1) )
+ {
+ case t_null:
+ ob.tx = (byte)bs_null;
+ break;
+ case t_mark:
+ ob.tx = (byte)bs_mark;
+ break;
+ case t_integer:
+ ob.tx = (byte)bs_integer;
+ ob.value.w = op1->value.intval;
+num: ob.size.w = 0; /* (matters for reals) */
+swb: /* swap bytes of value if needed */
+ if ( must_swap() )
+ { byte t;
+ swap_t(ob.value.b[0], ob.value.b[3]);
+ swap_t(ob.value.b[1], ob.value.b[2]);
+ }
+ break;
+ case t_real:
+ ob.tx = (byte)bs_real;
+ ob.value.f = op1->value.realval;
+ /***** handle non-IEEE native *****/
+ goto num;
+ case t_boolean:
+ ob.tx = (byte)bs_boolean;
+ ob.value.w = op1->value.boolval;
+ goto num;
+ case t_array:
+ ob.tx = (byte)bs_array;
+ if ( r_has_attr(op1, a_executable) )
+ ob.tx += (byte)bs_executable;
+ ob.size.w = r_size(op1);
+ ob.value.w = op[-3].value.intval;
+ op[-3].value.intval += ob.size.w * (ulong)sizeof(bin_seq_obj);
+ goto nsa;
+ case t_dictionary: /* EXTENSION */
+ ob.tx = (byte)bs_dictionary;
+ if ( r_has_attr(op1, a_executable) )
+ ob.tx += (byte)bs_executable;
+ ob.size.w = dict_length(op1) << 1;
+ ob.value.w = op[-3].value.intval;
+ op[-3].value.intval += ob.size.w * (ulong)sizeof(bin_seq_obj);
+ goto nsa;
+ case t_string:
+ ob.tx = (byte)bs_string;
+ if ( r_has_attr(op1, a_executable) )
+ ob.tx += (byte)bs_executable;
+ ob.size.w = r_size(op1);
+nos: ob.value.w = op[-2].value.intval;
+ op[-2].value.intval += ob.size.w;
+nsa: if ( must_swap() )
+ { byte t;
+ swap_t(ob.size.b[0], ob.size.b[1]);
+ }
+ goto swb;
+ case t_name:
+ ob.tx = (byte)bs_name;
+ name_string_ref(op1, &nstr);
+ ob.size.w = r_size(&nstr);
+ goto nos;
+ }
+ memcpy(op->value.bytes, (byte *)&ob, sizeof(bin_seq_obj));
+ op[-1] = *op;
+ r_set_size(op - 1, 8);
+ pop(1);
+ return 0;
+}
+
+/* ------ Initialization procedure ------ */
+
+BEGIN_OP_DEFS(zbseq_l2_op_defs) {
+ op_def_begin_level2(),
+ {"2.installnames", zinstallnames},
+ {"0currentobjectformat", zcurrentobjectformat},
+ {"1setobjectformat", zsetobjectformat},
+ {"3.bosheader", zbosheader},
+ {"4.bosobject", zbosobject},
+END_OP_DEFS(zbseq_init) }
+
+/* ------ Internal routines ------ */
+
+/* Put a short (16 bits). */
+private void
+store_short(register byte *p, short num, int order)
+{ byte a = num & 0xff;
+ byte b = (byte)(num >> 8);
+ if ( order & 1 )
+ p[0] = a, p[1] = b;
+ else
+ p[0] = b, p[1] = a;
+}
+
+/* Put a long (32 bits). */
+private void
+store_long(register byte *p, long num, int order)
+{ byte a = num & 0xff;
+ byte b = (byte)(num >> 8);
+ byte c = (byte)(num >> 16);
+ byte d = (byte)(num >> 24);
+ if ( order & 1 )
+ p[0] = a, p[1] = b, p[2] = c, p[3] = d;
+ else
+ p[0] = d, p[1] = c, p[2] = b, p[3] = a;
+}
diff --git a/pstoraster/zchar.c b/pstoraster/zchar.c
new file mode 100644
index 000000000..0ec873cdc
--- /dev/null
+++ b/pstoraster/zchar.c
@@ -0,0 +1,608 @@
+/* Copyright (C) 1989, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* zchar.c */
+/* Character operators */
+#include "ghost.h"
+#include "errors.h"
+#include "oper.h"
+#include "gsstruct.h"
+#include "gxarith.h"
+#include "gxfixed.h"
+#include "gxmatrix.h" /* for ifont.h */
+#include "gschar.h"
+#include "gxdevice.h" /* for gxfont.h */
+#include "gxfont.h"
+#include "gzpath.h"
+#include "gzstate.h"
+#include "dstack.h" /* for stack depth */
+#include "estack.h"
+#include "ialloc.h"
+#include "ichar.h"
+#include "idict.h"
+#include "ifont.h"
+#include "igstate.h"
+#include "ilevel.h"
+#include "iname.h"
+#include "ipacked.h"
+#include "store.h"
+
+/* Forward references */
+private bool map_glyph_to_char(P3(const ref *, const ref *, ref *));
+private int finish_show(P1(os_ptr));
+private int finish_stringwidth(P1(os_ptr));
+private int op_show_cleanup(P1(os_ptr));
+
+/* <string> show - */
+private int
+zshow(register os_ptr op)
+{ gs_show_enum *penum;
+ int code = op_show_setup(op, &penum);
+ if ( code != 0 )
+ return code;
+ if ( (code = gs_show_n_init(penum, igs, (char *)op->value.bytes, r_size(op))) < 0 )
+ { ifree_object(penum, "op_show_enum_setup");
+ return code;
+ }
+ op_show_finish_setup(penum, 1, finish_show);
+ return op_show_continue(op - 1);
+}
+
+/* <ax> <ay> <string> ashow - */
+private int
+zashow(register os_ptr op)
+{ gs_show_enum *penum;
+ int code;
+ float axy[2];
+ if ( (code = num_params(op - 1, 2, axy)) < 0 ||
+ (code = op_show_setup(op, &penum)) != 0
+ )
+ return code;
+ if ( (code = gs_ashow_n_init(penum, igs, axy[0], axy[1], (char *)op->value.bytes, r_size(op))) < 0 )
+ { ifree_object(penum, "op_show_enum_setup");
+ return code;
+ }
+ op_show_finish_setup(penum, 3, finish_show);
+ return op_show_continue(op - 3);
+}
+
+/* <cx> <cy> <char> <string> widthshow - */
+private int
+zwidthshow(register os_ptr op)
+{ gs_show_enum *penum;
+ int code;
+ float cxy[2];
+ check_type(op[-1], t_integer);
+ if ( (gs_char)(op[-1].value.intval) != op[-1].value.intval )
+ return_error(e_rangecheck);
+ if ( (code = num_params(op - 2, 2, cxy)) < 0 ||
+ (code = op_show_setup(op, &penum)) != 0
+ )
+ return code;
+ if ( (code = gs_widthshow_n_init(penum, igs, cxy[0], cxy[1],
+ (gs_char)op[-1].value.intval,
+ (char *)op->value.bytes,
+ r_size(op))) < 0 )
+ { ifree_object(penum, "op_show_enum_setup");
+ return code;
+ }
+ op_show_finish_setup(penum, 4, finish_show);
+ return op_show_continue(op - 4);
+}
+
+/* <cx> <cy> <char> <ax> <ay> <string> awidthshow - */
+private int
+zawidthshow(register os_ptr op)
+{ gs_show_enum *penum;
+ int code;
+ float cxy[2], axy[2];
+ check_type(op[-3], t_integer);
+ if ( (gs_char)(op[-3].value.intval) != op[-3].value.intval )
+ return_error(e_rangecheck);
+ if ( (code = num_params(op - 4, 2, cxy)) < 0 ||
+ (code = num_params(op - 1, 2, axy)) < 0 ||
+ (code = op_show_setup(op, &penum)) != 0
+ )
+ return code;
+ if ( (code = gs_awidthshow_n_init(penum, igs, cxy[0], cxy[1],
+ (gs_char)op[-3].value.intval,
+ axy[0], axy[1],
+ (char *)op->value.bytes,
+ r_size(op))) < 0 )
+ { ifree_object(penum, "op_show_enum_setup");
+ return code;
+ }
+ op_show_finish_setup(penum, 6, finish_show);
+ return op_show_continue(op - 6);
+}
+
+/* <proc> <string> kshow - */
+private int
+zkshow(register os_ptr op)
+{ gs_show_enum *penum;
+ int code;
+ check_proc(op[-1]);
+ if ( (code = op_show_setup(op, &penum)) != 0 )
+ return code;
+ if ( (code = gs_kshow_n_init(penum, igs, (char *)op->value.bytes, r_size(op))) < 0 )
+ { ifree_object(penum, "op_show_enum_setup");
+ return code;
+ }
+ op_show_finish_setup(penum, 2, finish_show);
+ sslot = op[-1]; /* save kerning proc */
+ return op_show_continue(op - 2);
+}
+
+/* Common finish procedure for all show operations. */
+/* Doesn't have to do anything. */
+private int
+finish_show(os_ptr op)
+{ return 0;
+}
+
+/* <string> stringwidth <wx> <wy> */
+private int
+zstringwidth(register os_ptr op)
+{ gs_show_enum *penum;
+ int code = op_show_setup(op, &penum);
+ if ( code != 0 )
+ return code;
+ if ( (code = gs_stringwidth_n_init(penum, igs, (char *)op->value.bytes, r_size(op))) < 0 )
+ { ifree_object(penum, "op_show_enum_setup");
+ return code;
+ }
+ op_show_finish_setup(penum, 1, finish_stringwidth);
+ return op_show_continue(op - 1);
+}
+/* Finishing procedure for stringwidth. */
+/* Pushes the accumulated width. */
+private int
+finish_stringwidth(register os_ptr op)
+{ gs_point width;
+ gs_show_width(senum, &width);
+ push(2);
+ make_real(op - 1, width.x);
+ make_real(op, width.y);
+ return 0;
+}
+
+/* Common code for charpath and .charboxpath. */
+private int
+zchar_path(register os_ptr op,
+ int (*init)(P5(gs_show_enum *, gs_state *, const char *, uint, bool)))
+{ gs_show_enum *penum;
+ int code;
+ check_type(*op, t_boolean);
+ code = op_show_setup(op - 1, &penum);
+ if ( code != 0 )
+ return code;
+ if ( (code = (*init)(penum, igs, (char *)op[-1].value.bytes, r_size(op - 1), op->value.boolval)) < 0 )
+ { ifree_object(penum, "op_show_enum_setup");
+ return code;
+ }
+ op_show_finish_setup(penum, 2, finish_show);
+ return op_show_continue(op - 2);
+}
+/* <string> <outline_bool> charpath - */
+private int
+zcharpath(register os_ptr op)
+{ return zchar_path(op, gs_charpath_n_init);
+}
+/* <string> <box_bool> .charboxpath - */
+private int
+zcharboxpath(register os_ptr op)
+{ return zchar_path(op, gs_charboxpath_n_init);
+}
+
+/* <wx> <wy> <llx> <lly> <urx> <ury> setcachedevice - */
+int
+zsetcachedevice(register os_ptr op)
+{ float wbox[6];
+ gs_show_enum *penum = op_show_find();
+ int code = num_params(op, 6, wbox);
+ if ( penum == 0 )
+ return_error(e_undefined);
+ if ( code < 0 )
+ return code;
+ if ( gs_show_width_only(penum) )
+ return op_show_return_width(op, 6, &wbox[0]);
+ code = gs_setcachedevice(penum, igs, wbox);
+ if ( code < 0 )
+ return code;
+ pop(6);
+ if ( code == 1 )
+ clear_pagedevice(istate);
+ return 0;
+}
+
+/* <w0x> <w0y> <llx> <lly> <urx> <ury> <w1x> <w1y> <vx> <vy> setcachedevice2 - */
+int
+zsetcachedevice2(os_ptr op)
+{ float wbox[10];
+ gs_show_enum *penum = op_show_find();
+ int code = num_params(op, 10, wbox);
+ if ( penum == 0 )
+ return_error(e_undefined);
+ if ( code < 0 )
+ return code;
+ if ( gs_show_width_only(penum) )
+ return op_show_return_width(op, 10,
+ (gs_rootfont(igs)->WMode ?
+ &wbox[6] : &wbox[0]));
+ code = gs_setcachedevice2(penum, igs, wbox);
+ if ( code < 0 )
+ return code;
+ pop(10);
+ if ( code == 1 )
+ clear_pagedevice(istate);
+ return 0;
+}
+
+/* <wx> <wy> setcharwidth - */
+private int
+zsetcharwidth(register os_ptr op)
+{ float width[2];
+ gs_show_enum *penum = op_show_find();
+ int code = num_params(op, 2, width);
+ if ( penum == 0 )
+ return_error(e_undefined);
+ if ( code < 0 )
+ return code;
+ if ( gs_show_width_only(penum) )
+ return op_show_return_width(op, 2, &width[0]);
+ code = gs_setcharwidth(penum, igs, width[0], width[1]);
+ if ( code < 0 )
+ return code;
+ pop(2);
+ return 0;
+}
+
+/* <dict> .fontbbox <llx> <lly> <urx> <ury> -true- */
+/* <dict> .fontbbox -false- */
+private int
+zfontbbox(register os_ptr op)
+{ float bbox[4];
+ int code;
+
+ check_type(*op, t_dictionary);
+ check_dict_read(*op);
+ code = font_bbox_param(op, bbox);
+ if ( code < 0 )
+ return code;
+ if ( bbox[0] < bbox[2] && bbox[1] < bbox[3] )
+ { push(4);
+ make_reals(op - 4, bbox, 4);
+ make_true(op);
+ }
+ else
+ { /* No bbox, or an empty one. */
+ make_false(op);
+ }
+ return 0;
+}
+
+/* ------ Initialization procedure ------ */
+
+BEGIN_OP_DEFS(zchar_op_defs) {
+ {"3ashow", zashow},
+ {"6awidthshow", zawidthshow},
+ {"2charpath", zcharpath},
+ {"2.charboxpath", zcharboxpath},
+ {"2kshow", zkshow},
+ {"6setcachedevice", zsetcachedevice},
+ {":setcachedevice2", zsetcachedevice2},
+ {"2setcharwidth", zsetcharwidth},
+ {"1show", zshow},
+ {"1stringwidth", zstringwidth},
+ {"4widthshow", zwidthshow},
+ /* Extensions */
+ {"1.fontbbox", zfontbbox},
+ /* Internal operators */
+ {"0%finish_show", finish_show},
+ {"0%finish_stringwidth", finish_stringwidth},
+ {"0%op_show_continue", op_show_continue},
+END_OP_DEFS(0) }
+
+/* ------ Subroutines ------ */
+
+/* Most of these are exported for zchar2.c. */
+
+/* Prepare to set up for a show operator. */
+/* Don't change any state yet. */
+int
+op_show_setup(os_ptr op, gs_show_enum **ppenum)
+{ check_read_type(*op, t_string);
+ return op_show_enum_setup(op, ppenum);
+}
+int
+op_show_enum_setup(os_ptr op, gs_show_enum **ppenum)
+{ /* Provide a special "hook" for an unusual application */
+ /* that needs to be able to intervene before any operator */
+ /* that renders or measures characters. */
+#ifdef OP_SHOW_ENUM_SETUP_HOOK
+ OP_SHOW_ENUM_SETUP_HOOK
+#endif
+ check_estack(snumpush + 2);
+ if ( (*ppenum = gs_show_enum_alloc((gs_memory_t *)imemory, igs, "op_show_enum_setup")) == 0 )
+ return_error(e_VMerror);
+ return 0;
+}
+
+/* Finish setting up a show operator. This can't fail, since */
+/* op_show_enum_setup did the check_estack. */
+void
+op_show_finish_setup(gs_show_enum *penum, int npop, op_proc_p endproc /* end procedure */)
+{ register es_ptr ep = esp + snumpush;
+ esp = ep;
+ make_mark_estack(ep - (snumpush - 1), es_show, op_show_cleanup);
+ if ( endproc == NULL )
+ endproc = finish_show;
+ make_null(&esslot(ep));
+ make_int(&essindex(ep), 0);
+ make_int(&esodepth(ep), 0); /* see gs_show_render case in */
+ /* op_show_continue_dispatch */
+ make_int(&esddepth(ep), 0); /* ditto */
+ make_op_estack(&eseproc(ep), endproc);
+ make_istruct(ep, 0, penum);
+}
+
+/* Continuation operator for character rendering. */
+int
+op_show_continue(os_ptr op)
+{ return op_show_continue_dispatch(op, gs_show_next(senum));
+}
+/* Note that this sets osp = op explicitly iff the dispatch succeeds. */
+/* This is so that the show operators don't pop anything from the o-stack */
+/* if they don't succeed. */
+/* Note also that if it returns an error, it has freed the enumerator. */
+int
+op_show_continue_dispatch(register os_ptr op, int code)
+{ gs_show_enum *penum = senum;
+ switch ( code )
+ {
+ case 0: /* all done */
+ { os_ptr save_osp = osp;
+ osp = op;
+ code = (*real_opproc(&seproc))(op);
+ op_show_free();
+ if ( code < 0 )
+ { osp = save_osp;
+ return code;
+ }
+ return o_pop_estack;
+ }
+ case gs_show_kern:
+ { ref *pslot = &sslot;
+ push(2);
+ make_int(op - 1, gs_kshow_previous_char(penum));
+ make_int(op, gs_kshow_next_char(penum));
+ push_op_estack(op_show_continue); /* continue after kerning */
+ *++esp = *pslot; /* kerning procedure */
+ }
+ return o_push_estack;
+ case gs_show_render:
+ { gs_font *pfont = gs_currentfont(igs);
+ font_data *pfdata = pfont_data(pfont);
+ gs_char chr = gs_show_current_char(penum);
+ gs_glyph glyph = gs_show_current_glyph(penum);
+ push(2);
+ op[-1] = pfdata->dict; /* push the font */
+ /*
+ * For Type 1 and Type 4 fonts, prefer BuildChar to
+ * BuildGlyph, so that PostScript procedures appearing in the
+ * CharStrings dictionary will receive the character code
+ * rather than the character name; for Type 3 fonts,
+ * prefer BuildGlyph to BuildChar.
+ */
+ if ( pfont->FontType == ft_user_defined )
+ { /* Type 3 font, prefer BuildGlyph. */
+ if ( level2_enabled &&
+ !r_has_type(&pfdata->BuildGlyph, t_null)
+ )
+ { name_index_ref(glyph, op);
+ esp[2] = pfdata->BuildGlyph;
+ }
+ else if ( r_has_type(&pfdata->BuildChar, t_null) )
+ goto err;
+ else if ( chr == gs_no_char )
+ { /* glyphshow, reverse map the character */
+ /* through the Encoding */
+ ref gref;
+ const ref *pencoding = &pfdata->Encoding;
+ name_index_ref(glyph, &gref);
+ if ( !map_glyph_to_char(&gref, pencoding,
+ (ref *)op)
+ )
+ { /* Not found, try .notdef */
+ name_enter_string(".notdef", &gref);
+ if ( !map_glyph_to_char(&gref,
+ pencoding,
+ (ref *)op)
+ )
+ goto err;
+ }
+ esp[2] = pfdata->BuildChar;
+ }
+ else
+ { make_int(op, chr);
+ esp[2] = pfdata->BuildChar;
+ }
+ }
+ else
+ { /* Type 1 or Type 4 font, prefer BuildChar. */
+ /* We know that both BuildChar and BuildGlyph */
+ /* are present. */
+ if ( chr != gs_no_char )
+ { make_int(op, chr);
+ esp[2] = pfdata->BuildChar;
+ }
+ else
+ { name_index_ref(glyph, op);
+ esp[2] = pfdata->BuildGlyph;
+ }
+ }
+ /* Save the stack depths in case we bail out. */
+ sodepth.value.intval = ref_stack_count(&o_stack) - 2;
+ sddepth.value.intval = ref_stack_count(&d_stack);
+ push_op_estack(op_show_continue);
+ ++esp; /* skip BuildChar or BuildGlyph proc */
+ }
+ return o_push_estack;
+ default: /* error */
+err: op_show_free();
+ if ( code < 0 )
+ return code;
+ else
+ return_error(e_invalidfont);
+ }
+}
+/* Reverse-map a glyph name to a character code for glyphshow. */
+private bool
+map_glyph_to_char(const ref *pgref, const ref *pencoding, ref *pch)
+{ uint esize = r_size(pencoding);
+ uint ch;
+ ref eref;
+ for ( ch = 0; ch < esize; ch++ )
+ { array_get(pencoding, (long)ch, &eref);
+ if ( obj_eq(pgref, &eref) )
+ { make_int(pch, ch);
+ return true;
+ }
+ }
+ return false;
+}
+
+/* Find the index of the e-stack mark for the current show enumerator. */
+/* Return 0 if we can't find the mark. */
+uint
+op_show_find_index(void)
+{ uint count = 0;
+ STACK_LOOP_BEGIN(&e_stack, ep, size)
+ for ( ep += size - 1; size != 0; size--, ep--, count++ )
+ if ( r_is_estack_mark(ep) && estack_mark_index(ep) == es_show )
+ return count;
+ STACK_LOOP_END(ep, size)
+ return 0; /* no mark */
+}
+
+/* Find the current show enumerator on the e-stack. */
+gs_show_enum *
+op_show_find(void)
+{ uint index = op_show_find_index();
+ if ( index == 0 )
+ return 0; /* no mark */
+ return r_ptr(ref_stack_index(&e_stack, index - (snumpush - 1)),
+ gs_show_enum);
+}
+
+/* Shortcut the BuildChar or BuildGlyph procedure at the point */
+/* of the setcharwidth or the setcachedevice[2] if we are in */
+/* a stringwidth or cshow, or if we are only collecting the scalable */
+/* width for an xfont character. */
+int
+op_show_return_width(os_ptr op, uint npop, float *pwidth)
+{ uint index = op_show_find_index();
+ es_ptr ep = (es_ptr)ref_stack_index(&e_stack, index - (snumpush - 1));
+ int code = gs_setcharwidth(esenum(ep), igs, pwidth[0], pwidth[1]);
+ uint ocount, dsaved, dcount;
+
+ if ( code < 0 )
+ return code;
+ /* Restore the operand and dictionary stacks. */
+ ocount = ref_stack_count(&o_stack) - (uint)esodepth(ep).value.intval;
+ if ( ocount < npop )
+ return_error(e_stackunderflow);
+ dsaved = (uint)esddepth(ep).value.intval;
+ dcount = ref_stack_count(&d_stack);
+ if ( dcount < dsaved )
+ return_error(e_dictstackunderflow);
+ while ( dcount > dsaved )
+ { code = zend(op);
+ if ( code < 0 )
+ return code;
+ dcount--;
+ }
+ ref_stack_pop(&o_stack, ocount);
+ /* We don't want to pop the mark or the continuation */
+ /* procedure (op_show_continue or cshow_continue). */
+ pop_estack(index - snumpush);
+ return o_pop_estack;
+}
+
+/* Discard the show record (after an error, or at the end). */
+private int
+op_show_cleanup(os_ptr op)
+{ register es_ptr ep = esp + snumpush;
+ gs_show_enum *penum = esenum(ep);
+ if ( r_is_struct(&esslot(ep)) )
+ ifree_object(esslot(ep).value.pstruct, "free_show(stream)");
+ gs_show_enum_release(penum, (gs_memory_t *)imemory);
+ return 0;
+}
+void
+op_show_free(void)
+{ esp -= snumpush;
+ op_show_cleanup(osp);
+}
+
+/* Get a FontBBox parameter from a font dictionary. */
+int
+font_bbox_param(const ref *pfdict, float bbox[4])
+{ ref *pbbox;
+ /*
+ * Pre-clear the bbox in case it's invalid. The Red Books say that
+ * FontBBox is required, but the Adobe interpreters don't require
+ * it, and a few user-written fonts don't supply it, or supply one
+ * of the wrong size (!); also, PageMaker 5.0 (an Adobe product!)
+ * sometimes emits an absurd bbox for Type 1 fonts converted from
+ * TrueType.
+ */
+ bbox[0] = bbox[1] = bbox[2] = bbox[3] = 0.0;
+ if ( dict_find_string(pfdict, "FontBBox", &pbbox) > 0 )
+ { if ( !r_is_array(pbbox) )
+ return_error(e_typecheck);
+ if ( r_size(pbbox) == 4 )
+ { const ref_packed *pbe = pbbox->value.packed;
+ ref rbe[4];
+ int i;
+ int code;
+ float dx, dy, ratio;
+
+ for ( i = 0; i < 4; i++ )
+ { packed_get(pbe, rbe + i);
+ pbe = packed_next(pbe);
+ }
+ if ( (code = num_params(rbe + 3, 4, bbox)) < 0 )
+ return code;
+ /* Require "reasonable" values. Thanks to Ray */
+ /* Johnston for suggesting the following test. */
+ dx = bbox[2] - bbox[0];
+ dy = bbox[3] - bbox[1];
+ if ( dx <= 0 || dy <= 0 ||
+ (ratio = dy / dx) < 0.125 || ratio > 8.0
+ )
+ bbox[0] = bbox[1] = bbox[2] = bbox[3] = 0.0;
+ }
+ }
+ return 0;
+}
diff --git a/pstoraster/zchar1.c b/pstoraster/zchar1.c
new file mode 100644
index 000000000..0edf341cc
--- /dev/null
+++ b/pstoraster/zchar1.c
@@ -0,0 +1,559 @@
+/* Copyright (C) 1993, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* zchar1.c */
+/* Type 1 character display operator */
+#include "ghost.h"
+#include "errors.h"
+#include "oper.h"
+#include "gsstruct.h"
+#include "gxfixed.h"
+#include "gxmatrix.h"
+#include "gxchar.h" /* for gs_type1_init in gstype1.h */
+ /* (should only be gschar.h) */
+#include "gxdevice.h" /* for gxfont.h */
+#include "gxfont.h"
+#include "gxfont1.h"
+#include "gxtype1.h"
+#include "gzstate.h" /* for path for gs_type1_init */
+ /* (should only be gsstate.h) */
+#include "gspaint.h" /* for gs_fill, gs_stroke */
+#include "gspath.h"
+#include "estack.h"
+#include "ialloc.h"
+#include "ichar.h"
+#include "icharout.h"
+#include "idict.h"
+#include "ifont.h"
+#include "igstate.h"
+#include "store.h"
+
+/* Test whether a font is Type 1 compatible. */
+#define font_is_type1_compatible(pfont)\
+ ((pfont)->FontType == ft_encrypted || (pfont)->FontType == ft_disk_based)
+
+/* Forward references */
+private int type1addpath_continue(P1(os_ptr));
+private int type1_call_OtherSubr(P3(gs_type1_state *, int (*)(P1(os_ptr)), const ref *));
+private int type1_continue_dispatch(P3(gs_type1_state *, const ref *, ref *));
+private int op_type1_cleanup(P1(os_ptr));
+private void op_type1_free(P1(os_ptr));
+
+/* ---------------- .type1execchar ---------------- */
+
+/*
+ * This is the workhorse for %Type1BuildChar, %Type1BuildGlyph,
+ * CCRun, and CID fonts. Eventually this will appear in the C API;
+ * even now, its normal control path doesn't use any continuations.
+ */
+
+/* Forward references */
+private void
+ type1_cis_get_metrics(P2(const gs_type1_state *pcis, float psbw[4]));
+/* <font> <code|name> <name> <charstring> .type1execchar - */
+private int type1getsbw_continue(P1(os_ptr));
+private int bbox_fill(P1(os_ptr));
+private int bbox_stroke(P1(os_ptr));
+private int nobbox_continue(P1(os_ptr));
+private int nobbox_fill(P1(os_ptr));
+private int nobbox_stroke(P1(os_ptr));
+private int
+ztype1execchar(register os_ptr op)
+{ gs_font *pfont;
+#define pbfont ((gs_font_base *)pfont)
+#define pfont1 ((gs_font_type1 *)pfont)
+ const gs_type1_data *pdata;
+ int code = font_param(op - 3, &pfont);
+ gs_show_enum *penum = op_show_find();
+ gs_type1_state cis;
+#define pcis (&cis)
+ int present;
+ float sbw[4];
+ ref other_subr;
+
+ if ( code < 0 )
+ return code;
+ if ( penum == 0 || !font_is_type1_compatible(pfont) )
+ return_error(e_undefined);
+ pdata = &pfont1->data;
+ /*
+ * Any reasonable implementation would execute something like
+ * 1 setmiterlimit 0 setlinejoin 0 setlinecap
+ * here, but apparently the Adobe implementations aren't reasonable.
+ *
+ * If this is a stroked font, set the stroke width.
+ */
+ if ( pfont->PaintType )
+ gs_setlinewidth(igs, pfont->StrokeWidth);
+ check_estack(3); /* for continuations */
+ /*
+ * Execute the definition of the character.
+ */
+ if ( r_is_proc(op) )
+ return zchar_exec_char_proc(op);
+ /*
+ * The definition must be a Type 1 CharString.
+ * Note that we do not require read access: this is deliberate.
+ */
+ check_type(*op, t_string);
+ if ( r_size(op) <= pdata->lenIV )
+ return_error(e_invalidfont);
+ /*
+ * In order to make character oversampling work, we must
+ * set up the cache before calling .type1addpath.
+ * To do this, we must get the bounding box from the FontBBox,
+ * and the width from the CharString or the Metrics.
+ * If the FontBBox isn't valid, we can't do any of this.
+ */
+ check_ostack(3); /* for .type1xxx args */
+ present = zchar_get_metrics(pbfont, op - 1, sbw);
+ if ( present < 0 )
+ return present;
+ /* Establish a current point. */
+ code = gs_moveto(igs, 0.0, 0.0);
+ if ( code < 0 )
+ return code;
+ code = gs_type1_init(pcis, penum, NULL,
+ gs_show_in_charpath(penum) != cpm_show,
+ pfont1->PaintType, pfont1);
+ if ( code < 0 )
+ return code;
+ if ( pfont1->FontBBox.q.x > pfont1->FontBBox.p.x &&
+ pfont1->FontBBox.q.y > pfont1->FontBBox.p.y
+ )
+ { /*
+ * We have a valid bounding box. If we don't have Metrics
+ * for this character, start interpreting the CharString;
+ * do the setcachedevice as soon as we know the
+ * (side bearing and) width.
+ */
+ if ( present == metricsNone )
+ { /* Get the width from the CharString, */
+ /* then set the cache device. */
+ ref cnref;
+
+ /* Since an OtherSubr callout might change osp, */
+ /* save the character name now. */
+ ref_assign(&cnref, op - 1);
+ code = type1_continue_dispatch(pcis, op, &other_subr);
+ switch ( code )
+ {
+ default: /* code < 0 or done, error */
+ return((code < 0 ? code :
+ gs_note_error(e_invalidfont)));
+ case type1_result_callothersubr: /* unknown OtherSubr */
+ return type1_call_OtherSubr(pcis,
+ type1getsbw_continue,
+ &other_subr);
+ case type1_result_sbw: /* [h]sbw, done */
+ break;
+ }
+ type1_cis_get_metrics(pcis, sbw);
+ return zchar_set_cache(osp, pbfont, &cnref,
+ NULL, sbw + 2,
+ &pfont1->FontBBox,
+ bbox_fill, bbox_stroke);
+ }
+ else
+ { /* We have the width and bounding box: */
+ /* set up the cache device now. */
+ return zchar_set_cache(op, pbfont, op - 1,
+ (present ==
+ metricsSideBearingAndWidth ?
+ sbw : NULL),
+ sbw + 2, &pfont1->FontBBox,
+ bbox_fill, bbox_stroke);
+ }
+ }
+ else
+ { /*
+ * The FontBBox is not valid. In this case,
+ * we do the .type1addpath first, then the setcachedevice.
+ * Oversampling is not possible.
+ */
+ const ref *opstr = op;
+ if ( present == metricsSideBearingAndWidth )
+ { gs_point sbpt;
+ sbpt.x = sbw[0], sbpt.y = sbw[1];
+ gs_type1_set_lsb(pcis, &sbpt);
+ }
+ /* Continue interpreting. */
+icont: code = type1_continue_dispatch(pcis, opstr, &other_subr);
+ switch ( code )
+ {
+ case 0: /* all done */
+ break;
+ default: /* code < 0, error */
+ return code;
+ case type1_result_callothersubr: /* unknown OtherSubr */
+ push_op_estack(nobbox_continue);
+ return type1_call_OtherSubr(pcis, type1addpath_continue,
+ &other_subr);
+ case type1_result_sbw: /* [h]sbw, just continue */
+ opstr = 0;
+ goto icont;
+ }
+ pop(1); op -= 1; /* pop the charstring */
+ return nobbox_continue(op);
+ }
+#undef pcis
+#undef pfont1
+#undef pbfont
+}
+
+/* Handle the results of gs_type1_interpret. */
+/* pcref points to a t_string ref. */
+private int
+type1_continue_dispatch(gs_type1_state *pcis, const ref *pcref, ref *pos)
+{ int value;
+ int code;
+ gs_const_string charstring;
+ gs_const_string *pchars;
+
+ if ( pcref == 0 )
+ { pchars = 0;
+ }
+ else
+ { charstring.data = pcref->value.const_bytes;
+ charstring.size = r_size(pcref);
+ pchars = &charstring;
+ }
+ code = gs_type1_interpret(pcis, pchars, &value);
+ switch ( code )
+ {
+ case type1_result_callothersubr:
+ { /* The Type 1 interpreter handles all known OtherSubrs, */
+ /* so this must be an unknown one. */
+ font_data *pfdata = pfont_data(gs_currentfont(igs));
+ code = array_get(&pfdata->u.type1.OtherSubrs,
+ (long)value, pos);
+ return (code < 0 ? code : type1_result_callothersubr);
+ }
+ }
+ return code;
+}
+
+/* Do a callout to an OtherSubr implemented in PostScript. */
+/* The caller must have done a check_estack. */
+private int
+type1_call_OtherSubr(gs_type1_state *pcis, int (*cont)(P1(os_ptr)),
+ const ref *pos)
+{ /* Move the Type 1 interpreter state to the heap. */
+ gs_type1_state *hpcis = ialloc_struct(gs_type1_state,
+ &st_gs_type1_state,
+ ".type1addpath");
+
+ if ( hpcis == 0 )
+ return_error(e_VMerror);
+ *hpcis = *pcis;
+ push_mark_estack(es_show, op_type1_cleanup);
+ ++esp;
+ make_istruct(esp, 0, hpcis);
+ push_op_estack(cont);
+ ++esp;
+ *esp = *pos;
+ return o_push_estack;
+}
+
+/* Continue from an OtherSubr callout while getting metrics. */
+private int
+type1getsbw_continue(os_ptr op)
+{ ref other_subr;
+ gs_type1_state *pcis = r_ptr(esp, gs_type1_state);
+ int code;
+
+ check_ostack(3); /* for returning the result */
+ code = type1_continue_dispatch(pcis, NULL, &other_subr);
+ op = osp; /* in case z1_push/pop_proc was called */
+ switch ( code )
+ {
+ default: /* code < 0 or done, error */
+ op_type1_free(op);
+ return((code < 0 ? code : gs_note_error(e_invalidfont)));
+ case type1_result_callothersubr: /* unknown OtherSubr */
+ push_op_estack(type1getsbw_continue);
+ ++esp;
+ *esp = other_subr;
+ return o_push_estack;
+ case type1_result_sbw: /* [h]sbw, done */
+ { float sbw[4];
+ const gs_font_base *pbfont =
+ (const gs_font_base *)pcis->pfont;
+
+ /* Get the metrics before freeing the state. */
+ type1_cis_get_metrics(pcis, sbw);
+ op_type1_free(op);
+ return zchar_set_cache(op, pbfont, op, sbw, sbw + 2,
+ &pbfont->FontBBox,
+ bbox_fill, bbox_stroke);
+ }
+ }
+}
+
+/* Clean up after a Type 1 callout. */
+private int
+op_type1_cleanup(os_ptr op)
+{ ifree_object(r_ptr(esp + 2, void), "op_type1_cleanup");
+ return 0;
+}
+private void
+op_type1_free(os_ptr op)
+{ /*
+ * In order to avoid popping from the e-stack and then pushing onto
+ * it, which would violate an interpreter invariant, we simply
+ * overwrite the two e-stack items being discarded (hpcis and the
+ * cleanup operator) with empty procedures.
+ */
+ make_empty_const_array(esp - 1, a_readonly + a_executable);
+ make_empty_const_array(esp, a_readonly + a_executable);
+ op_type1_cleanup(op);
+}
+
+/* <font> <code|name> <name> <charstring> <sbx> <sby> %bbox_{fill|stroke} - */
+/* <font> <code|name> <name> <charstring> %bbox_{fill|stroke} - */
+private int bbox_finish(P2(os_ptr, int (*)(P1(os_ptr))));
+private int
+bbox_fill(os_ptr op)
+{ return bbox_finish(op, nobbox_fill);
+}
+private int
+bbox_stroke(os_ptr op)
+{ return bbox_finish(op, nobbox_stroke);
+}
+private int
+bbox_finish(os_ptr op, int (*cont)(P1(os_ptr)))
+{ gs_font *pfont;
+#define pfont1 ((gs_font_type1 *)pfont)
+ gs_type1_data *pdata;
+ int code;
+ gs_show_enum *penum = op_show_find();
+ gs_type1_state cis; /* stack allocate to avoid sandbars */
+#define pcis (&cis)
+ float sbxy[2];
+ gs_point sbpt;
+ gs_point *psbpt = 0;
+ os_ptr opc = op;
+ const ref *opstr;
+ ref other_subr;
+
+ if ( !r_has_type(opc, t_string) )
+ { check_op(3);
+ code = num_params(op, 2, sbxy);
+ if ( code < 0 )
+ return code;
+ sbpt.x = sbxy[0];
+ sbpt.y = sbxy[1];
+ psbpt = &sbpt;
+ opc -= 2;
+ check_type(*opc, t_string);
+ }
+ code = font_param(opc - 3, &pfont);
+ if ( code < 0 )
+ return code;
+ if ( penum == 0 || !font_is_type1_compatible(pfont) )
+ return_error(e_undefined);
+ pdata = &pfont1->data;
+ if ( r_size(opc) <= pdata->lenIV )
+ return_error(e_invalidfont);
+ check_estack(5); /* in case we need to do a callout */
+ code = gs_type1_init(pcis, penum, psbpt,
+ gs_show_in_charpath(penum) != cpm_show,
+ pfont1->PaintType, pfont1);
+ if ( code < 0 )
+ return code;
+ opstr = opc;
+icont: code = type1_continue_dispatch(pcis, opstr, &other_subr);
+ switch ( code )
+ {
+ case 0: /* all done */
+ /* Call the continuation now. */
+ pop((psbpt == 0 ? 1 : 3));
+ return (*cont)(osp);
+ default: /* code < 0, error */
+ return code;
+ case type1_result_callothersubr: /* unknown OtherSubr */
+ push_op_estack(cont); /* call later */
+ return type1_call_OtherSubr(pcis, type1addpath_continue,
+ &other_subr);
+ case type1_result_sbw: /* [h]sbw, just continue */
+ opstr = 0;
+ goto icont;
+ }
+ pop((psbpt == 0 ? 1 : 3));
+ return code;
+#undef pfont1
+#undef pcis
+}
+
+/* Continue from an OtherSubr callout. */
+private int
+type1addpath_continue(os_ptr op)
+{ ref other_subr;
+ gs_type1_state *pcis = r_ptr(esp, gs_type1_state);
+ int code;
+cont: code = type1_continue_dispatch(pcis, NULL, &other_subr);
+ op = osp; /* in case z1_push/pop_proc was called */
+ switch ( code )
+ {
+ case 0: /* all done */
+ { /* Assume the OtherSubrs didn't mess with the o-stack.... */
+ int npop = (r_has_type(op, t_string) ? 1 : 3);
+ pop(npop); op -= npop;
+ op_type1_free(op);
+ return 0;
+ }
+ default: /* code < 0 or done, error */
+ op_type1_free(op);
+ return((code < 0 ? code : gs_note_error(e_invalidfont)));
+ case type1_result_callothersubr: /* unknown OtherSubr */
+ push_op_estack(type1addpath_continue);
+ ++esp;
+ *esp = other_subr;
+ return o_push_estack;
+ case type1_result_sbw: /* [h]sbw, just continue */
+ goto cont;
+ }
+}
+
+/* Finish the no-FontBBox case after constructing the path. */
+/* <font> <code|name> <name> %nobbox_continue - */
+private int
+nobbox_continue(os_ptr op)
+{ int code;
+ gs_rect bbox;
+ gs_font *pfont;
+#define pbfont ((gs_font_base *)pfont)
+ float sbw[4];
+
+ if ( (code = gs_pathbbox(igs, &bbox)) < 0 ||
+ (code = font_param(op - 2, &pfont)) < 0 ||
+ (code = zchar_get_metrics(pbfont, op, sbw)) < 0
+ )
+ return code;
+ if ( code == metricsNone )
+ { gs_point endpt;
+ if ( (code = gs_currentpoint(igs, &endpt)) < 0 )
+ return code;
+ sbw[2] = endpt.x, sbw[3] = endpt.y;
+ }
+ return zchar_set_cache(op, pbfont, op, NULL, sbw + 2, &bbox,
+ nobbox_fill, nobbox_stroke);
+#undef pbfont
+}
+/* Finish by popping the operands and filling or stroking. */
+private int
+nobbox_fill(os_ptr op)
+{ pop(3);
+ return gs_fill(igs);
+}
+private int
+nobbox_stroke(os_ptr op)
+{ pop(3);
+ return gs_stroke(igs);
+}
+
+/* ------ Internal procedures ------ */
+
+/* Get the metrics (l.s.b. and width) from the Type 1 interpreter. */
+private void
+type1_cis_get_metrics(const gs_type1_state *pcis, float psbw[4])
+{ psbw[0] = fixed2float(pcis->lsb.x);
+ psbw[1] = fixed2float(pcis->lsb.y);
+ psbw[2] = fixed2float(pcis->width.x);
+ psbw[3] = fixed2float(pcis->width.y);
+}
+
+/* ------ Initialization procedure ------ */
+
+BEGIN_OP_DEFS(zchar1_op_defs) {
+ {"4.type1execchar", ztype1execchar},
+ /* Internal operators */
+ {"0%type1addpath_continue", type1addpath_continue},
+ {"3%nobbox_fill", nobbox_fill},
+ {"3%nobbox_stroke", nobbox_stroke},
+ {"0%type1getsbw_continue", type1getsbw_continue},
+ {"4%bbox_fill", bbox_fill},
+ {"4%bbox_stroke", bbox_stroke},
+ {"3%nobbox_continue", nobbox_continue},
+END_OP_DEFS(0) }
+
+/* ------ Auxiliary procedures for type 1 fonts ------ */
+
+/* These are exported for zfont1.c. */
+
+int
+z1_subr_proc(gs_font_type1 *pfont, int index, gs_const_string *pstr)
+{ const font_data *pfdata = pfont_data(pfont);
+ ref subr;
+ int code;
+
+ code = array_get(&pfdata->u.type1.Subrs, index, &subr);
+ if ( code < 0 )
+ return code;
+ check_type_only(subr, t_string);
+ pstr->data = subr.value.const_bytes;
+ pstr->size = r_size(&subr);
+ return 0;
+}
+
+int
+z1_seac_proc(gs_font_type1 *pfont, int index, gs_const_string *pstr)
+{ const font_data *pfdata = pfont_data(pfont);
+ ref *pcstr;
+ ref enc_entry;
+ int code = array_get(&StandardEncoding, (long)index, &enc_entry);
+
+ if ( code < 0 )
+ return code;
+ if ( dict_find(&pfdata->CharStrings, &enc_entry, &pcstr) <= 0 )
+ return_error(e_undefined);
+ check_type_only(*pcstr, t_string);
+ pstr->data = pcstr->value.const_bytes;
+ pstr->size = r_size(pcstr);
+ return 0;
+}
+
+int
+z1_push_proc(gs_font_type1 *ignore, const fixed *pf, int count)
+{ const fixed *p = pf + count - 1;
+ int i;
+
+ check_ostack(count);
+ for ( i = 0; i < count; i++, p-- )
+ { osp++;
+ make_real(osp, fixed2float(*p));
+ }
+ return 0;
+}
+
+int
+z1_pop_proc(gs_font_type1 *ignore, fixed *pf)
+{ float val;
+ int code = num_params(osp, 1, &val);
+
+ if ( code < 0 )
+ return code;
+ *pf = float2fixed(val);
+ osp--;
+ return 0;
+}
diff --git a/pstoraster/zchar2.c b/pstoraster/zchar2.c
new file mode 100644
index 000000000..0c12744e2
--- /dev/null
+++ b/pstoraster/zchar2.c
@@ -0,0 +1,276 @@
+/* Copyright (C) 1992, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* zchar2.c */
+/* Level 2 character operators */
+#include "ghost.h"
+#include "errors.h"
+#include "oper.h"
+#include "gschar.h"
+#include "gsmatrix.h" /* for gxfont.h */
+#include "gsstruct.h" /* for st_stream */
+#include "gxfixed.h" /* for gxfont.h */
+#include "gxfont.h"
+#include "estack.h"
+#include "ialloc.h"
+#include "ichar.h"
+#include "ifont.h"
+#include "igstate.h"
+#include "iname.h"
+#include "store.h"
+#include "stream.h"
+#include "ibnum.h"
+#include "gspath.h" /* gs_rmoveto prototype */
+
+/* Table of continuation procedures. */
+private int xshow_continue(P1(os_ptr));
+private int yshow_continue(P1(os_ptr));
+private int xyshow_continue(P1(os_ptr));
+static const op_proc_p xyshow_continues[4] = {
+ 0, xshow_continue, yshow_continue, xyshow_continue
+};
+
+/* Forward references */
+private int cshow_continue(P1(os_ptr));
+private int moveshow(P2(os_ptr, int));
+private int moveshow_continue(P2(os_ptr, int));
+
+/* <proc> <string> cshow - */
+private int
+zcshow(os_ptr op)
+{ os_ptr proc_op = op - 1;
+ os_ptr str_op = op;
+ gs_show_enum *penum;
+ int code;
+
+ /* Even though this is not documented anywhere by Adobe, */
+ /* the Adobe interpreters apparently allow the string and */
+ /* the procedure to be provided in either order! */
+ if ( r_is_proc(proc_op) )
+ ;
+ else if ( r_is_proc(op) ) /* operands reversed */
+ { proc_op = op;
+ str_op = op - 1;
+ }
+ else
+ { check_op(2);
+ return_error(e_typecheck);
+ }
+ if ( (code = op_show_setup(str_op, &penum)) != 0 )
+ return code;
+ if ( (code = gs_cshow_n_init(penum, igs, (char *)str_op->value.bytes,
+ r_size(str_op))) < 0
+ )
+ { ifree_object(penum, "op_show_enum_setup");
+ return code;
+ }
+ op_show_finish_setup(penum, 2, NULL);
+ sslot = *proc_op; /* save kerning proc */
+ return cshow_continue(op - 2);
+}
+private int
+cshow_continue(os_ptr op)
+{ gs_show_enum *penum = senum;
+ int code;
+ check_estack(4); /* in case we call the procedure */
+ code = gs_show_next(penum);
+ if ( code != gs_show_move )
+ { code = op_show_continue_dispatch(op, code);
+ if ( code == o_push_estack ) /* must be gs_show_render */
+ { make_op_estack(esp - 1, cshow_continue);
+ }
+ return code;
+ }
+ /* Push the character code and width, and call the procedure. */
+ { ref *pslot = &sslot;
+ gs_point wpt;
+
+ gs_show_current_width(penum, &wpt);
+ push(3);
+ make_int(op - 2, gs_show_current_char(penum));
+ make_real(op - 1, wpt.x);
+ make_real(op, wpt.y);
+ push_op_estack(cshow_continue);
+ push_op_estack(zsetfont); /* restore font afterwards */
+ *++esp = *pfont_dict(gs_currentfont(igs));
+ gs_setfont(igs, gs_show_current_font(penum));
+ *++esp = *pslot; /* user procedure */
+ }
+ return o_push_estack;
+}
+
+/* <charname> glyphshow - */
+private int
+zglyphshow(os_ptr op)
+{ gs_show_enum *penum;
+ int code;
+ check_type(*op, t_name);
+ if ( (code = op_show_enum_setup(op, &penum)) != 0 )
+ return code;
+ if ( (code = gs_glyphshow_init(penum, igs, (gs_glyph)name_index(op))) < 0
+ )
+ { ifree_object(penum, "op_show_enum_setup");
+ return code;
+ }
+ op_show_finish_setup(penum, 1, NULL);
+ return op_show_continue(op - 1);
+}
+
+/* - rootfont <font> */
+private int
+zrootfont(os_ptr op)
+{ push(1);
+ *op = *pfont_dict(gs_rootfont(igs));
+ return 0;
+}
+
+/* <string> <numarray|numstring> xshow - */
+private int
+zxshow(os_ptr op)
+{ return moveshow(op, 1);
+}
+
+/* <string> <numarray|numstring> yshow - */
+private int
+zyshow(os_ptr op)
+{ return moveshow(op, 2);
+}
+
+/* <string> <numarray|numstring> xyshow - */
+private int
+zxyshow(os_ptr op)
+{ return moveshow(op, 3);
+}
+
+/* Common code for {x,y,xy}show */
+private int
+moveshow(os_ptr op, int xymask)
+{ gs_show_enum *penum;
+ int code = op_show_setup(op - 1, &penum);
+
+ if ( code != 0 )
+ return code;
+ if ( (code = gs_xyshow_n_init(penum, igs, (char *)op[-1].value.bytes, r_size(op - 1)) < 0) )
+ { ifree_object(penum, "op_show_enum_setup");
+ return code;
+ }
+ code = num_array_format(op);
+ if ( code < 0 )
+ { ifree_object(penum, "op_show_enum_setup");
+ return code;
+ }
+ op_show_finish_setup(penum, 2, NULL);
+ ref_assign(&sslot, op);
+ return moveshow_continue(op - 2, xymask);
+}
+
+/* Continuation procedures */
+
+private int
+xshow_continue(os_ptr op)
+{ return moveshow_continue(op, 1);
+}
+
+private int
+yshow_continue(os_ptr op)
+{ return moveshow_continue(op, 2);
+}
+
+private int
+xyshow_continue(os_ptr op)
+{ return moveshow_continue(op, 3);
+}
+
+/* Get one value from the encoded number string or array. */
+/* Sets pvalue->value.realval. */
+private int
+sget_real(const ref *nsp, int format, uint index, ref *pvalue)
+{ int code;
+ switch ( code = num_array_get(nsp, format, index, pvalue) )
+ {
+ case t_integer: pvalue->value.realval = pvalue->value.intval;
+ case t_real: return t_real;
+ case t_null: code = gs_note_error(e_rangecheck);
+ default: return code;
+ }
+}
+
+private int
+moveshow_continue(os_ptr op, int xymask)
+{ const ref *nsp = &sslot;
+ int format = num_array_format(nsp);
+ int code;
+ gs_show_enum *penum = senum;
+ uint index = ssindex.value.intval;
+
+ for ( ; ; )
+ { ref rwx, rwy;
+ code = gs_show_next(penum);
+ if ( code != gs_show_move )
+ { ssindex.value.intval = index;
+ code = op_show_continue_dispatch(op, code);
+ if ( code == o_push_estack ) /* must be gs_show_render */
+ { make_op_estack(esp - 1, xyshow_continues[xymask]);
+ }
+ return code;
+ }
+ /* Move according to the next value(s) from the stream. */
+ if ( xymask & 1 )
+ { code = sget_real(nsp, format, index++, &rwx);
+ if ( code < 0 ) break;
+ }
+ else
+ rwx.value.realval = 0;
+ if ( xymask & 2 )
+ { code = sget_real(nsp, format, index++, &rwy);
+ if ( code < 0 ) break;
+ }
+ else
+ rwy.value.realval = 0;
+ code = gs_rmoveto(igs, rwx.value.realval, rwy.value.realval);
+ if ( code < 0 ) break;
+ }
+ /* An error occurred. Clean up before returning. */
+ op_show_free();
+ return code;
+}
+
+/* ------ Initialization procedure ------ */
+
+BEGIN_OP_DEFS(zchar2_op_defs) {
+ {"2cshow", zcshow},
+ {"0rootfont", zrootfont},
+ /* Internal operators */
+ {"0%cshow_continue", cshow_continue},
+END_OP_DEFS(0) }
+BEGIN_OP_DEFS(zchar2_l2_op_defs) {
+ op_def_begin_level2(),
+ {"1glyphshow", zglyphshow},
+ {"2xshow", zxshow},
+ {"2xyshow", zxyshow},
+ {"2yshow", zyshow},
+ /* Internal operators */
+ {"0%xshow_continue", xshow_continue},
+ {"0%yshow_continue", yshow_continue},
+ {"0%xyshow_continue", xyshow_continue},
+END_OP_DEFS(0) }
diff --git a/pstoraster/zchar42.c b/pstoraster/zchar42.c
new file mode 100644
index 000000000..04d7ac061
--- /dev/null
+++ b/pstoraster/zchar42.c
@@ -0,0 +1,171 @@
+/* Copyright (C) 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* zchar42.c */
+/* Type 42 character display operator */
+#include "ghost.h"
+#include "errors.h"
+#include "oper.h"
+#include "gsmatrix.h"
+#include "gspaint.h" /* for gs_fill, gs_stroke */
+#include "gspath.h"
+#include "gxfixed.h"
+#include "gxchar.h"
+#include "gxfont.h"
+#include "gxfont42.h"
+#include "gxistate.h"
+#include "gxpath.h"
+#include "gzstate.h" /* only for ->path */
+#include "dstack.h" /* only for systemdict */
+#include "estack.h"
+#include "ichar.h"
+#include "icharout.h"
+#include "ifont.h" /* for font_param */
+#include "igstate.h"
+#include "store.h"
+
+/* Imported procedures */
+int gs_type42_append(P7(uint glyph_index, gs_imager_state *pis,
+ gx_path *ppath, const gs_log2_scale_point *pscale, bool charpath_flag,
+ int paint_type, gs_font_type42 *pfont));
+int gs_type42_get_metrics(P3(gs_font_type42 *pfont, uint glyph_index,
+ float psbw[4]));
+
+/* <font> <code|name> <name> <glyph_index> .type42execchar - */
+private int type42_fill(P1(os_ptr));
+private int type42_stroke(P1(os_ptr));
+private int
+ztype42execchar(register os_ptr op)
+{ gs_font *pfont;
+#define pbfont ((gs_font_base *)pfont)
+#define pfont42 ((gs_font_type42 *)pfont)
+ int code = font_param(op - 3, &pfont);
+ gs_show_enum *penum = op_show_find();
+ int present;
+ float sbw[4];
+
+ code = font_param(op - 3, &pfont);
+ if ( code < 0 )
+ return code;
+ if ( penum == 0 || pfont->FontType != ft_TrueType )
+ return_error(e_undefined);
+ /*
+ * Any reasonable implementation would execute something like
+ * 1 setmiterlimit 0 setlinejoin 0 setlinecap
+ * here, but apparently the Adobe implementations aren't reasonable.
+ *
+ * If this is a stroked font, set the stroke width.
+ */
+ if ( pfont->PaintType )
+ gs_setlinewidth(igs, pfont->StrokeWidth);
+ check_estack(3); /* for continuations */
+ /*
+ * Execute the definition of the character.
+ */
+ if ( r_is_proc(op) )
+ return zchar_exec_char_proc(op);
+ /*
+ * The definition must be a Type 42 glyph index.
+ * Note that we do not require read access: this is deliberate.
+ */
+ check_type(*op, t_integer);
+ check_ostack(3); /* for lsb values */
+ present = zchar_get_metrics(pbfont, op - 1, sbw);
+ if ( present < 0 )
+ return present;
+ /* Establish a current point. */
+ code = gs_moveto(igs, 0.0, 0.0);
+ if ( code < 0 )
+ return code;
+ /* Get the metrics and set the cache device. */
+ if ( present == metricsNone )
+ { code = gs_type42_get_metrics(pfont42, (uint)op->value.intval, sbw);
+ if ( code < 0 )
+ return code;
+ }
+ return zchar_set_cache(op, pbfont, op - 1,
+ (present == metricsSideBearingAndWidth ?
+ sbw : NULL),
+ sbw + 2, &pbfont->FontBBox,
+ type42_fill, type42_stroke);
+#undef pfont42
+#undef pbfont
+}
+
+/* Continue after a CDevProc callout. */
+private int type42_finish(P2(os_ptr op, int (*cont)(P1(gs_state *))));
+private int
+type42_fill(os_ptr op)
+{ return type42_finish(op, gs_fill);
+}
+private int
+type42_stroke(os_ptr op)
+{ return type42_finish(op, gs_stroke);
+}
+/* <font> <code|name> <name> <glyph_index> <sbx> <sby> %type42_{fill|stroke} - */
+/* <font> <code|name> <name> <glyph_index> %type42_{fill|stroke} - */
+private int
+type42_finish(os_ptr op, int (*cont)(P1(gs_state *)))
+{ gs_font *pfont;
+#define pfont42 ((gs_font_type42 *)pfont)
+ int code;
+ gs_show_enum *penum = op_show_find();
+ float sbxy[2];
+ gs_point sbpt;
+ gs_point *psbpt = 0;
+ os_ptr opc = op;
+
+ if ( !r_has_type(op - 3, t_dictionary) )
+ { check_op(6);
+ code = num_params(op, 2, sbxy);
+ if ( code < 0 )
+ return code;
+ sbpt.x = sbxy[0];
+ sbpt.y = sbxy[1];
+ psbpt = &sbpt;
+ opc -= 2;
+ }
+ check_type(*opc, t_integer);
+ code = font_param(opc - 3, &pfont);
+ if ( code < 0 )
+ return code;
+ if ( penum == 0 || pfont->FontType != ft_TrueType )
+ return_error(e_undefined);
+ code = gs_type42_append((uint)opc->value.intval,
+ (gs_imager_state *)penum->pgs,
+ penum->pgs->path,
+ &penum->log2_current_scale,
+ gs_show_in_charpath(penum) != cpm_show,
+ pfont->PaintType, pfont42);
+ if ( code < 0 )
+ return code;
+ pop((psbpt == 0 ? 4 : 6));
+ return (*cont)(penum->pgs);
+#undef pfont42
+}
+
+/* ------ Initialization procedure ------ */
+
+BEGIN_OP_DEFS(zchar42_op_defs) {
+ {"4.type42execchar", ztype42execchar},
+END_OP_DEFS(0) }
diff --git a/pstoraster/zcharout.c b/pstoraster/zcharout.c
new file mode 100644
index 000000000..5a6528a29
--- /dev/null
+++ b/pstoraster/zcharout.c
@@ -0,0 +1,239 @@
+/* Copyright (C) 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* zcharout.c */
+/* Common code for outline (Type 1 / 4 / 42) fonts */
+#include "ghost.h"
+#include "errors.h"
+#include "oper.h"
+#include "gschar.h"
+#include "gxdevice.h" /* for gxfont.h */
+#include "gxfont.h"
+#include "dstack.h" /* only for systemdict */
+#include "estack.h"
+#include "ichar.h"
+#include "icharout.h"
+#include "idict.h"
+#include "ifont.h"
+#include "igstate.h"
+#include "store.h"
+
+/* Imported operators */
+int zsetcachedevice(P1(os_ptr)); /* zchar.c */
+int zsetcachedevice2(P1(os_ptr)); /* zchar.c */
+
+/*
+ * Execute an outline defined by a PostScript procedure.
+ * The top elements of the stack are:
+ * <font> <code|name> <name> <outline_id>
+ */
+int
+zchar_exec_char_proc(os_ptr op)
+{ /*
+ * The definition is a PostScript procedure. Execute
+ * <code|name> proc
+ * within a systemdict begin/end and a font begin/end.
+ */
+ es_ptr ep;
+
+ check_estack(5);
+ ep = esp += 5;
+ make_op_estack(ep - 4, zend);
+ make_op_estack(ep - 3, zend);
+ ref_assign(ep - 2, op);
+ make_op_estack(ep - 1, zbegin);
+ make_op_estack(ep, zbegin);
+ ref_assign(op - 1, systemdict);
+ { ref rfont;
+ ref_assign(&rfont, op - 3);
+ ref_assign(op - 3, op - 2);
+ ref_assign(op - 2, &rfont);
+ }
+ pop(1);
+ return o_push_estack;
+}
+
+/*
+ * Get the metrics for a character from the Metrics dictionary of a base
+ * font. If present, store the l.s.b. in psbw[0,1] and the width in
+ * psbw[2,3].
+ */
+int /*metrics_present*/
+zchar_get_metrics(const gs_font_base *pbfont, const ref *pcnref,
+ float psbw[4])
+{ const ref *pfdict = &pfont_data(pbfont)->dict;
+ ref *pmdict;
+
+ if ( dict_find_string(pfdict, "Metrics", &pmdict) > 0 )
+ { ref *pmvalue;
+
+ check_type_only(*pmdict, t_dictionary);
+ check_dict_read(*pmdict);
+ if ( dict_find(pmdict, pcnref, &pmvalue) > 0 )
+ { if ( num_params(pmvalue, 1, psbw + 2) >= 0 )
+ { /* <wx> only */
+ psbw[3] = 0;
+ return metricsWidthOnly;
+ }
+ else
+ { int code;
+ check_read_type_only(*pmvalue, t_array);
+ switch ( r_size(pmvalue) )
+ {
+ case 2: /* [<sbx> <wx>] */
+ code = num_params(pmvalue->value.refs + 1,
+ 2, psbw);
+ psbw[2] = psbw[1];
+ psbw[1] = psbw[3] = 0;
+ break;
+ case 4: /* [<sbx> <sby> <wx> <wy>] */
+ code = num_params(pmvalue->value.refs + 3,
+ 4, psbw);
+ break;
+ default:
+ return_error(e_rangecheck);
+ }
+ if ( code < 0 )
+ return code;
+ return metricsSideBearingAndWidth;
+ }
+ }
+ }
+ return metricsNone;
+}
+
+/*
+ * Consult Metrics2 and CDevProc, and call setcachedevice[2]. Return
+ * o_push_estack if we had to call a CDevProc, or if we are skipping the
+ * rendering process (only getting the metrics).
+ */
+int
+zchar_set_cache(os_ptr op, const gs_font_base *pbfont, const ref *pcnref,
+ const float psb[2], const float pwidth[2], const gs_rect *pbbox,
+ int (*cont_fill)(P1(os_ptr)), int (*cont_stroke)(P1(os_ptr)))
+{ const ref *pfdict = &pfont_data(pbfont)->dict;
+ ref *pmdict;
+ ref *pcdevproc;
+ int have_cdevproc;
+ ref rpop;
+ bool metrics2 = false;
+ int (*cont)(P1(os_ptr));
+ float w2[10];
+ gs_show_enum *penum = op_show_find();
+
+ w2[0] = pwidth[0], w2[1] = pwidth[1];
+
+ /* Adjust the bounding box for stroking if needed. */
+
+ w2[2] = pbbox->p.x, w2[3] = pbbox->p.y;
+ w2[4] = pbbox->q.x, w2[5] = pbbox->q.y;
+ if ( pbfont->PaintType == 0 )
+ cont = cont_fill;
+ else
+ { double expand = max(1.415, gs_currentmiterlimit(igs)) *
+ gs_currentlinewidth(igs) / 2;
+
+ w2[2] -= expand, w2[3] -= expand;
+ w2[4] += expand, w2[5] += expand;
+ cont = cont_stroke;
+ }
+
+ /* Check for Metrics2. */
+
+ if ( dict_find_string(pfdict, "Metrics2", &pmdict) > 0 )
+ { ref *pmvalue;
+ check_type_only(*pmdict, t_dictionary);
+ check_dict_read(*pmdict);
+ if ( dict_find(pmdict, pcnref, &pmvalue) > 0 )
+ { check_read_type_only(*pmvalue, t_array);
+ if ( r_size(pmvalue) == 4 )
+ { int code = num_params(pmvalue->value.refs + 3,
+ 4, w2 + 6);
+ if ( code < 0 )
+ return code;
+ metrics2 = true;
+ }
+ }
+ }
+
+ /* Check for CDevProc or "short-circuiting". */
+
+ have_cdevproc = dict_find_string(pfdict, "CDevProc", &pcdevproc) > 0;
+ if ( have_cdevproc || gs_show_width_only(penum) )
+ { int i;
+ int (*zsetc)(P1(os_ptr));
+ int nparams;
+
+ if ( have_cdevproc )
+ { check_proc_only(*pcdevproc);
+ zsetc = zsetcachedevice2;
+ if ( !metrics2 )
+ { w2[6] = w2[0], w2[7] = w2[1];
+ w2[8] = w2[9] = 0;
+ }
+ nparams = 10;
+ }
+ else
+ { make_oper(&rpop, 0, zpop);
+ pcdevproc = &rpop;
+ if ( metrics2 )
+ zsetc = zsetcachedevice2, nparams = 10;
+ else
+ zsetc = zsetcachedevice, nparams = 6;
+ }
+ check_estack(3);
+ /* Push the l.s.b. for .type1addpath if necessary. */
+ if ( psb != 0 )
+ { push(nparams + 3);
+ make_real(op - (nparams + 2), psb[0]);
+ make_real(op - (nparams + 1), psb[1]);
+ }
+ else
+ { push(nparams + 1);
+ }
+ for ( i = 0; i < nparams; ++i )
+ make_real(op - nparams + i, w2[i]);
+ ref_assign(op, pcnref);
+ push_op_estack(cont);
+ push_op_estack(zsetc);
+ ++esp;
+ ref_assign(esp, pcdevproc);
+ return o_push_estack;
+ }
+ { int code =
+ (metrics2 ? gs_setcachedevice2(penum, igs, w2) :
+ gs_setcachedevice(penum, igs, w2));
+ if ( code < 0 )
+ return code;
+ }
+
+ /* No metrics modification, do the stroke or fill now. */
+
+ /* Push the l.s.b. for .type1addpath if necessary. */
+ if ( psb != 0 )
+ { push(2);
+ make_real(op - 1, psb[0]);
+ make_real(op, psb[1]);
+ }
+ return cont(op);
+}
diff --git a/pstoraster/zcie.c b/pstoraster/zcie.c
new file mode 100644
index 000000000..d74765ff6
--- /dev/null
+++ b/pstoraster/zcie.c
@@ -0,0 +1,632 @@
+/* Copyright (C) 1992, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* zcie.c */
+/* CIE color operators */
+#include "math_.h"
+#include "memory_.h"
+#include "ghost.h"
+#include "errors.h"
+#include "oper.h"
+#include "gsstruct.h"
+#include "gxcspace.h" /* gscolor2.h requires gscspace.h */
+#include "gscolor2.h"
+#include "gscie.h"
+#include "estack.h"
+#include "ialloc.h"
+#include "idict.h"
+#include "idparam.h"
+#include "igstate.h"
+#include "icie.h"
+#include "isave.h"
+#include "ivmspace.h"
+#include "store.h" /* for make_null */
+
+/* CIE color dictionaries are so complex that */
+/* we handle the CIE case of setcolorspace separately here. */
+
+/* Forward references */
+private int cache_common(P4(gs_cie_common *, const ref_cie_procs *,
+ void *, const gs_state *));
+
+/* Allocator structure types for CIE structures */
+private_st_cie_defg();
+private_st_cie_def();
+private_st_cie_abc();
+private_st_cie_a();
+
+/* Empty procedures */
+static ref empty_procs[4];
+
+/* Original CIE color space types. */
+/* We use CIExxx rather than CIEBasedxxx in some places because */
+/* gcc under VMS only retains 23 characters of procedure names, */
+/* and DEC C truncates all identifiers at 31 characters. */
+extern const gs_color_space_type
+ gs_color_space_type_CIEDEFG,
+ gs_color_space_type_CIEDEF,
+ gs_color_space_type_CIEABC,
+ gs_color_space_type_CIEA;
+/* Redefined CIE color space types (that load the cache when installed) */
+gs_color_space_type
+ cs_type_zCIEDEFG,
+ cs_type_zCIEDEF,
+ cs_type_zCIEABC,
+ cs_type_zCIEA;
+private cs_proc_install_cspace(cs_install_zCIEDEFG);
+private cs_proc_install_cspace(cs_install_zCIEDEF);
+private cs_proc_install_cspace(cs_install_zCIEABC);
+private cs_proc_install_cspace(cs_install_zCIEA);
+
+/* Initialization */
+private void
+zcie_init(void)
+{
+ /* Make the null (default) transformation procedures. */
+ make_empty_const_array(&empty_procs[0], a_readonly + a_executable);
+ make_empty_const_array(&empty_procs[1], a_readonly + a_executable);
+ make_empty_const_array(&empty_procs[2], a_readonly + a_executable);
+ make_empty_const_array(&empty_procs[3], a_readonly + a_executable);
+
+ /* Create the modified color space types. */
+ cs_type_zCIEDEFG = gs_color_space_type_CIEDEFG;
+ cs_type_zCIEDEFG.install_cspace = cs_install_zCIEDEFG;
+ cs_type_zCIEDEF = gs_color_space_type_CIEDEF;
+ cs_type_zCIEDEF.install_cspace = cs_install_zCIEDEF;
+ cs_type_zCIEABC = gs_color_space_type_CIEABC;
+ cs_type_zCIEABC.install_cspace = cs_install_zCIEABC;
+ cs_type_zCIEA = gs_color_space_type_CIEA;
+ cs_type_zCIEA.install_cspace = cs_install_zCIEA;
+
+}
+
+/* ------ Parameter extraction utilities ------ */
+
+/* Get a range array parameter from a dictionary. */
+/* We know that count <= 4. */
+int
+dict_ranges_param(const ref *pdref, const char _ds *kstr, int count,
+ gs_range *prange)
+{ int code = dict_float_array_param(pdref, kstr, count * 2,
+ (float *)prange, NULL);
+ if ( code < 0 )
+ return code;
+ else if ( code == 0 )
+ memcpy(prange, Range4_default.ranges, count * sizeof(gs_range));
+ else if ( code != count * 2 )
+ return_error(e_rangecheck);
+ return 0;
+}
+
+/* Get an array of procedures from a dictionary. */
+/* We know count <= countof(empty_procs). */
+int
+dict_proc_array_param(const ref *pdict, const char _ds *kstr,
+ uint count, ref *pparray)
+{ ref *pvalue;
+ if ( dict_find_string(pdict, kstr, &pvalue) > 0 )
+ { uint i;
+ check_array_only(*pvalue);
+ if ( r_size(pvalue) != count )
+ return_error(e_rangecheck);
+ for ( i = 0; i < count; i++ )
+ { ref proc;
+ array_get(pvalue, (long)i, &proc);
+ check_proc_only(proc);
+ }
+ *pparray = *pvalue;
+ }
+ else
+ make_const_array(pparray, a_readonly | avm_foreign,
+ count, &empty_procs[0]);
+ return 0;
+}
+
+/* Get WhitePoint and BlackPoint values. */
+int
+cie_points_param(const ref *pdref, gs_cie_wb *pwb)
+{ int code;
+ if ( (code = dict_float_array_param(pdref, "WhitePoint", 3, (float *)&pwb->WhitePoint, NULL)) != 3 ||
+ (code = dict_float_array_param(pdref, "BlackPoint", 3, (float *)&pwb->BlackPoint, (const float *)&BlackPoint_default)) != 3
+ )
+ return (code < 0 ? code : gs_note_error(e_rangecheck));
+ if ( pwb->WhitePoint.u <= 0 ||
+ pwb->WhitePoint.v != 1 ||
+ pwb->WhitePoint.w <= 0 ||
+ pwb->BlackPoint.u < 0 ||
+ pwb->BlackPoint.v < 0 ||
+ pwb->BlackPoint.w < 0
+ )
+ return_error(e_rangecheck);
+ return 0;
+}
+
+/* Process a 3- or 4-dimensional lookup table from a dictionary. */
+/* The caller has set pclt->n and pclt->m. */
+/* ptref is known to be a readable array of size at least n+1. */
+private int cie_3d_table_param(P4(const ref *ptable, uint count, uint nbytes,
+ gs_const_string *strings));
+int
+cie_table_param(const ref *ptref, gx_color_lookup_table *pclt,
+ gs_memory_t *mem)
+{ int n = pclt->n, m = pclt->m;
+ const ref *pta = ptref->value.const_refs;
+ int i;
+ uint nbytes;
+ int code;
+ gs_const_string *table;
+
+ for ( i = 0; i < n; ++i )
+ { check_type_only(pta[i], t_integer);
+ if ( pta[i].value.intval <= 1 || pta[i].value.intval > max_ushort )
+ return_error(e_rangecheck);
+ pclt->dims[i] = (int)pta[i].value.intval;
+ }
+ nbytes = m * pclt->dims[n-2] * pclt->dims[n-1];
+ if ( n == 3 )
+ { /* gs_alloc_byte_array is ****** WRONG ****** */
+ table =
+ (gs_const_string *)gs_alloc_byte_array(mem, pclt->dims[0],
+ sizeof(gs_const_string),
+ "cie_table_param");
+ if ( table == 0 )
+ return_error(e_VMerror);
+ code = cie_3d_table_param(pta + 3, pclt->dims[0], nbytes,
+ table);
+ }
+ else /* n == 4 */
+ { int d0 = pclt->dims[0], d1 = pclt->dims[1];
+ uint ntables = d0 * d1;
+ const ref *psuba;
+ check_read_type(pta[4], t_array);
+ if ( r_size(pta + 4) != d0 )
+ return_error(e_rangecheck);
+ /* gs_alloc_byte_array is ****** WRONG ****** */
+ table =
+ (gs_const_string *)gs_alloc_byte_array(mem, ntables,
+ sizeof(gs_const_string),
+ "cie_table_param");
+ if ( table == 0 )
+ return_error(e_VMerror);
+ psuba = pta[4].value.const_refs;
+ for ( i = 0; i < d0; ++i )
+ { code = cie_3d_table_param(psuba + i, d1, nbytes,
+ table + d1 * i);
+ if ( code < 0 )
+ break;
+ }
+ }
+ if ( code < 0 )
+ { gs_free_object(mem, table, "cie_table_param");
+ return code;
+ }
+ pclt->table = table;
+ return 0;
+}
+private int
+cie_3d_table_param(const ref *ptable, uint count, uint nbytes,
+ gs_const_string *strings)
+{ const ref *rstrings;
+ uint i;
+
+ check_read_type(*ptable, t_array);
+ if ( r_size(ptable) != count )
+ return_error(e_rangecheck);
+ rstrings = ptable->value.const_refs;
+ for ( i = 0; i < count; ++i )
+ { const ref *prt2 = rstrings + i;
+ check_read_type(*prt2, t_string);
+ if ( r_size(prt2) != nbytes )
+ return_error(e_rangecheck);
+ strings[i].data = rstrings[i].value.const_bytes;
+ strings[i].size = nbytes;
+ }
+ return 0;
+}
+
+/* ------ CIE setcolorspace ------ */
+
+/* Common code for the CIEBased* cases of setcolorspace. */
+private int
+cie_lmnp_param(const ref *pdref, gs_cie_common *pcie, ref_cie_procs *pcprocs)
+{ int code;
+ if ( (code = dict_range3_param(pdref, "RangeLMN", &pcie->RangeLMN)) < 0 ||
+ (code = dict_proc3_param(pdref, "DecodeLMN", &pcprocs->DecodeLMN)) < 0 ||
+ (code = dict_matrix3_param(pdref, "MatrixLMN", &pcie->MatrixLMN)) != matrix3_ok ||
+ (code = cie_points_param(pdref, &pcie->points)) < 0
+ )
+ return (code < 0 ? code : gs_note_error(e_rangecheck));
+ pcie->DecodeLMN = DecodeLMN_default;
+ return 0;
+}
+
+/* Common code for the CIEBasedABC/DEF[G] cases of setcolorspace. */
+private int
+cie_abc_param(const ref *pdref, gs_cie_abc_common *pcie,
+ ref_cie_procs *pcprocs)
+{ int code;
+ if ( (code = dict_range3_param(pdref, "RangeABC", &pcie->RangeABC)) < 0 ||
+ (code = dict_proc3_param(pdref, "DecodeABC", &pcprocs->Decode.ABC)) < 0 ||
+ (code = dict_matrix3_param(pdref, "MatrixABC", &pcie->MatrixABC)) != matrix3_ok ||
+ (code = cie_lmnp_param(pdref, &pcie->common, pcprocs)) < 0
+ )
+ return (code < 0 ? code : gs_note_error(e_rangecheck));
+ pcie->DecodeABC = DecodeABC_default;
+ return 0;
+}
+
+/* Finish setting a CIE space. */
+private int
+set_cie_finish(os_ptr op, gs_color_space *pcs, const ref_cie_procs *pcprocs)
+{ ref_colorspace cspace_old;
+ uint edepth = ref_stack_count(&e_stack);
+ int code;
+
+ /* The color space installation procedure may refer to */
+ /* istate->colorspace.procs. */
+ cspace_old = istate->colorspace;
+ istate->colorspace.procs.cie = *pcprocs;
+ code = gs_setcolorspace(igs, pcs);
+ if ( code < 0 )
+ { istate->colorspace = cspace_old;
+ ref_stack_pop_to(&e_stack, edepth);
+ return code;
+ }
+ pop(1);
+ return (ref_stack_count(&e_stack) == edepth ? 0 : o_push_estack); /* installation will load the caches */
+}
+
+#ifdef NEW_CIE /**************** ****************/
+
+/* <dict> .setciedefgspace - */
+private int
+zsetciedefgspace(register os_ptr op)
+{ gs_memory_t *mem = gs_state_memory(igs);
+ gs_color_space cs;
+ ref_color_procs procs;
+ gs_cie_defg *pcie;
+ int code;
+ ref *ptref;
+
+ check_type(*op, t_dictionary);
+ check_dict_read(*op);
+ procs = istate->colorspace.procs;
+ rc_alloc_struct_0(pcie, gs_cie_defg, &st_cie_defg, mem,
+ return_error(e_VMerror),
+ "setcolorspace(CIEBasedDEFG)");
+ if ( (code = dict_find_string(op, "Table", &ptref)) <= 0 )
+ return (code < 0 ? code : gs_note_error(e_rangecheck));
+ check_read_type(*ptref, t_array);
+ if ( r_size(ptref) != 5 )
+ return_error(e_rangecheck);
+ pcie->Table.n = 4;
+ pcie->Table.m = 3;
+ if ( (code = dict_ranges_param(op, "RangeDEFG", 4, pcie->RangeDEFG.ranges)) < 0 ||
+ (code = dict_proc_array_param(op, "DecodeDEFG", 4, &procs.cie.PreDecode.DEFG)) < 0 ||
+ (code = dict_ranges_param(op, "RangeHIJK", 4, pcie->RangeHIJK.ranges)) < 0 ||
+ (code = cie_table_param(ptref, &pcie->Table, mem)) < 0 ||
+ (code = cie_abc_param(op, &pcie->abc, &procs.cie)) < 0
+ )
+ { rc_free_struct(pcie, mem, "setcolorspace(CIEBasedDEFG)");
+ return code;
+ }
+ cs.params.defg = pcie;
+ cs.type = &cs_type_zCIEDEFG;
+ return set_cie_finish(op, &cs, &procs.cie);
+}
+
+/* <dict> .setciedefspace - */
+private int
+zsetciedefspace(register os_ptr op)
+{ gs_memory_t *mem = gs_state_memory(igs);
+ gs_color_space cs;
+ ref_color_procs procs;
+ gs_cie_def *pcie;
+ int code;
+ ref *ptref;
+
+ check_type(*op, t_dictionary);
+ check_dict_read(*op);
+ procs = istate->colorspace.procs;
+ rc_alloc_struct_0(pcie, gs_cie_def, &st_cie_def, mem,
+ return_error(e_VMerror),
+ "setcolorspace(CIEBasedDEF)");
+ if ( (code = dict_find_string(op, "Table", &ptref)) <= 0 )
+ return (code < 0 ? code : gs_note_error(e_rangecheck));
+ check_read_type(*ptref, t_array);
+ if ( r_size(ptref) != 4 )
+ return_error(e_rangecheck);
+ pcie->Table.n = 4;
+ pcie->Table.m = 3;
+ if ( (code = dict_range3_param(op, "RangeDEF", &pcie->RangeDEF)) < 0 ||
+ (code = dict_proc3_param(op, "DecodeDEF", &procs.cie.PreDecode.DEF)) < 0 ||
+ (code = dict_range3_param(op, "RangeHIJ", &pcie->RangeHIJ)) < 0 ||
+ (code = cie_table_param(ptref, &pcie->Table, mem)) < 0 ||
+ (code = cie_abc_param(op, &pcie->abc, &procs.cie)) < 0
+ )
+ { rc_free_struct(pcie, mem, "setcolorspace(CIEBasedDEF)");
+ return code;
+ }
+ cs.params.def = pcie;
+ cs.type = &cs_type_zCIEDEF;
+ return set_cie_finish(op, &cs, &procs.cie);
+}
+
+#endif /*NEW_CIE*/ /**************** ****************/
+
+/* <dict> .setcieabcspace - */
+private int
+zsetcieabcspace(register os_ptr op)
+{ gs_memory_t *mem = gs_state_memory(igs);
+ gs_color_space cs;
+ ref_color_procs procs;
+ gs_cie_abc *pcie;
+ int code;
+
+ check_type(*op, t_dictionary);
+ check_dict_read(*op);
+ procs = istate->colorspace.procs;
+ rc_alloc_struct_0(pcie, gs_cie_abc, &st_cie_abc, mem,
+ return_error(e_VMerror),
+ "setcolorspace(CIEBasedABC)");
+ code = cie_abc_param(op, pcie, &procs.cie);
+ if ( code < 0 )
+ { rc_free_struct(pcie, mem, "setcolorspace(CIEBasedABC)");
+ return code;
+ }
+ cs.params.abc = pcie;
+ cs.type = &cs_type_zCIEABC;
+ return set_cie_finish(op, &cs, &procs.cie);
+}
+
+/* <dict> .setcieaspace - */
+private int
+zsetcieaspace(register os_ptr op)
+{ gs_memory_t *mem = gs_state_memory(igs);
+ gs_color_space cs;
+ ref_color_procs procs;
+ gs_cie_a *pcie;
+ int code;
+
+ check_type(*op, t_dictionary);
+ check_dict_read(*op);
+ procs = istate->colorspace.procs;
+ if ( (code = dict_proc_param(op, "DecodeA", &procs.cie.Decode.A, true)) < 0 )
+ return code;
+ rc_alloc_struct_0(pcie, gs_cie_a, &st_cie_a, mem,
+ return_error(e_VMerror),
+ "setcolorspace(CIEBasedA)");
+ if ( (code = dict_float_array_param(op, "RangeA", 2, (float *)&pcie->RangeA, (const float *)&RangeA_default)) != 2 ||
+ (code = dict_float_array_param(op, "MatrixA", 3, (float *)&pcie->MatrixA, (const float *)&MatrixA_default)) != 3 ||
+ (code = cie_lmnp_param(op, &pcie->common, &procs.cie)) < 0
+ )
+ { rc_free_struct(pcie, mem, "setcolorspace(CIEBasedA)");
+ return (code < 0 ? code : gs_note_error(e_rangecheck));
+ }
+ pcie->DecodeA = DecodeA_default;
+ cs.params.a = pcie;
+ cs.type = &cs_type_zCIEA;
+ return set_cie_finish(op, &cs, &procs.cie);
+}
+
+/* ------ Install a CIE-based color space. ------ */
+
+/* The new CIEBasedDEF[G] spaces aren't really implemented yet.... */
+private int
+cs_install_zCIEDEFG(gs_color_space *pcs, gs_state *pgs)
+{ return_error(e_undefined);
+}
+private int
+cs_install_zCIEDEF(gs_color_space *pcs, gs_state *pgs)
+{ return_error(e_undefined);
+}
+
+private int cie_abc_finish(P1(os_ptr));
+private int
+cs_install_zCIEABC(gs_color_space *pcs, gs_state *pgs)
+{ es_ptr ep = esp;
+ gs_cie_abc *pcie = pcs->params.abc;
+ const int_gstate *pigs = gs_int_gstate(pgs);
+ const ref_cie_procs *pcprocs = &pigs->colorspace.procs.cie;
+ int code =
+ (*gs_color_space_type_CIEABC.install_cspace)(pcs, pgs); /* former routine */
+ if ( code < 0 ||
+ (code = cie_cache_joint(&pigs->colorrendering.procs, pgs)) < 0 || /* do this last */
+ (code = cie_cache_push_finish(cie_abc_finish, pgs, pcie)) < 0 ||
+#ifdef NEW_CIE
+ (code = cie_prepare_cache3(&pcie->abc.RangeABC, pcprocs->Decode.ABC.value.const_refs, &pcie->abc.caches.DecodeABC[0], pcie, pgs, "Decode.ABC")) < 0 ||
+ (code = cache_common(&pcie->abc.common, pcprocs, pcie, pgs)) < 0
+#else
+ (code = cie_prepare_cache3(&pcie->RangeABC, pcprocs->Decode.ABC.value.const_refs, &pcie->caches.DecodeABC[0], pcie, pgs, "Decode.ABC")) < 0 ||
+ (code = cache_common(&pcie->common, pcprocs, pcie, pgs)) < 0
+#endif
+ )
+ { esp = ep;
+ return code;
+ }
+ return o_push_estack;
+}
+private int
+cie_abc_finish(os_ptr op)
+{ gs_cie_abc_complete(r_ptr(op, gs_cie_abc));
+ pop(1);
+ return 0;
+}
+
+private int cie_a_finish(P1(os_ptr));
+private int
+cs_install_zCIEA(gs_color_space *pcs, gs_state *pgs)
+{ es_ptr ep = esp;
+ gs_cie_a *pcie = pcs->params.a;
+ const int_gstate *pigs = gs_int_gstate(pgs);
+ const ref_cie_procs *pcprocs = &pigs->colorspace.procs.cie;
+ int code =
+ (*gs_color_space_type_CIEA.install_cspace)(pcs, pgs); /* former routine */
+ if ( code < 0 ||
+ (code = cie_cache_joint(&pigs->colorrendering.procs, pgs)) < 0 || /* do this last */
+ (code = cie_cache_push_finish(cie_a_finish, pgs, pcie)) < 0 ||
+ (code = cie_prepare_cache(&pcie->RangeA, &pcprocs->Decode.A, &pcie->caches.DecodeA.floats, pcie, pgs, "Decode.A")) < 0 ||
+ (code = cache_common(&pcie->common, pcprocs, pcie, pgs)) < 0
+ )
+ { esp = ep;
+ return code;
+ }
+ return o_push_estack;
+}
+private int
+cie_a_finish(os_ptr op)
+{ gs_cie_a_complete(r_ptr(op, gs_cie_a));
+ pop(1);
+ return 0;
+}
+
+/* Common cache code */
+private int
+cache_common(gs_cie_common *pcie, const ref_cie_procs *pcprocs,
+ void *container, const gs_state *pgs)
+{ return cie_prepare_cache3(&pcie->RangeLMN,
+ pcprocs->DecodeLMN.value.const_refs,
+ &pcie->caches.DecodeLMN[0], container, pgs,
+ "Decode.LMN");
+}
+
+/* ------ Internal routines ------ */
+
+/* Prepare to cache the values for one or more procedures. */
+private int cie_cache_finish(P1(os_ptr));
+int
+cie_prepare_cache(const gs_range *domain, const ref *proc,
+ cie_cache_floats *pcache, void *container, const gs_state *pgs,
+ client_name_t cname)
+{ int space = imemory_space((gs_ref_memory_t *)gs_state_memory(pgs));
+ gs_for_loop_params flp;
+ register es_ptr ep;
+
+ check_estack(9);
+ ep = esp;
+ gs_cie_cache_init(&pcache->params, &flp, domain, cname);
+ pcache->params.is_identity = r_size(proc) == 0;
+ make_real(ep + 9, flp.init);
+ make_real(ep + 8, flp.step);
+ make_real(ep + 7, flp.limit);
+ ep[6] = *proc;
+ r_clear_attrs(ep + 6, a_executable);
+ make_op_estack(ep + 5, zcvx);
+ make_op_estack(ep + 4, zfor);
+ make_op_estack(ep + 3, cie_cache_finish);
+ /*
+ * The caches are embedded in the middle of other
+ * structures, so we represent the pointer to the cache
+ * as a pointer to the container plus an offset.
+ */
+ make_int(ep + 2, (char *)pcache - (char *)container);
+ make_struct(ep + 1, space, container);
+ esp += 9;
+ return o_push_estack;
+}
+private int
+cie_prepare_caches(const gs_range *domains, const ref *procs,
+ cie_cache_floats **ppc, int count, void *container, const gs_state *pgs,
+ client_name_t cname)
+{ int i, code = 0;
+ for ( i = 0; i < count; ++i )
+ if ( (code = cie_prepare_cache(domains + i, procs + i, ppc[i],
+ container, pgs, cname)) < 0
+ )
+ return code;
+ return code;
+}
+int
+cie_prepare_caches_3(const gs_range3 *domains, const ref *procs,
+ cie_cache_floats *pc0, cie_cache_floats *pc1, cie_cache_floats *pc2,
+ void *container, const gs_state *pgs, client_name_t cname)
+{ cie_cache_floats *pc3[3];
+ pc3[0] = pc0, pc3[1] = pc1, pc3[2] = pc2;
+ return cie_prepare_caches((const gs_range *)domains, procs, pc3, 3,
+ container, pgs, cname);
+}
+
+/* Store the result of caching one procedure. */
+private int
+cie_cache_finish(os_ptr op)
+{ cie_cache_floats *pcache;
+ int code;
+ check_esp(2);
+ /* See above for the container + offset representation of */
+ /* the pointer to the cache. */
+ pcache = (cie_cache_floats *)(r_ptr(esp - 1, char) + esp->value.intval);
+ code = num_params(op, gx_cie_cache_size, &pcache->values[0]);
+ if_debug3('c', "[c]cache 0x%lx base=%g, factor=%g:\n",
+ (ulong)pcache, pcache->params.base, pcache->params.factor);
+ if ( code < 0 )
+ { /* We might have underflowed the current stack block. */
+ /* Handle the parameters one-by-one. */
+ uint i;
+ for ( i = 0; i < gx_cie_cache_size; i++ )
+ { code = real_param(ref_stack_index(&o_stack,
+ gx_cie_cache_size - 1 - i),
+ &pcache->values[i]);
+ if ( code < 0 )
+ return code;
+ }
+ }
+#ifdef DEBUG
+ if ( gs_debug_c('c') )
+ { int i;
+ for ( i = 0; i < gx_cie_cache_size; i += 4 )
+ dprintf5("[c] cache[%3d]=%g, %g, %g, %g\n", i,
+ pcache->values[i], pcache->values[i + 1],
+ pcache->values[i + 2], pcache->values[i + 3]);
+ }
+#endif
+ ref_stack_pop(&o_stack, gx_cie_cache_size);
+ esp -= 2; /* pop pointer to cache */
+ return o_pop_estack;
+}
+
+/* Push a finishing procedure on the e-stack. */
+/* ptr will be the top element of the o-stack. */
+int
+cie_cache_push_finish(int (*proc)(P1(os_ptr)), gs_state *pgs, void *ptr)
+{ check_estack(2);
+ push_op_estack(proc);
+ ++esp;
+ make_struct(esp, imemory_space((gs_ref_memory_t *)gs_state_memory(pgs)), ptr);
+ return o_push_estack;
+}
+
+/* ------ Initialization procedure ------ */
+
+BEGIN_OP_DEFS(zcie_l2_op_defs) {
+ op_def_begin_level2(),
+ {"1.setcieaspace", zsetcieaspace},
+ {"1.setcieabcspace", zsetcieabcspace},
+#ifdef NEW_CIE
+ {"1.setciedefspace", zsetciedefspace},
+ {"1.setciedefgspace", zsetciedefgspace},
+#endif
+ /* Internal operators */
+ {"1%cie_abc_finish", cie_abc_finish},
+ {"1%cie_a_finish", cie_a_finish},
+ {"0%cie_cache_finish", cie_cache_finish},
+END_OP_DEFS(zcie_init) }
diff --git a/pstoraster/zcolor.c b/pstoraster/zcolor.c
new file mode 100644
index 000000000..3a354ea26
--- /dev/null
+++ b/pstoraster/zcolor.c
@@ -0,0 +1,248 @@
+/* Copyright (C) 1989, 1992, 1993, 1994 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* zcolor.c */
+/* Color operators */
+#include "ghost.h"
+#include "errors.h"
+#include "oper.h"
+#include "estack.h"
+#include "ialloc.h"
+#include "igstate.h"
+#include "iutil.h"
+#include "store.h"
+#include "gxfixed.h"
+#include "gxmatrix.h"
+#include "gzstate.h"
+#include "gxdevice.h"
+#include "gxcmap.h"
+#include "icolor.h"
+
+/* Import the 'for' operator */
+extern int
+ zfor_fraction(P1(os_ptr));
+
+/* Imported from gsht.c */
+void gx_set_effective_transfer(P1(gs_state *));
+
+/* Define the generic transfer function for the library layer. */
+/* This just returns what's already in the map. */
+float
+gs_mapped_transfer(floatp value, const gx_transfer_map *pmap)
+{ return gx_map_color_float(pmap, value);
+}
+
+/* Define the number of stack slots needed for zcolor_remap_one. */
+const int zcolor_remap_one_ostack = 4;
+const int zcolor_remap_one_estack = 3;
+
+/* - currentalpha <alpha> */
+private int
+zcurrentalpha(register os_ptr op)
+{ push(1);
+ make_real(op, gs_currentalpha(igs));
+ return 0;
+}
+
+/* - currentgray <gray> */
+private int
+zcurrentgray(register os_ptr op)
+{ push(1);
+ make_real(op, gs_currentgray(igs));
+ return 0;
+}
+
+/* - currentrgbcolor <red> <green> <blue> */
+private int
+zcurrentrgbcolor(register os_ptr op)
+{ float par[3];
+ gs_currentrgbcolor(igs, par);
+ push(3);
+ make_reals(op - 2, par, 3);
+ return 0;
+}
+
+/* - currenttransfer <proc> */
+private int
+zcurrenttransfer(register os_ptr op)
+{ push(1);
+ *op = istate->transfer_procs.colored.gray;
+ return 0;
+}
+
+/* - processcolors <int> - */
+/* Note: this is an undocumented operator that is not supported */
+/* in Level 2. */
+private int
+zprocesscolors(register os_ptr op)
+{ push(1);
+ make_int(op, gs_currentdevice(igs)->color_info.num_components);
+ return 0;
+}
+
+/* <alpha> setalpha - */
+private int
+zsetalpha(register os_ptr op)
+{ float alpha;
+ int code;
+ if ( real_param(op, &alpha) < 0 )
+ return_op_typecheck(op);
+ if ( (code = gs_setalpha(igs, alpha)) < 0 )
+ return code;
+ pop(1);
+ return 0;
+}
+
+/* <gray> setgray - */
+private int
+zsetgray(register os_ptr op)
+{ float gray;
+ int code;
+ if ( real_param(op, &gray) < 0 )
+ return_op_typecheck(op);
+ if ( (code = gs_setgray(igs, gray)) < 0 )
+ return code;
+ make_null(&istate->colorspace.array);
+ pop(1);
+ return 0;
+}
+
+/* <red> <green> <blue> setrgbcolor - */
+private int
+zsetrgbcolor(register os_ptr op)
+{ float par[3];
+ int code;
+ if ( (code = num_params(op, 3, par)) < 0 ||
+ (code = gs_setrgbcolor(igs, par[0], par[1], par[2])) < 0
+ )
+ return code;
+ make_null(&istate->colorspace.array);
+ pop(3);
+ return 0;
+}
+
+/* <proc> settransfer - */
+private int
+zsettransfer(register os_ptr op)
+{ int code;
+ check_proc(*op);
+ check_ostack(zcolor_remap_one_ostack - 1);
+ check_estack(1 + zcolor_remap_one_estack);
+ istate->transfer_procs.colored.red =
+ istate->transfer_procs.colored.green =
+ istate->transfer_procs.colored.blue =
+ istate->transfer_procs.colored.gray = *op;
+ code = gs_settransfer_remap(igs, gs_mapped_transfer, false);
+ if ( code < 0 ) return code;
+ push_op_estack(zcolor_reset_transfer);
+ pop(1); op--;
+ return zcolor_remap_one(&istate->transfer_procs.colored.gray, op,
+ igs->set_transfer.colored.gray, igs,
+ zcolor_remap_one_finish);
+}
+
+/* ------ Internal routines ------ */
+
+/* Prepare to remap one color component */
+/* (also used for black generation and undercolor removal). */
+/* Use the 'for' operator to gather the values. */
+/* The caller must have done the necessary check_ostack and check_estack. */
+int
+zcolor_remap_one(const ref *pproc, register os_ptr op, gx_transfer_map *pmap,
+ const gs_state *pgs, int (*finish_proc)(P1(os_ptr)))
+{ osp = op += 4;
+ make_int(op - 3, 0);
+ make_int(op - 2, 1);
+ make_int(op - 1, transfer_map_size - 1);
+ *op = *pproc;
+ ++esp;
+ make_struct(esp, imemory_space((gs_ref_memory_t *)pgs->memory),
+ pmap);
+ push_op_estack(finish_proc);
+ push_op_estack(zfor_fraction);
+ return o_push_estack;
+}
+
+/* Store the result of remapping a component. */
+private int
+zcolor_remap_one_store(os_ptr op, floatp min_value)
+{ int i;
+ gx_transfer_map *pmap = r_ptr(esp, gx_transfer_map);
+ if ( ref_stack_count(&o_stack) < transfer_map_size )
+ return_error(e_stackunderflow);
+ for ( i = 0; i < transfer_map_size; i++ )
+ { float v;
+ int code =
+ real_param(ref_stack_index(&o_stack,
+ transfer_map_size - 1 - i),
+ &v);
+ if ( code < 0 ) return code;
+ pmap->values[i] =
+ (v < min_value ? float2frac(min_value) :
+ v >= 1.0 ? frac_1 :
+ float2frac(v));
+ }
+ ref_stack_pop(&o_stack, transfer_map_size);
+ esp--; /* pop pointer to transfer map */
+ return o_pop_estack;
+}
+int
+zcolor_remap_one_finish(os_ptr op)
+{ return zcolor_remap_one_store(op, 0.0);
+}
+int
+zcolor_remap_one_signed_finish(os_ptr op)
+{ return zcolor_remap_one_store(op, -1.0);
+}
+
+/* Finally, reset the effective transfer functions and */
+/* invalidate the current color. */
+int
+zcolor_reset_transfer(os_ptr op)
+{ gx_set_effective_transfer(igs);
+ return zcolor_remap_color(op);
+}
+int
+zcolor_remap_color(os_ptr op)
+{ gx_unset_dev_color(igs);
+ return 0;
+}
+
+/* ------ Initialization procedure ------ */
+
+BEGIN_OP_DEFS(zcolor_op_defs) {
+ {"0currentalpha", zcurrentalpha},
+ {"0currentgray", zcurrentgray},
+ {"0currentrgbcolor", zcurrentrgbcolor},
+ {"0currenttransfer", zcurrenttransfer},
+ {"0processcolors", zprocesscolors},
+ {"1setalpha", zsetalpha},
+ {"1setgray", zsetgray},
+ {"3setrgbcolor", zsetrgbcolor},
+ {"1settransfer", zsettransfer},
+ /* Internal operators */
+ {"1%zcolor_remap_one_finish", zcolor_remap_one_finish},
+ {"1%zcolor_remap_one_signed_finish", zcolor_remap_one_signed_finish},
+ {"0%zcolor_reset_transfer", zcolor_reset_transfer},
+ {"0%zcolor_remap_color", zcolor_remap_color},
+END_OP_DEFS(0) }
diff --git a/pstoraster/zcolor1.c b/pstoraster/zcolor1.c
new file mode 100644
index 000000000..deb3f0272
--- /dev/null
+++ b/pstoraster/zcolor1.c
@@ -0,0 +1,217 @@
+/* Copyright (C) 1993, 1994, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* zcolor1.c */
+/* Level 1 extended color operators */
+#include "ghost.h"
+#include "errors.h"
+#include "oper.h"
+#include "estack.h"
+#include "ialloc.h"
+#include "igstate.h"
+#include "iutil.h"
+#include "store.h"
+#include "gxfixed.h"
+#include "gxmatrix.h"
+#include "gzstate.h"
+#include "gxdevice.h"
+#include "gxcmap.h"
+#include "gscolor1.h"
+#include "gxcspace.h"
+#include "icolor.h"
+#include "iimage.h"
+
+/* - currentblackgeneration <proc> */
+private int
+zcurrentblackgeneration(register os_ptr op)
+{ push(1);
+ *op = istate->black_generation;
+ return 0;
+}
+
+/* - currentcmykcolor <cyan> <magenta> <yellow> <black> */
+private int
+zcurrentcmykcolor(register os_ptr op)
+{ float par[4];
+ gs_currentcmykcolor(igs, par);
+ push(4);
+ make_reals(op - 3, par, 4);
+ return 0;
+}
+
+/* - currentcolortransfer <redproc> <greenproc> <blueproc> <grayproc> */
+private int
+zcurrentcolortransfer(register os_ptr op)
+{ push(4);
+ op[-3] = istate->transfer_procs.colored.red;
+ op[-2] = istate->transfer_procs.colored.green;
+ op[-1] = istate->transfer_procs.colored.blue;
+ *op = istate->transfer_procs.colored.gray;
+ return 0;
+}
+
+/* - currentundercolorremoval <proc> */
+private int
+zcurrentundercolorremoval(register os_ptr op)
+{ push(1);
+ *op = istate->undercolor_removal;
+ return 0;
+}
+
+/* <proc> setblackgeneration - */
+private int
+zsetblackgeneration(register os_ptr op)
+{ int code;
+ check_proc(*op);
+ check_ostack(zcolor_remap_one_ostack - 1);
+ check_estack(1 + zcolor_remap_one_estack);
+ code = gs_setblackgeneration_remap(igs, gs_mapped_transfer, false);
+ if ( code < 0 )
+ return code;
+ istate->black_generation = *op;
+ pop(1); op--;
+ push_op_estack(zcolor_remap_color);
+ return zcolor_remap_one(&istate->black_generation, op,
+ igs->black_generation, igs,
+ zcolor_remap_one_finish);
+}
+
+/* <cyan> <magenta> <yellow> <black> setcmykcolor - */
+private int
+zsetcmykcolor(register os_ptr op)
+{ float par[4];
+ int code;
+ if ( (code = num_params(op, 4, par)) < 0 ||
+ (code = gs_setcmykcolor(igs, par[0], par[1], par[2], par[3])) < 0
+ )
+ return code;
+ make_null(&istate->colorspace.array);
+ pop(4);
+ return 0;
+}
+
+/* <redproc> <greenproc> <blueproc> <grayproc> setcolortransfer - */
+private int
+zsetcolortransfer(register os_ptr op)
+{ int code;
+ check_proc(op[-3]);
+ check_proc(op[-2]);
+ check_proc(op[-1]);
+ check_proc(*op);
+ check_ostack(zcolor_remap_one_ostack * 4 - 4);
+ check_estack(1 + zcolor_remap_one_estack * 4);
+ istate->transfer_procs.colored.red = op[-3];
+ istate->transfer_procs.colored.green = op[-2];
+ istate->transfer_procs.colored.blue = op[-1];
+ istate->transfer_procs.colored.gray = *op;
+ if ( (code = gs_setcolortransfer_remap(igs,
+ gs_mapped_transfer, gs_mapped_transfer,
+ gs_mapped_transfer, gs_mapped_transfer,
+ false)) < 0
+ )
+ return code;
+ /* Use osp rather than op here, because zcolor_remap_one pushes. */
+ pop(4); op -= 4;
+ push_op_estack(zcolor_reset_transfer);
+ if ( (code = zcolor_remap_one(&istate->transfer_procs.colored.red,
+ osp, igs->set_transfer.colored.red, igs,
+ zcolor_remap_one_finish)) < 0 ||
+ (code = zcolor_remap_one(&istate->transfer_procs.colored.green,
+ osp, igs->set_transfer.colored.green, igs,
+ zcolor_remap_one_finish)) < 0 ||
+ (code = zcolor_remap_one(&istate->transfer_procs.colored.blue,
+ osp, igs->set_transfer.colored.blue, igs,
+ zcolor_remap_one_finish)) < 0
+ )
+ return code;
+ return zcolor_remap_one(&istate->transfer_procs.colored.gray,
+ osp, igs->set_transfer.colored.gray, igs,
+ zcolor_remap_one_finish);
+}
+
+/* <proc> setundercolorremoval - */
+private int
+zsetundercolorremoval(register os_ptr op)
+{ int code;
+ check_proc(*op);
+ check_ostack(zcolor_remap_one_ostack - 1);
+ check_estack(1 + zcolor_remap_one_estack);
+ code = gs_setundercolorremoval_remap(igs, gs_mapped_transfer, false);
+ if ( code < 0 )
+ return code;
+ istate->undercolor_removal = *op;
+ pop(1); op--;
+ push_op_estack(zcolor_remap_color);
+ return zcolor_remap_one(&istate->undercolor_removal, op,
+ igs->undercolor_removal, igs,
+ zcolor_remap_one_signed_finish);
+}
+
+/* <width> <height> <bits/comp> <matrix> */
+/* true <datasrc_0> ... <datasrc_ncomp-1> <ncomp> colorimage - */
+/* false <datasrc> <ncomp> colorimage - */
+private int
+zcolorimage(register os_ptr op)
+{ int spp; /* samples per pixel */
+ int npop = 7;
+ os_ptr procp = op - 2;
+ const gs_color_space *pcs;
+
+ bool multi = false;
+ check_int_leu(*op, 4); /* ncolors */
+ check_type(op[-1], t_boolean); /* multiproc */
+ switch ( (spp = (int)(op->value.intval)) )
+ {
+ case 1:
+ pcs = gs_color_space_DeviceGray();
+ break;
+ case 3:
+ pcs = gs_color_space_DeviceRGB();
+ goto color;
+ case 4:
+ pcs = gs_color_space_DeviceCMYK();
+color: if ( op[-1].value.boolval ) /* planar format */
+ { npop += spp - 1;
+ procp -= spp - 1,
+ multi = true;
+ }
+ break;
+ default:
+ return_error(e_rangecheck);
+ }
+ return zimage_opaque_setup(procp, multi, pcs, npop);
+}
+
+/* ------ Initialization procedure ------ */
+
+BEGIN_OP_DEFS(zcolor1_op_defs) {
+ {"0currentblackgeneration", zcurrentblackgeneration},
+ {"0currentcmykcolor", zcurrentcmykcolor},
+ {"0currentcolortransfer", zcurrentcolortransfer},
+ {"0currentundercolorremoval", zcurrentundercolorremoval},
+ {"1setblackgeneration", zsetblackgeneration},
+ {"4setcmykcolor", zsetcmykcolor},
+ {"4setcolortransfer", zsetcolortransfer},
+ {"1setundercolorremoval", zsetundercolorremoval},
+ {"7colorimage", zcolorimage},
+END_OP_DEFS(0) }
diff --git a/pstoraster/zcolor2.c b/pstoraster/zcolor2.c
new file mode 100644
index 000000000..1795f6f89
--- /dev/null
+++ b/pstoraster/zcolor2.c
@@ -0,0 +1,197 @@
+/* Copyright (C) 1992, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* zcolor2.c */
+/* Level 2 color operators */
+#include "ghost.h"
+#include "errors.h"
+#include "oper.h"
+#include "gscolor.h"
+#include "gsmatrix.h"
+#include "gsstruct.h"
+#include "gxcspace.h"
+#include "gxfixed.h" /* for gxcolor2.h */
+#include "gxcolor2.h"
+#include "gxdcolor.h" /* for gxpcolor.h */
+#include "gxdevice.h"
+#include "gxdevmem.h" /* for gxpcolor.h */
+#include "gxpcolor.h"
+#include "estack.h"
+#include "ialloc.h"
+#include "istruct.h"
+#include "idict.h"
+#include "idparam.h"
+#include "igstate.h"
+#include "store.h"
+
+/* Declare a local alias for st_pattern_instance. */
+/* We do this so we can have configurations with */
+/* the base Level 2 color machinery but without patterns. */
+gs_memory_type_ptr_t zcolor2_st_pattern_instance_p = 0;
+
+/* Forward references */
+private int store_color_params(P3(os_ptr, const gs_paint_color *, const gs_color_space *));
+private int load_color_params(P3(os_ptr, gs_paint_color *, const gs_color_space *));
+
+/* - currentcolor <param1> ... <paramN> */
+private int
+zcurrentcolor(register os_ptr op)
+{ const gs_client_color *pc = gs_currentcolor(igs);
+ const gs_color_space *pcs = gs_currentcolorspace(igs);
+ int n;
+ check_ostack(5); /* Worst case: CMYK + pattern */
+ if ( pcs->type->index == gs_color_space_index_Pattern )
+ { n = 1;
+ if ( pc->pattern != 0 && pc->pattern->template.PaintType == 2 ) /* uncolored */
+ n += store_color_params(op, &pc->paint,
+ (const gs_color_space *)&pcs->params.pattern.base_space);
+ op[n] = istate->pattern;
+ }
+ else
+ n = store_color_params(op, &pc->paint, pcs);
+ push(n);
+ return 0;
+}
+
+/* - .currentcolorspace <array|int> */
+private int
+zcurrentcolorspace(register os_ptr op)
+{ push(1);
+ if ( r_has_type(&istate->colorspace.array, t_null) )
+ { /* Return the color space index. */
+ /* This is only possible if the color was set by */
+ /* setgray, sethsb/rgbcolor, or setcmykcolor. */
+ make_int(op, (int)(gs_currentcolorspace(igs)->type->index));
+ }
+ else
+ *op = istate->colorspace.array;
+ return 0;
+}
+
+/* - currentoverprint <bool> */
+private int
+zcurrentoverprint(register os_ptr op)
+{ push(1);
+ make_bool(op, gs_currentoverprint(igs));
+ return 0;
+}
+
+/* <param1> ... <paramN> setcolor - */
+private int
+zsetcolor(register os_ptr op)
+{ gs_client_color c;
+ const gs_color_space *pcs = gs_currentcolorspace(igs);
+ int n, code;
+ gs_pattern_instance *pinst = 0;
+ if ( pcs->type->index == gs_color_space_index_Pattern )
+ { /* Make sure *op is a real Pattern. */
+ ref *pImpl;
+ check_type(*op, t_dictionary);
+ check_dict_read(*op);
+ if ( dict_find_string(op, "Implementation", &pImpl) <= 0 ||
+ !r_has_stype(pImpl, imemory, *zcolor2_st_pattern_instance_p)
+ )
+ return_error(e_rangecheck);
+ pinst = r_ptr(pImpl, gs_pattern_instance);
+ c.pattern = pinst;
+ if ( pinst->template.PaintType == 2 ) /* uncolored */
+ { if ( !pcs->params.pattern.has_base_space )
+ return_error(e_rangecheck);
+ n = load_color_params(op - 1, &c.paint,
+ (const gs_color_space *)&pcs->params.pattern.base_space);
+ if ( n < 0 ) return n;
+ n++;
+ }
+ else
+ n = 1;
+ }
+ else
+ { n = load_color_params(op, &c.paint, pcs);
+ c.pattern = 0; /* for GC */
+ }
+ if ( n < 0 )
+ return n;
+ code = gs_setcolor(igs, &c);
+ if ( code < 0 )
+ return code;
+ if ( pinst != 0 )
+ istate->pattern = *op;
+ pop(n);
+ return code;
+}
+
+/* <array> .setcolorspace - */
+private int
+zsetcolorspace(register os_ptr op)
+{ check_type(*op, t_array);
+ istate->colorspace.array = *op;
+ pop(1);
+ return 0;
+}
+
+/* <bool> setoverprint - */
+private int
+zsetoverprint(register os_ptr op)
+{ check_type(*op, t_boolean);
+ gs_setoverprint(igs, op->value.boolval);
+ pop(1);
+ return 0;
+}
+
+/* ------ Initialization procedure ------ */
+
+BEGIN_OP_DEFS(zcolor2_l2_op_defs) {
+ op_def_begin_level2(),
+ {"0currentcolor", zcurrentcolor},
+ {"0.currentcolorspace", zcurrentcolorspace},
+ {"0currentoverprint", zcurrentoverprint},
+ {"1setcolor", zsetcolor},
+ {"1.setcolorspace", zsetcolorspace},
+ {"1setoverprint", zsetoverprint},
+END_OP_DEFS(0) }
+
+/* ------ Internal procedures ------ */
+
+/* Store non-pattern color values on the operand stack. */
+/* Return the number of values stored. */
+private int
+store_color_params(os_ptr op, const gs_paint_color *pc,
+ const gs_color_space *pcs)
+{ int n = pcs->type->num_components;
+ if ( pcs->type->index == gs_color_space_index_Indexed )
+ make_int(op + 1, (int)pc->values[0]);
+ else
+ make_reals(op + 1, pc->values, n);
+ return n;
+}
+
+/* Load non-pattern color values from the operand stack. */
+/* Return the number of values stored. */
+private int
+load_color_params(os_ptr op, gs_paint_color *pc,
+ const gs_color_space *pcs)
+{ int n = pcs->type->num_components;
+ int code = num_params(op, n, pc->values);
+ if ( code < 0 ) return code;
+ return n;
+}
diff --git a/pstoraster/zcontrol.c b/pstoraster/zcontrol.c
new file mode 100644
index 000000000..61e76a381
--- /dev/null
+++ b/pstoraster/zcontrol.c
@@ -0,0 +1,639 @@
+/* Copyright (C) 1989, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* zcontrol.c */
+/* Control operators */
+#include "string_.h"
+#include "ghost.h"
+#include "errors.h"
+#include "oper.h"
+#include "estack.h"
+#include "ipacked.h"
+#include "iutil.h"
+#include "store.h"
+
+/* Make an invalid file object. */
+extern void make_invalid_file(P1(ref *)); /* in zfile.c */
+
+/* Forward references */
+int no_cleanup(P1(os_ptr));
+private uint count_to_stopped(P0());
+
+/* See the comment in opdef.h for an invariant which allows */
+/* more efficient implementation of for, loop, and repeat. */
+
+/* <[test0 body0 ...]> .cond - */
+private int cond_continue(P1(os_ptr));
+private int
+zcond(register os_ptr op)
+{ es_ptr ep = esp;
+ /* Push the array on the e-stack and call the continuation. */
+ if ( !r_is_array(op) )
+ return_op_typecheck(op);
+ check_execute(*op);
+ if ( (r_size(op) & 1) != 0)
+ return_error(e_rangecheck);
+ if ( r_size(op) == 0 )
+ return zpop(op);
+ check_estack(3);
+ esp = ep += 3;
+ ref_assign(ep - 2, op); /* the cond body */
+ make_op_estack(ep - 1, cond_continue);
+ array_get(op, 0L, ep);
+ esfile_check_cache();
+ pop(1);
+ return o_push_estack;
+}
+private int
+cond_continue(register os_ptr op)
+{ es_ptr ep = esp;
+ int code;
+ /* The top element of the e-stack is the remaining tail of */
+ /* the cond body. The top element of the o-stack should be */
+ /* the (boolean) result of the test that is the first element */
+ /* of the tail. */
+ check_type(*op, t_boolean);
+ if ( op->value.boolval )
+ { /* true */
+ array_get(ep, 1L, ep);
+ esfile_check_cache();
+ code = o_pop_estack;
+ }
+ else if ( r_size(ep) > 2 )
+ { /* false */
+ const ref_packed *elts = ep->value.packed;
+ check_estack(2);
+ r_dec_size(ep, 2);
+ elts = packed_next(elts);
+ elts = packed_next(elts);
+ ep->value.packed = elts;
+ array_get(ep, 0L, ep + 2);
+ make_op_estack(ep + 1, cond_continue);
+ esp = ep + 2;
+ esfile_check_cache();
+ code = o_push_estack;
+ }
+ else
+ { /* fall off end of cond */
+ esp = ep - 1;
+ code = o_pop_estack;
+ }
+ pop(1); /* get rid of the boolean */
+ return code;
+}
+
+/* <obj> exec - */
+int
+zexec(register os_ptr op)
+{ check_op(1);
+ if ( !r_has_attr(op, a_executable) )
+ return 0; /* literal object just gets pushed back */
+ check_estack(1);
+ ++esp;
+ ref_assign(esp, op);
+ esfile_check_cache();
+ pop(1);
+ return o_push_estack;
+}
+
+/* <obj> superexec - */
+/* THIS IS NOT REALLY IMPLEMENTED YET. */
+private int
+zsuperexec(os_ptr op)
+{ return zexec(op);
+}
+
+/* <bool> <proc> if - */
+int
+zif(register os_ptr op)
+{ check_type(op[-1], t_boolean);
+ check_proc(*op);
+ if ( op[-1].value.boolval )
+ { check_estack(1);
+ ++esp;
+ ref_assign(esp, op);
+ esfile_check_cache();
+ }
+ pop(2);
+ return o_push_estack;
+}
+
+/* <bool> <proc_true> <proc_false> ifelse - */
+int
+zifelse(register os_ptr op)
+{ check_type(op[-2], t_boolean);
+ check_proc(op[-1]);
+ check_proc(*op);
+ check_estack(1);
+ ++esp;
+ if ( op[-2].value.boolval )
+ { ref_assign(esp, op - 1);
+ }
+ else
+ { ref_assign(esp, op);
+ }
+ esfile_check_cache();
+ pop(3);
+ return o_push_estack;
+}
+
+/* <init> <step> <limit> <proc> for - */
+private int
+ for_pos_int_continue(P1(os_ptr)),
+ for_neg_int_continue(P1(os_ptr)),
+ for_real_continue(P1(os_ptr));
+int
+zfor(register os_ptr op)
+{ register es_ptr ep;
+ check_estack(7);
+ ep = esp + 6;
+ check_proc(*op);
+ /* Push a mark, the control variable, the initial value, */
+ /* the increment, the limit, and the procedure, */
+ /* and invoke the continuation operator. */
+ if ( r_has_type(op - 3, t_integer) &&
+ r_has_type(op - 2, t_integer)
+ )
+ { make_int(ep - 4, op[-3].value.intval);
+ make_int(ep - 3, op[-2].value.intval);
+ switch ( r_type(op - 1) )
+ {
+ case t_integer:
+ make_int(ep - 2, op[-1].value.intval);
+ break;
+ case t_real:
+ make_int(ep - 2, (long)op[-1].value.realval);
+ break;
+ default:
+ return_op_typecheck(op - 1);
+ }
+ if ( ep[-3].value.intval >= 0 )
+ make_op_estack(ep, for_pos_int_continue);
+ else
+ make_op_estack(ep, for_neg_int_continue);
+ }
+ else
+ { float params[3];
+ int code;
+ if ( (code = num_params(op - 1, 3, params)) < 0 )
+ return code;
+ make_real(ep - 4, params[0]);
+ make_real(ep - 3, params[1]);
+ make_real(ep - 2, params[2]);
+ make_op_estack(ep, for_real_continue);
+ }
+ make_mark_estack(ep - 5, es_for, no_cleanup);
+ ref_assign(ep - 1, op);
+ esp = ep;
+ pop(4);
+ return o_push_estack;
+}
+/* Continuation operators for for, separate for positive integer, */
+/* negative integer, and real. */
+/* Execution stack contains mark, control variable, increment, */
+/* limit, and procedure (procedure is topmost.) */
+/* Continuation operator for positive integers. */
+private int
+for_pos_int_continue(register os_ptr op)
+{ register es_ptr ep = esp;
+ long var = ep[-3].value.intval;
+ if ( var > ep[-1].value.intval )
+ { esp -= 5; /* pop everything */
+ return o_pop_estack;
+ }
+ push(1);
+ make_int(op, var);
+ ep[-3].value.intval = var + ep[-2].value.intval;
+ ref_assign_inline(ep + 2, ep); /* saved proc */
+ esp = ep + 2;
+ return o_push_estack;
+}
+/* Continuation operator for negative integers. */
+private int
+for_neg_int_continue(register os_ptr op)
+{ register es_ptr ep = esp;
+ long var = ep[-3].value.intval;
+ if ( var < ep[-1].value.intval )
+ { esp -= 5; /* pop everything */
+ return o_pop_estack;
+ }
+ push(1);
+ make_int(op, var);
+ ep[-3].value.intval = var + ep[-2].value.intval;
+ ref_assign(ep + 2, ep); /* saved proc */
+ esp = ep + 2;
+ return o_push_estack;
+}
+/* Continuation operator for reals. */
+private int
+for_real_continue(register os_ptr op)
+{ es_ptr ep = esp;
+ float var = ep[-3].value.realval;
+ float incr = ep[-2].value.realval;
+ if ( incr >= 0 ? (var > ep[-1].value.realval) :
+ (var < ep[-1].value.realval)
+ )
+ { esp -= 5; /* pop everything */
+ return o_pop_estack;
+ }
+ push(1);
+ ref_assign(op, ep - 3);
+ ep[-3].value.realval = var + incr;
+ esp = ep + 2;
+ ref_assign(ep + 2, ep); /* saved proc */
+ return o_push_estack;
+}
+
+/* Here we provide an internal variant of 'for' that enumerates the */
+/* values 0, 1/N, 2/N, ..., 1 precisely. The arguments must be */
+/* the integers 0, 1, and N. We need this for */
+/* loading caches such as the transfer function cache. */
+private int for_fraction_continue(P1(os_ptr));
+int
+zfor_fraction(register os_ptr op)
+{ int code = zfor(op);
+ if ( code < 0 ) return code; /* shouldn't ever happen! */
+ make_op_estack(esp, for_fraction_continue);
+ return code;
+}
+/* Continuation procedure */
+private int
+for_fraction_continue(register os_ptr op)
+{ register es_ptr ep = esp;
+ int code = for_pos_int_continue(op);
+ if ( code != o_push_estack )
+ return code;
+ /* We must use osp instead of op here, because */
+ /* for_pos_int_continue pushes a value on the o-stack. */
+ make_real(osp, (float)osp->value.intval / ep[-1].value.intval);
+ return code;
+}
+
+/* <int> <proc> repeat - */
+private int repeat_continue(P1(os_ptr));
+private int
+zrepeat(register os_ptr op)
+{ check_type(op[-1], t_integer);
+ check_proc(*op);
+ if ( op[-1].value.intval < 0 )
+ return_error(e_rangecheck);
+ check_estack(5);
+ /* Push a mark, the count, and the procedure, and invoke */
+ /* the continuation operator. */
+ push_mark_estack(es_for, no_cleanup);
+ *++esp = op[-1];
+ *++esp = *op;
+ make_op_estack(esp + 1, repeat_continue);
+ pop(2);
+ return repeat_continue(op - 2);
+}
+/* Continuation operator for repeat */
+private int
+repeat_continue(register os_ptr op)
+{ es_ptr ep = esp; /* saved proc */
+ if ( --(ep[-1].value.intval) >= 0 ) /* continue */
+ { esp += 2;
+ ref_assign(esp, ep);
+ return o_push_estack;
+ }
+ else /* done */
+ { esp -= 3; /* pop mark, count, proc */
+ return o_pop_estack;
+ }
+}
+
+/* <proc> loop */
+private int loop_continue(P1(os_ptr));
+private int
+zloop(register os_ptr op)
+{ check_proc(*op);
+ check_estack(4);
+ /* Push a mark and the procedure, and invoke */
+ /* the continuation operator. */
+ push_mark_estack(es_for, no_cleanup);
+ *++esp = *op;
+ make_op_estack(esp + 1, loop_continue);
+ pop(1);
+ return loop_continue(op - 1);
+}
+/* Continuation operator for loop */
+private int
+loop_continue(register os_ptr op)
+{ register es_ptr ep = esp; /* saved proc */
+ ref_assign(ep + 2, ep);
+ esp = ep + 2;
+ return o_push_estack;
+}
+
+/* - exit - */
+private int
+zexit(register os_ptr op)
+{ uint scanned = 0;
+ STACK_LOOP_BEGIN(&e_stack, ep, used)
+ { uint count = used;
+ ep += used - 1;
+ for ( ; count; count--, ep-- )
+ if ( r_is_estack_mark(ep) )
+ switch ( estack_mark_index(ep) )
+ {
+ case es_for:
+ pop_estack(scanned + (used - count + 1));
+ return o_pop_estack;
+ case es_stopped:
+ return_error(e_invalidexit); /* not a loop */
+ }
+ scanned += used;
+ }
+ STACK_LOOP_END(ep, used)
+ /* Return e_invalidexit if there is no mark at all. */
+ /* This is different from PostScript, which aborts. */
+ /* It shouldn't matter in practice. */
+ return_error(e_invalidexit);
+}
+
+/* <result> .stop - */
+private int
+zstop(register os_ptr op)
+{ uint count = count_to_stopped();
+ if ( count )
+ { pop_estack((uint)count);
+ return o_pop_estack;
+ }
+ /* Return e_invalidexit if there is no mark at all. */
+ /* This is different from PostScript, which aborts. */
+ /* It shouldn't matter in practice. */
+ return_error(e_invalidexit);
+}
+
+/* <obj> <result> .stopped <result> */
+private int
+zstopped(register os_ptr op)
+{ check_op(2);
+ /* Mark the execution stack, and push the default result */
+ /* in case control returns normally. */
+ check_estack(3);
+ push_mark_estack(es_stopped, no_cleanup);
+ *++esp = *op; /* save the result */
+ *++esp = op[-1]; /* execute the operand */
+ esfile_check_cache();
+ pop(2);
+ return o_push_estack;
+}
+
+/* - .instopped false */
+/* - .instopped <result> true */
+private int
+zinstopped(register os_ptr op)
+{ uint count = count_to_stopped();
+ if ( count )
+ { push(2);
+ op[-1] = *ref_stack_index(&e_stack, count - 2); /* default result */
+ make_true(op);
+ }
+ else
+ { push(1);
+ make_false(op);
+ }
+ return 0;
+}
+
+/* - countexecstack <int> */
+private int
+zcountexecstack(register os_ptr op)
+{ push(1);
+ make_int(op, ref_stack_count(&e_stack));
+ return 0;
+}
+
+/* <array> execstack <subarray> */
+private int execstack_continue(P1(os_ptr));
+private int
+zexecstack(register os_ptr op)
+{ /*
+ * We can't do this directly, because the interpreter
+ * might have cached some state. To force the interpreter
+ * to update the stored state, we push a continuation on
+ * the exec stack; the continuation is executed immediately,
+ * and does the actual transfer.
+ */
+ uint depth = ref_stack_count(&e_stack);
+
+ check_write_type(*op, t_array);
+ if ( depth > r_size(op) )
+ return_error(e_rangecheck);
+ check_estack(1);
+ r_set_size(op, (uint)depth);
+ push_op_estack(execstack_continue);
+ return o_push_estack;
+}
+/* Continuation operator to do the actual transfer. */
+/* r_size(op) was set just above. */
+private int
+execstack_continue(register os_ptr op)
+{ int code =
+ ref_stack_store(&e_stack, op, r_size(op), 0, 0, true, "execstack");
+ uint asize = r_size(op);
+ uint i;
+ ref *rp;
+
+ if ( code < 0 )
+ return code;
+ /*
+ * Clear the executable bit in any internal operators, and
+ * convert t_structs and t_astructs (which can only appear
+ * in connection with stack marks, which means that they will
+ * probably be freed when unwinding) to something harmless.
+ */
+ for ( i = 0, rp = op->value.refs; i < asize; i++, rp++ )
+ switch ( r_type(rp) )
+ {
+ case t_operator:
+ { uint opidx = op_index(rp);
+ if ( opidx == 0 || op_def_is_internal(op_def_table[opidx]) )
+ r_clear_attrs(rp, a_executable);
+ break;
+ }
+ case t_struct:
+ case t_astruct:
+ { const char *tname =
+ gs_struct_type_name_string(gs_object_type(imemory,
+ rp->value.pstruct));
+ make_const_string(rp, a_readonly | avm_foreign,
+ strlen(tname), (const byte *)tname);
+ break;
+ }
+ }
+ return 0;
+}
+
+/* - .needinput - */
+private int
+zneedinput(register os_ptr op)
+{ return e_NeedInput; /* interpreter will exit to caller */
+}
+
+/* <obj> <int> .quit - */
+private int
+zquit(register os_ptr op)
+{ check_op(2);
+ check_type(*op, t_integer);
+ return e_Quit; /* Interpreter will do the exit */
+}
+
+/* - currentfile <file> */
+private ref *zget_current_file(P0());
+private int
+zcurrentfile(register os_ptr op)
+{ ref *fp;
+ push(1);
+ /* Check the cache first */
+ if ( esfile != 0 )
+ {
+#ifdef DEBUG
+ /* Check that esfile is valid range. */
+ ref *efp = zget_current_file();
+ if ( esfile != efp )
+ { lprintf2("currentfile: esfile=0x%lx, efp=0x%lx\n",
+ (ulong)esfile, (ulong)efp);
+ ref_assign(op, efp);
+ }
+ else
+#endif
+ ref_assign(op, esfile);
+ }
+ else if ( (fp = zget_current_file()) == 0 )
+ { /* Return an invalid file object. */
+ /* This doesn't make a lot of sense to me, */
+ /* but it's what the PostScript manual specifies. */
+ make_invalid_file(op);
+ }
+ else
+ { ref_assign(op, fp);
+ esfile_set_cache(fp);
+ }
+ /* Make the returned value literal. */
+ r_clear_attrs(op, a_executable);
+ return 0;
+}
+/* Get the current file from which the interpreter is reading. */
+private ref *
+zget_current_file(void)
+{ STACK_LOOP_BEGIN(&e_stack, ep, used)
+ { uint count = used;
+ ep += used - 1;
+ for ( ; count; count--, ep-- )
+ if ( r_has_type_attrs(ep, t_file, a_executable) )
+ return ep;
+ }
+ STACK_LOOP_END(ep, used)
+ return 0;
+}
+
+/* ------ Non-operator routines ------ */
+
+/* Test whether we are inside a `stopped'. */
+/* The top level of the interpreter uses this. */
+int
+in_stopped(void)
+{ return count_to_stopped() != 0;
+}
+
+/* ------ Initialization procedure ------ */
+
+BEGIN_OP_DEFS(zcontrol_op_defs) {
+ {"1.cond", zcond},
+ {"0countexecstack", zcountexecstack},
+ {"0currentfile", zcurrentfile},
+ {"1exec", zexec},
+ {"0execstack", zexecstack},
+ {"0exit", zexit},
+ {"2if", zif},
+ {"3ifelse", zifelse},
+ {"0.instopped", zinstopped},
+ {"0.needinput", zneedinput},
+ {"4for", zfor},
+ {"1loop", zloop},
+ {"2.quit", zquit},
+ {"2repeat", zrepeat},
+ {"1.stop", zstop},
+ {"2.stopped", zstopped},
+ /* Internal operators */
+ {"1%cond_continue", cond_continue},
+ {"0%execstack_continue", execstack_continue},
+ {"0%for_pos_int_continue", for_pos_int_continue},
+ {"0%for_neg_int_continue", for_neg_int_continue},
+ {"0%for_real_continue", for_real_continue},
+ {"4%for_fraction", zfor_fraction},
+ {"0%for_fraction_continue", for_fraction_continue},
+ {"0%loop_continue", loop_continue},
+ {"0%repeat_continue", repeat_continue},
+ /* Operators defined in internaldict */
+ op_def_begin_dict("internaldict"),
+ {"1superexec", zsuperexec},
+END_OP_DEFS(0) }
+
+/* ------ Internal routines ------ */
+
+/* Vacuous cleanup routine */
+int
+no_cleanup(os_ptr op)
+{ return 0;
+}
+
+/* Count the number of elements down to and including the first 'stopped' */
+/* mark on the e-stack. Return 0 if there is no 'stopped' mark. */
+private uint
+count_to_stopped(void)
+{ uint scanned = 0;
+ STACK_LOOP_BEGIN(&e_stack, ep, used)
+ { uint count = used;
+ ep += used - 1;
+ for ( ; count; count--, ep-- )
+ if ( r_is_estack_mark(ep) &&
+ estack_mark_index(ep) == es_stopped
+ )
+ return scanned + (used - count + 1);
+ scanned += used;
+ }
+ STACK_LOOP_END(ep, used)
+ return 0;
+}
+
+/* Pop the e-stack, executing cleanup procedures as needed. */
+/* We could make this more efficient using the STACK_LOOP macros, */
+/* but it isn't used enough to make this worthwhile. */
+void
+pop_estack(uint count)
+{ uint idx = 0;
+ uint popped = 0;
+ esfile_clear_cache();
+ for ( ; idx < count; idx++ )
+ { ref *ep = ref_stack_index(&e_stack, idx - popped);
+ if ( r_is_estack_mark(ep) )
+ { ref_stack_pop(&e_stack, idx + 1 - popped);
+ popped = idx + 1;
+ (*real_opproc(ep))(osp);
+ }
+ }
+ ref_stack_pop(&e_stack, count - popped);
+}
diff --git a/pstoraster/zcrd.c b/pstoraster/zcrd.c
new file mode 100644
index 000000000..dd7533432
--- /dev/null
+++ b/pstoraster/zcrd.c
@@ -0,0 +1,316 @@
+/* Copyright (C) 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* zcrd.c */
+/* CIE color rendering operators */
+#include "math_.h"
+#include "ghost.h"
+#include "errors.h"
+#include "oper.h"
+#include "gsstruct.h"
+#include "gscspace.h"
+#include "gscolor2.h"
+#include "gscie.h"
+#include "estack.h"
+#include "ialloc.h"
+#include "idict.h"
+#include "idparam.h"
+#include "igstate.h"
+#include "icie.h"
+#include "isave.h"
+#include "ivmspace.h"
+#include "store.h" /* for make_null */
+
+/* Forward references */
+private int zcrd_params(P4(os_ptr, gs_cie_render *, ref_cie_render_procs *, gs_memory_t *));
+private int cache_colorrendering(P3(gs_cie_render *,
+ const ref_cie_render_procs *, gs_state *));
+
+/* Allocator structure type for CIE rendering structure */
+private_st_cie_render();
+
+/* - currentcolorrendering <dict> */
+private int
+zcurrentcolorrendering(register os_ptr op)
+{ push(1);
+ *op = istate->colorrendering.dict;
+ return 0;
+}
+
+/* <dict> setcolorrendering - */
+private int
+zsetcolorrendering(register os_ptr op)
+{ gs_memory_t *mem = gs_state_memory(igs);
+ int code;
+ es_ptr ep = esp;
+ gs_cie_render *pcie;
+ ref_cie_render_procs procs_old;
+
+ check_read_type(*op, t_dictionary);
+ check_dict_read(*op);
+ rc_alloc_struct_0(pcie, gs_cie_render, &st_cie_render, mem,
+ return_error(e_VMerror),
+ "setcolorrendering");
+ /* gs_setcolorrendering may refer to istate->colorrendering.procs. */
+ procs_old = istate->colorrendering.procs;
+ code = zcrd_params(op, pcie, &istate->colorrendering.procs, mem);
+ if ( code < 0 ||
+ (code = gs_setcolorrendering(igs, pcie)) < 0 ||
+ (code = cache_colorrendering(pcie, &istate->colorrendering.procs, igs)) < 0
+ )
+ { rc_free_struct(pcie, mem, "setcolorrendering");
+ istate->colorrendering.procs = procs_old;
+ esp = ep;
+ return code;
+ }
+ istate->colorrendering.dict = *op;
+ pop(1);
+ return (esp == ep ? 0 : o_push_estack);
+}
+/* Get the CRD parameters from the PostScript dictionary. */
+private int
+zcrd_params(os_ptr op, gs_cie_render *pcie,
+ ref_cie_render_procs *pcprocs, gs_memory_t *mem)
+{ int code;
+ int ignore;
+ ref *pRT;
+
+ if ( (code = dict_int_param(op, "ColorRenderingType", 1, 1, 0, &ignore)) < 0 ||
+ (code = dict_matrix3_param(op, "MatrixLMN", &pcie->MatrixLMN)) != matrix3_ok ||
+ (code = dict_proc3_param(op, "EncodeLMN", &pcprocs->EncodeLMN)) < 0 ||
+ (code = dict_range3_param(op, "RangeLMN", &pcie->RangeLMN)) < 0 ||
+ (code = dict_matrix3_param(op, "MatrixABC", &pcie->MatrixABC)) != matrix3_ok ||
+ (code = dict_proc3_param(op, "EncodeABC", &pcprocs->EncodeABC)) < 0 ||
+ (code = dict_range3_param(op, "RangeABC", &pcie->RangeABC)) < 0 ||
+ (code = cie_points_param(op, &pcie->points)) < 0 ||
+ (code = dict_matrix3_param(op, "MatrixPQR", &pcie->MatrixPQR)) != matrix3_ok ||
+ (code = dict_range3_param(op, "RangePQR", &pcie->RangePQR)) < 0 ||
+ (code = dict_proc3_param(op, "TransformPQR", &pcprocs->TransformPQR)) < 0
+ )
+ return (code < 0 ? code : gs_note_error(e_rangecheck));
+#define rRT pcie->RenderTable.lookup
+ if ( dict_find_string(op, "RenderTable", &pRT) > 0 )
+ { const ref *prte;
+ int i;
+
+ check_read_type(*pRT, t_array);
+ if ( r_size(pRT) < 5 )
+ return_error(e_rangecheck);
+ prte = pRT->value.const_refs;
+ check_type_only(prte[4], t_integer);
+ if ( !(prte[4].value.intval == 3 || prte[4].value.intval == 4) )
+ return_error(e_rangecheck);
+ rRT.n = 3;
+ rRT.m = prte[4].value.intval;
+ if ( r_size(pRT) != rRT.m + 5 )
+ return_error(e_rangecheck);
+ prte += 5;
+ for ( i = 0; i < rRT.m; i++ )
+ check_proc_only(prte[i]);
+ code = cie_table_param(pRT, &rRT, mem);
+ if ( code < 0 )
+ return code;
+ make_const_array(&pcprocs->RenderTableT,
+ a_readonly | r_space(pRT),
+ rRT.m, prte);
+ }
+ else
+ { rRT.table = 0;
+ make_null(&pcprocs->RenderTableT);
+ }
+#undef rRT
+ pcie->EncodeLMN = Encode_default;
+ pcie->EncodeABC = Encode_default;
+ pcie->TransformPQR = TransformPQR_default;
+ pcie->RenderTable.T = RenderTableT_default;
+ return 0;
+}
+
+/* Cache the results of the color rendering procedures. */
+private int cie_cache_render_finish(P1(os_ptr));
+private int
+cache_colorrendering(gs_cie_render *pcie,
+ const ref_cie_render_procs *pcrprocs, gs_state *pgs)
+{ es_ptr ep = esp;
+ int code = gs_cie_render_init(pcie); /* sets Domain values */
+ int i;
+
+ /* We must run gs_cie_render_complete when we're done. */
+ if ( code < 0 ||
+ (gs_cie_cs_common(pgs) != 0 &&
+ (code = cie_cache_joint(pcrprocs, pgs)) < 0) || /* do this last */
+ (code = cie_cache_push_finish(cie_cache_render_finish, pgs, pcie)) < 0 ||
+ (code = cie_prepare_cache3(&pcie->DomainLMN, pcrprocs->EncodeLMN.value.const_refs, &pcie->caches.EncodeLMN[0], pcie, pgs, "Encode.LMN")) < 0 ||
+ (code = cie_prepare_cache3(&pcie->DomainABC, pcrprocs->EncodeABC.value.const_refs, &pcie->caches.EncodeABC[0], pcie, pgs, "Encode.ABC")) < 0
+ )
+ { esp = ep;
+ return code;
+ }
+ if ( pcie->RenderTable.lookup.table != 0 )
+ { bool is_identity = true;
+ for ( i = 0; i < pcie->RenderTable.lookup.m; i++ )
+ if ( r_size(pcrprocs->RenderTableT.value.const_refs + i) != 0 )
+ { is_identity = false;
+ break;
+ }
+ pcie->caches.RenderTableT_is_identity = is_identity;
+ if ( !is_identity )
+ for ( i = 0; i < pcie->RenderTable.lookup.m; i++ )
+ if ( (code =
+ cie_prepare_cache(Range4_default.ranges,
+ pcrprocs->RenderTableT.value.const_refs + i,
+ &pcie->caches.RenderTableT[i].floats,
+ pcie, pgs, "RenderTable.T")) < 0
+ )
+ { esp = ep;
+ return code;
+ }
+ }
+ return o_push_estack;
+}
+
+/* Finish up after loading the rendering caches. */
+private int
+cie_cache_render_finish(os_ptr op)
+{ gs_cie_render *pcie = r_ptr(op, gs_cie_render);
+ int code;
+ if ( pcie->RenderTable.lookup.table != 0 && !pcie->caches.RenderTableT_is_identity )
+ { /* Convert the RenderTableT cache from floats to fracs. */
+ int j;
+ for ( j = 0; j < pcie->RenderTable.lookup.m; j++ )
+ gs_cie_cache_to_fracs(&pcie->caches.RenderTableT[j]);
+ }
+ code = gs_cie_render_complete(pcie);
+ if ( code < 0 )
+ return code;
+ pop(1);
+ return 0;
+}
+
+/* ------ Internal routines ------ */
+
+/* Load the joint caches. */
+private int
+ cie_exec_tpqr(P1(os_ptr)),
+ cie_post_exec_tpqr(P1(os_ptr)),
+ cie_tpqr_finish(P1(os_ptr));
+int
+cie_cache_joint(const ref_cie_render_procs *pcrprocs, gs_state *pgs)
+{ const gs_cie_render *pcier = gs_currentcolorrendering(pgs);
+ /* The former installation procedures have allocated */
+ /* the joint caches and filled in points_sd. */
+ gx_cie_joint_caches *pjc = gx_currentciecaches(pgs);
+ ref pqr_procs;
+#define pqr_refs pqr_procs.value.refs
+ uint space;
+ int code;
+ int i;
+
+ if ( pcier == 0 ) /* cache is not set up yet */
+ return 0;
+ if ( pjc == 0 )
+ return_error(e_VMerror);
+ code = ialloc_ref_array(&pqr_procs, a_readonly, 3*(1+4+4*6),
+ "cie_cache_common");
+ if ( code < 0 ) return code;
+ /* When we're done, deallocate the procs and complete the caches. */
+ check_estack(3);
+ cie_cache_push_finish(cie_tpqr_finish, pgs, pgs);
+ *++esp = pqr_procs;
+ space = r_space(&pqr_procs);
+ for ( i = 0; i < 3; i++ )
+ { ref *p = pqr_refs + 3 + (4+4*6) * i;
+ const float *ppt = (float *)&pjc->points_sd;
+ int j;
+
+ make_array(pqr_refs + i, a_readonly | a_executable | space,
+ 4, p);
+ make_array(p, a_readonly | space, 4*6, p + 4);
+ p[1] = pcrprocs->TransformPQR.value.refs[i];
+ make_oper(p + 2, 0, cie_exec_tpqr);
+ make_oper(p + 3, 0, cie_post_exec_tpqr);
+ for ( j = 0, p += 4; j < 4*6; j++, p++, ppt++ )
+ make_real(p, *ppt);
+ }
+ return cie_prepare_cache3(&pcier->RangePQR,
+ pqr_procs.value.const_refs,
+ &pjc->TransformPQR[0],
+ pjc, pgs, "Transform.PQR");
+}
+
+/* Private operator to shuffle arguments for the TransformPQR procedure: */
+/* v [ws wd bs bd] proc -> -mark- ws wd bs bd v proc + exec */
+private int
+cie_exec_tpqr(register os_ptr op)
+{ const ref *ppt = op[-1].value.const_refs;
+ uint space = r_space(op - 1);
+ int i;
+
+ check_op(4);
+ push(4);
+ *op = op[-4]; /* proc */
+ op[-1] = op[-6]; /* v */
+ for ( i = 0; i < 4; i++ )
+ make_const_array(op - 5 + i, a_readonly | space,
+ 6, ppt + i * 6);
+ make_mark(op - 6);
+ return zexec(op);
+}
+
+/* Remove extraneous values from the stack after executing */
+/* the TransformPQR procedure. -mark- ... v -> v */
+private int
+cie_post_exec_tpqr(register os_ptr op)
+{ uint count = ref_stack_counttomark(&o_stack);
+ ref vref;
+
+ if ( count < 2 )
+ return_error(e_unmatchedmark);
+ vref = *op;
+ ref_stack_pop(&o_stack, count - 1);
+ *osp = vref;
+ return 0;
+}
+
+/* Free the procs array and complete the joint caches. */
+private int
+cie_tpqr_finish(register os_ptr op)
+{ gs_state *pgs = r_ptr(op, gs_state);
+ ifree_ref_array(op - 1, "cie_tpqr_finish");
+ gs_cie_cs_complete(pgs, false);
+ pop(2);
+ return 0;
+}
+
+/* ------ Initialization procedure ------ */
+
+BEGIN_OP_DEFS(zcrd_l2_op_defs) {
+ op_def_begin_level2(),
+ {"0currentcolorrendering", zcurrentcolorrendering},
+ {"1setcolorrendering", zsetcolorrendering},
+ /* Internal operators */
+ {"1%cie_render_finish", cie_cache_render_finish},
+ {"3%cie_exec_tpqr", cie_exec_tpqr},
+ {"2%cie_post_exec_tpqr", cie_post_exec_tpqr},
+ {"1%cie_tpqr_finish", cie_tpqr_finish},
+END_OP_DEFS(0) }
diff --git a/pstoraster/zcsindex.c b/pstoraster/zcsindex.c
new file mode 100644
index 000000000..9b128a751
--- /dev/null
+++ b/pstoraster/zcsindex.c
@@ -0,0 +1,225 @@
+/* Copyright (C) 1993, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* zcsindex.c */
+/* Indexed color space support */
+#include "memory_.h"
+#include "ghost.h"
+#include "errors.h"
+#include "oper.h"
+#include "gsstruct.h"
+#include "gscolor.h"
+#include "gsmatrix.h" /* for gxcolor2.h */
+#include "gxcspace.h"
+#include "gxfixed.h" /* ditto */
+#include "gxcolor2.h"
+#include "estack.h"
+#include "ialloc.h"
+#include "icsmap.h"
+#include "igstate.h"
+#include "ivmspace.h"
+#include "store.h"
+
+/* Imported from gscolor2.c */
+extern const gs_color_space_type gs_color_space_type_Indexed;
+
+/* Forward references. */
+private int indexed_map1(P1(os_ptr));
+
+/* Allocator type for indexed map */
+private_st_indexed_map();
+
+/* Free the map when freeing the gs_indexed_map structure. */
+private void
+rc_free_indexed_map(gs_memory_t *mem, void *data, client_name_t cname)
+{ /*
+ * A bug in the SGI Irix 4.05 compiler requires the following:
+ */
+ char *cdata = (char *)data;
+ gs_free_object(mem, ((gs_indexed_map *)cdata)->values, cname);
+ gs_free_object(mem, cdata, cname);
+}
+
+/* Indexed lookup procedure that just consults the cache. */
+private int
+lookup_indexed(const gs_indexed_params *params, int index, float *values)
+{ int m = params->base_space.type->num_components;
+ const float *pv = &params->lookup.map->values[index * m];
+ switch ( m )
+ {
+ default: return_error(e_rangecheck);
+ case 4: values[3] = pv[3];
+ case 3: values[2] = pv[2];
+ values[1] = pv[1];
+ case 1: values[0] = pv[0];
+ }
+ return 0;
+}
+
+/* <array> .setindexedspace - */
+/* The current color space is the base space for the indexed space. */
+private int
+zsetindexedspace(register os_ptr op)
+{ ref *pproc = &istate->colorspace.procs.special.index_proc;
+ const ref *pcsa;
+ gs_color_space cs;
+ gs_base_color_space cs_base;
+ ref_colorspace cspace_old;
+ uint edepth = ref_stack_count(&e_stack);
+ int num_entries;
+ int code;
+
+ check_read_type(*op, t_array);
+ if ( r_size(op) != 4 )
+ return_error(e_rangecheck);
+ pcsa = op->value.const_refs + 1;
+ check_type_only(pcsa[1], t_integer);
+ if ( pcsa[1].value.intval < 0 || pcsa[1].value.intval > 4095 )
+ return_error(e_rangecheck);
+ num_entries = (int)pcsa[1].value.intval + 1;
+ cs = *gs_currentcolorspace(igs);
+ if ( !cs.type->can_be_base_space )
+ return_error(e_rangecheck);
+ cspace_old = istate->colorspace;
+ /*
+ * We can't count on C compilers to recognize the aliasing
+ * that would be involved in a direct assignment.
+ * Formerly, we used the following code:
+ cs_base = *(gs_base_color_space *)&cs;
+ cs.params.indexed.base_space = cs_base;
+ * But the Watcom C 10.0 compiler is too smart: it turns this into
+ * a direct assignment (and compiles incorrect code for it),
+ * defeating our purpose. Instead, we have to do it by brute force:
+ */
+ memcpy(&cs_base, &cs, sizeof(gs_base_color_space));
+ cs.params.indexed.base_space = cs_base;
+ if ( r_has_type(&pcsa[2], t_string) )
+ { int num_values = num_entries * cs.type->num_components;
+ check_read(pcsa[2]);
+ if ( r_size(&pcsa[2]) != num_values )
+ return_error(e_rangecheck);
+ cs.params.indexed.lookup.table.data =
+ pcsa[2].value.const_bytes;
+ cs.params.indexed.use_proc = 0;
+ make_null(pproc);
+ code = 0;
+ }
+ else
+ { gs_indexed_map *map;
+ check_proc(pcsa[2]);
+ code = zcs_begin_map(&map, &pcsa[2], num_entries,
+ (const gs_base_color_space *)&cs,
+ indexed_map1);
+ if ( code < 0 )
+ return code;
+ cs.params.indexed.use_proc = 1;
+ *pproc = pcsa[2];
+ map->proc.lookup_index = lookup_indexed;
+ cs.params.indexed.lookup.map = map;
+ }
+ cs.params.indexed.hival = num_entries - 1;
+ cs.type = &gs_color_space_type_Indexed;
+ code = gs_setcolorspace(igs, &cs);
+ if ( code < 0 )
+ { istate->colorspace = cspace_old;
+ ref_stack_pop_to(&e_stack, edepth);
+ return code;
+ }
+ pop(1);
+ return (ref_stack_count(&e_stack) == edepth ? 0 : o_push_estack); /* installation will load the caches */
+}
+
+/* Continuation procedure for saving mapped Indexed color values. */
+private int
+indexed_map1(os_ptr op)
+{ es_ptr ep = esp;
+ int i = (int)ep[csme_index].value.intval;
+ if ( i >= 0 ) /* i.e., not first time */
+ { int m = (int)ep[csme_num_components].value.intval;
+ int code = num_params(op, m, &r_ptr(&ep[csme_map], gs_indexed_map)->values[i * m]);
+ if ( code < 0 )
+ return code;
+ pop(m); op -= m;
+ if ( i == (int)ep[csme_hival].value.intval )
+ { /* All done. */
+ esp -= num_csme;
+ return o_pop_estack;
+ }
+ }
+ push(1);
+ ep[csme_index].value.intval = ++i;
+ make_int(op, i);
+ make_op_estack(ep + 1, indexed_map1);
+ ep[2] = ep[csme_proc]; /* lookup proc */
+ esp = ep + 2;
+ return o_push_estack;
+}
+
+/* ------ Initialization procedure ------ */
+
+BEGIN_OP_DEFS(zcsindex_l2_op_defs) {
+ op_def_begin_level2(),
+ {"1.setindexedspace", zsetindexedspace},
+ /* Internal operators */
+ {"1%indexed_map1", indexed_map1},
+END_OP_DEFS(0) }
+
+/* ------ Internal routines ------ */
+
+/* Allocate, and prepare to load, the index or tint map. */
+int
+zcs_begin_map(gs_indexed_map **pmap, const ref *pproc, int num_entries,
+ const gs_base_color_space *base_space, int (*map1)(P1(os_ptr)))
+{ gs_memory_t *mem = gs_state_memory(igs);
+ int num_components = base_space->type->num_components;
+ int num_values = num_entries * num_components;
+ gs_indexed_map *map;
+ es_ptr ep;
+ float *values;
+ rc_alloc_struct_0(map, gs_indexed_map, &st_indexed_map,
+ mem, return_error(e_VMerror),
+ "setcolorspace(mapped)");
+ values =
+ (float *)gs_alloc_byte_array(mem, num_values, sizeof(float),
+ "setcolorspace(mapped)");
+ if ( values == 0 )
+ { gs_free_object(mem, map, "setcolorspace(mapped)");
+ return_error(e_VMerror);
+ }
+ map->rc.free = rc_free_indexed_map;
+ map->num_values = num_values;
+ map->values = values;
+ *pmap = map;
+ /* Map the entire set of color indices. Since the */
+ /* o-stack may not be able to hold 4*4096 values, we have */
+ /* to load them into the cache as they are generated. */
+ check_estack(num_csme + 1); /* 1 extra for map1 proc */
+ ep = esp += num_csme;
+ make_int(ep + csme_num_components, num_components);
+ make_struct(ep + csme_map, imemory_space((gs_ref_memory_t *)mem), map);
+ ep[csme_proc] = *pproc;
+ make_int(ep + csme_hival, num_entries - 1);
+ make_int(ep + csme_index, -1);
+ push_op_estack(map1);
+ return o_push_estack;
+}
diff --git a/pstoraster/zcssepr.c b/pstoraster/zcssepr.c
new file mode 100644
index 000000000..fe3687cc8
--- /dev/null
+++ b/pstoraster/zcssepr.c
@@ -0,0 +1,154 @@
+/* Copyright (C) 1994, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* zcssepr.c */
+/* Separation color space support */
+#include "ghost.h"
+#include "errors.h"
+#include "oper.h"
+#include "gsstruct.h"
+#include "gscolor.h"
+#include "gsmatrix.h" /* for gxcolor2.h */
+#include "gxcspace.h"
+#include "gxfixed.h" /* ditto */
+#include "gxcolor2.h"
+#include "estack.h"
+#include "ialloc.h"
+#include "icsmap.h"
+#include "igstate.h"
+#include "ivmspace.h"
+#include "store.h"
+
+/* Imported from gscsepr.c */
+extern const gs_color_space_type gs_color_space_type_Separation;
+
+/* Forward references */
+private int separation_map1(P1(os_ptr));
+
+/* Define the separation cache size. This makes many useful tint values */
+/* map to integer cache indices. */
+#define separation_cache_size 360
+
+/* Tint transform procedure that just consults the cache. */
+private int
+lookup_tint(const gs_separation_params *params, floatp tint, float *values)
+{ int m = params->alt_space.type->num_components;
+ const gs_indexed_map *map = params->map;
+ int value_index =
+ (tint < 0 ? 0 : tint > 1 ? map->num_values - m :
+ (int)(tint * separation_cache_size + 0.5) * m);
+ const float *pv = &map->values[value_index];
+ switch ( m )
+ {
+ default: return_error(e_rangecheck);
+ case 4: values[3] = pv[3];
+ case 3: values[2] = pv[2];
+ values[1] = pv[1];
+ case 1: values[0] = pv[0];
+ }
+ return 0;
+}
+
+/* <array> .setseparationspace - */
+/* The current color space is the alternate space for the separation space. */
+private int
+zsetseparationspace(register os_ptr op)
+{ const ref *pcsa;
+ gs_color_space cs;
+ ref_colorspace cspace_old;
+ uint edepth = ref_stack_count(&e_stack);
+ gs_indexed_map *map;
+ int code;
+
+ check_read_type(*op, t_array);
+ if ( r_size(op) != 4 )
+ return_error(e_rangecheck);
+ pcsa = op->value.const_refs + 1;
+ switch ( r_type(pcsa) )
+ {
+ default:
+ return_error(e_typecheck);
+ case t_string:
+ case t_name:
+ ;
+ }
+ check_proc(pcsa[2]);
+ cs = *gs_currentcolorspace(igs);
+ if ( !cs.type->can_be_base_space )
+ return_error(e_rangecheck);
+ code = zcs_begin_map(&map, &pcsa[2], separation_cache_size + 1,
+ (const gs_base_color_space *)&cs,
+ separation_map1);
+ if ( code < 0 )
+ return code;
+ map->proc.tint_transform = lookup_tint;
+ cs.params.separation.alt_space = *(gs_base_color_space *)&cs;
+ cs.params.separation.map = map;
+ cspace_old = istate->colorspace;
+ istate->colorspace.procs.special.separation.layer_name = pcsa[0];
+ istate->colorspace.procs.special.separation.tint_transform = pcsa[2];
+ cs.type = &gs_color_space_type_Separation;
+ code = gs_setcolorspace(igs, &cs);
+ if ( code < 0 )
+ { istate->colorspace = cspace_old;
+ ref_stack_pop_to(&e_stack, edepth);
+ return code;
+ }
+ pop(1);
+ return (ref_stack_count(&e_stack) == edepth ? 0 : o_push_estack); /* installation will load the caches */
+}
+
+/* Continuation procedure for saving transformed tint values. */
+private int
+separation_map1(os_ptr op)
+{ es_ptr ep = esp;
+ int i = (int)ep[csme_index].value.intval;
+ if ( i >= 0 ) /* i.e., not first time */
+ { int m = (int)ep[csme_num_components].value.intval;
+ int code = num_params(op, m, &r_ptr(&ep[csme_map], gs_indexed_map)->values[i * m]);
+ if ( code < 0 )
+ return code;
+ pop(m); op -= m;
+ if ( i == (int)ep[csme_hival].value.intval )
+ { /* All done. */
+ esp -= num_csme;
+ return o_pop_estack;
+ }
+ }
+ push(1);
+ ep[csme_index].value.intval = ++i;
+ make_real(op, i / (float)separation_cache_size);
+ make_op_estack(ep + 1, separation_map1);
+ ep[2] = ep[csme_proc]; /* tint_transform */
+ esp = ep + 2;
+ return o_push_estack;
+}
+
+/* ------ Initialization procedure ------ */
+
+BEGIN_OP_DEFS(zcssepr_l2_op_defs) {
+ op_def_begin_level2(),
+ {"1.setseparationspace", zsetseparationspace},
+ /* Internal operators */
+ {"1%separation_map1", separation_map1},
+END_OP_DEFS(0) }
diff --git a/pstoraster/zdevcal.c b/pstoraster/zdevcal.c
new file mode 100644
index 000000000..e51fecf2b
--- /dev/null
+++ b/pstoraster/zdevcal.c
@@ -0,0 +1,75 @@
+/* Copyright (C) 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* zdevcal.c */
+/* %Calendar% IODevice */
+#include "time_.h"
+#include "ghost.h"
+#include "gxiodev.h"
+#include "istack.h"
+#include "iparam.h"
+
+/* ------ %Calendar% ------ */
+
+private iodev_proc_get_params(calendar_get_params);
+gx_io_device gs_iodev_calendar =
+ { "%Calendar%", "Special",
+ { iodev_no_init, iodev_no_open_device, iodev_no_open_file,
+ iodev_no_fopen, iodev_no_fclose,
+ iodev_no_delete_file, iodev_no_rename_file, iodev_no_file_status,
+ iodev_no_enumerate_files, NULL, NULL,
+ calendar_get_params, iodev_no_put_params
+ }
+ };
+
+/* Get the date and time. */
+private int
+calendar_get_params(gx_io_device *iodev, gs_param_list *plist)
+{ int code;
+ time_t t;
+ struct tm *pltime;
+ struct tm ltime;
+ bool running;
+
+ if ( time(&t) == -1 || (pltime = localtime(&t)) == 0 )
+ { ltime.tm_sec = ltime.tm_min = ltime.tm_hour =
+ ltime.tm_mday = ltime.tm_mon = ltime.tm_year = 0;
+ running = false;
+ }
+ else
+ { ltime = *pltime;
+ ltime.tm_year += 1900;
+ ltime.tm_mon++; /* 1-origin */
+ running = true;
+ }
+ if ( (code = param_write_int(plist, "Year", &ltime.tm_year)) < 0 ||
+ (code = param_write_int(plist, "Month", &ltime.tm_mon)) < 0 ||
+ (code = param_write_int(plist, "Day", &ltime.tm_mday)) < 0 ||
+ (code = param_write_int(plist, "Weekday", &ltime.tm_wday)) < 0 ||
+ (code = param_write_int(plist, "Hour", &ltime.tm_hour)) < 0 ||
+ (code = param_write_int(plist, "Minute", &ltime.tm_min)) < 0 ||
+ (code = param_write_int(plist, "Second", &ltime.tm_sec)) < 0
+ )
+ return code;
+ return param_write_bool(plist, "Running", &running);
+}
diff --git a/pstoraster/zdevice.c b/pstoraster/zdevice.c
new file mode 100644
index 000000000..bdfea7eda
--- /dev/null
+++ b/pstoraster/zdevice.c
@@ -0,0 +1,326 @@
+/* Copyright (C) 1989, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* zdevice.c */
+/* Device-related operators */
+#include "ghost.h"
+#include "errors.h"
+#include "oper.h"
+#include "ialloc.h"
+#include "idict.h"
+#include "igstate.h"
+#include "iname.h"
+#include "interp.h"
+#include "iparam.h"
+#include "ivmspace.h"
+#include "gsmatrix.h"
+#include "gsstate.h"
+#include "gxdevice.h"
+#include "store.h"
+
+/* <device> copydevice <newdevice> */
+private int
+zcopydevice(register os_ptr op)
+{ gx_device *new_dev;
+ int code;
+
+ check_read_type(*op, t_device);
+ code = gs_copydevice(&new_dev, op->value.pdevice, imemory);
+ if ( code < 0 )
+ return code;
+ new_dev->memory = imemory;
+ make_tav(op, t_device, icurrent_space | a_all, pdevice, new_dev);
+ return 0;
+}
+
+/* <device> <y> <string> copyscanlines <substring> */
+private int
+zcopyscanlines(register os_ptr op)
+{ os_ptr op1 = op - 1;
+ os_ptr op2 = op - 2;
+ gx_device *dev;
+ int code;
+ uint bytes_copied;
+
+ check_read_type(*op2, t_device);
+ dev = op2->value.pdevice;
+ check_type(*op1, t_integer);
+ if ( op1->value.intval < 0 || op1->value.intval > dev->height )
+ return_error(e_rangecheck);
+ check_write_type(*op, t_string);
+ code = gs_copyscanlines(dev, (int)op1->value.intval,
+ op->value.bytes, r_size(op), NULL,
+ &bytes_copied);
+ if ( code < 0 )
+ return_error(e_rangecheck); /* not a memory device */
+ *op2 = *op;
+ r_set_size(op2, bytes_copied);
+ pop(2);
+ return 0;
+}
+
+/* - currentdevice <device> */
+int
+zcurrentdevice(register os_ptr op)
+{ gx_device *dev = gs_currentdevice(igs);
+ gs_ref_memory_t *mem = (gs_ref_memory_t *)dev->memory;
+
+ push(1);
+ make_tav(op, t_device,
+ (mem == 0 ? avm_foreign : imemory_space(mem)) | a_all,
+ pdevice, dev);
+ return 0;
+}
+
+/* - flushpage - */
+int
+zflushpage(register os_ptr op)
+{ return gs_flushpage(igs);
+}
+
+/* <int> .getdevice <device> */
+private int
+zgetdevice(register os_ptr op)
+{ const gx_device *dev;
+
+ check_type(*op, t_integer);
+ if ( op->value.intval != (int)(op->value.intval) )
+ return_error(e_rangecheck); /* won't fit in an int */
+ dev = gs_getdevice((int)(op->value.intval));
+ if ( dev == 0 ) /* index out of range */
+ return_error(e_rangecheck);
+ /* Device prototypes are read-only; */
+ /* the cast is logically unnecessary. */
+ make_tav(op, t_device, avm_foreign | a_readonly, pdevice,
+ (gx_device *)dev);
+ return 0;
+}
+
+/* <device> <key_dict|null> .getdeviceparams <mark> <name> <value> ... */
+private int
+zgetdeviceparams(os_ptr op)
+{ ref rkeys;
+ gx_device *dev;
+ stack_param_list list;
+ int code;
+ ref *pmark;
+
+ check_read_type(op[-1], t_device);
+ rkeys = *op;
+ dev = op[-1].value.pdevice;
+ pop(1);
+ stack_param_list_write(&list, &o_stack, &rkeys);
+ code = gs_getdeviceparams(dev, (gs_param_list *)&list);
+ if ( code < 0 )
+ { /* We have to put back the top argument. */
+ if ( list.count > 0 )
+ ref_stack_pop(&o_stack, list.count * 2 - 1);
+ else
+ ref_stack_push(&o_stack, 1);
+ *osp = rkeys;
+ return code;
+ }
+ pmark = ref_stack_index(&o_stack, list.count * 2);
+ make_mark(pmark);
+ return 0;
+}
+
+/* <matrix> <width> <height> <palette> <word?> makewordimagedevice <device> */
+private int
+zmakewordimagedevice(register os_ptr op)
+{ os_ptr op1 = op - 1;
+ gs_matrix imat;
+ gx_device *new_dev;
+ const byte *colors;
+ int colors_size;
+ int code;
+
+ check_int_leu(op[-3], max_uint >> 1); /* width */
+ check_int_leu(op[-2], max_uint >> 1); /* height */
+ check_type(*op, t_boolean);
+ if ( r_has_type(op1, t_null) ) /* true color */
+ { colors = 0;
+ colors_size = -24; /* 24-bit true color */
+ }
+ else if ( r_has_type(op1, t_integer) )
+ { switch ( op1->value.intval )
+ {
+ case 16: case 24: case 32: break;
+ default: return_error(e_rangecheck);
+ }
+ colors = 0;
+ colors_size = -op1->value.intval;
+ }
+ else
+ { check_type(*op1, t_string); /* palette */
+ if ( r_size(op1) > 3*256 )
+ return_error(e_rangecheck);
+ colors = op1->value.bytes;
+ colors_size = r_size(op1);
+ }
+ if ( (code = read_matrix(op - 4, &imat)) < 0 )
+ return code;
+ /* Everything OK, create device */
+ code = gs_makewordimagedevice(&new_dev, &imat,
+ (int)op[-3].value.intval,
+ (int)op[-2].value.intval,
+ colors, colors_size,
+ op->value.boolval, true, imemory);
+ if ( code == 0 )
+ { new_dev->memory = imemory;
+ make_tav(op - 4, t_device, imemory_space(iimemory) | a_all,
+ pdevice, new_dev);
+ pop(4);
+ }
+ return code;
+}
+
+/* - nulldevice - */
+/* Note that nulldevice clears the current pagedevice. */
+private int
+znulldevice(register os_ptr op)
+{ gs_nulldevice(igs);
+ clear_pagedevice(istate);
+ return 0;
+}
+
+/* <num_copies> <flush_bool> .outputpage - */
+private int
+zoutputpage(register os_ptr op)
+{ int code;
+
+ check_type(op[-1], t_integer);
+ check_type(*op, t_boolean);
+ code = gs_output_page(igs, (int)op[-1].value.intval,
+ op->value.boolval);
+ if ( code < 0 )
+ return code;
+ pop(2);
+ return 0;
+}
+
+/* <device> <policy_dict|null> <require_all> <mark> <name> <value> ... */
+/* .putdeviceparams */
+/* (on success) <device> <eraseflag> */
+/* (on failure) <device> <policy_dict|null> <require_all> <mark> */
+/* <name1> <error1> ... */
+/* For a key that simply was not recognized, if require_all is true, */
+/* the result will be an /undefined error; if require_all is false, */
+/* the key will be ignored. */
+/* Note that .putdeviceparams clears the current pagedevice. */
+private int
+zputdeviceparams(os_ptr op)
+{ uint count = ref_stack_counttomark(&o_stack);
+ ref *prequire_all;
+ ref *ppolicy;
+ ref *pdev;
+ gx_device *dev;
+ stack_param_list list;
+ int code;
+ int old_width, old_height;
+ int i, dest;
+
+ if ( count == 0 )
+ return_error(e_unmatchedmark);
+ prequire_all = ref_stack_index(&o_stack, count);
+ ppolicy = ref_stack_index(&o_stack, count + 1);
+ pdev = ref_stack_index(&o_stack, count + 2);
+ if ( pdev == 0 )
+ return_error(e_stackunderflow);
+ check_type_only(*prequire_all, t_boolean);
+ check_write_type_only(*pdev, t_device);
+ dev = pdev->value.pdevice;
+ code = stack_param_list_read(&list, &o_stack, 0, ppolicy,
+ prequire_all->value.boolval);
+ if ( code < 0 )
+ return code;
+ old_width = dev->width;
+ old_height = dev->height;
+ code = gs_putdeviceparams(dev, (gs_param_list *)&list);
+ /* Check for names that were undefined or caused errors. */
+ for ( dest = count - 2, i = 0; i < count >> 1; i++ )
+ if ( list.results[i] < 0 )
+ { *ref_stack_index(&o_stack, dest) =
+ *ref_stack_index(&o_stack, count - (i << 1) - 2);
+ gs_errorname(list.results[i],
+ ref_stack_index(&o_stack, dest - 1));
+ dest -= 2;
+ }
+ if ( code < 0 )
+ { /* There were errors reported. */
+ ref_stack_pop(&o_stack, dest + 1);
+ return 0;
+ }
+ if ( code > 0 || (code == 0 && (dev->width != old_width || dev->height != old_height)) )
+ { /* The device was open and is now closed, */
+ /* or its dimensions have changed. */
+ /* If it was the current device, */
+ /* call setdevice to reinstall it and erase the page. */
+ /****** DOESN'T FIND ALL THE GSTATES THAT REFERENCE THE DEVICE. ******/
+ if ( gs_currentdevice(igs) == dev )
+ { bool was_open = dev->is_open;
+ code = gs_setdevice_no_erase(igs, dev);
+ /* If the device wasn't closed, */
+ /* setdevice won't erase the page. */
+ if ( was_open && code >= 0 )
+ code = 1;
+ }
+ }
+ if ( code < 0 )
+ return code;
+ ref_stack_pop(&o_stack, count + 1);
+ make_bool(osp, code);
+ clear_pagedevice(istate);
+ return 0;
+}
+
+/* <device> .setdevice <eraseflag> */
+/* Note that .setdevice clears the current pagedevice. */
+int
+zsetdevice(register os_ptr op)
+{ int code;
+
+ check_write_type(*op, t_device);
+ code = gs_setdevice_no_erase(igs, op->value.pdevice);
+ if ( code < 0 )
+ return code;
+ make_bool(op, 1);
+ clear_pagedevice(istate);
+ return code;
+}
+
+/* ------ Initialization procedure ------ */
+
+BEGIN_OP_DEFS(zdevice_op_defs) {
+ {"1copydevice", zcopydevice},
+ {"3copyscanlines", zcopyscanlines},
+ {"0currentdevice", zcurrentdevice},
+ {"0flushpage", zflushpage},
+ {"1.getdevice", zgetdevice},
+ {"2.getdeviceparams", zgetdeviceparams},
+ {"5makewordimagedevice", zmakewordimagedevice},
+ {"0nulldevice", znulldevice},
+ {"2.outputpage", zoutputpage},
+ {"3.putdeviceparams", zputdeviceparams},
+ {"1.setdevice", zsetdevice},
+END_OP_DEFS(0) }
diff --git a/pstoraster/zdevice2.c b/pstoraster/zdevice2.c
new file mode 100644
index 000000000..998f69001
--- /dev/null
+++ b/pstoraster/zdevice2.c
@@ -0,0 +1,333 @@
+/* Copyright (C) 1993, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* zdevice2.c */
+/* Level 2 device operators */
+#include "math_.h"
+#include "memory_.h"
+#include "ghost.h"
+#include "errors.h"
+#include "oper.h"
+#include "dstack.h" /* for dict_find_name */
+#include "estack.h"
+#include "idict.h"
+#include "idparam.h"
+#include "igstate.h"
+#include "iname.h"
+#include "store.h"
+#include "gxdevice.h"
+#include "gsstate.h"
+
+/* Imported data */
+extern op_proc_p zcopy_procs[t_next_index];
+
+/* Forward references */
+private int z2copy_gstate(P1(os_ptr));
+private int near push_callout(P1(const char _ds *));
+
+/* Initialize by adding changing the `copy' operator for gstates. */
+/* This is a hack -- we know that gstates are the only */
+/* t_astruct subtype that implements copy. */
+private void
+zdevice2_init(void)
+{ zcopy_procs[t_astruct] = z2copy_gstate;
+}
+
+/* - .currentshowpagecount <count> true */
+/* - .currentshowpagecount false */
+private int
+zcurrentshowpagecount(register os_ptr op)
+{ gx_device *dev = gs_currentdevice(igs);
+
+ if ( (*dev_proc(dev, get_page_device))(dev) == 0 )
+ { push(1);
+ make_false(op);
+ }
+ else
+ { push(2);
+ make_int(op - 1, dev->ShowpageCount);
+ make_true(op);
+ }
+ return 0;
+}
+
+/* - .currentpagedevice <dict> <bool> */
+private int
+zcurrentpagedevice(register os_ptr op)
+{ gx_device *dev = gs_currentdevice(igs);
+
+ push(2);
+ if ( (*dev_proc(dev, get_page_device))(dev) != 0 )
+ { op[-1] = istate->pagedevice;
+ make_true(op);
+ }
+ else
+ { op[-1] = i_null_pagedevice;
+ make_false(op);
+ }
+ return 0;
+}
+
+/* <dict> .setpagedevice - */
+private int
+zsetpagedevice(register os_ptr op)
+{ int code;
+
+/******
+ if ( igs->in_cachedevice )
+ return_error(e_undefined);
+ ******/
+ check_type(*op, t_dictionary);
+ check_dict_read(*op);
+ /* Make the dictionary read-only. */
+ code = zreadonly(op);
+ if ( code < 0 )
+ return code;
+ istate->pagedevice = *op;
+ pop(1);
+ return 0;
+}
+
+/* Default Install/BeginPage/EndPage procedures */
+/* that just call the procedure in the device. */
+
+/* - .callinstall - */
+private int
+zcallinstall(os_ptr op)
+{ gx_device *dev = gs_currentdevice(igs);
+
+ if ( (dev = (*dev_proc(dev, get_page_device))(dev)) != 0 )
+ { int code = (*dev->page_procs.install)(dev, igs);
+ if ( code < 0 )
+ return code;
+ }
+ return 0;
+}
+
+/* <showpage_count> .callbeginpage - */
+private int
+zcallbeginpage(os_ptr op)
+{ gx_device *dev = gs_currentdevice(igs);
+
+ check_type(*op, t_integer);
+ if ( (dev = (*dev_proc(dev, get_page_device))(dev)) != 0 )
+ { int code = (*dev->page_procs.begin_page)(dev, igs);
+ if ( code < 0 )
+ return code;
+ }
+ pop(1);
+ return 0;
+}
+
+/* <showpage_count> <reason_int> .callendpage <flush_bool> */
+private int
+zcallendpage(os_ptr op)
+{ gx_device *dev = gs_currentdevice(igs);
+ int code;
+
+ check_type(op[-1], t_integer);
+ check_type(*op, t_integer);
+ if ( (dev = (*dev_proc(dev, get_page_device))(dev)) != 0 )
+ { code = (*dev->page_procs.end_page)(dev, (int)op->value.intval, igs);
+ if ( code < 0 )
+ return code;
+ if ( code > 1 )
+ return_error(e_rangecheck);
+ }
+ else
+ { code = (op->value.intval == 2 ? 0 : 1);
+ }
+ make_bool(op - 1, code);
+ pop(1);
+ return 0;
+}
+
+/* ------ Wrappers for operators that save the graphics state. ------ */
+
+/* When saving the state with the current device a page device, */
+/* we need to make sure that the page device dictionary exists */
+/* so that grestore can use it to reset the device parameters. */
+/* This may have significant performance consequences, but we don't see */
+/* any way around it. */
+
+/* Check whether we need to call out to create the page device dictionary. */
+#define save_page_device(pgs)\
+ (gs_int_gstate(pgs)->pagedevice.value.pdict ==\
+ i_null_pagedevice.value.pdict &&\
+ (*dev_proc(gs_currentdevice(pgs), get_page_device))(gs_currentdevice(pgs))\
+ != 0)
+
+/* - gsave - */
+private int
+z2gsave(os_ptr op)
+{ if ( !save_page_device(igs) )
+ return gs_gsave(igs);
+ return push_callout("%gsavepagedevice");
+}
+
+/* - save - */
+private int
+z2save(os_ptr op)
+{ if ( !save_page_device(igs) )
+ return zsave(op);
+ return push_callout("%savepagedevice");
+}
+
+/* - gstate <gstate> */
+private int
+z2gstate(os_ptr op)
+{ if ( !save_page_device(igs) )
+ return zgstate(op);
+ return push_callout("%gstatepagedevice");
+}
+
+/* <gstate1> <gstate2> copy <gstate2> */
+private int
+z2copy_gstate(os_ptr op)
+{ if ( !save_page_device(igs) )
+ return zcopy_gstate(op);
+ return push_callout("%copygstatepagedevice");
+}
+
+/* <gstate1> <gstate2> currentgstate <gstate2> */
+private int
+z2currentgstate(os_ptr op)
+{ if ( !save_page_device(igs) )
+ return zcurrentgstate(op);
+ return push_callout("%currentgstatepagedevice");
+}
+
+/* ------ Wrappers for operators that reset the graphics state. ------ */
+
+/* Check whether we need to call out to restore the page device. */
+private bool near
+restore_page_device(const gs_state *pgs_old, const gs_state *pgs_new)
+{ gx_device *dev_old = gs_currentdevice(pgs_old);
+ gx_device *dev_new;
+ gx_device *dev_t1;
+ gx_device *dev_t2;
+
+ if ( (dev_t1 = (*dev_proc(dev_old, get_page_device))(dev_old)) == 0 )
+ return false;
+ dev_new = gs_currentdevice(pgs_new);
+ if ( dev_old != dev_new )
+ { if ( (dev_t2 = (*dev_proc(dev_new, get_page_device))(dev_new)) == 0 )
+ return false;
+ if ( dev_t1 != dev_t2 )
+ return true;
+ }
+ /* The current implementation of setpagedevice just sets new */
+ /* parameters in the same device object, so we have to check */
+ /* whether the page device dictionaries are the same. */
+ { const ref *pdict1 = &gs_int_gstate(pgs_old)->pagedevice;
+ const ref *pdict2 = &gs_int_gstate(pgs_new)->pagedevice;
+ return pdict1->value.pdict != pdict2->value.pdict;
+ }
+}
+
+/* - grestore - */
+private int
+z2grestore(os_ptr op)
+{ if ( !restore_page_device(igs, gs_state_saved(igs)) )
+ return gs_grestore(igs);
+ return push_callout("%grestorepagedevice");
+}
+
+/* - grestoreall - */
+private int
+z2grestoreall(os_ptr op)
+{ for ( ; ; )
+ { if ( !restore_page_device(igs, gs_state_saved(igs)) )
+ { gs_grestore(igs);
+ if ( !gs_state_saved(gs_state_saved(igs)) )
+ break;
+ }
+ else
+ return push_callout("%grestoreallpagedevice");
+ }
+ return 0;
+}
+
+/* <save> restore - */
+private int
+z2restore(os_ptr op)
+{ for ( ; ; )
+ { if ( !restore_page_device(igs, gs_state_saved(igs)) )
+ { zgrestore(op);
+ if ( !gs_state_saved(gs_state_saved(igs)) )
+ break;
+ }
+ else
+ return push_callout("%restorepagedevice");
+ }
+ return zrestore(op);
+}
+
+/* <gstate> setgstate - */
+private int
+z2setgstate(os_ptr op)
+{ check_stype(*op, st_igstate_obj);
+ if ( !restore_page_device(igs, igstate_ptr(op)) )
+ return zsetgstate(op);
+ return push_callout("%setgstatepagedevice");
+}
+
+/* ------ Initialization procedure ------ */
+
+BEGIN_OP_DEFS(zdevice2_l2_op_defs) {
+ op_def_begin_level2(),
+ {"0.currentshowpagecount", zcurrentshowpagecount},
+ {"0.currentpagedevice", zcurrentpagedevice},
+ {"1.setpagedevice", zsetpagedevice},
+ /* Note that the following replace prior definitions */
+ /* in the indicated files: */
+ {"0gsave", z2gsave}, /* zgstate.c */
+ {"0save", z2save}, /* zvmem.c */
+ {"0gstate", z2gstate}, /* zdps1.c */
+ {"1currentgstate", z2currentgstate}, /* zdps1.c */
+ {"0grestore", z2grestore}, /* zgstate.c */
+ {"0grestoreall", z2grestoreall}, /* zgstate.c */
+ {"1restore", z2restore}, /* zvmem.c */
+ {"1setgstate", z2setgstate}, /* zdps1.c */
+ /* Default Install/BeginPage/EndPage procedures */
+ /* that just call the procedure in the device. */
+ {"0.callinstall", zcallinstall},
+ {"1.callbeginpage", zcallbeginpage},
+ {"2.callendpage", zcallendpage},
+END_OP_DEFS(zdevice2_init) }
+
+/* ------ Internal routines ------ */
+
+/* Call out to a PostScript procedure. */
+private int near
+push_callout(const char _ds *callout_name)
+{ int code;
+
+ check_estack(1);
+ code = name_enter_string(callout_name, esp + 1);
+ if ( code < 0 )
+ return code;
+ ++esp;
+ r_set_attrs(esp, a_executable);
+ return o_push_estack;
+}
diff --git a/pstoraster/zdict.c b/pstoraster/zdict.c
new file mode 100644
index 000000000..1d92032a0
--- /dev/null
+++ b/pstoraster/zdict.c
@@ -0,0 +1,490 @@
+/* Copyright (C) 1989, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* zdict.c */
+/* Dictionary operators */
+#include "ghost.h"
+#include "errors.h"
+#include "oper.h"
+#include "idict.h"
+#include "dstack.h"
+#include "ilevel.h" /* for [count]dictstack */
+#include "iname.h" /* for dict_find_name */
+#include "ipacked.h" /* for inline dict lookup */
+#include "ivmspace.h"
+#include "store.h"
+
+/* <int> dict <dict> */
+int
+zdict(register os_ptr op)
+{ check_type(*op, t_integer);
+ check_int_leu(*op, dict_max_size);
+ return dict_create((uint)op->value.intval, op);
+}
+
+/* <dict> maxlength <int> */
+private int
+zmaxlength(register os_ptr op)
+{ check_type(*op, t_dictionary);
+ check_dict_read(*op);
+ make_int(op, dict_maxlength(op));
+ return 0;
+}
+
+/* <dict> begin - */
+int
+zbegin(register os_ptr op)
+{ check_type(*op, t_dictionary);
+ check_dict_read(*op);
+ if ( dsp == dstop )
+ return_error(e_dictstackoverflow);
+ ++dsp;
+ ref_assign(dsp, op);
+ dict_set_top();
+ pop(1);
+ return 0;
+}
+
+/* - end - */
+int
+zend(register os_ptr op)
+{ if ( ref_stack_count_inline(&d_stack) == min_dstack_size )
+ { /* We would underflow the d-stack. */
+ return_error(e_dictstackunderflow);
+ }
+ while ( dsp == dsbot )
+ { /* We would underflow the current block. */
+ ref_stack_pop_block(&d_stack);
+ }
+ dsp--;
+ dict_set_top();
+ return 0;
+}
+
+/* <key> <value> def - */
+/* We make this into a separate procedure because */
+/* the interpreter will almost always call it directly. */
+int
+zop_def(register os_ptr op)
+{ register os_ptr op1 = op - 1;
+ ref *pvslot;
+ /* The following combines a check_op(2) with a type check. */
+ switch ( r_type(op1) )
+ {
+ case t_name:
+ { /* We can use the fast single-probe lookup here. */
+ uint nidx = name_index(op1);
+ uint htemp;
+ if_dict_find_name_by_index_top(nidx, htemp, pvslot)
+ { if ( dtop_can_store(op) )
+ goto ra;
+ }
+ break; /* handle all slower cases */
+ }
+ case t_null:
+ return_error(e_typecheck);
+ case t__invalid:
+ return_error(e_stackunderflow);
+ }
+ /* Combine the check for a writable top dictionary with */
+ /* the global/local store check. See dstack.h for details. */
+ if ( !dtop_can_store(op) )
+ { int code;
+ check_dict_write(*dsp);
+ /*
+ * If the dictionary is writable, the problem must be
+ * an invalid store. We need a special check to allow
+ * storing references to local objects in systemdict,
+ * or in dictionaries known in systemdict,
+ * during initialization (see ivmspace.h).
+ */
+ if ( ialloc_is_in_save() )
+ return_error(e_invalidaccess);
+ if ( dsp->value.pdict != systemdict->value.pdict )
+ { /* See if systemdict is still writable, */
+ /* i.e., we are still doing initialization. */
+ int index;
+ ref elt[2]; /* key, value */
+ check_dict_write(*systemdict);
+ /* See if this dictionary is known in systemdict. */
+ for ( index = dict_first(systemdict);
+ (index = dict_next(systemdict, index, &elt[0])) >= 0;
+ )
+ if ( r_has_type(&elt[1], t_dictionary) &&
+ elt[1].value.pdict == dsp->value.pdict
+ )
+ break;
+ if ( index < 0 )
+ return_error(e_invalidaccess);
+ }
+ switch ( code = dict_find(dsp, op1, &pvslot) )
+ {
+ case 1: /* found */
+ goto ra;
+ default: /* some other error */
+ return code;
+ /*
+ * If we have to grow the dictionary, do it now, so that
+ * the allocator will allocate the copy in the correct space.
+ */
+ case e_dictfull:
+ if ( !dict_auto_expand )
+ return_error(e_dictfull);
+ code = dict_grow(dsp);
+ if ( code < 0 )
+ return code;
+ case 0:
+ ;
+ }
+ /* Temporarily identify the dictionary as local, */
+ /* so the store check in dict_put won't fail. */
+ { uint space = r_space(dsp);
+ r_set_space(dsp, avm_local);
+ code = dict_put(dsp, op1, op);
+ r_set_space(dsp, space);
+ return code;
+ }
+ }
+ /* Save a level of procedure call in the common (redefinition) */
+ /* case. With the current interfaces, we pay a double lookup */
+ /* in the uncommon case. */
+ if ( dict_find(dsp, op1, &pvslot) <= 0 )
+ return dict_put(dsp, op1, op);
+ra: ref_assign_old_inline(&dsp->value.pdict->values, pvslot, op,
+ "dict_put(value)");
+ return 0;
+}
+int
+zdef(os_ptr op)
+{ int code = zop_def(op);
+ if ( code >= 0 ) { pop(2); }
+ return code;
+}
+
+/* <key> load <value> */
+private int
+zload(register os_ptr op)
+{ ref *pvalue;
+ switch ( r_type(op) )
+ {
+ case t_name:
+ /* Use the fast lookup. */
+ if ( (pvalue = dict_find_name(op)) == 0 )
+ return_error(e_undefined);
+ ref_assign(op, pvalue);
+ return 0;
+ case t_null:
+ return_error(e_typecheck);
+ case t__invalid:
+ return_error(e_stackunderflow);
+ default:
+ { /* Use an explicit loop. */
+ uint size = ref_stack_count(&d_stack);
+ uint i;
+ for ( i = 0; i < size; i++ )
+ { ref *dp = ref_stack_index(&d_stack, i);
+ check_dict_read(*dp);
+ if ( dict_find(dp, op, &pvalue) > 0 )
+ { ref_assign(op, pvalue);
+ return 0;
+ }
+ }
+ return_error(e_undefined);
+ }
+ }
+}
+
+/* get - implemented in zgeneric.c */
+
+/* put - implemented in zgeneric.c */
+
+/* <dict> <key> .undef - */
+/* <dict> <key> undef - */
+private int
+zundef(register os_ptr op)
+{ check_type(op[-1], t_dictionary);
+ check_dict_write(op[-1]);
+ dict_undef(op - 1, op); /* ignore undefined error */
+ pop(2);
+ return 0;
+}
+
+/* <dict> <key> known <bool> */
+private int
+zknown(register os_ptr op)
+{ register os_ptr op1 = op - 1;
+ ref *pvalue;
+ check_type(*op1, t_dictionary);
+ check_dict_read(*op1);
+ make_bool(op1, (dict_find(op1, op, &pvalue) > 0 ? 1 : 0));
+ pop(1);
+ return 0;
+}
+
+/* <key> where <dict> true */
+/* <key> where false */
+int
+zwhere(register os_ptr op)
+{ check_op(1);
+ STACK_LOOP_BEGIN(&d_stack, bot, size)
+ { const ref *pdref = bot + size;
+ ref *pvalue;
+ while ( pdref-- > bot )
+ { check_dict_read(*pdref);
+ if ( dict_find(pdref, op, &pvalue) > 0 )
+ { push(1);
+ ref_assign(op - 1, pdref);
+ make_true(op);
+ return 0;
+ }
+ }
+ }
+ STACK_LOOP_END(bot, size)
+ make_false(op);
+ return 0;
+}
+
+/* copy for dictionaries -- called from zcopy in zgeneric.c. */
+/* Only the type of *op has been checked. */
+int
+zcopy_dict(register os_ptr op)
+{ os_ptr op1 = op - 1;
+ int code;
+ check_type(*op1, t_dictionary);
+ check_dict_read(*op1);
+ check_dict_write(*op);
+ if ( !dict_auto_expand &&
+ (dict_length(op) != 0 || dict_maxlength(op) < dict_length(op1))
+ )
+ return_error(e_rangecheck);
+ code = dict_copy(op1, op);
+ if ( code < 0 )
+ return code;
+ /*
+ * In Level 1 systems, we must copy the access attributes too.
+ * The only possible effect this can have is to make the
+ * copy read-only if the original dictionary is read-only.
+ */
+ if ( !level2_enabled )
+ r_copy_attrs(dict_access_ref(op), a_write, dict_access_ref(op1));
+ ref_assign(op1, op);
+ pop(1);
+ return 0;
+}
+
+/* - currentdict <dict> */
+private int
+zcurrentdict(register os_ptr op)
+{ push(1);
+ ref_assign(op, dsp);
+ return 0;
+}
+
+/* - countdictstack <int> */
+private int
+zcountdictstack(register os_ptr op)
+{ uint count = ref_stack_count(&d_stack);
+ push(1);
+ if ( !level2_enabled )
+ count--; /* see dstack.h */
+ make_int(op, count);
+ return 0;
+}
+
+/* <array> dictstack <subarray> */
+private int
+zdictstack(register os_ptr op)
+{ uint count = ref_stack_count(&d_stack);
+ check_write_type(*op, t_array);
+ if ( !level2_enabled )
+ count--; /* see dstack.h */
+ return ref_stack_store(&d_stack, op, count, 0, 0, true, "dictstack");
+}
+
+/* - cleardictstack - */
+private int
+zcleardictstack(os_ptr op)
+{ while ( zend(op) >= 0 ) ;
+ return 0;
+}
+
+/* ------ Extensions ------ */
+
+/* <dict1> <dict2> .dictcopynew <dict2> */
+private int
+zdictcopynew(register os_ptr op)
+{ os_ptr op1 = op - 1;
+ int code;
+ check_type(*op1, t_dictionary);
+ check_dict_read(*op1);
+ check_type(*op, t_dictionary);
+ check_dict_write(*op);
+ /* This is only recognized in Level 2 mode. */
+ if ( !dict_auto_expand )
+ return_error(e_undefined);
+ code = dict_copy_new(op1, op);
+ if ( code < 0 )
+ return code;
+ ref_assign(op1, op);
+ pop(1);
+ return 0;
+}
+
+/* -mark- <key0> <value0> <key1> <value1> ... .dicttomark <dict> */
+/* This is the Level 2 >> operator. */
+private int
+zdicttomark(register os_ptr op)
+{ uint count2 = ref_stack_counttomark(&o_stack);
+ ref rdict;
+ int code;
+ uint idx;
+
+ if ( count2 == 0 )
+ return_error(e_unmatchedmark);
+ count2--;
+ if ( (count2 & 1) != 0 )
+ return_error(e_rangecheck);
+ if ( count2 >> 1 > dict_max_size )
+ return_error(e_rangecheck);
+ code = dict_create(count2 >> 1, &rdict);
+ if ( code < 0 )
+ return code;
+ /* << /a 1 /a 2 >> => << /a 1 >>, i.e., */
+ /* we must enter the keys in top-to-bottom order. */
+ for ( idx = 0; idx < count2; idx += 2 )
+ { code = dict_put(&rdict,
+ ref_stack_index (&o_stack, idx + 1),
+ ref_stack_index (&o_stack, idx));
+ if ( code < 0 )
+ { /* There's no way to free the dictionary -- too bad. */
+ return code;
+ }
+ }
+ ref_stack_pop(&o_stack, count2);
+ ref_assign(osp, &rdict);
+ return code;
+}
+
+/* <dict> <key> <value> .forceput - */
+/*
+ * This forces a "put" even if the dictionary is not writable, and (if
+ * the dictionary is systemdict) even if the value is in local VM.
+ * It is meant to be used only for replacing the value of FontDirectory
+ * in systemdict when switching between local and global VM,
+ * and a few similar applications. After initialization, this operator
+ * should no longer be accessible by name.
+ */
+private int
+zforceput(register os_ptr op)
+{ os_ptr odp = op - 2;
+ int code;
+ check_type(*odp, t_dictionary);
+ if ( odp->value.pdict == systemdict->value.pdict )
+ { uint space = r_space(odp);
+ r_set_space(odp, avm_local);
+ code = dict_put(odp, op - 1, op);
+ r_set_space(odp, space);
+ }
+ else
+ code = dict_put(odp, op - 1, op);
+ if ( code < 0 )
+ return code;
+ pop(3);
+ return 0;
+}
+
+/* <dict> <key> .knownget <value> true */
+/* <dict> <key> .knownget false */
+private int
+zknownget(register os_ptr op)
+{ register os_ptr op1 = op - 1;
+ ref *pvalue;
+ check_type(*op1, t_dictionary);
+ check_dict_read(*op1);
+ if ( dict_find(op1, op, &pvalue) <= 0 )
+ { make_false(op1);
+ pop(1);
+ }
+ else
+ { ref_assign(op1, pvalue);
+ make_true(op);
+ }
+ return 0;
+}
+
+/* <dict> <key> .knownundef <bool> */
+private int
+zknownundef(register os_ptr op)
+{ os_ptr op1 = op - 1;
+ int code;
+ check_type(*op1, t_dictionary);
+ check_dict_write(*op1);
+ code = dict_undef(op1, op);
+ make_bool(op1, code == 0);
+ pop(1);
+ return 0;
+}
+
+/* <dict> <int> .setmaxlength - */
+private int
+zsetmaxlength(register os_ptr op)
+{ uint new_size;
+ int code;
+ os_ptr op1 = op - 1;
+ check_type(*op1, t_dictionary);
+ check_dict_write(*op1);
+ check_type(*op, t_integer);
+ check_int_leu(*op, dict_max_size);
+ new_size = (uint)op->value.intval;
+ if ( dict_length(op - 1) > new_size )
+ return_error(e_dictfull);
+ code = dict_resize(op - 1, new_size);
+ if ( code >= 0 )
+ pop(2);
+ return code;
+}
+
+/* ------ Initialization procedure ------ */
+
+BEGIN_OP_DEFS(zdict_op_defs) {
+ {"0cleardictstack", zcleardictstack},
+ {"1begin", zbegin},
+ {"0countdictstack", zcountdictstack},
+ {"0currentdict", zcurrentdict},
+ {"2def", zdef},
+ {"1dict", zdict},
+ {"0dictstack", zdictstack},
+ {"0end", zend},
+ {"2known", zknown},
+ {"1load", zload},
+ {"1maxlength", zmaxlength},
+ {"2.undef", zundef}, /* we need this even in Level 1 */
+ {"1where", zwhere},
+ /* Extensions */
+ {"2.dictcopynew", zdictcopynew},
+ {"1.dicttomark", zdicttomark},
+ {"3.forceput", zforceput},
+ {"2.knownget", zknownget},
+ {"1.knownundef", zknownundef},
+ {"2.setmaxlength", zsetmaxlength},
+END_OP_DEFS(0) }
diff --git a/pstoraster/zdps1.c b/pstoraster/zdps1.c
new file mode 100644
index 000000000..930eb418f
--- /dev/null
+++ b/pstoraster/zdps1.c
@@ -0,0 +1,450 @@
+/* Copyright (C) 1990, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* zdps1.c */
+/* Display PostScript graphics extensions */
+#include "ghost.h"
+#include "errors.h"
+#include "oper.h"
+#include "gsmatrix.h"
+#include "gspath.h"
+#include "gspath2.h"
+#include "gsstate.h"
+#include "ialloc.h"
+#include "igstate.h"
+#include "ivmspace.h"
+#include "store.h"
+#include "stream.h"
+#include "ibnum.h"
+
+/* Imported data */
+extern op_proc_p zcopy_procs[t_next_index];
+
+/* Forward references */
+private int gstate_unshare(P1(os_ptr));
+
+/* Structure descriptors */
+public_st_igstate_obj();
+
+/* Initialize by adding an entry for gstates to the `copy' operator. */
+/* This is done with a hack -- we know that gstates are the only */
+/* t_astruct subtype that implements copy. */
+private void
+zdps1_init(void)
+{ /* zdevice2_init might have already initialized this. */
+ /* A hack on top of a hack! */
+ if ( zcopy_procs[t_astruct] == zcopy_procs[t_struct] )
+ zcopy_procs[t_astruct] = zcopy_gstate;
+}
+
+/* ------ Graphics state ------ */
+
+/* <bool> setstrokeadjust - */
+private int
+zsetstrokeadjust(register os_ptr op)
+{ check_type(*op, t_boolean);
+ gs_setstrokeadjust(igs, op->value.boolval);
+ pop(1);
+ return 0;
+}
+
+/* - currentstrokeadjust <bool> */
+private int
+zcurrentstrokeadjust(register os_ptr op)
+{ push(1);
+ make_bool(op, gs_currentstrokeadjust(igs));
+ return 0;
+}
+
+/* <x> <y> sethalftonephase - */
+private int
+zsethalftonephase(register os_ptr op)
+{ int code;
+ long x, y;
+ check_type(op[-1], t_integer);
+ check_type(*op, t_integer);
+ x = op[-1].value.intval;
+ y = op->value.intval;
+ if ( x != (int)x || y != (int)y )
+ return_error(e_rangecheck);
+ code = gs_sethalftonephase(igs, (int)x, (int)y);
+ if ( code >= 0 ) pop(2);
+ return code;
+}
+
+/* - currenthalftonephase <x> <y> */
+private int
+zcurrenthalftonephase(register os_ptr op)
+{ gs_int_point phase;
+ gs_currenthalftonephase(igs, &phase);
+ push(2);
+ make_int(op - 1, phase.x);
+ make_int(op, phase.y);
+ return 0;
+}
+
+/* ------ Graphics state objects ------ */
+
+/* Check to make sure that all the elements of a graphics state */
+/* can be stored in the given allocation space. */
+/****** DOESN'T CHECK THE NON-REFS. ****** */
+private int
+gstate_check_space(int_gstate *isp, uint space)
+{
+#define gsref_check(p) store_check_space(space, p)
+ int_gstate_map_refs(isp, gsref_check);
+#undef gsref_check
+ return 0;
+}
+
+/* - gstate <gstate> */
+int
+zgstate(register os_ptr op)
+{ gs_state *pnew;
+ igstate_obj *pigo;
+ int_gstate *isp;
+ gs_memory_t *mem;
+ int code = gstate_check_space(istate, icurrent_space);
+ if ( code < 0 ) return code;
+ pigo = ialloc_struct(igstate_obj, &st_igstate_obj, "gstate");
+ if ( pigo == 0 )
+ return_error(e_VMerror);
+ mem = gs_state_swap_memory(igs, imemory);
+ pnew = gs_gstate(igs);
+ gs_state_swap_memory(igs, mem);
+ if ( pnew == 0 )
+ { ifree_object(pigo, "gstate");
+ return_error(e_VMerror);
+ }
+ isp = gs_int_gstate(pnew);
+ int_gstate_map_refs(isp, ref_mark_new);
+ push(1);
+ /*
+ * Since igstate_obj isn't a ref, but only contains a ref,
+ * save won't clear its l_new bit automatically, and
+ * restore won't set it automatically; we have to make sure
+ * this ref is on the changes chain.
+ */
+ make_iastruct(op, a_all, pigo);
+ make_null(&pigo->gstate);
+ ref_save(op, &pigo->gstate, "gstate");
+ make_istruct_new(&pigo->gstate, 0, pnew);
+ return 0;
+}
+
+/* copy for gstates */
+int
+zcopy_gstate(register os_ptr op)
+{ os_ptr op1 = op - 1;
+ gs_state *pgs;
+ gs_state *pgs1;
+ int_gstate *pistate;
+ gs_memory_t *mem;
+ int code;
+ check_stype(*op, st_igstate_obj);
+ check_stype(*op1, st_igstate_obj);
+ check_write(*op);
+ code = gstate_unshare(op);
+ if ( code < 0 )
+ return code;
+ pgs = igstate_ptr(op);
+ pgs1 = igstate_ptr(op1);
+ pistate = gs_int_gstate(pgs);
+ code = gstate_check_space(gs_int_gstate(pgs1), r_space(op));
+ if ( code < 0 ) return code;
+#define gsref_save(p) ref_save(op, p, "copygstate")
+ int_gstate_map_refs(pistate, gsref_save);
+#undef gsref_save
+ mem = gs_state_swap_memory(pgs, imemory);
+ code = gs_copygstate(pgs, pgs1);
+ gs_state_swap_memory(pgs, mem);
+ if ( code < 0 )
+ return code;
+ int_gstate_map_refs(pistate, ref_mark_new);
+ *op1 = *op;
+ pop(1);
+ return 0;
+}
+
+/* <gstate> currentgstate <gstate> */
+int
+zcurrentgstate(register os_ptr op)
+{ gs_state *pgs;
+ int_gstate *pistate;
+ int code;
+ gs_memory_t *mem;
+ check_stype(*op, st_igstate_obj);
+ check_write(*op);
+ code = gstate_unshare(op);
+ if ( code < 0 )
+ return code;
+ pgs = igstate_ptr(op);
+ pistate = gs_int_gstate(pgs);
+ code = gstate_check_space(istate, r_space(op));
+ if ( code < 0 ) return code;
+#define gsref_save(p) ref_save(op, p, "currentgstate")
+ int_gstate_map_refs(pistate, gsref_save);
+#undef gsref_save
+ mem = gs_state_swap_memory(pgs, imemory);
+ code = gs_currentgstate(pgs, igs);
+ gs_state_swap_memory(pgs, mem);
+ if ( code < 0 )
+ return code;
+ int_gstate_map_refs(pistate, ref_mark_new);
+ return 0;
+}
+
+/* <gstate> setgstate - */
+int
+zsetgstate(register os_ptr op)
+{ int code;
+ check_stype(*op, st_igstate_obj);
+ check_read(*op);
+ code = gs_setgstate(igs, igstate_ptr(op));
+ if ( code < 0 ) return code;
+ pop(1);
+ return 0;
+}
+
+/* ------ Rectangles ------- */
+
+/* We preallocate a short list for rectangles, because */
+/* the rectangle operators usually will involve very few rectangles. */
+#define max_local_rect 5
+typedef struct local_rects_s {
+ gs_rect *pr;
+ uint count;
+ gs_rect rl[max_local_rect];
+} local_rects;
+
+/* Forward references */
+private int rect_get(P2(local_rects *, os_ptr));
+private void rect_release(P1(local_rects *));
+
+/* <x> <y> <width> <height> .rectappend - */
+/* <numarray|numstring> .rectappend - */
+private int
+zrectappend(os_ptr op)
+{ local_rects lr;
+ int npop = rect_get(&lr, op);
+ int code;
+ if ( npop < 0 ) return npop;
+ code = gs_rectappend(igs, lr.pr, lr.count);
+ rect_release(&lr);
+ if ( code < 0 ) return code;
+ pop(npop);
+ return 0;
+}
+
+/* <x> <y> <width> <height> rectclip - */
+/* <numarray|numstring> rectclip - */
+private int
+zrectclip(os_ptr op)
+{ local_rects lr;
+ int npop = rect_get(&lr, op);
+ int code;
+ if ( npop < 0 ) return npop;
+ code = gs_rectclip(igs, lr.pr, lr.count);
+ rect_release(&lr);
+ if ( code < 0 ) return code;
+ pop(npop);
+ return 0;
+}
+
+/* <x> <y> <width> <height> rectfill - */
+/* <numarray|numstring> rectfill - */
+private int
+zrectfill(os_ptr op)
+{ local_rects lr;
+ int npop = rect_get(&lr, op);
+ int code;
+ if ( npop < 0 ) return npop;
+ code = gs_rectfill(igs, lr.pr, lr.count);
+ rect_release(&lr);
+ if ( code < 0 ) return code;
+ pop(npop);
+ return 0;
+}
+
+/* <x> <y> <width> <height> rectstroke - */
+/* <numarray|numstring> rectstroke - */
+private int
+zrectstroke(os_ptr op)
+{ gs_matrix mat;
+ local_rects lr;
+ int npop, code;
+ if ( read_matrix(op, &mat) >= 0 )
+ { /* Concatenate the matrix to the CTM just before */
+ /* stroking the path. */
+ npop = rect_get(&lr, op - 1);
+ if ( npop < 0 ) return npop;
+ code = gs_rectstroke(igs, lr.pr, lr.count, &mat);
+ npop++;
+ }
+ else
+ { /* No matrix. */
+ npop = rect_get(&lr, op);
+ if ( npop < 0 ) return npop;
+ code = gs_rectstroke(igs, lr.pr, lr.count, (gs_matrix *)0);
+ }
+ rect_release(&lr);
+ if ( code < 0 ) return code;
+ pop(npop);
+ return 0;
+}
+
+/* --- Internal routines --- */
+
+/* Get rectangles from the stack. */
+/* Return the number of elements to pop (>0) if OK, <0 if error. */
+private int
+rect_get(local_rects *plr, os_ptr op)
+{ const ref *opp;
+ ref sref;
+ int format, code, npop;
+ uint n, count;
+ gs_rect *pr;
+
+ switch ( r_type(op) )
+ {
+ case t_array:
+ case t_mixedarray:
+ case t_shortarray:
+ case t_string:
+ code = num_array_format(op);
+ if ( code < 0 )
+ return code;
+ format = code;
+ count = num_array_size(op, format);
+ if ( count % 4 )
+ return_error(e_rangecheck);
+ count /= 4, npop = 1;
+ opp = op;
+ break;
+ default: /* better be 4 numbers */
+ make_array(&sref, a_readonly, 4, op - 3);
+ format = num_array;
+ count = 1, npop = 4;
+ opp = &sref;
+ break;
+ }
+ plr->count = count;
+ if ( count <= max_local_rect )
+ pr = plr->rl;
+ else
+ { pr = (gs_rect *)ialloc_byte_array(count, sizeof(gs_rect),
+ "rect_get");
+ if ( pr == 0 )
+ return_error(e_VMerror);
+ }
+ plr->pr = pr;
+ for ( n = 0; n < count; n++, pr++ )
+ { ref rnum;
+ float rv[4];
+ int i;
+
+ for ( i = 0; i < 4; i++ )
+ { code = num_array_get(opp, format, (n << 2) + i, &rnum);
+ switch ( code )
+ {
+ case t_integer:
+ rv[i] = rnum.value.intval;
+ break;
+ case t_real:
+ rv[i] = rnum.value.realval;
+ break;
+ default: /* code < 0 */
+ return code;
+ }
+ }
+ pr->q.x = (pr->p.x = rv[0]) + rv[2];
+ pr->q.y = (pr->p.y = rv[1]) + rv[3];
+ }
+ return npop;
+}
+
+/* Release the rectangle list if needed. */
+private void
+rect_release(local_rects *plr)
+{ if ( plr->pr != plr->rl )
+ ifree_object(plr->pr, "rect_release");
+}
+
+/* ------ Graphics state ------ */
+
+/* <llx> <lly> <urx> <ury> setbbox - */
+int
+zsetbbox(register os_ptr op)
+{ float box[4];
+ int code = num_params(op, 4, box);
+ if ( code < 0 ) return code;
+ if ( (code = gs_setbbox(igs, box[0], box[1], box[2], box[3])) < 0 )
+ return code;
+ pop(4);
+ return 0;
+}
+
+/* ------ Initialization procedure ------ */
+
+BEGIN_OP_DEFS(zdps1_l2_op_defs) {
+ op_def_begin_level2(),
+ /* Graphics state */
+ {"0currenthalftonephase", zcurrenthalftonephase},
+ {"0currentstrokeadjust", zcurrentstrokeadjust},
+ {"2sethalftonephase", zsethalftonephase},
+ {"1setstrokeadjust", zsetstrokeadjust},
+ /* Graphics state objects */
+ {"1currentgstate", zcurrentgstate},
+ {"0gstate", zgstate},
+ {"1setgstate", zsetgstate},
+ /* Rectangles */
+ {"1.rectappend", zrectappend},
+ {"1rectclip", zrectclip},
+ {"1rectfill", zrectfill},
+ {"1rectstroke", zrectstroke},
+ /* Graphics state components */
+ {"4setbbox", zsetbbox},
+END_OP_DEFS(zdps1_init) }
+
+/* ------ Internal routines ------ */
+
+/* Ensure that a gstate is not shared with an outer save level. */
+/* *op is of type t_astruct(igstate_obj). */
+private int
+gstate_unshare(os_ptr op)
+{ ref *pgsref = &r_ptr(op, igstate_obj)->gstate;
+ gs_state *pgs = r_ptr(pgsref, gs_state);
+ gs_state *pnew;
+ int_gstate *isp;
+ if ( !ref_must_save(pgsref) )
+ return 0;
+ /* Copy the gstate. */
+ pnew = gs_gstate(pgs);
+ if ( pnew == 0 )
+ return_error(e_VMerror);
+ isp = gs_int_gstate(pnew);
+ int_gstate_map_refs(isp, ref_mark_new);
+ ref_do_save(op, pgsref, "gstate_unshare");
+ make_istruct_new(pgsref, 0, pnew);
+ return 0;
+}
diff --git a/pstoraster/zfbcp.c b/pstoraster/zfbcp.c
new file mode 100644
index 000000000..e9e65eec0
--- /dev/null
+++ b/pstoraster/zfbcp.c
@@ -0,0 +1,91 @@
+/* Copyright (C) 1994, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* zfbcp.c */
+/* (T)BCP filter creation */
+#include "memory_.h"
+#include "ghost.h"
+#include "errors.h"
+#include "oper.h"
+#include "gsstruct.h"
+#include "ialloc.h"
+#include "stream.h"
+#include "strimpl.h"
+#include "sfilter.h"
+#include "ifilter.h"
+
+/* Define null handlers for the BCP out-of-band signals. */
+private int
+no_bcp_signal_interrupt(stream_state *st)
+{ return 0;
+}
+private int
+no_bcp_request_status(stream_state *st)
+{ return 0;
+}
+
+/* <source> BCPEncode/filter <file> */
+/* <source> <dict_ignored> BCPEncode/filter <file> */
+private int
+zBCPE(os_ptr op)
+{ return filter_write_simple(op, &s_BCPE_template);
+}
+
+/* <target> BCPDecode/filter <file> */
+/* <target> <dict_ignored> BCPDecode/filter <file> */
+private int
+zBCPD(os_ptr op)
+{ stream_BCPD_state state;
+ state.signal_interrupt = no_bcp_signal_interrupt;
+ state.request_status = no_bcp_request_status;
+ return filter_read(op, (r_has_type(op, t_dictionary) ? 1 : 0),
+ &s_BCPD_template, (stream_state *)&state, 0);
+}
+
+/* <source> TBCPEncode/filter <file> */
+/* <source> <dict_ignored> TBCPEncode/filter <file> */
+private int
+zTBCPE(os_ptr op)
+{ return filter_write_simple(op, &s_TBCPE_template);
+}
+
+/* <target> TBCPDecode/filter <file> */
+/* <target> <dict_ignored> TBCPDecode/filter <file> */
+private int
+zTBCPD(os_ptr op)
+{ stream_BCPD_state state;
+ state.signal_interrupt = no_bcp_signal_interrupt;
+ state.request_status = no_bcp_request_status;
+ return filter_read(op, (r_has_type(op, t_dictionary) ? 1 : 0),
+ &s_TBCPD_template, (stream_state *)&state, 0);
+}
+
+/* ------ Initialization procedure ------ */
+
+BEGIN_OP_DEFS(zfbcp_op_defs) {
+ op_def_begin_filter(),
+ {"1BCPEncode", zBCPE},
+ {"1BCPDecode", zBCPD},
+ {"1TBCPEncode", zTBCPE},
+ {"1TBCPDecode", zTBCPD},
+END_OP_DEFS(0) }
diff --git a/pstoraster/zfdctc.c b/pstoraster/zfdctc.c
new file mode 100644
index 000000000..c1c92bb04
--- /dev/null
+++ b/pstoraster/zfdctc.c
@@ -0,0 +1,372 @@
+/* Copyright (C) 1994 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+#include <config.h>
+#ifdef HAVE_LIBJPEG
+
+/* zfdctc.c */
+/* Common code for DCT filter creation */
+#include "memory_.h"
+#include "stdio_.h" /* for jpeglib.h */
+#include "jpeglib.h"
+#include "ghost.h"
+#include "errors.h"
+#include "opcheck.h"
+#include "idict.h"
+#include "idparam.h"
+#include "imemory.h" /* for iutil.h */
+#include "ipacked.h"
+#include "iutil.h"
+#include "strimpl.h"
+#include "sdct.h"
+#include "sjpeg.h"
+
+/* Forward references */
+private int quant_params(P4(const ref *, int, UINT16 *, floatp));
+int zfdct_byte_params(P4(const ref *, int, int, UINT8 *));
+
+/* Common setup for encoding and decoding filters. */
+int
+zfdct_setup_quantization_tables(const ref *op, stream_DCT_state *pdct,
+ bool is_encode)
+{ int code;
+ int i, j;
+ ref *pdval;
+ const ref *pa;
+ const ref *QuantArrays[NUM_QUANT_TBLS]; /* for detecting duplicates */
+ int num_in_tables;
+ int num_out_tables;
+ jpeg_component_info * comp_info;
+ JQUANT_TBL ** table_ptrs;
+ JQUANT_TBL * this_table;
+
+ if ( op == 0 || dict_find_string(op, "QuantTables", &pdval) <= 0 )
+ return 0;
+ if ( !r_has_type(pdval, t_array) )
+ return_error(e_typecheck);
+ if ( is_encode )
+ { num_in_tables = pdct->data.compress->cinfo.num_components;
+ if ( r_size(pdval) < num_in_tables )
+ return_error(e_rangecheck);
+ comp_info = pdct->data.compress->cinfo.comp_info;
+ table_ptrs = pdct->data.compress->cinfo.quant_tbl_ptrs;
+ }
+ else
+ { num_in_tables = r_size(pdval);
+ comp_info = NULL; /* do not set for decompress case */
+ table_ptrs = pdct->data.decompress->dinfo.quant_tbl_ptrs;
+ }
+ num_out_tables = 0;
+ for ( i = 0, pa = pdval->value.const_refs;
+ i < num_in_tables; i++, pa++
+ )
+ { for ( j = 0; j < num_out_tables; j++ )
+ { if ( obj_eq(pa, QuantArrays[j]) )
+ break;
+ }
+ if ( comp_info != NULL )
+ comp_info[i].quant_tbl_no = j;
+ if ( j < num_out_tables )
+ continue;
+ if ( ++num_out_tables > NUM_QUANT_TBLS )
+ return_error(e_rangecheck);
+ QuantArrays[j] = pa;
+ this_table = table_ptrs[j];
+ if ( this_table == NULL )
+ { this_table = gs_jpeg_alloc_quant_table(pdct);
+ if ( this_table == NULL )
+ return_error(e_VMerror);
+ table_ptrs[j] = this_table;
+ }
+ if ( r_size(pa) != DCTSIZE2 )
+ return_error(e_rangecheck);
+ code = quant_params(pa, DCTSIZE2,
+ this_table->quantval, pdct->QFactor);
+ if ( code < 0 )
+ return code;
+ }
+ return 0;
+}
+
+int
+zfdct_setup_huffman_tables(const ref *op, stream_DCT_state *pdct,
+ bool is_encode)
+{ int code;
+ int i, j;
+ ref *pdval;
+ const ref *pa;
+ const ref *DCArrays[NUM_HUFF_TBLS]; /* for detecting duplicates */
+ const ref *ACArrays[NUM_HUFF_TBLS];
+ int num_in_tables;
+ int ndc, nac;
+ int codes_size;
+ jpeg_component_info * comp_info;
+ JHUFF_TBL ** dc_table_ptrs;
+ JHUFF_TBL ** ac_table_ptrs;
+ JHUFF_TBL ** this_table_ptr;
+ JHUFF_TBL * this_table;
+ int max_tables = 2; /* baseline limit */
+
+ if ( op == 0 ) /* no dictionary */
+ return 0;
+ if ( (code = dict_find_string(op, "HuffTables", &pdval)) <= 0)
+ return 0;
+ if ( !r_has_type(pdval, t_array) )
+ return_error(e_typecheck);
+ if ( is_encode )
+ { num_in_tables = pdct->data.compress->cinfo.input_components * 2;
+ if ( r_size(pdval) < num_in_tables )
+ return_error(e_rangecheck);
+ comp_info = pdct->data.compress->cinfo.comp_info;
+ dc_table_ptrs = pdct->data.compress->cinfo.dc_huff_tbl_ptrs;
+ ac_table_ptrs = pdct->data.compress->cinfo.ac_huff_tbl_ptrs;
+ if ( pdct->data.common->Relax )
+ max_tables = max(pdct->data.compress->cinfo.input_components, 2);
+ }
+ else
+ { num_in_tables = r_size(pdval);
+ comp_info = NULL; /* do not set for decompress case */
+ dc_table_ptrs = pdct->data.decompress->dinfo.dc_huff_tbl_ptrs;
+ ac_table_ptrs = pdct->data.decompress->dinfo.ac_huff_tbl_ptrs;
+ if ( pdct->data.common->Relax )
+ max_tables = NUM_HUFF_TBLS;
+ }
+ ndc = nac = 0;
+ for ( i = 0, pa = pdval->value.const_refs;
+ i < num_in_tables; i++, pa++
+ )
+ { if ( i & 1 )
+ { for ( j = 0; j < nac; j++ )
+ { if ( obj_eq(pa, ACArrays[j]) )
+ break;
+ }
+ if ( comp_info != NULL )
+ comp_info[i>>1].ac_tbl_no = j;
+ if ( j < nac )
+ continue;
+ if ( ++nac > NUM_HUFF_TBLS )
+ return_error(e_rangecheck);
+ ACArrays[j] = pa;
+ this_table_ptr = ac_table_ptrs + j;
+ }
+ else
+ { for ( j = 0; j < ndc; j++ )
+ { if ( obj_eq(pa, DCArrays[j]) )
+ break;
+ }
+ if ( comp_info != NULL )
+ comp_info[i>>1].dc_tbl_no = j;
+ if ( j < ndc )
+ continue;
+ if ( ++ndc > NUM_HUFF_TBLS )
+ return_error(e_rangecheck);
+ DCArrays[j] = pa;
+ this_table_ptr = dc_table_ptrs + j;
+ }
+ this_table = *this_table_ptr;
+ if ( this_table == NULL )
+ { this_table = gs_jpeg_alloc_huff_table(pdct);
+ if ( this_table == NULL )
+ return_error(e_VMerror);
+ *this_table_ptr = this_table;
+ }
+ if ( r_size(pa) < 16 )
+ return_error(e_rangecheck);
+ code = zfdct_byte_params(pa, 0, 16, this_table->bits + 1);
+ if ( code < 0 )
+ return code;
+ for ( codes_size = 0, j = 1; j <= 16; j++ )
+ codes_size += this_table->bits[j];
+ if ( codes_size > 256 || r_size(pa) != codes_size+16 )
+ return_error(e_rangecheck);
+ code = zfdct_byte_params(pa, 16, codes_size, this_table->huffval);
+ if ( code < 0 )
+ return code;
+ }
+ if ( nac > max_tables || ndc > max_tables )
+ return_error(e_rangecheck);
+ return 0;
+}
+
+/* The main procedure */
+int
+zfdct_setup(const ref *op, stream_DCT_state *pdct)
+{ const ref *dop;
+ int npop;
+ int code;
+
+ /* Initialize the state in case we bail out. */
+ pdct->Markers.data = 0;
+ pdct->Markers.size = 0;
+ if ( !r_has_type(op, t_dictionary) )
+ { npop = 0;
+ dop = 0;
+ }
+ else
+ { check_dict_read(*op);
+ npop = 1;
+ dop = op;
+ }
+ /* These parameters are common to both, and are all defaultable. */
+ if ( (code = dict_int_param(dop, "Picky", 0, 1, 0,
+ &pdct->data.common->Picky)) < 0 ||
+ (code = dict_int_param(dop, "Relax", 0, 1, 0,
+ &pdct->data.common->Relax)) < 0 ||
+ (code = dict_int_param(dop, "ColorTransform", -1, 2, -1,
+ &pdct->ColorTransform)) < 0 ||
+ (code = dict_float_param(dop, "QFactor", 1.0,
+ &pdct->QFactor)) < 0
+ )
+ return code;
+ if ( pdct->QFactor < 0.0 || pdct->QFactor > 1000000.0 )
+ return_error(e_rangecheck);
+ return npop;
+}
+
+/* ------ Internal routines ------ */
+
+/* Get N quantization values from an array or a string. */
+
+private int
+quant_params(const ref *op, int count, UINT16 *pvals, floatp QFactor)
+{ int i;
+ const ref_packed *pref;
+ double val;
+ /* Adobe specifies the values to be supplied in zigzag order.
+ * For IJG versions newer than v6, we need to convert this order
+ * to natural array order. Older IJG versions want zigzag order.
+ */
+#if JPEG_LIB_VERSION >= 61
+ /* natural array position of n'th element of JPEG zigzag order */
+ static const int natural_order[DCTSIZE2] = {
+ 0, 1, 8, 16, 9, 2, 3, 10,
+ 17, 24, 32, 25, 18, 11, 4, 5,
+ 12, 19, 26, 33, 40, 48, 41, 34,
+ 27, 20, 13, 6, 7, 14, 21, 28,
+ 35, 42, 49, 56, 57, 50, 43, 36,
+ 29, 22, 15, 23, 30, 37, 44, 51,
+ 58, 59, 52, 45, 38, 31, 39, 46,
+ 53, 60, 61, 54, 47, 55, 62, 63
+ };
+#define jpeg_order(x) natural_order[x]
+#else
+#define jpeg_order(x) (x)
+#endif
+ switch ( r_type(op) )
+ {
+ case t_string:
+ check_read(*op);
+ for ( i = 0; i < count; i++ )
+ {
+ val = op->value.const_bytes[i] * QFactor;
+ if ( val < 1 ) val = 1;
+ if ( val > 255 ) val = 255;
+ pvals[jpeg_order(i)] = (UINT16) (val + 0.5);
+ }
+ return 0;
+ case t_array:
+ check_read(*op);
+ pref = (const ref_packed *)op->value.const_refs;
+ break;
+ case t_shortarray:
+ case t_mixedarray:
+ check_read(*op);
+ pref = op->value.packed;
+ break;
+ default:
+ return_error(e_typecheck);
+ }
+ for ( i = 0; i < count; pref = packed_next(pref), i++ )
+ { ref nref;
+ packed_get(pref, &nref);
+ switch ( r_type(&nref) )
+ {
+ case t_integer:
+ val = nref.value.intval * QFactor;
+ break;
+ case t_real:
+ val = nref.value.realval * QFactor;
+ break;
+ default:
+ return_error(e_typecheck);
+ }
+ if ( val < 1 ) val = 1;
+ if ( val > 255 ) val = 255;
+ pvals[jpeg_order(i)] = (UINT16) (val + 0.5);
+ }
+ return 0;
+#undef jpeg_order
+}
+
+/* Get N byte-size values from an array or a string.
+ * Used for HuffTables, HSamples, VSamples.
+ */
+int
+zfdct_byte_params(const ref *op, int start, int count, UINT8 *pvals)
+{ int i;
+ const ref_packed *pref;
+ UINT8 *pval;
+ switch ( r_type(op) )
+ {
+ case t_string:
+ check_read(*op);
+ for ( i = 0, pval = pvals; i < count; i++, pval++ )
+ *pval = (UINT8)op->value.const_bytes[start+i];
+ return 0;
+ case t_array:
+ check_read(*op);
+ pref = (const ref_packed *)(op->value.const_refs + start);
+ break;
+ case t_shortarray:
+ case t_mixedarray:
+ check_read(*op);
+ pref = op->value.packed;
+ for ( i = 0; i < start; i++ )
+ pref = packed_next(pref);
+ break;
+ default:
+ return_error(e_typecheck);
+ }
+ for ( i = 0, pval = pvals; i < count;
+ pref = packed_next(pref), i++, pval++
+ )
+ { ref nref;
+ packed_get(pref, &nref);
+ switch ( r_type(&nref) )
+ {
+ case t_integer:
+ if ( nref.value.intval < 0 || nref.value.intval > 255 )
+ return_error(e_rangecheck);
+ *pval = (UINT8)nref.value.intval;
+ break;
+ case t_real:
+ if ( nref.value.realval < 0 || nref.value.realval > 255 )
+ return_error(e_rangecheck);
+ *pval = (UINT8)(nref.value.realval + 0.5);
+ break;
+ default:
+ return_error(e_typecheck);
+ }
+ }
+ return 0;
+}
+#endif /* HAVE_LIBJPEG */
diff --git a/pstoraster/zfdctd.c b/pstoraster/zfdctd.c
new file mode 100644
index 000000000..30676e12d
--- /dev/null
+++ b/pstoraster/zfdctd.c
@@ -0,0 +1,103 @@
+/* Copyright (C) 1994 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+#include <config.h>
+#ifdef HAVE_LIBJPEG
+
+/* zfdctd.c */
+/* DCTDecode filter creation */
+#include "memory_.h"
+#include "stdio_.h" /* for jpeglib.h */
+#include "jpeglib.h"
+#include "ghost.h"
+#include "errors.h"
+#include "oper.h"
+#include "strimpl.h"
+#include "sdct.h"
+#include "sjpeg.h"
+#include "ifilter.h"
+
+/* Import the common setup routines from zfdctc.c */
+int zfdct_setup(P2(const ref *op, stream_DCT_state *pdct));
+int zfdct_setup_quantization_tables(P3(const ref *op, stream_DCT_state *pdct,
+ bool is_encode));
+int zfdct_setup_huffman_tables(P3(const ref *op, stream_DCT_state *pdct,
+ bool is_encode));
+
+/* <source> <dict> DCTDecode/filter <file> */
+/* <source> DCTDecode/filter <file> */
+private int
+zDCTD(os_ptr op)
+{ stream_DCT_state state;
+ jpeg_decompress_data *jddp;
+ int code;
+ int npop;
+ const ref *dop;
+ uint dspace;
+
+ /* First allocate space for IJG parameters. */
+ jddp = gs_malloc(1, sizeof(*jddp), "zDCTD");
+ if ( jddp == 0 )
+ return_error(e_VMerror);
+ state.data.decompress = jddp;
+ jddp->scanline_buffer = NULL; /* set this early for safe error exit */
+ if ( (code = gs_jpeg_create_decompress(&state)) < 0 )
+ goto fail; /* correct to do jpeg_destroy here */
+ /* Read parameters from dictionary */
+ if ( (code = zfdct_setup(op, &state)) < 0 )
+ goto fail;
+ npop = code;
+ if ( npop == 0 )
+ dop = 0, dspace = 0;
+ else
+ dop = op, dspace = r_space(op);
+ /* DCTDecode accepts quantization and huffman tables
+ * in case these tables have been omitted from the datastream.
+ */
+ if ( (code = zfdct_setup_huffman_tables(dop, &state, false)) < 0 ||
+ (code = zfdct_setup_quantization_tables(dop, &state, false)) < 0
+ )
+ goto fail;
+ /* Create the filter. */
+ jddp->template = s_DCTD_template;
+ code = filter_read(op, npop, &jddp->template,
+ (stream_state *)&state, dspace);
+ if ( code >= 0 ) /* Success! */
+ return code;
+ /* We assume that if filter_read fails, the stream has not been
+ * registered for closing, so s_DCTD_release will never be called.
+ * Therefore we free the allocated memory before failing.
+ */
+
+fail:
+ gs_jpeg_destroy(&state);
+ gs_free(jddp, 1, sizeof(*jddp), "zDCTD fail");
+ return code;
+}
+
+/* ------ Initialization procedure ------ */
+
+BEGIN_OP_DEFS(zfdctd_op_defs) {
+ op_def_begin_filter(),
+ {"2DCTDecode", zDCTD},
+END_OP_DEFS(0) }
+#endif /* HAVE_LIBJPEG */
diff --git a/pstoraster/zfdcte.c b/pstoraster/zfdcte.c
new file mode 100644
index 000000000..dac5e8925
--- /dev/null
+++ b/pstoraster/zfdcte.c
@@ -0,0 +1,270 @@
+/* Copyright (C) 1994 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+#include <config.h>
+#ifdef HAVE_LIBJPEG
+
+/* zfdcte.c */
+/* DCTEncode filter creation */
+#include "memory_.h"
+#include "stdio_.h" /* for jpeglib.h */
+#include "jpeglib.h"
+#include "ghost.h"
+#include "errors.h"
+#include "oper.h"
+#include "idict.h"
+#include "idparam.h"
+#include "strimpl.h"
+#include "sdct.h"
+#include "sjpeg.h"
+#include "ifilter.h"
+
+/* Import the common setup routines from zfdctc.c */
+int zfdct_setup(P2(const ref *op, stream_DCT_state *pdct));
+int zfdct_setup_quantization_tables(P3(const ref *op, stream_DCT_state *pdct,
+ bool is_encode));
+int zfdct_setup_huffman_tables(P3(const ref *op, stream_DCT_state *pdct,
+ bool is_encode));
+int zfdct_byte_params(P4(const ref *op, int start, int count, UINT8 *pvals));
+
+/* Collect encode-only parameters. */
+private int
+dct_setup_samples(const ref *op, const char _ds *kstr, int num_colors,
+ jpeg_compress_data *jcdp, bool is_vert)
+{ int code;
+ int i;
+ ref *pdval;
+ jpeg_component_info * comp_info = jcdp->cinfo.comp_info;
+ UINT8 samples[4];
+ /* Adobe default is all sampling factors = 1,
+ * which is NOT the IJG default, so we must always assign values.
+ */
+ if ( op != 0 && dict_find_string(op, kstr, &pdval) > 0 )
+ { if ( r_size(pdval) < num_colors )
+ return_error(e_rangecheck);
+ if ( (code = zfdct_byte_params(pdval, 0, num_colors, samples)) < 0 )
+ return code;
+ }
+ else
+ { samples[0] = samples[1] = samples[2] = samples[3] = 1;
+ }
+ for ( i = 0; i < num_colors; i++ )
+ { if ( samples[i] < 1 || samples[i] > 4 )
+ return_error(e_rangecheck);
+ if ( is_vert )
+ comp_info[i].v_samp_factor = samples[i];
+ else
+ comp_info[i].h_samp_factor = samples[i];
+ }
+ return 0;
+}
+
+private int
+zfdcte_setup(const ref *op, stream_DCT_state *pdct)
+{ jpeg_compress_data *jcdp = pdct->data.compress;
+ uint Columns, Rows, Resync;
+ int num_colors;
+ int Blend;
+ ref *mstr;
+ int i;
+ int code;
+
+ /* Required parameters for DCTEncode.
+ * (DCTDecode gets the equivalent info from the SOF marker.)
+ */
+ if ( (code = dict_uint_param(op, "Columns", 1, 0xffff, 0,
+ &Columns)) < 0 ||
+ (code = dict_uint_param(op, "Rows", 1, 0xffff, 0,
+ &Rows)) < 0 ||
+ (code = dict_int_param(op, "Colors", 1, 4, -1,
+ &num_colors)) < 0
+ )
+ return code;
+ /* Set up minimal image description & call set_defaults */
+ jcdp->cinfo.image_width = Columns;
+ jcdp->cinfo.image_height = Rows;
+ jcdp->cinfo.input_components = num_colors;
+ switch ( num_colors )
+ {
+ case 1:
+ jcdp->cinfo.in_color_space = JCS_GRAYSCALE;
+ break;
+ case 3:
+ jcdp->cinfo.in_color_space = JCS_RGB;
+ break;
+ case 4:
+ jcdp->cinfo.in_color_space = JCS_CMYK;
+ break;
+ default:
+ jcdp->cinfo.in_color_space = JCS_UNKNOWN;
+ }
+ if ( (code = gs_jpeg_set_defaults(pdct)) < 0 )
+ return code;
+ /* Change IJG colorspace defaults as needed;
+ * set ColorTransform to what will go in the Adobe marker.
+ */
+ switch ( num_colors )
+ {
+ case 3:
+ if ( pdct->ColorTransform < 0 )
+ pdct->ColorTransform = 1; /* default */
+ if ( pdct->ColorTransform == 0 )
+ {
+ if ( (code = gs_jpeg_set_colorspace(pdct, JCS_RGB)) < 0 )
+ return code;
+ }
+ else
+ pdct->ColorTransform = 1; /* flag YCC xform */
+ break;
+ case 4:
+ if ( pdct->ColorTransform < 0 )
+ pdct->ColorTransform = 0; /* default */
+ if ( pdct->ColorTransform != 0 )
+ { if ( (code = gs_jpeg_set_colorspace(pdct, JCS_YCCK)) < 0 )
+ return code;
+ pdct->ColorTransform = 2; /* flag YCCK xform */
+ }
+ else
+ { if ( (code = gs_jpeg_set_colorspace(pdct, JCS_CMYK)) < 0 )
+ return code;
+ }
+ break;
+ default:
+ pdct->ColorTransform = 0; /* no transform otherwise */
+ break;
+ }
+ /* Optional encoding-only parameters */
+ if ( dict_find_string(op, "Markers", &mstr) > 0 )
+ { check_read_type(*mstr, t_string);
+ pdct->Markers.data = mstr->value.const_bytes;
+ pdct->Markers.size = r_size(mstr);
+ }
+ if ( (code = dict_bool_param(op, "NoMarker", false,
+ &pdct->NoMarker)) < 0 ||
+ (code = dict_uint_param(op, "Resync", 0, 0xffff, 0,
+ &Resync)) < 0 ||
+ (code = dict_int_param(op, "Blend", 0, 1, 0,
+ &Blend)) < 0 ||
+ (code = dct_setup_samples(op, "HSamples", num_colors,
+ jcdp, false)) < 0 ||
+ (code = dct_setup_samples(op, "VSamples", num_colors,
+ jcdp, true)) < 0
+ )
+ return code;
+ jcdp->cinfo.write_JFIF_header = FALSE;
+ jcdp->cinfo.write_Adobe_marker = FALSE; /* must do it myself */
+ jcdp->cinfo.restart_interval = Resync;
+ /* What to do with Blend ??? */
+ if ( pdct->data.common->Relax == 0 )
+ { jpeg_component_info *comp_info = jcdp->cinfo.comp_info;
+ int num_samples;
+ for ( i = 0, num_samples = 0; i < num_colors; i++ )
+ num_samples += comp_info[i].h_samp_factor *
+ comp_info[i].v_samp_factor;
+ if ( num_samples > 10 )
+ return_error(e_rangecheck);
+ /* Note: by default the IJG software does not allow
+ * num_samples to exceed 10, Relax or no. For full
+ * compatibility with Adobe's non-JPEG-compliant
+ * software, set MAX_BLOCKS_IN_MCU to 64 in jpeglib.h.
+ */
+ }
+ return 0;
+}
+
+/* <target> <dict> DCTEncode/filter <file> */
+private int
+zDCTE(os_ptr op)
+{ stream_DCT_state state;
+ jpeg_compress_data *jcdp;
+ int code;
+ int npop;
+ const ref *dop;
+ uint dspace;
+ ref *pdval;
+
+ /* First allocate space for IJG parameters. */
+ jcdp = gs_malloc(1, sizeof(*jcdp), "zDCTE");
+ if ( jcdp == 0 )
+ return_error(e_VMerror);
+ state.data.compress = jcdp;
+ if ( (code = gs_jpeg_create_compress(&state)) < 0 )
+ goto fail; /* correct to do jpeg_destroy here */
+ /* Read parameters from dictionary */
+ if ( (code = zfdct_setup(op, &state)) < 0 )
+ goto fail;
+ npop = code;
+ if ( npop == 0 )
+ dop = 0, dspace = 0;
+ else
+ dop = op, dspace = r_space(op);
+ if ( (code = zfdcte_setup(dop, &state)) < 0 )
+ goto fail;
+ /* Check for QFactor without QuantTables. */
+ if ( dop == 0 || dict_find_string(dop, "QuantTables", &pdval) <= 0 )
+ { /* No QuantTables, but maybe a QFactor to apply to default. */
+ if ( state.QFactor != 1.0 )
+ { code = gs_jpeg_set_linear_quality(&state,
+ (int) (min(state.QFactor, 100.0)
+ * 100.0 + 0.5),
+ TRUE);
+ if ( code < 0 )
+ return code;
+ }
+
+ }
+ if ( (code = zfdct_setup_huffman_tables(dop, &state, true)) < 0 ||
+ (code = zfdct_setup_quantization_tables(dop, &state, true)) < 0
+ )
+ goto fail;
+ /* Create the filter. */
+ jcdp->template = s_DCTE_template;
+ /* 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->template.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->template.min_out_size =
+ max(s_DCTE_template.min_out_size, state.Markers.size);
+ code = filter_write(op, npop, &jcdp->template,
+ (stream_state *)&state, dspace);
+ if ( code >= 0 ) /* Success! */
+ return code;
+ /* We assume that if filter_write fails, the stream has not been
+ * registered for closing, so s_DCTE_release will never be called.
+ * Therefore we free the allocated memory before failing.
+ */
+
+fail:
+ gs_jpeg_destroy(&state);
+ gs_free(jcdp, 1, sizeof(*jcdp), "zDCTE fail");
+ return code;
+}
+
+/* ------ Initialization procedure ------ */
+
+BEGIN_OP_DEFS(zfdcte_op_defs) {
+ op_def_begin_filter(),
+ {"2DCTEncode", zDCTE},
+END_OP_DEFS(0) }
+#endif /* HAVE_LIBJPEG */
diff --git a/pstoraster/zfdecode.c b/pstoraster/zfdecode.c
new file mode 100644
index 000000000..f506fa286
--- /dev/null
+++ b/pstoraster/zfdecode.c
@@ -0,0 +1,350 @@
+/* Copyright (C) 1994, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* zfdecode.c */
+/* Additional decoding filter creation */
+#include "memory_.h"
+#include "ghost.h"
+#include "errors.h"
+#include "oper.h"
+#include "gsstruct.h"
+#include "ialloc.h"
+#include "idict.h"
+#include "idparam.h"
+#include "store.h"
+#include "stream.h" /* for setting is_temp */
+#include "strimpl.h"
+#include "sfilter.h"
+#include "sa85x.h"
+#include "scfx.h"
+#include "scf.h"
+#include "slzwx.h"
+#include "spdiffx.h"
+#include "spngpx.h"
+#include "ifilter.h"
+
+/* Import the Level 2 scanner extensions. */
+extern const stream_template _ds *scan_ascii85_template;
+
+/* Initialize the Level 2 scanner for ASCII85 strings. */
+private void
+zfdecode_init(void)
+{ scan_ascii85_template = &s_A85D_template;
+}
+
+/* ------ ASCII85 filters ------ */
+
+/* We include both encoding and decoding filters here, */
+/* because it would be a nuisance to separate them. */
+
+/* <target> ASCII85Encode/filter <file> */
+/* <target> <dict_ignored> ASCII85Encode/filter <file> */
+private int
+zA85E(os_ptr op)
+{ return filter_write_simple(op, &s_A85E_template);
+}
+
+/* <source> ASCII85Decode/filter <file> */
+/* <source> <dict_ignored> ASCII85Decode/filter <file> */
+private int
+zA85D(os_ptr op)
+{ return filter_read_simple(op, &s_A85D_template);
+}
+
+/* ------ CCITTFaxDecode filter ------ */
+
+/* Define a limit on the Rows parameter, close to max_int. */
+#define cf_max_height 32000
+
+/* Common setup for encoding and decoding filters. */
+int
+zcf_setup(os_ptr op, stream_CF_state *pcfs)
+{ int code;
+ if ( (code = dict_bool_param(op, "Uncompressed", false,
+ &pcfs->Uncompressed)) < 0 ||
+ (code = dict_int_param(op, "K", -cf_max_height, cf_max_height, 0,
+ &pcfs->K)) < 0 ||
+ (code = dict_bool_param(op, "EndOfLine", false,
+ &pcfs->EndOfLine)) < 0 ||
+ (code = dict_bool_param(op, "EncodedByteAlign", false,
+ &pcfs->EncodedByteAlign)) < 0 ||
+ (code = dict_int_param(op, "Columns", 0, cfe_max_width, 1728,
+ &pcfs->Columns)) < 0 ||
+ (code = dict_int_param(op, "Rows", 0, cf_max_height, 0,
+ &pcfs->Rows)) < 0 ||
+ (code = dict_bool_param(op, "EndOfBlock", true,
+ &pcfs->EndOfBlock)) < 0 ||
+ (code = dict_bool_param(op, "BlackIs1", false,
+ &pcfs->BlackIs1)) < 0 ||
+ (code = dict_int_param(op, "DamagedRowsBeforeError", 0,
+ cf_max_height, 0,
+ &pcfs->DamagedRowsBeforeError)) < 0 ||
+ (code = dict_bool_param(op, "FirstBitLowOrder", false,
+ &pcfs->FirstBitLowOrder)) < 0 ||
+ (code = dict_int_param(op, "DecodedByteAlign", 1, 16, 1,
+ &pcfs->DecodedByteAlign)) < 0
+ )
+ return code;
+ if ( pcfs->DecodedByteAlign & (pcfs->DecodedByteAlign - 1) )
+ return_error(e_rangecheck); /* not a power of 2 */
+ return 0;
+}
+
+/* <source> <dict> CCITTFaxDecode/filter <file> */
+/* <source> CCITTFaxDecode/filter <file> */
+private int
+zCFD(os_ptr op)
+{ os_ptr dop;
+ int npop;
+ stream_CFD_state cfs;
+ int code;
+
+ if ( r_has_type(op, t_dictionary) )
+ { check_dict_read(*op);
+ dop = op, npop = 1;
+ }
+ else
+ dop = 0, npop = 0;
+ code = zcf_setup(dop, (stream_CF_state *)&cfs);
+ if ( code < 0 )
+ return code;
+ return filter_read(op, npop, &s_CFD_template, (stream_state *)&cfs, 0);
+}
+
+/* ------ Common setup for possibly pixel-oriented decoding filters ------ */
+
+/* Forward declarations */
+int zpd_setup(P2(os_ptr op, stream_PDiff_state *ppds));
+int zpp_setup(P2(os_ptr op, stream_PNGP_state *ppps));
+
+int
+filter_read_predictor(os_ptr op, int npop, const stream_template *template,
+ stream_state *st)
+{ int predictor, code;
+ stream_PDiff_state pds;
+ stream_PNGP_state pps;
+
+ if ( r_has_type(op, t_dictionary) )
+ { if ( (code = dict_int_param(op, "Predictor", 0, 15, 1, &predictor)) < 0 )
+ return code;
+ switch ( predictor )
+ {
+ case 0: /* identity */
+ predictor = 1;
+ case 1: /* identity */
+ break;
+ case 2: /* componentwise horizontal differencing */
+ code = zpd_setup(op, &pds);
+ break;
+ case 10: case 11: case 12: case 13: case 14: case 15:
+ /* PNG prediction */
+ code = zpp_setup(op, &pps);
+ break;
+ default:
+ return_error(e_rangecheck);
+ }
+ if ( code < 0 )
+ return code;
+ }
+ else
+ predictor = 1;
+ if ( predictor == 1 )
+ return filter_read(op, npop, template, st, 0);
+ { /* We need to cascade filters. */
+ ref rsource, rdict, rfd;
+ int code;
+
+ /* Save the operands, just in case. */
+ ref_assign(&rsource, op - 1);
+ ref_assign(&rdict, op);
+ code = filter_read(op, 1, template, st, 0);
+ if ( code < 0 )
+ return code;
+ /* filter_read changed osp.... */
+ op = osp;
+ ref_assign(&rfd, op);
+ code =
+ (predictor == 2 ?
+ filter_read(op, 0, &s_PDiffD_template, (stream_state *)&pds, 0) :
+ filter_read(op, 0, &s_PNGPD_template, (stream_state *)&pps, 0));
+ if ( code < 0 )
+ { /* Restore the operands. Don't bother trying to clean up */
+ /* the first stream. */
+ osp = ++op;
+ ref_assign(op - 1, &rsource);
+ ref_assign(op, &rdict);
+ return code;
+ }
+ filter_mark_temp(&rfd, 2); /* Mark the decompression stream as temporary. */
+ return code;
+ }
+}
+
+/* ------ Generalized LZW/GIF decoding filter ------ */
+
+/* Common setup for encoding and decoding filters. */
+int
+zlz_setup(os_ptr op, stream_LZW_state *plzs)
+{ int code;
+ const ref *dop;
+ int npop;
+
+ if ( r_has_type(op, t_dictionary) )
+ { check_dict_read(*op);
+ dop = op, npop = 1;
+ }
+ else
+ dop = 0, npop = 0;
+ if ( /* Following are not PostScript standard */
+ (code = dict_int_param(dop, "EarlyChange", 0, 1, 1,
+ &plzs->EarlyChange)) < 0 ||
+ (code = dict_int_param(dop, "InitialCodeLength", 2, 11, 8,
+ &plzs->InitialCodeLength)) < 0 ||
+ (code = dict_bool_param(dop, "FirstBitLowOrder", false,
+ &plzs->FirstBitLowOrder)) < 0 ||
+ (code = dict_bool_param(dop, "BlockData", false,
+ &plzs->BlockData)) < 0
+ )
+ return code;
+ return npop;
+}
+
+/* <source> LZWDecode/filter <file> */
+/* <source> <dict> LZWDecode/filter <file> */
+private int
+zLZWD(os_ptr op)
+{ stream_LZW_state lzs;
+ int code = zlz_setup(op, &lzs);
+
+ if ( code < 0 )
+ return code;
+ return filter_read_predictor(op, code, &s_LZWD_template,
+ (stream_state *)&lzs);
+}
+
+/* ------ Color differencing filters ------ */
+
+/* We include both encoding and decoding filters here, */
+/* because it would be a nuisance to separate them. */
+
+/* Common setup for encoding and decoding filters. */
+int
+zpd_setup(os_ptr op, stream_PDiff_state *ppds)
+{ int code, bpc;
+
+ check_type(*op, t_dictionary);
+ check_dict_read(*op);
+ if ( (code = dict_int_param(op, "Colors", 1, 4, 1,
+ &ppds->Colors)) < 0 ||
+ (code = dict_int_param(op, "BitsPerComponent", 1, 8, 8,
+ &bpc)) < 0 ||
+ (bpc & (bpc - 1)) != 0 ||
+ (code = dict_int_param(op, "Columns", 1, max_int, 1,
+ &ppds->Columns)) < 0
+ )
+ return (code < 0 ? code : gs_note_error(e_rangecheck));
+ ppds->BitsPerComponent = bpc;
+ return 0;
+}
+
+/* <target> <dict> PixelDifferenceEncode/filter <file> */
+private int
+zPDiffE(os_ptr op)
+{ stream_PDiff_state pds;
+ int code = zpd_setup(op, &pds);
+
+ if ( code < 0 )
+ return code;
+ return filter_write(op, 1, &s_PDiffE_template, (stream_state *)&pds, 0);
+}
+
+/* <source> <dict> PixelDifferenceDecode/filter <file> */
+private int
+zPDiffD(os_ptr op)
+{ stream_PDiff_state pds;
+ int code = zpd_setup(op, &pds);
+
+ if ( code < 0 )
+ return code;
+ return filter_read(op, 1, &s_PDiffD_template, (stream_state *)&pds, 0);
+}
+
+/* ------ PNG pixel predictor filters ------ */
+
+/* Common setup for encoding and decoding filters. */
+int
+zpp_setup(os_ptr op, stream_PNGP_state *ppps)
+{ int code, bpc;
+
+ check_type(*op, t_dictionary);
+ check_dict_read(*op);
+ if ( (code = dict_int_param(op, "Colors", 1, 16, 1,
+ &ppps->Colors)) < 0 ||
+ (code = dict_int_param(op, "BitsPerComponent", 1, 16, 8,
+ &bpc)) < 0 ||
+ (bpc & (bpc - 1)) != 0 ||
+ (code = dict_uint_param(op, "Columns", 1, max_uint, 1,
+ &ppps->Columns)) < 0 ||
+ (code = dict_int_param(op, "Predictor", 10, 15, 15,
+ &ppps->Predictor)) < 0
+ )
+ return (code < 0 ? code : gs_note_error(e_rangecheck));
+ ppps->BitsPerComponent = bpc;
+ return 0;
+}
+
+/* <target> <dict> PNGPredictorEncode/filter <file> */
+private int
+zPNGPE(os_ptr op)
+{ stream_PNGP_state pps;
+ int code = zpp_setup(op, &pps);
+
+ if ( code < 0 )
+ return code;
+ return filter_write(op, 1, &s_PNGPE_template, (stream_state *)&pps, 0);
+}
+
+/* <source> <dict> PNGPredictorDecode/filter <file> */
+private int
+zPNGPD(os_ptr op)
+{ stream_PNGP_state pps;
+ int code = zpp_setup(op, &pps);
+
+ if ( code < 0 )
+ return code;
+ return filter_read(op, 1, &s_PNGPD_template, (stream_state *)&pps, 0);
+}
+
+/* ---------------- Initialization procedure ---------------- */
+
+BEGIN_OP_DEFS(zfdecode_op_defs) {
+ op_def_begin_filter(),
+ {"1ASCII85Encode", zA85E},
+ {"1ASCII85Decode", zA85D},
+ {"2CCITTFaxDecode", zCFD},
+ {"1LZWDecode", zLZWD},
+ {"2PixelDifferenceDecode", zPDiffD},
+ {"2PixelDifferenceEncode", zPDiffE},
+ {"2PNGPredictorDecode", zPNGPD},
+ {"2PNGPredictorEncode", zPNGPE},
+END_OP_DEFS(zfdecode_init) }
diff --git a/pstoraster/zfile.c b/pstoraster/zfile.c
new file mode 100644
index 000000000..138361c11
--- /dev/null
+++ b/pstoraster/zfile.c
@@ -0,0 +1,887 @@
+/* Copyright (C) 1989, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* zfile.c */
+/* Non-I/O file operators */
+#include "memory_.h"
+#include "string_.h"
+#include "ghost.h"
+#include "gp.h"
+#include "gsstruct.h" /* for registering root */
+#include "errors.h"
+#include "oper.h"
+#include "estack.h" /* for filenameforall, .runexec */
+#include "ialloc.h"
+#include "ilevel.h" /* %names only work in Level 2 */
+#include "interp.h" /* gs_errorinfo_put_string prototype */
+#include "isave.h" /* for restore */
+#include "iutil.h"
+#include "stream.h"
+#include "strimpl.h"
+#include "sfilter.h"
+#include "gxiodev.h" /* must come after stream.h */
+ /* and before files.h */
+#include "files.h" /* ditto */
+#include "fname.h" /* ditto */
+#include "main.h" /* for gs_lib_paths */
+#include "store.h"
+
+/* Import the file_open routine for %os%, which is the default. */
+extern iodev_proc_open_file(iodev_os_open_file);
+
+/* Forward references: file opening. */
+int file_open(P6(const byte *, uint, const char *, uint, ref *, stream **));
+
+/* Forward references: other. */
+private int runexec_finish(P1(os_ptr));
+private int runexec_cleanup(P1(os_ptr));
+private stream_proc_report_error(filter_report_error);
+
+/*
+ * Since there can be many file objects referring to the same file/stream,
+ * we can't simply free a stream when we close it. On the other hand,
+ * we don't want freed streams to clutter up memory needlessly.
+ * Our solution is to retain the freed streams, and reuse them.
+ * To prevent an old file object from being able to access a reused stream,
+ * we keep a serial number in each stream, and check it against a serial
+ * number stored in the file object (as the "size"); when we close a file,
+ * we increment its serial number. If the serial number ever overflows,
+ * we leave it at zero, and do not reuse the stream.
+ * (This will never happen.)
+ *
+ * Storage management for this scheme is a little tricky.
+ * We maintain an invariant that says that a stream opened at a given
+ * save level always uses a stream structure allocated at that level.
+ * By doing this, we don't need to keep track separately of streams open
+ * at a level vs. streams allocated at a level. To make this interact
+ * properly with save and restore, we maintain a list of all streams
+ * currently allocated, both open and closed. The save_count member
+ * of a stream indicates the number of unmatched saves at which
+ * the given stream was the head of the list. Thus the streams
+ * allocated at the current level are precisely those from the head of the
+ * list up to and not including the first stream with non-zero save_count.
+ *
+ * We want to close streams freed by restore and by garbage collection.
+ * We use the finalization procedure for this. For restore, we don't
+ * have to do anything special to make this happen; we only need to ensure
+ * that we remove from the list of allocated streams any streams being freed,
+ * as just described. For garbage collection, we do something more drastic:
+ * we simply clear the list of known streams (at all save levels).
+ * Any streams open at the time of garbage collection will no longer
+ * participate in the list of known streams, but this does no harm;
+ * it simply means that they won't get reused, and can only be reclaimed
+ * by a future garbage collection or restore.
+ */
+private stream *file_list;
+private gs_gc_root_t file_list_root;
+
+/*
+ * Define the default stream buffer sizes. For file streams,
+ * this is arbitrary, since the C library or operating system
+ * does its own buffering in addition.
+ * However, the buffer size for eexec decoding is NOT arbitrary:
+ * it must be at most 512.
+ */
+#define default_buffer_size 512
+const uint file_default_buffer_size = default_buffer_size;
+
+/* An invalid file object */
+stream *invalid_file_entry; /* exported for zfileio.c */
+private gs_gc_root_t invalid_file_root;
+
+/* Initialize the file table */
+private void
+zfile_init(void)
+{
+ /* Create and initialize an invalid (closed) stream. */
+ /* Initialize the stream for the sake of the GC, */
+ /* and so it can act as an empty input stream. */
+
+ stream *s = s_alloc(imemory_system, "zfile_init");
+ sread_string(s, NULL, 0);
+ s->next = s->prev = 0;
+ s->save_count = 0;
+ s_init_no_id(s);
+ invalid_file_entry = s;
+ gs_register_struct_root(imemory, &invalid_file_root,
+ (void **)&invalid_file_entry,
+ "invalid_file_entry");
+
+ /* Initialize the bookkeeping list. */
+
+ file_list = 0;
+ gs_register_struct_root(imemory, &file_list_root,
+ (void **)&file_list, "file_list");
+}
+
+/* Make an invalid file object. */
+void
+make_invalid_file(ref *fp)
+{ make_file(fp, avm_system, ~0, invalid_file_entry);
+}
+
+/* <name_string> <access_string> file <file> */
+int
+zfile(register os_ptr op)
+{ char file_access[3];
+ parsed_file_name pname;
+ const byte *astr;
+ int code;
+ stream *s;
+ check_read_type(*op, t_string);
+ astr = op->value.const_bytes;
+ switch ( r_size(op) )
+ {
+ case 2:
+ if ( astr[1] != '+' )
+ return_error(e_invalidfileaccess);
+ file_access[1] = '+';
+ file_access[2] = 0;
+ break;
+ case 1:
+ file_access[1] = 0;
+ break;
+ default:
+ return_error(e_invalidfileaccess);
+ }
+ switch ( astr[0] )
+ {
+ case 'r': case 'w': case 'a':
+ break;
+ default:
+ return_error(e_invalidfileaccess);
+ }
+ file_access[0] = astr[0];
+ code = parse_file_name(op - 1, &pname);
+ if ( code < 0 )
+ return code;
+ if ( pname.iodev == NULL )
+ pname.iodev = iodev_default;
+ if ( pname.fname == NULL ) /* just a device */
+ code = (*pname.iodev->procs.open_device)(pname.iodev,
+ file_access, &s, imemory);
+ else /* file */
+ { iodev_proc_open_file((*open_file)) =
+ pname.iodev->procs.open_file;
+ if ( open_file == 0 )
+ open_file = iodev_os_open_file;
+ code = (*open_file)(pname.iodev, pname.fname, pname.len,
+ file_access, &s, imemory);
+ }
+ if ( code < 0 )
+ return code;
+ make_stream_file(op - 1, s, file_access);
+ pop(1);
+ return code;
+}
+
+/* ------ Level 2 extensions ------ */
+
+/* <string> deletefile - */
+private int
+zdeletefile(register os_ptr op)
+{ parsed_file_name pname;
+ int code = parse_real_file_name(op, &pname, "deletefile");
+ if ( code < 0 )
+ return code;
+ code = (*pname.iodev->procs.delete_file)(pname.iodev, pname.fname);
+ free_file_name(&pname, "deletefile");
+ if ( code < 0 )
+ return code;
+ pop(1);
+ return 0;
+}
+
+/* <template> <proc> <scratch> filenameforall - */
+/****** NOT CONVERTED FOR IODEVICES YET ******/
+private int file_continue(P1(os_ptr));
+private int file_cleanup(P1(os_ptr));
+private int
+zfilenameforall(register os_ptr op)
+{ file_enum *pfen;
+ int code;
+ check_write_type(*op, t_string);
+ check_proc(op[-1]);
+ check_read_type(op[-2], t_string);
+ /* Push a mark, the pattern, the scratch string, the enumerator, */
+ /* and the procedure, and invoke the continuation. */
+ check_estack(7);
+ pfen = gp_enumerate_files_init((char *)op[-2].value.bytes, r_size(op - 2), imemory);
+ if ( pfen == 0 )
+ return_error(e_VMerror);
+ push_mark_estack(es_for, file_cleanup);
+ *++esp = op[-2];
+ *++esp = *op;
+ ++esp;
+ make_istruct(esp, 0, pfen);
+ *++esp = op[-1];
+ pop(3); op -= 3;
+ code = file_continue(op);
+ return (code == o_pop_estack ? o_push_estack : code);
+}
+/* Continuation operator for enumerating files */
+private int
+file_continue(register os_ptr op)
+{ es_ptr pscratch = esp - 2;
+ file_enum *pfen = r_ptr(esp - 1, file_enum);
+ uint len = r_size(pscratch);
+ uint code =
+ gp_enumerate_files_next(pfen, (char *)pscratch->value.bytes, len);
+ if ( code == ~(uint)0 ) /* all done */
+ { esp -= 4; /* pop proc, pfen, scratch, mark */
+ return o_pop_estack;
+ }
+ else if ( code > len ) /* overran string */
+ return_error(e_rangecheck);
+ else
+ { push(1);
+ ref_assign(op, pscratch);
+ r_set_size(op, code);
+ push_op_estack(file_continue); /* come again */
+ *++esp = pscratch[2]; /* proc */
+ return o_push_estack;
+ }
+}
+/* Cleanup procedure for enumerating files */
+private int
+file_cleanup(os_ptr op)
+{ gp_enumerate_files_close(r_ptr(esp + 4, file_enum));
+ return 0;
+}
+
+/* <string1> <string2> renamefile - */
+private int
+zrenamefile(register os_ptr op)
+{ parsed_file_name pname1, pname2;
+ int code = parse_real_file_name(op - 1, &pname1, "renamefile(from)");
+ if ( code < 0 )
+ return code;
+ pname2.fname = 0;
+ code = parse_real_file_name(op, &pname2, "renamefile(to)");
+ if ( code < 0 || pname1.iodev != pname2.iodev ||
+ (code = (*pname1.iodev->procs.rename_file)(pname1.iodev,
+ pname1.fname, pname2.fname)) < 0
+ )
+ { if ( code >= 0 )
+ code = gs_note_error(e_invalidfileaccess);
+ }
+ free_file_name(&pname2, "renamefile(to)");
+ free_file_name(&pname1, "renamefile(from)");
+ if ( code < 0 )
+ return code;
+ pop(2);
+ return 0;
+}
+
+/* <file> status <open_bool> */
+/* <string> status <pages> <bytes> <ref_time> <creation_time> true */
+/* <string> status false */
+private int
+zstatus(register os_ptr op)
+{ switch ( r_type(op) )
+ {
+ case t_file:
+ { stream *s;
+ make_bool(op, (file_is_valid(s, op) ? 1 : 0));
+ } return 0;
+ case t_string:
+ { parsed_file_name pname;
+ struct stat fstat;
+ int code = parse_file_name(op, &pname);
+ if ( code < 0 )
+ return code;
+ code = terminate_file_name(&pname, "status");
+ if ( code < 0 )
+ return code;
+ code = (*pname.iodev->procs.file_status)(pname.iodev,
+ pname.fname, &fstat);
+ switch ( code )
+ {
+ case 0:
+ push(4);
+ make_int(op - 4, stat_blocks(&fstat));
+ make_int(op - 3, fstat.st_size);
+ make_int(op - 2, fstat.st_mtime);
+ make_int(op - 1, fstat.st_ctime);
+ make_bool(op, 1);
+ break;
+ case e_undefinedfilename:
+ make_bool(op, 0);
+ code = 0;
+ }
+ free_file_name(&pname, "status");
+ return code;
+ }
+ default:
+ return_op_typecheck(op);
+ }
+}
+
+/* ------ Non-standard extensions ------ */
+
+/* <dir> <name> .filenamedirseparator <string> */
+int
+zfilenamedirseparator(os_ptr op)
+{ const char *sepr;
+
+ check_read_type(*op, t_string);
+ check_read_type(op[-1], t_string);
+ sepr =
+ gp_file_name_concat_string((const char *)op[-1].value.const_bytes,
+ r_size(op - 1),
+ (const char *)op->value.const_bytes,
+ r_size(op));
+ make_const_string(op - 1, avm_foreign | a_readonly,
+ strlen(sepr), (const byte *)sepr);
+ pop(1);
+ return 0;
+}
+
+/* - .filenamelistseparator <string> */
+int
+zfilenamelistseparator(os_ptr op)
+{ push(1);
+ make_const_string(op, avm_foreign | a_readonly, 1,
+ (const byte *)&gp_file_name_list_separator);
+ return 0;
+}
+
+/* <name> .filenamesplit <dir> <base> <extension> */
+int
+zfilenamesplit(os_ptr op)
+{ check_read_type(*op, t_string);
+ /****** NOT IMPLEMENTED YET ******/
+ return_error(e_undefined);
+}
+
+/* <string> findlibfile <found_string> <file> true */
+/* <string> findlibfile <string> false */
+int
+zfindlibfile(register os_ptr op)
+{ int code;
+#define maxclen 200
+ byte cname[maxclen];
+ uint clen;
+ parsed_file_name pname;
+ stream *s;
+ check_ostack(2);
+ code = parse_file_name(op, &pname);
+ if ( code < 0 )
+ return code;
+ if ( pname.iodev == NULL )
+ pname.iodev = iodev_default;
+ if ( pname.iodev != iodev_default )
+ { /* Non-OS devices don't have search paths (yet). */
+ code =
+ (pname.fname == NULL ?
+ (*pname.iodev->procs.open_device)(pname.iodev, "r",
+ &s, imemory) :
+ (*pname.iodev->procs.open_file)(pname.iodev,
+ pname.fname, pname.len, "r",
+ &s, imemory));
+ if ( code < 0 )
+ { push(1);
+ make_false(op);
+ return 0;
+ }
+ make_stream_file(op + 1, s, "r");
+ }
+ else
+ { byte *cstr;
+ code = lib_file_open(pname.fname, pname.len, cname, maxclen,
+ &clen, op + 1);
+ if ( code == e_VMerror )
+ return code;
+ if ( code < 0 )
+ { push(1);
+ make_false(op);
+ return 0;
+ }
+ cstr = ialloc_string(clen, "findlibfile");
+ if ( cstr == 0 )
+ return_error(e_VMerror);
+ memcpy(cstr, cname, clen);
+ make_string(op, a_all | icurrent_space, clen, cstr);
+ }
+ push(2);
+ make_true(op);
+ return 0;
+}
+
+/* <executable_file> .runexec - */
+private int
+zrunexec(register os_ptr op)
+{ check_type_access(*op, t_file, a_executable | a_read | a_execute);
+ check_estack(4); /* cleanup, file, finish, file */
+ push_mark_estack(es_other, runexec_cleanup);
+ *++esp = *op;
+ push_op_estack(runexec_finish);
+ return zexec(op);
+}
+/* Finish normally. */
+private int
+runexec_finish(os_ptr op)
+{ check_ostack(1);
+ esp -= 2;
+ runexec_cleanup(op);
+ return o_pop_estack;
+}
+/* Clean up by closing the file. */
+private int
+runexec_cleanup(os_ptr op)
+{ check_ostack(1);
+ *++osp = esp[2];
+ return zclosefile(osp);
+}
+
+/* ------ Initialization procedure ------ */
+
+BEGIN_OP_DEFS(zfile_op_defs) {
+ {"1deletefile", zdeletefile},
+ {"2file", zfile},
+ {"3filenameforall", zfilenameforall},
+ {"2.filenamedirseparator", zfilenamedirseparator},
+ {"0.filenamelistseparator", zfilenamelistseparator},
+ {"1.filenamesplit", zfilenamesplit},
+ {"1findlibfile", zfindlibfile},
+ {"2renamefile", zrenamefile},
+ {"1.runexec", zrunexec},
+ {"1status", zstatus},
+ /* Internal operators */
+ {"0%file_continue", file_continue},
+ {"0%runexec_finish", runexec_finish},
+END_OP_DEFS(zfile_init) }
+
+/* ------ Stream opening ------ */
+
+/* Make a t_file reference to a stream. */
+void
+make_stream_file(ref *pfile, stream *s, const char *access)
+{ uint attrs =
+ (access[1] == '+' ? a_write + a_read + a_execute : 0) |
+ imemory_space((gs_ref_memory_t *)s->memory);
+ if ( access[0] == 'r' )
+ { make_file(pfile, attrs | (a_read | a_execute), s->read_id, s);
+ s->write_id = 0;
+ }
+ else
+ { make_file(pfile, attrs | a_write, s->write_id, s);
+ s->read_id = 0;
+ }
+}
+
+/* Open an OS-level file (like fopen), using the search paths if necessary. */
+/* Note that it does not automatically look in the current */
+/* directory first (or at all): this is like Unix, and unlike MS-DOS. */
+private int
+lib_file_fopen(gx_io_device *iodev, const char *bname,
+ const char *ignore_access, FILE **pfile, char *rfname, uint rnamelen)
+{ char fmode[3]; /* r, [b], null */
+ int len = strlen(bname);
+ const gs_file_path *pfpath = &gs_lib_path;
+ uint pi;
+
+ strcpy(fmode, "r");
+ strcat(fmode, gp_fmode_binary_suffix);
+ if ( gp_file_name_is_absolute(bname, len) )
+ return (*iodev->procs.fopen)(iodev, bname, fmode, pfile,
+ rfname, rnamelen);
+ /* Go through the list of search paths */
+ for ( pi = 0; pi < r_size(&pfpath->list); ++pi )
+ { const ref *prdir = pfpath->list.value.refs + pi;
+ const char *pstr = (const char *)prdir->value.const_bytes;
+ uint plen = r_size(prdir);
+ const char *cstr =
+ gp_file_name_concat_string(pstr, plen, bname, len);
+ int up, i;
+ int code;
+
+ /* Concatenate the prefix, combiner, and file name. */
+ /* Do this carefully in case rfname is the same */
+ /* as fname. (We don't worry about the case */
+ /* where rfname only overlaps fname.) */
+ up = plen + strlen(cstr);
+ if ( up + len + 1 > rnamelen )
+ return_error(e_limitcheck);
+ for ( i = len + 1; --i >= 0; )
+ rfname[i + up] = bname[i];
+ memcpy(rfname, pstr, plen);
+ memcpy(rfname + plen, cstr, strlen(cstr));
+ code = (*iodev->procs.fopen)(iodev, rfname, fmode,
+ pfile, rfname, rnamelen);
+ if ( code >= 0 )
+ return code;
+ /* strcpy isn't guaranteed to work for overlapping */
+ /* source and destination, so: */
+ if ( rfname == bname )
+ for ( i = 0; (rfname[i] = rfname[i + up]) != 0; i++ )
+ ;
+ }
+ return_error(e_undefinedfilename);
+}
+/* The startup code calls this to open @-files. */
+FILE *
+lib_fopen(const char *bname)
+{ FILE *file = NULL;
+ /* We need a buffer to hold the expanded file name. */
+#define max_filename 129
+ char buffer[max_filename];
+ int code = lib_file_fopen(iodev_default, bname, "r", &file,
+ buffer, max_filename);
+#undef max_filename
+ return (code < 0 ? NULL : file);
+}
+
+/* Open a file stream on an OS file and create a file object, */
+/* using the search paths. */
+/* The startup code calls this to open the initialization file gs_init.ps. */
+int
+lib_file_open(const char *fname, uint len, byte *cname, uint max_clen,
+ uint *pclen, ref *pfile)
+{ stream *s;
+ int code = file_open_stream(fname, len, "r",
+ default_buffer_size, &s, lib_file_fopen);
+ char *bname;
+ uint blen;
+
+ if ( code < 0 )
+ return code;
+ /* Get the name from the stream buffer. */
+ bname = (char *)s->cbuf;
+ blen = strlen(bname);
+ if ( blen > max_clen )
+ { sclose(s);
+ return_error(e_limitcheck);
+ }
+ memcpy(cname, bname, blen);
+ *pclen = blen;
+ make_stream_file(pfile, s, "r");
+ return 0;
+}
+
+/* Open a file stream that reads a string. */
+/* (This is currently used only by the ccinit feature.) */
+/* The string must be allocated in non-garbage-collectable (foreign) space. */
+int
+file_read_string(const byte *str, uint len, ref *pfile)
+{ stream *s = file_alloc_stream(imemory, "file_read_string");
+ int space;
+
+ if ( s == 0 )
+ return_error(e_VMerror);
+ space = icurrent_space;
+ sread_string(s, str, len);
+ s->foreign = 1;
+ s->write_id = 0;
+ make_file(pfile, a_readonly | space, s->read_id, s);
+ s->save_close = s->procs.close;
+ s->procs.close = file_close_disable;
+ return 0;
+}
+
+/* Open a file stream, optionally on an OS file. */
+/* Return 0 if successful, error code if not. */
+/* On a successful return, the C file name is in the stream buffer. */
+/* If fname==0, set up the file entry, stream, and buffer, */
+/* but don't open an OS file or initialize the stream. */
+int
+file_open_stream(const char *fname, uint len, const char *file_access,
+ uint buffer_size, stream **ps, iodev_proc_fopen_t fopen_proc)
+{ byte *buffer;
+ register stream *s;
+ if ( buffer_size == 0 )
+ buffer_size = default_buffer_size;
+ if ( len >= buffer_size )
+ return_error(e_limitcheck); /* we copy the file name into the buffer */
+ /* Allocate the stream first, since it persists */
+ /* even after the file has been closed. */
+ s = file_alloc_stream(imemory, "file_open_stream");
+ if ( s == 0 )
+ return_error(e_VMerror);
+ /* Allocate the buffer. */
+ buffer = ialloc_bytes(buffer_size, "file_open(buffer)");
+ if ( buffer == 0 )
+ return_error(e_VMerror);
+ if ( fname != 0 )
+ { /* Copy the name (so we can terminate it with a zero byte.) */
+ char *file_name = (char *)buffer;
+ char fmode[4]; /* r/w/a, [+], [b], null */
+ FILE *file;
+ int code;
+ memcpy(file_name, fname, len);
+ file_name[len] = 0; /* terminate string */
+ /* Open the file, always in binary mode. */
+ strcpy(fmode, file_access);
+ strcat(fmode, gp_fmode_binary_suffix);
+ /****** iodev_default IS QUESTIONABLE ******/
+ code = (*fopen_proc)(iodev_default, file_name, fmode, &file,
+ (char *)buffer, buffer_size);
+ if (code < 0 )
+ { ifree_object(buffer, "file_open(buffer)");
+ return code;
+ }
+ /* Set up the stream. */
+ switch ( fmode[0] )
+ {
+ case 'a':
+ sappend_file(s, file, buffer, buffer_size);
+ break;
+ case 'r':
+ sread_file(s, file, buffer, buffer_size);
+ break;
+ case 'w':
+ swrite_file(s, file, buffer, buffer_size);
+ }
+ if ( fmode[1] == '+' )
+ s->file_modes |= s_mode_read | s_mode_write;
+ s->save_close = s->procs.close;
+ s->procs.close = file_close_file;
+ }
+ else /* save the buffer and size */
+ { s->cbuf = buffer;
+ s->bsize = s->cbsize = buffer_size;
+ }
+ *ps = s;
+ return 0;
+}
+
+/* Open a file stream for a filter. */
+int
+filter_open(const char *file_access, uint buffer_size, ref *pfile,
+ const stream_procs _ds *procs, const stream_template *template,
+ const stream_state *st)
+{ stream *s;
+ uint ssize = gs_struct_type_size(template->stype);
+ stream_state *sst = 0;
+ int code;
+
+ if ( template->stype != &st_stream_state )
+ { sst = s_alloc_state(imemory, template->stype,
+ "filter_open(stream_state)");
+ if ( sst == 0 )
+ return_error(e_VMerror);
+ }
+ code = file_open_stream((char *)0, 0, file_access,
+ buffer_size, &s, (iodev_proc_fopen_t)0);
+ if ( code < 0 )
+ { ifree_object(sst, "filter_open(stream_state)");
+ return code;
+ }
+ s_std_init(s, s->cbuf, s->bsize, procs,
+ (*file_access == 'r' ? s_mode_read : s_mode_write));
+ s->procs.process = template->process;
+ s->save_close = s->procs.close;
+ s->procs.close = file_close_file;
+ if ( sst == 0 )
+ { /* This stream doesn't have any state of its own. */
+ /* Hack: use the stream itself as the state. */
+ sst = (stream_state *)s;
+ }
+ else if ( st != 0 ) /* might not have client parameters */
+ memcpy(sst, st, ssize);
+ s->state = sst;
+ sst->template = template;
+ sst->memory = imemory;
+ sst->report_error = filter_report_error;
+ if ( template->init != 0 )
+ { code = (*template->init)(sst);
+ if ( code < 0 )
+ { ifree_object(sst, "filter_open(stream_state)");
+ ifree_object(s->cbuf, "filter_open(buffer)");
+ return code;
+ }
+ }
+ make_stream_file(pfile, s, file_access);
+ return 0;
+}
+
+/* Report an error by storing it in $error.errorinfo. */
+private int
+filter_report_error(stream_state *st, const char *str)
+{ if_debug1('s', "[s]stream error: %s\n", str);
+ return gs_errorinfo_put_string(str);
+}
+
+/* Allocate and return a file stream. */
+/* Return 0 if the allocation failed. */
+/* The stream is initialized to an invalid state, so the caller need not */
+/* worry about cleaning up if a later step in opening the stream fails. */
+stream *
+file_alloc_stream(gs_memory_t *mem, client_name_t cname)
+{ stream *s;
+ /* Look first for a free stream allocated at this level. */
+ s = file_list;
+ while ( s != 0 && s->save_count == 0 )
+ { if ( !s_is_valid(s) && s->read_id != 0 /* i.e. !overflowed */
+ && s->memory == mem
+ )
+ { s->is_temp = 0; /* not a temp stream */
+ return s;
+ }
+ s = s->next;
+ }
+ s = s_alloc(mem, cname);
+ if ( s == 0 )
+ return 0;
+ s_init_ids(s);
+ s->is_temp = 0; /* not a temp stream */
+ /* Disable the stream now (in case we can't open the file, */
+ /* or a filter init procedure fails) so that `restore' won't */
+ /* crash when it tries to close open files. */
+ s_disable(s);
+ /* Add s to the list of files. */
+ if ( file_list != 0 )
+ file_list->prev = s;
+ s->next = file_list;
+ s->prev = 0;
+ s->save_count = 0;
+ file_list = s;
+ return s;
+}
+
+/* ------ Stream closing ------ */
+
+/* Finish closing a file stream. This used to check whether it was */
+/* currentfile, but we don't have to do this any longer. */
+/* This replaces the close procedure for the std* streams, */
+/* which cannot actually be closed. */
+/* This is exported for ziodev.c. */
+int
+file_close_finish(stream *s)
+{ return 0;
+}
+
+/* Close a file stream, but don't deallocate the buffer. */
+/* This replaces the close procedure for %lineedit and %statementedit. */
+/* (This is WRONG: these streams should allocate a new buffer each time */
+/* they are opened, but that would overstress the allocator right now.) */
+/* This is exported for ziodev.c. */
+/* This also replaces the close procedure for the string-reading */
+/* stream created for gs_run_string. */
+int
+file_close_disable(stream *s)
+{ int code = (*s->save_close)(s);
+ if ( code )
+ return code;
+ /* Increment the IDs to prevent further access. */
+ s->read_id = s->write_id = (s->read_id | s->write_id) + 1;
+ return file_close_finish(s);
+}
+
+/* Close a file stream. This replaces the close procedure in the stream */
+/* for normal (OS) files and for filters. */
+int
+file_close_file(stream *s)
+{ stream *stemp = s->strm;
+ gs_memory_t *mem;
+ int code = file_close_disable(s);
+ if ( code )
+ return code;
+ /*
+ * Check for temporary streams created for filters.
+ * There may be more than one in the case of a procedure-based filter,
+ * or if we created an intermediate stream to ensure
+ * a large enough buffer. Note that these streams may have been
+ * allocated by file_alloc_stream, so we mustn't free them.
+ */
+ while ( stemp != 0 && stemp->is_temp != 0 )
+ { stream *snext = stemp->strm;
+ mem = stemp->memory;
+ if ( stemp->is_temp > 1 )
+ gs_free_object(mem, stemp->cbuf,
+ "file_close(temp stream buffer)");
+ s_disable(stemp);
+ stemp = snext;
+ }
+ mem = s->memory;
+ gs_free_object(mem, s->cbuf, "file_close(buffer)");
+ return 0;
+}
+
+/* Close a file object. */
+/* This is exported only for gsmain.c. */
+int
+file_close(ref *pfile)
+{ stream *s;
+ if ( file_is_valid(s, pfile) ) /* closing a closed file is a no-op */
+ { if ( sclose(s) )
+ return_error(e_ioerror);
+ }
+ return 0;
+}
+
+/* ------ Memory management ------ */
+
+/* Mark the current file list at a save. */
+void
+file_save(void)
+{ if_debug1('u', "[u]file_save 0x%lx\n",
+ (ulong)file_list);
+ if ( file_list != 0 )
+ file_list->save_count++;
+}
+
+/* Update the file list before a restore. */
+void
+file_restore(const alloc_save_t *save, const gs_memory_t *mem)
+{ stream *prev = 0;
+ stream *s;
+ stream **ps;
+ /* We must be careful to unlink only those streams which */
+ /* were allocated in the VM space being restored. */
+ for ( ps = &file_list; (s = *ps) != 0 && s->save_count == 0; )
+ if ( s->memory == mem )
+ { if ( (*ps = s->next) != 0 )
+ (*ps)->prev = prev;
+ }
+ else
+ prev = s, ps = &s->next;
+ if ( s != 0 ) /* i.e., s->save_count != 0 */
+ s->save_count--;
+ if_debug2('u', "[u]file_restore 0x%lx for 0x%lx\n",
+ (ulong)file_list, (ulong)save);
+}
+
+/* Note that a save has been forgotten. */
+void
+file_forget_save(const alloc_save_t *save)
+{ stream *s;
+ for ( s = file_list; s != 0 && s->save_count == 0; )
+ s = s->next;
+ if ( s != 0 )
+ s->save_count--;
+ if_debug2('u', "[u]file_forget_save 0x%lx for 0x%lx\n",
+ (ulong)file_list, (ulong)save);
+}
+
+/* Clear the file list for a GC. */
+void
+file_gc_prepare(void)
+{ /* We have to unlink every stream from its neighbors, */
+ /* so that referenced streams don't keep all streams around. */
+ while ( file_list != 0 )
+ { stream *s = file_list;
+ file_list = s->next;
+ s->prev = s->next = 0;
+ }
+}
diff --git a/pstoraster/zfileio.c b/pstoraster/zfileio.c
new file mode 100644
index 000000000..e8af87d4d
--- /dev/null
+++ b/pstoraster/zfileio.c
@@ -0,0 +1,778 @@
+/* Copyright (C) 1989, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* zfileio.c */
+/* File I/O operators */
+#include "ghost.h"
+#include "gp.h"
+#include "errors.h"
+#include "oper.h"
+#include "stream.h"
+#include "files.h"
+#include "store.h"
+#include "strimpl.h" /* for ifilter.h */
+#include "ifilter.h" /* for procedure streams */
+#include "gsmatrix.h" /* for gxdevice.h */
+#include "gxdevice.h"
+#include "gxdevmem.h"
+
+/* Forward references */
+private int write_string(P2(ref *, stream *));
+private int handle_read_status(P4(int, const ref *, const uint *,
+ int (*)(P1(os_ptr))));
+private int handle_write_status(P4(int, const ref *, const uint *,
+ int (*)(P1(os_ptr))));
+
+/* ------ Operators ------ */
+
+/* <file> closefile - */
+int
+zclosefile(register os_ptr op)
+{ stream *s;
+ check_type(*op, t_file);
+ if ( file_is_valid(s, op) ) /* closing a closed file is a no-op */
+ { int status = sclose(s);
+ if ( status != 0 )
+ { if ( s_is_writing(s) )
+ return handle_write_status(status, op, NULL,
+ zclosefile);
+ else
+ return handle_read_status(status, op, NULL,
+ zclosefile);
+ }
+ }
+ pop(1);
+ return 0;
+}
+
+/* <file> read <int> -true- */
+/* <file> read -false- */
+private int
+zread(register os_ptr op)
+{ stream *s;
+ int ch;
+ check_read_file(s, op);
+ ch = sgetc(s);
+ if ( ch >= 0 )
+ { push(1);
+ make_int(op - 1, ch);
+ make_bool(op, 1);
+ }
+ else if ( ch == EOFC )
+ make_bool(op, 0);
+ else
+ return handle_read_status(ch, op, NULL, zread);
+ return 0;
+}
+
+/* <file> <int> write - */
+int
+zwrite(register os_ptr op)
+{ stream *s;
+ byte ch;
+ int status;
+ check_write_file(s, op - 1);
+ check_type(*op, t_integer);
+ ch = (byte)op->value.intval;
+ status = sputc(s, (byte)ch);
+ if ( status >= 0 )
+ { pop(2);
+ return 0;
+ }
+ return handle_write_status(status, op - 1, NULL, zwrite);
+}
+
+/* <file> <string> readhexstring <substring> <filled_bool> */
+private int zreadhexstring_continue(P1(os_ptr));
+/* We keep track of the odd digit in the next byte of the string */
+/* beyond the bytes already used. (This is just for convenience; */
+/* we could do the same thing by passing 2 state parameters to the */
+/* continuation procedure instead of 1.) */
+private int
+zreadhexstring_at(register os_ptr op, uint start)
+{ stream *s;
+ uint len, nread;
+ byte *str;
+ int odd;
+ stream_cursor_write cw;
+ int status;
+ check_read_file(s, op - 1);
+ /*check_write_type(*op, t_string);*/ /* done by caller */
+ str = op->value.bytes;
+ len = r_size(op);
+ if ( start < len )
+ { odd = str[start];
+ if ( odd > 0xf ) odd = -1;
+ }
+ else
+ odd = -1;
+ cw.ptr = str + start - 1;
+ cw.limit = str + len - 1;
+ for ( ; ; )
+ { status = s_hex_process(&s->cursor.r, &cw, &odd,
+ hex_ignore_garbage);
+ if ( status == 1 ) /* filled the string */
+ { ref_assign_inline(op - 1, op);
+ make_true(op);
+ return 0;
+ }
+ else if ( status != 0 ) /* error or EOF */
+ break;
+ /* Didn't fill, keep going. */
+ status = spgetc(s);
+ if ( status < 0 )
+ break;
+ sputback(s);
+ }
+ nread = cw.ptr + 1 - str;
+ if ( status != EOFC )
+ { /* Error */
+ if ( nread < len )
+ str[nread] = (odd < 0 ? 0x10 : odd);
+ return handle_read_status(status, op - 1, &nread,
+ zreadhexstring_continue);
+ }
+ /* Reached end-of-file before filling the string. */
+ /* Return an appropriate substring. */
+ ref_assign_inline(op - 1, op);
+ r_set_size(op - 1, nread);
+ make_false(op);
+ return 0;
+}
+private int
+zreadhexstring(os_ptr op)
+{ check_write_type(*op, t_string);
+ if ( r_size(op) > 0 )
+ *op->value.bytes = 0x10;
+ return zreadhexstring_at(op, 0);
+}
+/* Continue a readhexstring operation after a callout. */
+/* *op is the index within the string. */
+private int
+zreadhexstring_continue(register os_ptr op)
+{ int code;
+ check_type(*op, t_integer);
+ if ( op->value.intval < 0 || op->value.intval > r_size(op - 1) )
+ return_error(e_rangecheck);
+ check_write_type(op[-1], t_string);
+ code = zreadhexstring_at(op - 1, (uint)op->value.intval);
+ if ( code >= 0 )
+ pop(1);
+ return code;
+}
+
+/* <file> <string> writehexstring - */
+private int zwritehexstring_continue(P1(os_ptr));
+private int
+zwritehexstring_at(register os_ptr op, uint odd)
+{ register stream *s;
+ register byte ch;
+ register const byte *p;
+ register const char _ds *hex_digits = "0123456789abcdef";
+ register uint len;
+ int status;
+#define max_hex 128
+ byte buf[max_hex];
+ check_write_file(s, op - 1);
+ check_read_type(*op, t_string);
+ p = op->value.bytes;
+ len = r_size(op);
+ while ( len )
+ { uint len1 = min(len, max_hex / 2);
+ register byte *q = buf;
+ uint count = len1;
+ ref rbuf;
+ do
+ { ch = *p++;
+ *q++ = hex_digits[ch >> 4];
+ *q++ = hex_digits[ch & 0xf];
+ }
+ while ( --count );
+ r_set_size(&rbuf, (len1 << 1) - odd);
+ rbuf.value.bytes = buf + odd;
+ status = write_string(&rbuf, s);
+ switch ( status )
+ {
+ default:
+ return_error(e_ioerror);
+ case 0:
+ len -= len1;
+ odd = 0;
+ continue;
+ case INTC:
+ case CALLC:
+ count = rbuf.value.bytes - buf;
+ op->value.bytes += count >> 1;
+ r_set_size(op, len - (count >> 1));
+ count &= 1;
+ return handle_write_status(status, op - 1, &count,
+ zwritehexstring_continue);
+ }
+ }
+ pop(2);
+ return 0;
+#undef max_hex
+}
+private int
+zwritehexstring(os_ptr op)
+{ return zwritehexstring_at(op, 0);
+}
+/* Continue a writehexstring operation after a callout. */
+/* *op is the odd/even hex digit flag for the first byte. */
+private int
+zwritehexstring_continue(register os_ptr op)
+{ int code;
+ check_type(*op, t_integer);
+ if ( (op->value.intval & ~1) != 0 )
+ return_error(e_rangecheck);
+ code = zwritehexstring_at(op - 1, (uint)op->value.intval);
+ if ( code >= 0 )
+ pop(1);
+ return code;
+}
+
+/* <file> <string> readstring <substring> <filled_bool> */
+private int zreadstring_continue(P1(os_ptr));
+private int
+zreadstring_at(register os_ptr op, uint start)
+{ stream *s;
+ uint len, rlen;
+ int status;
+ check_read_file(s, op - 1);
+ check_write_type(*op, t_string);
+ len = r_size(op);
+ status = sgets(s, op->value.bytes + start, len - start, &rlen);
+ rlen += start;
+ switch ( status )
+ {
+ case EOFC:
+ case 0:
+ break;
+ default:
+ return handle_read_status(status, op - 1, &rlen,
+ zreadstring_continue);
+ }
+ /*
+ * The most recent Adobe specification says that readstring
+ * must signal a rangecheck if the string length is zero.
+ * I can't imagine the motivation for this, but we emulate it.
+ * It's safe to check it here, rather than earlier, because if
+ * len is zero, sgets will return 0 immediately with rlen = 0.
+ */
+ if ( len == 0 )
+ return_error(e_rangecheck);
+ r_set_size(op, rlen);
+ op[-1] = *op;
+ make_bool(op, (rlen == len ? 1 : 0));
+ return 0;
+}
+private int
+zreadstring(os_ptr op)
+{ return zreadstring_at(op, 0);
+}
+/* Continue a readstring operation after a callout. */
+/* *op is the index within the string. */
+private int
+zreadstring_continue(register os_ptr op)
+{ int code;
+ check_type(*op, t_integer);
+ if ( op->value.intval < 0 || op->value.intval > r_size(op - 1) )
+ return_error(e_rangecheck);
+ code = zreadstring_at(op - 1, (uint)op->value.intval);
+ if ( code >= 0 )
+ pop(1);
+ return code;
+}
+
+/* <file> <string> writestring - */
+private int
+zwritestring(register os_ptr op)
+{ stream *s;
+ int status;
+ check_write_file(s, op - 1);
+ check_read_type(*op, t_string);
+ status = write_string(op, s);
+ if ( status >= 0 )
+ { pop(2);
+ return 0;
+ }
+ return handle_write_status(status, op - 1, NULL, zwritestring);
+}
+
+/* <file> <string> readline <substring> <bool> */
+private int zreadline(P1(os_ptr));
+private int zreadline_continue(P1(os_ptr));
+/*
+ * We could handle readline the same way as readstring,
+ * except for the anomalous situation where we get interrupted
+ * between the CR and the LF of an end-of-line marker.
+ * We hack around this in the following way: if we get interrupted
+ * before we've read any characters, we just restart the readline;
+ * if we get interrupted at any other time, we use readline_continue;
+ * we use start=0 (which we have just ruled out as a possible start value
+ * for readline_continue) to indicate interruption after the CR.
+ */
+private int
+zreadline_at(register os_ptr op, uint count, bool in_eol)
+{ stream *s;
+ byte *ptr;
+ uint len;
+ int status;
+ check_read_file(s, op - 1);
+ check_write_type(*op, t_string);
+ ptr = op->value.bytes;
+ len = r_size(op);
+ status = zreadline_from(s, ptr, len, &count, &in_eol);
+ switch ( status )
+ {
+ case 0:
+ case EOFC:
+ break;
+ case 1:
+ return_error(e_rangecheck);
+ default:
+ if ( count == 0 && !in_eol )
+ return handle_read_status(status, op - 1, NULL,
+ zreadline);
+ else
+ { if ( in_eol )
+ { r_set_size(op, count);
+ count = 0;
+ }
+ return handle_read_status(status, op - 1, &count,
+ zreadline_continue);
+ }
+ }
+ r_set_size(op, count);
+ op[-1] = *op;
+ make_bool(op, status == 0);
+ return 0;
+}
+private int
+zreadline(register os_ptr op)
+{ return zreadline_at(op, 0, false);
+}
+/* Continue a readline operation after a callout. */
+/* *op is the index within the string, or 0 for an interrupt after a CR. */
+private int
+zreadline_continue(register os_ptr op)
+{ uint size = r_size(op - 1);
+ uint start;
+ int code;
+
+ check_type(*op, t_integer);
+ if ( op->value.intval < 0 || op->value.intval > size )
+ return_error(e_rangecheck);
+ start = (uint)op->value.intval;
+ code = (start == 0 ? zreadline_at(op - 1, size, true) :
+ zreadline_at(op - 1, start, false));
+ if ( code >= 0 )
+ pop(1);
+ return code;
+}
+
+/* Internal readline routine. */
+/* Returns a stream status value, or 1 if we overflowed the string. */
+/* This is exported for %lineedit. */
+int
+zreadline_from(stream *s, byte *ptr, uint size, uint *pcount, bool *pin_eol)
+{ uint count = *pcount;
+
+ /* Most systems define \n as 0xa and \r as 0xd; however, */
+ /* OS-9 has \n == \r == 0xd and \l == 0xa. The following */
+ /* code works properly regardless of environment. */
+#if '\n' == '\r'
+# define LF 0xa
+#else
+# define LF '\n'
+#endif
+
+top: if ( *pin_eol )
+ { /*
+ * We're in the middle of checking for a two-character
+ * end-of-line sequence. If we get an EOF here, stop, but
+ * don't signal EOF now; wait till the next read.
+ */
+ int ch = spgetcc(s, false);
+ if ( ch == EOFC )
+ { *pin_eol = false;
+ return 0;
+ }
+ else if ( ch < 0 )
+ return ch;
+ else if ( ch != LF )
+ sputback(s);
+ *pin_eol = false;
+ return 0;
+ }
+ for ( ; ; )
+ { int ch = sgetc(s);
+ if ( ch < 0 ) /* EOF or exception */
+ { *pcount = count;
+ return ch;
+ }
+
+ switch ( ch )
+ {
+ case '\r':
+ {
+#if '\n' == '\r' /* OS-9 or similar */
+ stream *ins;
+ int code = zget_stdin(&ins);
+ if ( code < 0 || s != ins )
+#endif
+ { *pcount = count;
+ *pin_eol = true;
+ goto top;
+ }
+ }
+ /* falls through */
+ case LF:
+#undef LF
+ *pcount = count;
+ return 0;
+ }
+ if ( count >= size ) /* filled the string */
+ { sputback(s);
+ *pcount = count;
+ return 1;
+ }
+ ptr[count++] = ch;
+ }
+ /*return 0;*/ /* not reached */
+}
+
+/* <file> bytesavailable <int> */
+private int
+zbytesavailable(register os_ptr op)
+{ stream *s;
+ long avail;
+ check_read_file(s, op);
+ switch ( savailable(s, &avail) )
+ {
+ default:
+ return_error(e_ioerror);
+ case EOFC:
+ avail = -1;
+ case 0:
+ ;
+ }
+ make_int(op, avail);
+ return 0;
+}
+
+/* - flush - */
+int
+zflush(register os_ptr op)
+{ stream *s;
+ int code = zget_stderr(&s);
+ if ( code < 0 )
+ return code;
+ sflush(s);
+ return 0;
+}
+
+/* <file> flushfile - */
+private int
+zflushfile(register os_ptr op)
+{ stream *s;
+ int status;
+ check_file(s, op);
+ status = sflush(s);
+ if ( status == 0 )
+ { pop(1);
+ return 0;
+ }
+ return handle_write_status(status, op, NULL, zflushfile);
+}
+
+/* <file> resetfile - */
+private int
+zresetfile(register os_ptr op)
+{ stream *s;
+ /* According to Adobe, resetfile is a no-op on closed files. */
+ check_type(*op, t_file);
+ if ( file_is_valid(s, op) )
+ sreset(s);
+ pop(1);
+ return 0;
+}
+
+/* <string> print - */
+private int
+zprint(register os_ptr op)
+{ stream *s;
+ int status;
+ ref rstdout;
+ int code;
+ check_read_type(*op, t_string);
+ code = zget_stderr(&s);
+ if ( code < 0 )
+ return code;
+ status = write_string(op, s);
+ if ( status >= 0 )
+ { pop(1);
+ return 0;
+ }
+ /* Convert print to writestring on the fly. */
+ make_stream_file(&rstdout, s, "w");
+ code = handle_write_status(status, &rstdout, NULL, zwritestring);
+ if ( code != o_push_estack )
+ return code;
+ push(1);
+ *op = op[-1];
+ op[-1] = rstdout;
+ return code;
+}
+
+/* <bool> echo - */
+private int
+zecho(register os_ptr op)
+{ check_type(*op, t_boolean);
+ /****** NOT IMPLEMENTED YET ******/
+ pop(1);
+ return 0;
+}
+
+/* ------ Level 2 extensions ------ */
+
+/* <file> fileposition <int> */
+private int
+zfileposition(register os_ptr op)
+{ stream *s;
+ check_file(s, op);
+ make_int(op, stell(s));
+ return 0;
+}
+
+/* <file> <int> setfileposition - */
+private int
+zsetfileposition(register os_ptr op)
+{ stream *s;
+ check_file(s, op - 1);
+ check_type(*op, t_integer);
+ if ( sseek(s, op->value.intval) < 0 )
+ return_error(e_ioerror);
+ pop(2);
+ return 0;
+}
+
+/* ------ Non-standard extensions ------ */
+
+/* <file> <int> unread - */
+private int
+zunread(register os_ptr op)
+{ stream *s;
+ ulong ch;
+ check_read_file(s, op - 1);
+ check_type(*op, t_integer);
+ ch = op->value.intval;
+ if ( ch > 0xff )
+ return_error(e_rangecheck);
+ if ( sungetc(s, (byte)ch) < 0 )
+ return_error(e_ioerror);
+ pop(2);
+ return 0;
+}
+
+/* <file> <object> <==flag> .writecvp - */
+private int zwritecvp_continue(P1(os_ptr));
+private int
+zwritecvp_at(register os_ptr op, uint start)
+{ stream *s;
+#define max_cvs 128
+ byte str[max_cvs];
+ ref rstr;
+ const byte *pchars = str;
+ uint len;
+ int code, status;
+
+ check_write_file(s, op - 2);
+ check_type(*op, t_boolean);
+ code = obj_cvp(op - 1, str, max_cvs, &len, &pchars, op->value.boolval);
+ if ( code < 0 )
+ { if ( pchars == str )
+ return code;
+ }
+ if ( start > len )
+ return_error(e_rangecheck);
+ r_set_size(&rstr, len - start);
+ rstr.value.const_bytes = pchars + start;
+ status = write_string(&rstr, s);
+ switch ( status )
+ {
+ default:
+ return_error(e_ioerror);
+ case 0:
+ break;
+ case INTC:
+ case CALLC:
+ len -= r_size(&rstr);
+ return handle_write_status(status, op - 2, &len,
+ zwritecvp_continue);
+ }
+ pop(3);
+ return 0;
+#undef max_cvs
+}
+private int
+zwritecvp(os_ptr op)
+{ return zwritecvp_at(op, 0);
+}
+/* Continue a .writecvp after a callout. */
+/* *op is the index within the string. */
+private int
+zwritecvp_continue(os_ptr op)
+{ int code;
+ check_type(*op, t_integer);
+ if ( op->value.intval != (uint)op->value.intval )
+ return_error(e_rangecheck);
+ code = zwritecvp_at(op - 1, (uint)op->value.intval);
+ if ( code >= 0 )
+ pop(1);
+ return code;
+}
+
+/* ------ Initialization procedure ------ */
+
+BEGIN_OP_DEFS(zfileio_op_defs) {
+ {"1bytesavailable", zbytesavailable},
+ {"1closefile", zclosefile},
+ /* currentfile is in zcontrol.c */
+ {"1echo", zecho},
+ {"1fileposition", zfileposition},
+ {"0flush", zflush},
+ {"1flushfile", zflushfile},
+ {"1print", zprint},
+ {"1read", zread},
+ {"2readhexstring", zreadhexstring},
+ {"2readline", zreadline},
+ {"2readstring", zreadstring},
+ {"1resetfile", zresetfile},
+ {"2setfileposition", zsetfileposition},
+ {"2unread", zunread},
+ {"2write", zwrite},
+ {"3.writecvp", zwritecvp},
+ {"2writehexstring", zwritehexstring},
+ {"2writestring", zwritestring},
+END_OP_DEFS(0) }
+
+/* ------ Non-operator routines ------ */
+
+/* Switch a file open for read/write access but currently in write mode */
+/* to read mode. */
+int
+file_switch_to_read(const ref *op)
+{ stream *s = fptr(op);
+ if ( s->write_id != r_size(op) || s->file == 0 ) /* not valid */
+ return_error(e_invalidaccess);
+ if ( sswitch(s, false) < 0 )
+ return_error(e_ioerror);
+ s->read_id = s->write_id; /* enable reading */
+ s->write_id = 0; /* disable writing */
+ return 0;
+}
+
+/* Switch a file open for read/write access but currently in read mode */
+/* to write mode. */
+int
+file_switch_to_write(const ref *op)
+{ stream *s = fptr(op);
+ if ( s->read_id != r_size(op) || s->file == 0 ) /* not valid */
+ return_error(e_invalidaccess);
+ if ( sswitch(s, true) < 0 )
+ return_error(e_ioerror);
+ s->write_id = s->read_id; /* enable writing */
+ s->read_id = 0; /* disable reading */
+ return 0;
+}
+
+/* ------ Internal routines ------ */
+
+/* Write a string on a file. The file and string have been validated. */
+/* If the status is INTC or CALLC, updates the string on the o-stack. */
+private int
+write_string(ref *op, stream *s)
+{ const byte *data = op->value.const_bytes;
+ uint len = r_size(op);
+ uint wlen;
+ int status = sputs(s, data, len, &wlen);
+ switch ( status )
+ {
+ case INTC:
+ case CALLC:
+ op->value.const_bytes = data + wlen;
+ r_set_size(op, len - wlen);
+ /* falls through */
+ default: /* 0, EOFC, ERRC */
+ return status;
+ }
+}
+
+/* Handle an exceptional status return from a read stream. */
+/* fop points to the ref for the stream. */
+/* ch may be any stream exceptional value. */
+/* Return 0, 1 (EOF), o_push_estack, or an error. */
+private int
+handle_read_status(int ch, const ref *fop, const uint *pindex,
+ int (*cont)(P1(os_ptr)))
+{ switch ( ch )
+ {
+ default: /* error */
+ return_error(e_ioerror);
+ case EOFC:
+ return 1;
+ case INTC:
+ case CALLC:
+ if ( pindex )
+ { ref index;
+ make_int(&index, *pindex);
+ return s_handle_read_exception(ch, fop, &index, 1, cont);
+ }
+ else
+ return s_handle_read_exception(ch, fop, NULL, 0, cont);
+ }
+}
+
+/* Handle an exceptional status return from a write stream. */
+/* fop points to the ref for the stream. */
+/* ch may be any stream exceptional value. */
+/* Return 0, o_push_estack, or an error. */
+private int
+handle_write_status(int ch, const ref *fop, const uint *pindex,
+ int (*cont)(P1(os_ptr)))
+{ switch ( ch )
+ {
+ default: /* error */
+ return_error(e_ioerror);
+ case INTC:
+ case CALLC:
+ if ( pindex )
+ { ref index;
+ make_int(&index, *pindex);
+ return s_handle_write_exception(ch, fop, &index, 1, cont);
+ }
+ else
+ return s_handle_write_exception(ch, fop, NULL, 0, cont);
+ }
+}
diff --git a/pstoraster/zfilter.c b/pstoraster/zfilter.c
new file mode 100644
index 000000000..2966ec401
--- /dev/null
+++ b/pstoraster/zfilter.c
@@ -0,0 +1,422 @@
+/* Copyright (C) 1993, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* zfilter.c */
+/* Filter creation */
+#include "memory_.h"
+#include "ghost.h"
+#include "errors.h"
+#include "oper.h"
+#include "gsstruct.h"
+#include "ialloc.h"
+#include "idict.h"
+#include "idparam.h"
+#include "stream.h"
+#include "strimpl.h"
+#include "sfilter.h"
+#include "srlx.h"
+#include "sstring.h"
+#include "ifilter.h"
+#include "files.h" /* for filter_open, file_d'_buffer_size */
+
+/* Define whether we are including some non-standard filters for testing. */
+#define TEST
+
+/* <source> ASCIIHexEncode/filter <file> */
+/* <source> <dict_ignored> ASCIIHexEncode/filter <file> */
+private int
+zAXE(os_ptr op)
+{ return filter_write_simple(op, &s_AXE_template);
+}
+
+/* <target> ASCIIHexDecode/filter <file> */
+/* <target> <dict_ignored> ASCIIHexDecode/filter <file> */
+private int
+zAXD(os_ptr op)
+{ return filter_read_simple(op, &s_AXD_template);
+}
+
+/* <target> NullEncode/filter <file> */
+/* <target> <dict_ignored> NullEncode/filter <file> */
+private int
+zNullE(os_ptr op)
+{ return filter_write_simple(op, &s_NullE_template);
+}
+
+/* <source> <bool> PFBDecode/filter <file> */
+/* <source> <bool> <dict_ignored> PFBDecode/filter <file> */
+private int
+zPFBD(os_ptr op)
+{ stream_PFBD_state state;
+ os_ptr sop = op;
+ int npop = 1;
+
+ if ( r_has_type(op, t_dictionary) )
+ ++npop, --sop;
+ check_type(*sop, t_boolean);
+ state.binary_to_hex = sop->value.boolval;
+ return filter_read(op, npop, &s_PFBD_template, (stream_state *)&state,
+ 0);
+}
+
+/* <target> PSStringEncode/filter <file> */
+/* <target> <dict_ignored> PSStringEncode/filter <file> */
+private int
+zPSSE(os_ptr op)
+{ return filter_write_simple(op, &s_PSSE_template);
+}
+
+/* ------ RunLength filters ------ */
+
+/* Common setup for RLE and RLD filters. */
+private int
+rl_setup(os_ptr op, bool *eod)
+{ if ( r_has_type(op, t_dictionary) )
+ { int code;
+ check_dict_read(*op);
+ if ( (code = dict_bool_param(op, "EndOfData", true, eod)) < 0 )
+ return code;
+ return 1;
+ }
+ else
+ { *eod = true;
+ return 0;
+ }
+}
+
+/* <target> <record_size> RunLengthEncode/filter <file> */
+/* <target> <record_size> <dict> RunLengthEncode/filter <file> */
+private int
+zRLE(register os_ptr op)
+{ stream_RLE_state state;
+ int code = rl_setup(op, &state.EndOfData);
+ if ( code < 0 )
+ return code;
+ check_int_leu(op[-code], max_uint);
+ state.record_size = op->value.intval;
+ return filter_write(op, 1 + code, &s_RLE_template, (stream_state *)&state, 0);
+}
+
+/* <source> RunLengthDecode/filter <file> */
+/* <source> <dict> RunLengthDecode/filter <file> */
+private int
+zRLD(os_ptr op)
+{ stream_RLD_state state;
+ int code = rl_setup(op, &state.EndOfData);
+ if ( code < 0 )
+ return code;
+ return filter_read(op, code, &s_RLD_template, (stream_state *)&state, 0);
+}
+
+/* <source> <EODcount> <EODstring> SubFileDecode/filter <file> */
+/* <source> <EODcount> <EODstring> <dict_ignored> SubFileDecode/filter <file> */
+private int
+zSFD(os_ptr op)
+{ stream_SFD_state state;
+ os_ptr sop = op;
+ int npop = 2;
+
+ if ( r_has_type(op, t_dictionary) )
+ ++npop, --sop;
+ check_type(sop[-1], t_integer);
+ check_read_type(*sop, t_string);
+ if ( sop[-1].value.intval < 0 )
+ return_error(e_rangecheck);
+ state.count = sop[-1].value.intval;
+ state.eod.data = sop->value.const_bytes;
+ state.eod.size = r_size(sop);
+ return filter_read(op, npop, &s_SFD_template, (stream_state *)&state,
+ r_space(sop));
+}
+
+#ifdef TEST
+
+#include "store.h"
+
+/* <size> BigStringEncode/filter <file> */
+private int BSE_close(P1(stream *));
+private int
+zBSE(os_ptr op)
+{ stream *s;
+ byte *data;
+ long len;
+
+ check_type(op[-0], t_integer);
+ len = op[-0].value.intval;
+ if ( len < 0 )
+ return_error(e_rangecheck);
+
+ data = ialloc_string(len, "BigStringEncode(string)");
+ if ( !data )
+ return_error(e_VMerror);
+ s = file_alloc_stream(imemory, "BigStringEncode(stream)");
+ if ( !s )
+ { ifree_string(data, len, "BigStringEncode(string)");
+ return_error(e_VMerror);
+ }
+ swrite_string(s, data, len);
+ s->is_temp = 0;
+ s->read_id = 0;
+ s->procs.close = BSE_close;
+ s->save_close = BSE_close;
+ make_file(op,
+ ((a_write | a_execute) | icurrent_space),
+ s->write_id,
+ s);
+ return 0;
+}
+private int
+BSE_close(stream *s)
+{ return 0;
+}
+
+#endif /* TEST */
+
+/* ------ Utilities ------ */
+
+/* Forward references */
+private int filter_ensure_buf(P3(stream **, uint, bool));
+
+/* Set up an input filter. */
+const stream_procs s_new_read_procs =
+{ s_std_noavailable, s_std_noseek, s_std_read_reset,
+ s_std_read_flush, s_filter_close
+};
+int
+filter_read(os_ptr op, int npop, const stream_template *template,
+ stream_state *st, uint space)
+{ uint min_size = template->min_out_size + max_min_left;
+ uint save_space = ialloc_space(idmemory);
+ register os_ptr sop = op - npop;
+ stream *s;
+ stream *sstrm;
+ int code;
+
+ /* Check to make sure that the underlying data */
+ /* can function as a source for reading. */
+ switch ( r_type(sop) )
+ {
+ case t_string:
+ check_read(*sop);
+ ialloc_set_space(idmemory, max(space, r_space(sop)));
+ sstrm = file_alloc_stream(imemory,
+ "filter_read(string stream)");
+ if ( sstrm == 0 )
+ { code = gs_note_error(e_VMerror);
+ goto out;
+ }
+ sread_string(sstrm, sop->value.bytes, r_size(sop));
+ sstrm->is_temp = 1;
+ break;
+ case t_file:
+ check_read_known_file(sstrm, sop, return);
+ ialloc_set_space(idmemory, max(space, r_space(sop)));
+ goto ens;
+ default:
+ check_proc(*sop);
+ ialloc_set_space(idmemory, max(space, r_space(sop)));
+ code = sread_proc(sop, &sstrm);
+ if ( code < 0 )
+ goto out;
+ sstrm->is_temp = 2;
+ens: code = filter_ensure_buf(&sstrm,
+ template->min_in_size +
+ sstrm->state->template->min_out_size,
+ false);
+ if ( code < 0 )
+ goto out;
+ break;
+ }
+ if ( min_size < 128 )
+ min_size = file_default_buffer_size;
+ code = filter_open("r", min_size, (ref *)sop,
+ &s_new_read_procs, template, st);
+ if ( code < 0 )
+ goto out;
+ s = fptr(sop);
+ s->strm = sstrm;
+ pop(npop);
+out: ialloc_set_space(idmemory, save_space);
+ return code;
+}
+int
+filter_read_simple(os_ptr op, const stream_template *template)
+{ return filter_read(op, (r_has_type(op, t_dictionary) ? 1 : 0),
+ template, NULL, 0);
+}
+
+/* Set up an output filter. */
+const stream_procs s_new_write_procs =
+{ s_std_noavailable, s_std_noseek, s_std_write_reset,
+ s_std_write_flush, s_filter_close
+};
+int
+filter_write(os_ptr op, int npop, const stream_template *template,
+ stream_state *st, uint space)
+{ uint min_size = template->min_in_size + max_min_left;
+ uint save_space = ialloc_space(idmemory);
+ register os_ptr sop = op - npop;
+ stream *s;
+ stream *sstrm;
+ int code;
+
+ /* Check to make sure that the underlying data */
+ /* can function as a sink for writing. */
+ switch ( r_type(sop) )
+ {
+ case t_string:
+ check_write(*sop);
+ ialloc_set_space(idmemory, max(space, r_space(sop)));
+ sstrm = file_alloc_stream(imemory,
+ "filter_write(string)");
+ if ( sstrm == 0 )
+ { code = gs_note_error(e_VMerror);
+ goto out;
+ }
+ swrite_string(sstrm, sop->value.bytes, r_size(sop));
+ sstrm->is_temp = 1;
+ break;
+ case t_file:
+ check_write_known_file(sstrm, sop, return);
+ ialloc_set_space(idmemory, max(space, r_space(sop)));
+ goto ens;
+ default:
+ check_proc(*sop);
+ ialloc_set_space(idmemory, max(space, r_space(sop)));
+ code = swrite_proc(sop, &sstrm);
+ if ( code < 0 )
+ goto out;
+ sstrm->is_temp = 2;
+ens: code = filter_ensure_buf(&sstrm,
+ template->min_out_size +
+ sstrm->state->template->min_in_size,
+ true);
+ if ( code < 0 )
+ goto out;
+ break;
+ }
+ if ( min_size < 128 )
+ min_size = file_default_buffer_size;
+ code = filter_open("w", min_size, (ref *)sop,
+ &s_new_write_procs, template, st);
+ if ( code < 0 )
+ goto out;
+ s = fptr(sop);
+ s->strm = sstrm;
+ pop(npop);
+out: ialloc_set_space(idmemory, save_space);
+ return code;
+}
+int
+filter_write_simple(os_ptr op, const stream_template *template)
+{ return filter_write(op, (r_has_type(op, t_dictionary) ? 1 : 0),
+ template, NULL, 0);
+}
+
+/* Define a byte-at-a-time NullDecode filter for intermediate buffers. */
+/* (The standard NullDecode filter can read ahead too far.) */
+private int
+s_Null1D_process(stream_state *st, stream_cursor_read *pr,
+ stream_cursor_write *pw, bool last)
+{ if ( pr->ptr >= pr->limit )
+ return 0;
+ if ( pw->ptr >= pw->limit )
+ return 1;
+ *++(pw->ptr) = *++(pr->ptr);
+ return 1;
+}
+private const stream_template s_Null1D_template =
+{ &st_stream_state, NULL, s_Null1D_process, 1, 1
+};
+
+/* Ensure a minimum buffer size for a filter. */
+/* This may require creating an intermediate stream. */
+private int
+filter_ensure_buf(stream **ps, uint min_buf_size, bool writing)
+{ stream *s = *ps;
+ uint min_size = min_buf_size + max_min_left;
+ stream *bs;
+ ref bsop;
+ int code;
+
+ if ( s->modes == 0 /* stream is closed */ || s->bsize >= min_size )
+ return 0;
+ /* Otherwise, allocate an intermediate stream. */
+ if ( s->cbuf == 0 )
+ { /* This is a newly created procedure stream. */
+ /* Just allocate a buffer for it. */
+ uint len = max(min_size, 128);
+ byte *buf = ialloc_bytes(len, "filter_ensure_buf");
+ if ( buf == 0 )
+ return_error(e_VMerror);
+ s->cbuf = buf;
+ s->srptr = s->srlimit = s->swptr = buf - 1;
+ s->swlimit = buf - 1 + len;
+ s->bsize = s->cbsize = len;
+ return 0;
+ }
+ else
+ { /* Allocate an intermediate stream. */
+ if ( writing )
+ code = filter_open("w", min_size, &bsop, &s_new_write_procs,
+ &s_NullE_template, NULL);
+ else
+ code = filter_open("r", min_size, &bsop, &s_new_read_procs,
+ &s_Null1D_template, NULL);
+ if ( code < 0 )
+ return code;
+ bs = fptr(&bsop);
+ bs->strm = s;
+ bs->is_temp = 2;
+ *ps = bs;
+ return code;
+ }
+}
+
+/* Mark a (filter) stream as temporary. */
+/* We define this here to avoid importing stream.h into zf*.c. */
+void
+filter_mark_temp(const ref *fop, int is_temp)
+{ fptr(fop)->is_temp = is_temp;
+}
+
+/* ------ Initialization procedure ------ */
+
+BEGIN_OP_DEFS(zfilter_op_defs) {
+ /* We enter PSStringEncode and SubFileDecode (only) */
+ /* as separate operators. */
+ {"1.psstringencode", zPSSE},
+ {"3.subfiledecode", zSFD},
+ op_def_begin_filter(),
+ {"1ASCIIHexEncode", zAXE},
+ {"1ASCIIHexDecode", zAXD},
+ {"1NullEncode", zNullE},
+ {"2PFBDecode", zPFBD},
+ {"1PSStringEncode", zPSSE},
+ {"2RunLengthEncode", zRLE},
+ {"1RunLengthDecode", zRLD},
+ {"3SubFileDecode", zSFD},
+#ifdef TEST
+ {"1BigStringEncode", zBSE},
+#endif
+END_OP_DEFS(0) }
diff --git a/pstoraster/zfilter2.c b/pstoraster/zfilter2.c
new file mode 100644
index 000000000..c9eba265f
--- /dev/null
+++ b/pstoraster/zfilter2.c
@@ -0,0 +1,154 @@
+/* Copyright (C) 1991, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* zfilter2.c */
+/* Additional filter creation */
+#include "memory_.h"
+#include "ghost.h"
+#include "errors.h"
+#include "oper.h"
+#include "gsstruct.h"
+#include "ialloc.h"
+#include "idict.h"
+#include "idparam.h"
+#include "store.h"
+#include "strimpl.h"
+#include "sfilter.h"
+#include "scfx.h"
+#include "slzwx.h"
+#include "spdiffx.h"
+#include "spngpx.h"
+#include "ifilter.h"
+
+/* Import setup code from zfdecode.c */
+int zcf_setup(P2(os_ptr op, stream_CF_state *pcfs));
+int zlz_setup(P2(os_ptr op, stream_LZW_state *plzs));
+int zpd_setup(P2(os_ptr op, stream_PDiff_state *ppds));
+int zpp_setup(P2(os_ptr op, stream_PNGP_state *ppps));
+
+/* ------ CCITTFaxEncode filter ------ */
+
+/* <target> <dict> CCITTFaxEncode/filter <file> */
+private int
+zCFE(os_ptr op)
+{ stream_CFE_state cfs;
+ int code;
+
+ check_type(*op, t_dictionary);
+ check_dict_read(*op);
+ code = zcf_setup(op, (stream_CF_state *)&cfs);
+ if ( code < 0 )
+ return code;
+ return filter_write(op, 1, &s_CFE_template, (stream_state *)&cfs, 0);
+}
+
+/* ------ Common setup for possibly pixel-oriented encoding filters ------ */
+
+int
+filter_write_predictor(os_ptr op, int npop, const stream_template *template,
+ stream_state *st)
+{ int predictor, code;
+ stream_PDiff_state pds;
+ stream_PNGP_state pps;
+
+ if ( r_has_type(op, t_dictionary) )
+ { if ( (code = dict_int_param(op, "Predictor", 0, 15, 1, &predictor)) < 0 )
+ return code;
+ switch ( predictor )
+ {
+ case 0: /* identity */
+ predictor = 1;
+ case 1: /* identity */
+ break;
+ case 2: /* componentwise horizontal differencing */
+ code = zpd_setup(op, &pds);
+ break;
+ case 10: case 11: case 12: case 13: case 14: case 15:
+ /* PNG prediction */
+ code = zpp_setup(op, &pps);
+ break;
+ default:
+ return_error(e_rangecheck);
+ }
+ if ( code < 0 )
+ return code;
+ }
+ else
+ predictor = 1;
+ if ( predictor == 1 )
+ return filter_write(op, npop, template, st, 0);
+ { /* We need to cascade filters. */
+ ref rtarget, rdict, rfd;
+ int code;
+
+ /* Save the operands, just in case. */
+ ref_assign(&rtarget, op - 1);
+ ref_assign(&rdict, op);
+ code = filter_write(op, 1, template, st, 0);
+ if ( code < 0 )
+ return code;
+ /* filter_write changed osp.... */
+ op = osp;
+ ref_assign(&rfd, op);
+ code =
+ (predictor == 2 ?
+ filter_read(op, 0, &s_PDiffE_template, (stream_state *)&pds, 0) :
+ filter_read(op, 0, &s_PNGPE_template, (stream_state *)&pps, 0));
+ if ( code < 0 )
+ { /* Restore the operands. Don't bother trying to clean up */
+ /* the first stream. */
+ osp = ++op;
+ ref_assign(op - 1, &rtarget);
+ ref_assign(op, &rdict);
+ return code;
+ }
+ filter_mark_temp(&rfd, 2); /* Mark the compression stream as temporary. */
+ return code;
+ }
+}
+
+/* ------ LZW encoding filter ------ */
+
+/* <target> LZWEncode/filter <file> */
+/* <target> <dict> LZWEncode/filter <file> */
+/* Note: the default implementation of this filter, in slzwce.c, */
+/* does not use any algorithms that could reasonably be claimed */
+/* to be subject to Unisys' Welch Patent. */
+private int
+zLZWE(os_ptr op)
+{ stream_LZW_state lzs;
+ int code = zlz_setup(op, &lzs);
+
+ if ( code < 0 )
+ return code;
+ return filter_write_predictor(op, code, &s_LZWE_template,
+ (stream_state *)&lzs);
+}
+
+/* ================ Initialization procedure ================ */
+
+BEGIN_OP_DEFS(zfilter2_op_defs) {
+ op_def_begin_filter(),
+ {"2CCITTFaxEncode", zCFE},
+ {"1LZWEncode", zLZWE},
+END_OP_DEFS(0) }
diff --git a/pstoraster/zfilterx.c b/pstoraster/zfilterx.c
new file mode 100644
index 000000000..32a07ae1d
--- /dev/null
+++ b/pstoraster/zfilterx.c
@@ -0,0 +1,319 @@
+/* Copyright (C) 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* zfilterx.c */
+/* Extended (non-standard) filter creation */
+#include "memory_.h"
+#include "ghost.h"
+#include "errors.h"
+#include "oper.h"
+#include "gsstruct.h"
+#include "ialloc.h"
+#include "idict.h"
+#include "idparam.h"
+#include "store.h"
+#include "strimpl.h"
+#include "sfilter.h"
+#include "sbwbs.h"
+#include "sbhc.h"
+#include "sbtx.h"
+#include "shcgen.h"
+#include "smtf.h"
+#include "spcxx.h"
+#include "ifilter.h"
+
+/* ------ Bounded Huffman code filters ------ */
+
+/* Common setup for encoding and decoding filters */
+private int
+bhc_setup(os_ptr op, stream_BHC_state *pbhcs)
+{ int code;
+ int num_counts;
+ int data[max_hc_length + 1 + 256 + max_zero_run + 1];
+ uint dsize;
+ int i;
+ uint num_values, accum;
+ ushort *counts;
+ ushort *values;
+
+ check_type(*op, t_dictionary);
+ check_dict_read(*op);
+ if ( (code = dict_bool_param(op, "FirstBitLowOrder", false,
+ &pbhcs->FirstBitLowOrder)) < 0 ||
+ (code = dict_int_param(op, "MaxCodeLength", 1, max_hc_length,
+ max_hc_length, &num_counts)) < 0 ||
+ (code = dict_bool_param(op, "EndOfData", true,
+ &pbhcs->EndOfData)) < 0 ||
+ (code = dict_uint_param(op, "EncodeZeroRuns", 2, 256,
+ 256, &pbhcs->EncodeZeroRuns)) < 0 ||
+ /* Note: the code returned from the following call */
+ /* is actually the number of elements in the array. */
+ (code = dict_int_array_param(op, "Tables", countof(data),
+ data)) <= 0
+ )
+ return (code < 0 ? code : gs_note_error(e_rangecheck));
+ dsize = code;
+ if ( dsize <= num_counts + 2 )
+ return_error(e_rangecheck);
+ for ( i = 0, num_values = 0, accum = 0; i <= num_counts;
+ i++, accum <<= 1
+ )
+ { int count = data[i];
+ if ( count < 0 )
+ return_error(e_rangecheck);
+ num_values += count;
+ accum += count;
+ }
+ if ( dsize != num_counts + 1 + num_values ||
+ accum != 1 << (num_counts + 1) ||
+ pbhcs->EncodeZeroRuns >
+ (pbhcs->EndOfData ? num_values - 1 : num_values)
+ )
+ return_error(e_rangecheck);
+ for ( ; i < num_counts + 1 + num_values; i++ )
+ { int value = data[i];
+ if ( value < 0 || value >= num_values )
+ return_error(e_rangecheck);
+ }
+ pbhcs->definition.counts = counts =
+ (ushort *)ialloc_byte_array(num_counts + 1, sizeof(ushort),
+ "bhc_setup(counts)");
+ pbhcs->definition.values = values =
+ (ushort *)ialloc_byte_array(num_values, sizeof(ushort),
+ "bhc_setup(values)");
+ if ( counts == 0 || values == 0 )
+ { ifree_object(values, "bhc_setup(values)");
+ ifree_object(counts, "bhc_setup(counts)");
+ return_error(e_VMerror);
+ }
+ for ( i = 0; i <= num_counts; i++ )
+ counts[i] = data[i];
+ pbhcs->definition.counts = counts;
+ pbhcs->definition.num_counts = num_counts;
+ for ( i = 0; i < num_values; i++ )
+ values[i] = data[i + num_counts + 1];
+ pbhcs->definition.values = values;
+ pbhcs->definition.num_values = num_values;
+ return 0;
+}
+
+/* <target> <dict> BoundedHuffmanEncode/filter <file> */
+private int
+zBHCE(os_ptr op)
+{ stream_BHCE_state bhcs;
+ int code;
+ code = bhc_setup(op, (stream_BHC_state *)&bhcs);
+ if ( code < 0 )
+ return code;
+ return filter_write(op, 1, &s_BHCE_template, (stream_state *)&bhcs, 0);
+}
+
+/* <source> <dict> BoundedHuffmanDecode/filter <file> */
+private int
+zBHCD(os_ptr op)
+{ stream_BHCD_state bhcs;
+ int code;
+ code = bhc_setup(op, (stream_BHC_state *)&bhcs);
+ if ( code < 0 )
+ return code;
+ return filter_read(op, 1, &s_BHCD_template, (stream_state *)&bhcs, 0);
+}
+
+/* <array> <max_length> .computecodes <array> */
+/* The first max_length+1 elements of the array will be filled in with */
+/* the code counts; the remaining elements will be replaced with */
+/* the code values. This is the form needed for the Tables element of */
+/* the dictionary parameter for the BoundedHuffman filters. */
+private int
+zcomputecodes(os_ptr op)
+{ os_ptr op1 = op - 1;
+ uint asize;
+ hc_definition def;
+ ushort *data;
+ long *freqs;
+ int code = 0;
+ check_type(*op, t_integer);
+ check_write_type(*op1, t_array);
+ asize = r_size(op1);
+ if ( op->value.intval < 1 || op->value.intval > max_hc_length )
+ return_error(e_rangecheck);
+ def.num_counts = op->value.intval;
+ if ( asize < def.num_counts + 2 )
+ return_error(e_rangecheck);
+ def.num_values = asize - (def.num_counts + 1);
+ data = (ushort *)gs_alloc_byte_array(imemory, asize, sizeof(ushort),
+ "zcomputecodes");
+ freqs = (long *)gs_alloc_byte_array(imemory, def.num_values,
+ sizeof(long),
+ "zcomputecodes(freqs)");
+ if ( data == 0 || freqs == 0 )
+ code = gs_note_error(e_VMerror);
+ else
+ { uint i;
+ def.counts = data;
+ def.values = data + (def.num_counts + 1);
+ for ( i = 0; i < def.num_values; i++ )
+ { const ref *pf =
+ op1->value.const_refs + i + def.num_counts + 1;
+ if ( !r_has_type(pf, t_integer) )
+ { code = gs_note_error(e_typecheck);
+ break;
+ }
+ freqs[i] = pf->value.intval;
+ }
+ if ( !code )
+ { code = hc_compute(&def, freqs, imemory);
+ if ( code >= 0 )
+ { /* Copy back results. */
+ for ( i = 0; i < asize; i++ )
+ make_int(op1->value.refs + i, data[i]);
+ }
+ }
+ }
+ gs_free_object(imemory, freqs, "zcomputecodes(freqs)");
+ gs_free_object(imemory, data, "zcomputecodes");
+ if ( code < 0 )
+ return code;
+ pop(1);
+ return code;
+}
+
+/* ------ Burrows/Wheeler block sorting filters ------ */
+
+/* Common setup for encoding and decoding filters */
+private int
+bwbs_setup(os_ptr op, stream_BWBS_state *pbwbss)
+{ int code;
+ if ( (code = dict_int_param(op, "BlockSize",
+ 1, max_int / sizeof(int) - 10, 16384,
+ &pbwbss->BlockSize)) < 0
+ )
+ return code;
+ return 0;
+}
+
+/* <target> <dict> BWBlockSortEncode/filter <file> */
+private int
+zBWBSE(os_ptr op)
+{ stream_BWBSE_state bwbss;
+ int code;
+ check_type(*op, t_dictionary);
+ check_dict_read(*op);
+ code = bwbs_setup(op, (stream_BWBS_state *)&bwbss);
+ if ( code < 0 )
+ return code;
+ return filter_write(op, 1, &s_BWBSE_template, (stream_state *)&bwbss, 0);
+}
+
+/* <source> <dict> BWBlockSortDecode/filter <file> */
+private int
+zBWBSD(os_ptr op)
+{ stream_BWBSD_state bwbss;
+ int code;
+ code = bwbs_setup(op, (stream_BWBS_state *)&bwbss);
+ if ( code < 0 )
+ return code;
+ return filter_read(op, 1, &s_BWBSD_template, (stream_state *)&bwbss, 0);
+}
+
+/* ------ Byte translation filters ------ */
+
+/* Common setup */
+private int
+bt_setup(os_ptr op, stream_BT_state *pbts)
+{ int npop = 1;
+ if ( r_has_type(op, t_dictionary) )
+ ++npop, --op;
+ check_read_type(*op, t_string);
+ if ( r_size(op) != 256 )
+ return_error(e_rangecheck);
+ memcpy(pbts->table, op->value.const_bytes, 256);
+ return npop;
+}
+
+/* <target> <table> ByteTranslateEncode/filter <file> */
+/* <target> <table> <dict_ignored> ByteTranslateEncode/filter <file> */
+private int
+zBTE(os_ptr op)
+{ stream_BT_state bts;
+ int code = bt_setup(op, &bts);
+
+ if ( code < 0 )
+ return code;
+ return filter_write(op, code, &s_BTE_template, (stream_state *)&bts,
+ 0);
+}
+
+/* <target> <table> ByteTranslateDecode/filter <file> */
+/* <target> <table> <dict_ignored> ByteTranslateDecode/filter <file> */
+private int
+zBTD(os_ptr op)
+{ stream_BT_state bts;
+ int code = bt_setup(op, &bts);
+
+ if ( code < 0 )
+ return code;
+ return filter_read(op, code, &s_BTD_template, (stream_state *)&bts, 0);
+}
+
+/* ------ Move-to-front filters ------ */
+
+/* <target> MoveToFrontEncode/filter <file> */
+/* <target> <dict_ignored> MoveToFrontEncode/filter <file> */
+private int
+zMTFE(os_ptr op)
+{ return filter_write_simple(op, &s_MTFE_template);
+}
+
+/* <source> MoveToFrontDecode/filter <file> */
+/* <source> <dict_ignored> MoveToFrontDecode/filter <file> */
+private int
+zMTFD(os_ptr op)
+{ return filter_read_simple(op, &s_MTFD_template);
+}
+
+/* ------ PCX decoding filter ------ */
+
+/* <source> PCXDecode/filter <file> */
+/* <source> <dict_ignored> PCXDecode/filter <file> */
+private int
+zPCXD(os_ptr op)
+{ return filter_read_simple(op, &s_PCXD_template);
+}
+
+/* ================ Initialization procedure ================ */
+
+BEGIN_OP_DEFS(zfilterx_op_defs) {
+ {"2.computecodes", zcomputecodes}, /* not a filter */
+ op_def_begin_filter(),
+ /* Non-standard filters */
+ {"2BoundedHuffmanEncode", zBHCE},
+ {"2BoundedHuffmanDecode", zBHCD},
+ {"2BWBlockSortEncode", zBWBSE},
+ {"2BWBlockSortDecode", zBWBSD},
+ {"2ByteTranslateEncode", zBTE},
+ {"2ByteTranslateDecode", zBTD},
+ {"1MoveToFrontEncode", zMTFE},
+ {"1MoveToFrontDecode", zMTFD},
+ {"1PCXDecode", zPCXD},
+END_OP_DEFS(0) }
diff --git a/pstoraster/zfname.c b/pstoraster/zfname.c
new file mode 100644
index 000000000..a2ea8b81e
--- /dev/null
+++ b/pstoraster/zfname.c
@@ -0,0 +1,111 @@
+/* Copyright (C) 1993 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* zfname.c */
+/* File name utilities */
+#include "memory_.h"
+#include "ghost.h"
+#include "errors.h"
+#include "oper.h"
+#include "ialloc.h"
+#include "stream.h"
+#include "gxiodev.h" /* must come after stream.h */
+#include "fname.h"
+
+/* Parse a file name into device and individual name. */
+/* The device may be NULL, or the name may be NULL, but not both. */
+/* According to the Adobe documentation, %device and %device% */
+/* are equivalent; both return name==NULL. */
+int
+parse_file_name(const ref *op, parsed_file_name *pfn)
+{ const byte *pname;
+ uint len, dlen;
+ const byte *pdelim;
+ gx_io_device *iodev;
+
+ check_read_type(*op, t_string);
+ len = r_size(op);
+ pname = op->value.const_bytes;
+ if ( len == 0 )
+ return_error(e_undefinedfilename);
+ if ( pname[0] != '%' ) /* no device */
+ { pfn->iodev = NULL;
+ pfn->fname = (const char *)pname;
+ pfn->len = len;
+ return 0;
+ }
+ pdelim = (const byte *)memchr(pname + 1, '%', len - 1);
+ if ( pdelim == NULL ) /* %device */
+ dlen = len;
+ else if ( pdelim[1] == 0 ) /* %device% */
+ { pdelim = NULL;
+ dlen = len;
+ }
+ else
+ { dlen = pdelim - pname;
+ pdelim++, len--;
+ }
+ iodev = gs_findiodevice(pname, dlen);
+ if ( iodev == 0 )
+ return_error(e_undefinedfilename);
+ pfn->iodev = iodev;
+ pfn->fname = (const char *)pdelim;
+ pfn->len = len - dlen;
+ return 0;
+}
+
+/* Parse a real (non-device) file name and convert to a C string. */
+int
+parse_real_file_name(const ref *op, parsed_file_name *pfn, client_name_t cname)
+{ int code = parse_file_name(op, pfn);
+ if ( code < 0 )
+ return code;
+ if ( pfn->len == 0 )
+ return_error(e_invalidfileaccess); /* device only */
+ return terminate_file_name(pfn, cname);
+}
+
+/* Convert a file name to a C string by adding a null terminator. */
+int
+terminate_file_name(parsed_file_name *pfn, client_name_t cname)
+{ uint len = pfn->len;
+ ref fnref;
+ const char *fname;
+ if ( pfn->iodev == NULL ) /* no device */
+ pfn->iodev = iodev_default;
+ fnref.value.const_bytes = (const byte *)pfn->fname;
+ r_set_size(&fnref, len);
+ fname = ref_to_string(&fnref, imemory, cname);
+ if ( fname == 0 )
+ return_error(e_VMerror);
+ pfn->fname = fname;
+ pfn->len = len + 1; /* null terminator */
+ return 0;
+}
+
+/* Free a file name that was copied to a C string. */
+void
+free_file_name(parsed_file_name *pfn, client_name_t cname)
+{ if ( pfn->fname != 0 )
+ ifree_string((byte *)pfn->fname, pfn->len, cname);
+}
diff --git a/pstoraster/zfont.c b/pstoraster/zfont.c
new file mode 100644
index 000000000..3fbf19a51
--- /dev/null
+++ b/pstoraster/zfont.c
@@ -0,0 +1,405 @@
+/* Copyright (C) 1989, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* zfont.c */
+/* Generic font operators */
+#include "ghost.h"
+#include "errors.h"
+#include "oper.h"
+#include "gsstruct.h" /* for registering root */
+#include "gzstate.h" /* must precede gxdevice */
+#include "gxdevice.h" /* must precede gxfont */
+#include "gschar.h"
+#include "gxfont.h"
+#include "gxfcache.h"
+#include "bfont.h"
+#include "ialloc.h"
+#include "idict.h"
+#include "igstate.h"
+#include "iname.h" /* for name_mark_index */
+#include "isave.h"
+#include "store.h"
+#include "ivmspace.h"
+
+/* Forward references */
+private int make_font(P2(os_ptr, const gs_matrix *));
+private void make_uint_array(P3(os_ptr, const uint *, int));
+
+/* The (global) font directory */
+gs_font_dir *ifont_dir = 0; /* needed for buildfont */
+private gs_gc_root_t font_dir_root;
+
+/* Initialize the font operators */
+private bool
+cc_mark_glyph_name(gs_glyph glyph)
+{ return name_mark_index((uint)glyph);
+}
+private void
+zfont_init(void)
+{ ifont_dir = gs_font_dir_alloc(imemory);
+ ifont_dir->ccache.mark_glyph = cc_mark_glyph_name;
+ gs_register_struct_root(imemory, &font_dir_root,
+ (void **)&ifont_dir, "ifont_dir");
+}
+
+/* <font> <scale> scalefont <new_font> */
+private int
+zscalefont(register os_ptr op)
+{ int code;
+ float scale;
+ gs_matrix mat;
+ if ( (code = num_params(op, 1, &scale)) < 0 )
+ return code;
+ if ( (code = gs_make_scaling(scale, scale, &mat)) < 0 )
+ return code;
+ return make_font(op, &mat);
+}
+
+/* <font> <matrix> makefont <new_font> */
+private int
+zmakefont(register os_ptr op)
+{ int code;
+ gs_matrix mat;
+ if ( (code = read_matrix(op, &mat)) < 0 )
+ return code;
+ return make_font(op, &mat);
+}
+
+/* <font> setfont - */
+int
+zsetfont(register os_ptr op)
+{ gs_font *pfont;
+ int code = font_param(op, &pfont);
+ if ( code < 0 || (code = gs_setfont(igs, pfont)) < 0 )
+ return code;
+ pop(1);
+ return code;
+}
+
+/* - currentfont <font> */
+private int
+zcurrentfont(register os_ptr op)
+{ push(1);
+ *op = *pfont_dict(gs_currentfont(igs));
+ return 0;
+}
+
+/* - cachestatus <mark> <bsize> <bmax> <msize> <mmax> <csize> <cmax> <blimit> */
+private int
+zcachestatus(register os_ptr op)
+{ uint status[7];
+ gs_cachestatus(ifont_dir, status);
+ push(7);
+ make_uint_array(op - 6, status, 7);
+ return 0;
+}
+
+/* <blimit> setcachelimit - */
+private int
+zsetcachelimit(register os_ptr op)
+{ check_int_leu(*op, max_uint);
+ gs_setcachelimit(ifont_dir, (uint)op->value.intval);
+ pop(1);
+ return 0;
+}
+
+/* <mark> <size> <lower> <upper> setcacheparams - */
+private int
+zsetcacheparams(register os_ptr op)
+{ uint params[3];
+ int i, code;
+ os_ptr opp = op;
+
+ for ( i = 0; i < 3 && !r_has_type(opp, t_mark) ; i++, opp-- )
+ { check_int_leu(*opp, max_uint);
+ params[i] = opp->value.intval;
+ }
+ switch ( i )
+ {
+ case 3:
+ if ( (code = gs_setcachesize(ifont_dir, params[2])) < 0 )
+ return code;
+ case 2:
+ if ( (code = gs_setcachelower(ifont_dir, params[1])) < 0 )
+ return code;
+ case 1:
+ if ( (code = gs_setcacheupper(ifont_dir, params[0])) < 0 )
+ return code;
+ case 0: ;
+ }
+ return zcleartomark(op);
+}
+
+/* - currentcacheparams <mark> <size> <lower> <upper> */
+private int
+zcurrentcacheparams(register os_ptr op)
+{ uint params[3];
+
+ params[0] = gs_currentcachesize(ifont_dir);
+ params[1] = gs_currentcachelower(ifont_dir);
+ params[2] = gs_currentcacheupper(ifont_dir);
+ push(4);
+ make_mark(op - 3);
+ make_uint_array(op - 2, params, 3);
+ return 0;
+}
+
+/* ------ Initialization procedure ------ */
+
+BEGIN_OP_DEFS(zfont_op_defs) {
+ {"0currentfont", zcurrentfont},
+ {"2makefont", zmakefont},
+ {"2scalefont", zscalefont},
+ {"1setfont", zsetfont},
+ {"0cachestatus", zcachestatus},
+ {"1setcachelimit", zsetcachelimit},
+ {"1setcacheparams", zsetcacheparams},
+ {"0currentcacheparams", zcurrentcacheparams},
+END_OP_DEFS(zfont_init) }
+
+/* ------ Subroutines ------ */
+
+/* Validate a font parameter. */
+int
+font_param(const ref *pfdict, gs_font **ppfont)
+{ /* Check that pfdict is a read-only dictionary, */
+ /* and that it has a FID entry. */
+ ref *pid;
+ gs_font *pfont;
+
+ check_type(*pfdict, t_dictionary);
+ if ( dict_find_string(pfdict, "FID", &pid) <= 0 ||
+ !r_has_type(pid, t_fontID)
+ )
+ return_error(e_invalidfont);
+ pfont = r_ptr(pid, gs_font);
+ *ppfont = pfont;
+ if ( pfont == 0 )
+ return_error(e_invalidfont); /* unregistered font */
+ return 0;
+}
+
+/* Add the FID entry to a font dictionary. */
+int
+add_FID(ref *fp /* t_dictionary */, gs_font *pfont)
+{ ref fid;
+ make_tav_new(&fid, t_fontID, a_readonly | icurrent_space,
+ pstruct, (void *)pfont);
+ return dict_put_string(fp, "FID", &fid);
+}
+
+/* Make a transformed font (common code for makefont/scalefont). */
+private int
+make_font(os_ptr op, const gs_matrix *pmat)
+{ os_ptr fp = op - 1;
+ gs_font *oldfont, *newfont;
+ int code;
+ ref *pencoding;
+
+ code = font_param(fp, &oldfont);
+ if ( code < 0 )
+ return code;
+ { uint space = ialloc_space(idmemory);
+ ialloc_set_space(idmemory, r_space(fp));
+ if ( dict_find_string(fp, "Encoding", &pencoding) <= 0 ||
+ !r_is_array(pencoding)
+ )
+ code = gs_note_error(e_invalidfont);
+ else
+ { /*
+ * Temporarily substitute the new dictionary
+ * for the old one, in case the Encoding changed.
+ */
+ ref olddict;
+ olddict = *pfont_dict(oldfont);
+ *pfont_dict(oldfont) = *fp;
+ code = gs_makefont(ifont_dir, oldfont, pmat, &newfont);
+ *pfont_dict(oldfont) = olddict;
+ }
+ ialloc_set_space(idmemory, space);
+ }
+ if ( code < 0 )
+ return code;
+ /*
+ * We have to allow for the possibility that the font's Encoding
+ * is different from that of the base font. Note that the
+ * font_data of the new font was simply copied from the old one.
+ */
+ if ( !obj_eq(pencoding, &pfont_data(newfont)->Encoding) )
+ { if ( newfont->FontType == ft_composite )
+ return_error(e_rangecheck);
+ /* We should really do validity checking here.... */
+ ref_assign(&pfont_data(newfont)->Encoding, pencoding);
+ lookup_gs_simple_font_encoding((gs_font_base *)newfont);
+ }
+ *fp = *pfont_dict(newfont);
+ pop(1);
+ return 0;
+}
+/* Create the transformed font dictionary. */
+/* This is the make_font completion procedure for all non-composite fonts */
+/* created at the interpreter level (see build_gs_simple_font in zfont2.c.) */
+int
+zbase_make_font(gs_font_dir *pdir, const gs_font *oldfont,
+ const gs_matrix *pmat, gs_font **ppfont)
+{ /* We must call gs_base_make_font so that the XUID gets copied */
+ /* if necessary. */
+ int code = gs_base_make_font(pdir, oldfont, pmat, ppfont);
+ if ( code < 0 )
+ return code;
+ return zdefault_make_font(pdir, oldfont, pmat, ppfont);
+}
+int
+zdefault_make_font(gs_font_dir *pdir, const gs_font *oldfont,
+ const gs_matrix *pmat, gs_font **ppfont)
+{ gs_font *newfont = *ppfont;
+ ref *fp = pfont_dict(oldfont);
+ font_data *pdata;
+ ref newdict, newmat, scalemat;
+ uint dlen = dict_maxlength(fp);
+ uint mlen = dict_length(fp) + 3; /* FontID, OrigFont, ScaleMatrix */
+ int code;
+
+ if ( dlen < mlen )
+ dlen = mlen;
+ if ( (pdata = ialloc_struct(font_data, &st_font_data,
+ "make_font(font_data)")) == 0
+ )
+ return_error(e_VMerror);
+ if ( (code = dict_create(dlen, &newdict)) < 0 ||
+ (code = dict_copy(fp, &newdict)) < 0 ||
+ (code = ialloc_ref_array(&newmat, a_all, 12, "make_font(matrices)")) < 0
+ )
+ return code;
+ refset_null(newmat.value.refs, 12);
+ ref_assign(&scalemat, &newmat);
+ r_set_size(&scalemat, 6);
+ scalemat.value.refs += 6;
+ /*
+ * Create the scaling matrix. We could do this several different
+ * ways: by "dividing" the new FontMatrix by the base FontMatrix, by
+ * multiplying the current scaling matrix by a ScaleMatrix kept in
+ * the gs_font, or by multiplying the current scaling matrix by the
+ * ScaleMatrix from the font dictionary. We opt for the last of
+ * these.
+ */
+ { gs_matrix scale, prev_scale;
+ ref *ppsm;
+ if ( !(dict_find_string(fp, "ScaleMatrix", &ppsm) > 0 &&
+ read_matrix(ppsm, &prev_scale) >= 0 &&
+ gs_matrix_multiply(pmat, &prev_scale, &scale) >= 0)
+ )
+ scale = *pmat;
+ write_matrix(&scalemat, &scale);
+ }
+ r_clear_attrs(&scalemat, a_write);
+ r_set_size(&newmat, 6);
+ write_matrix(&newmat, &newfont->FontMatrix);
+ r_clear_attrs(&newmat, a_write);
+ if ( (code = dict_put_string(&newdict, "FontMatrix", &newmat)) < 0 ||
+ (code = dict_put_string(&newdict, "OrigFont", pfont_dict(oldfont->base))) < 0 ||
+ (code = dict_put_string(&newdict, "ScaleMatrix", &scalemat)) < 0 ||
+ (code = add_FID(&newdict, newfont)) < 0
+ )
+ return code;
+ newfont->client_data = pdata;
+ *pdata = *pfont_data(oldfont);
+ pdata->dict = newdict;
+ r_clear_attrs(dict_access_ref(&newdict), a_write);
+ return 0;
+}
+
+/* Convert an array of (unsigned) integers to stack form. */
+private void
+make_uint_array(register os_ptr op, const uint *intp, int count)
+{ int i;
+ for ( i = 0; i < count; i++, op++, intp++ )
+ make_int(op, *intp);
+}
+
+/* Remove scaled font and character cache entries that would be */
+/* invalidated by a restore. */
+private bool
+purge_if_name_removed(cached_char *cc, void *vsave)
+{ return alloc_name_index_is_since_save(cc->code, vsave);
+}
+void
+font_restore(const alloc_save_t *save)
+{ gs_font_dir *pdir = ifont_dir;
+ if ( pdir == 0 ) /* not initialized yet */
+ return;
+
+ /* Purge original (unscaled) fonts. */
+
+ { gs_font *pfont;
+otop: for ( pfont = pdir->orig_fonts; pfont != 0;
+ pfont = pfont->next
+ )
+ { if ( alloc_is_since_save((char *)pfont, save) )
+ { gs_purge_font(pfont); goto otop; }
+ }
+ }
+
+ /* Purge cached scaled fonts. */
+
+ { gs_font *pfont;
+top: for ( pfont = pdir->scaled_fonts; pfont != 0;
+ pfont = pfont->next
+ )
+ { if ( alloc_is_since_save((char *)pfont, save) )
+ { gs_purge_font(pfont); goto top; }
+ }
+ }
+
+ /* Purge xfonts and uncached scaled fonts. */
+
+ { cached_fm_pair *pair;
+ uint n;
+ for ( pair = pdir->fmcache.mdata, n = pdir->fmcache.mmax;
+ n > 0; pair++, n--
+ )
+ if ( !fm_pair_is_free(pair) )
+ { if ( (pair->font != 0 &&
+ alloc_is_since_save((char *)pair->font, save)) ||
+ (uid_is_XUID(&pair->UID) &&
+ alloc_is_since_save((char *)pair->UID.xvalues,
+ save))
+ )
+ gs_purge_fm_pair(pdir, pair, 0);
+ else
+ if ( pair->xfont != 0 &&
+ alloc_is_since_save((char *)pair->xfont, save)
+ )
+ gs_purge_fm_pair(pdir, pair, 1);
+ }
+ }
+
+ /* Purge characters with names about to be removed. */
+ /* We only need to do this if any new names have been created */
+ /* since the save. */
+
+ if ( alloc_any_names_since_save(save) )
+ gx_purge_selected_cached_chars(pdir, purge_if_name_removed,
+ (void *)save);
+
+}
diff --git a/pstoraster/zfont0.c b/pstoraster/zfont0.c
new file mode 100644
index 000000000..7d908ae20
--- /dev/null
+++ b/pstoraster/zfont0.c
@@ -0,0 +1,271 @@
+/* Copyright (C) 1991, 1992, 1993 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* zfont0.c */
+/* Composite font creation operator */
+#include "ghost.h"
+#include "errors.h"
+#include "oper.h"
+#include "gsstruct.h"
+/*
+ * The following lines used to say:
+ * #include "gsmatrix.h"
+ * #include "gxdevice.h" /. for gxfont.h ./
+ * Tony Li says the longer list is necessary to keep the GNU compiler
+ * happy, but this is pretty hard to understand....
+ */
+#include "gxfixed.h"
+#include "gxmatrix.h"
+#include "gzstate.h" /* must precede gxdevice */
+#include "gxdevice.h" /* must precede gxfont */
+#include "gschar.h"
+#include "gxfont.h"
+#include "gxfont0.h"
+#include "bfont.h"
+#include "ialloc.h"
+#include "idict.h"
+#include "igstate.h"
+#include "store.h"
+
+/* Composite font procedures */
+extern font_proc_init_fstack(gs_type0_init_fstack);
+extern font_proc_next_char(gs_type0_next_char);
+extern font_proc_define_font(gs_type0_define_font);
+extern font_proc_make_font(gs_type0_make_font);
+
+/* Forward references */
+private font_proc_define_font(ztype0_define_font);
+private font_proc_make_font(ztype0_make_font);
+private int ensure_char_entry(P4(os_ptr, const char _ds *, byte *, int));
+
+/* <string|name> <font_dict> .buildfont0 <string|name> <font> */
+/* Build a type 0 (composite) font. */
+private int
+zbuildfont0(os_ptr op)
+{ ref *pfmaptype;
+ gs_type0_data data;
+ ref *pfdepvector;
+ ref *pprefenc;
+ ref *psubsvector;
+ gs_font_type0 *pfont;
+ font_data *pdata;
+ int i;
+ int code;
+ check_type(*op, t_dictionary);
+ if ( dict_find_string(op, "FMapType", &pfmaptype) <= 0 ||
+ !r_has_type(pfmaptype, t_integer) ||
+ pfmaptype->value.intval < (int)fmap_type_min ||
+ pfmaptype->value.intval > (int)fmap_type_max ||
+ dict_find_string(op, "FDepVector", &pfdepvector) <= 0 ||
+ !r_is_array(pfdepvector)
+ )
+ return_error(e_invalidfont);
+ data.FMapType = (fmap_type)pfmaptype->value.intval;
+ /* Check that every element of the FDepVector is a font. */
+ data.fdep_size = r_size(pfdepvector);
+ for ( i = 0; i < data.fdep_size; i++ )
+ { ref fdep;
+ ref *pfid;
+ gs_font *psub;
+ array_get(pfdepvector, i, &fdep);
+ if ( !r_has_type(&fdep, t_dictionary) ||
+ dict_find_string(&fdep, "FID", &pfid) <= 0 ||
+ !r_has_type(pfid, t_fontID)
+ )
+ return_error(e_invalidfont);
+ /*
+ * Check the inheritance rules. Allowed configurations
+ * (paths from root font) are defined by the regular
+ * expression:
+ * (shift | double_escape escape* | escape*)
+ * non_modal* non_composite
+ */
+ psub = r_ptr(pfid, gs_font);
+ if ( psub->FontType == ft_composite )
+ {
+#define psub0 ((gs_font_type0 *)psub)
+ fmap_type fmt = psub0->data.FMapType;
+ if ( fmt == fmap_double_escape ||
+ fmt == fmap_shift ||
+ (fmt == fmap_escape &&
+ !(data.FMapType == fmap_escape ||
+ data.FMapType == fmap_double_escape))
+ )
+ return_error(e_invalidfont);
+#undef psub0
+ }
+ }
+ switch ( data.FMapType )
+ {
+ case fmap_escape: case fmap_double_escape: /* need EscChar */
+ code = ensure_char_entry(op, "EscChar", &data.EscChar, 255);
+ break;
+ case fmap_shift: /* need ShiftIn & ShiftOut */
+ code = ensure_char_entry(op, "ShiftIn", &data.ShiftIn, 15);
+ if ( code >= 0 )
+ code = ensure_char_entry(op, "ShiftOut", &data.ShiftOut, 14);
+ break;
+ case fmap_SubsVector: /* need SubsVector */
+ { uint svsize;
+ if ( dict_find_string(op, "SubsVector", &psubsvector) <= 0 ||
+ !r_has_type(psubsvector, t_string) ||
+ (svsize = r_size(psubsvector)) == 0 ||
+ (data.subs_width = (int)*psubsvector->value.bytes + 1) > 4 ||
+ (svsize - 1) % data.subs_width != 0
+ )
+ return_error(e_invalidfont);
+ data.subs_size = (svsize - 1) / data.subs_width;
+ data.SubsVector.data = psubsvector->value.bytes + 1;
+ data.SubsVector.size = svsize - 1;
+ }
+ default:
+ code = 0;
+ }
+ if ( code < 0 ) return code;
+ { build_proc_refs build;
+ code = build_proc_name_refs(&build,
+ "%Type0BuildChar", "%Type0BuildGlyph");
+ if ( code < 0 ) return code;
+ code = build_gs_font(op, (gs_font **)&pfont,
+ ft_composite, &st_gs_font_type0, &build);
+ }
+ if ( code != 0 ) return code;
+ pfont->procs.init_fstack = gs_type0_init_fstack;
+ pfont->procs.next_char = gs_type0_next_char;
+ pfont->procs.define_font = ztype0_define_font;
+ pfont->procs.make_font = ztype0_make_font;
+ if ( dict_find_string(op, "PrefEnc", &pprefenc) <= 0 )
+ { ref nul;
+ make_null_new(&nul);
+ if ( (code = dict_put_string(op, "PrefEnc", &nul)) < 0 )
+ return code;
+ }
+ /* Fill in the font data */
+ pdata = pfont_data(pfont);
+ data.encoding_size = r_size(&pdata->Encoding);
+ data.Encoding =
+ (uint *)ialloc_byte_array(data.encoding_size, sizeof(uint),
+ "buildfont0(Encoding)");
+ if ( data.Encoding == 0 )
+ return_error(e_VMerror);
+ /* Fill in the encoding vector, checking to make sure that */
+ /* each element is an integer between 0 and fdep_size-1. */
+ for ( i = 0; i < data.encoding_size; i++ )
+ { ref enc;
+ array_get(&pdata->Encoding, i, &enc);
+ check_int_leu_only(enc, data.fdep_size);
+ data.Encoding[i] = (uint)enc.value.intval;
+ }
+ data.FDepVector =
+ ialloc_struct_array(data.fdep_size, gs_font *,
+ &st_gs_font_ptr_element,
+ "buildfont0(FDepVector)");
+ if ( data.FDepVector == 0 )
+ return_error(e_VMerror);
+ for ( i = 0; i < data.fdep_size; i++ )
+ { ref fdep;
+ ref *pfid;
+ array_get(pfdepvector, i, &fdep);
+ /* The lookup can't fail, because of the pre-check above. */
+ dict_find_string(&fdep, "FID", &pfid);
+ data.FDepVector[i] = r_ptr(pfid, gs_font);
+ }
+ pfont->data = data;
+ return define_gs_font((gs_font *)pfont);
+}
+/* If a newly defined or scaled composite font had to scale */
+/* any composite sub-fonts, adjust the parent font's FDepVector. */
+/* This is called only if gs_type0_define/make_font */
+/* actually changed the FDepVector. */
+private int
+ztype0_adjust_FDepVector(gs_font_type0 *pfont)
+{ gs_font **pdep = pfont->data.FDepVector;
+ ref newdep;
+ uint fdep_size = pfont->data.fdep_size;
+ ref *prdep;
+ uint i;
+ int code = ialloc_ref_array(&newdep, a_readonly, fdep_size,
+ "ztype0_adjust_matrix");
+ if ( code < 0 )
+ return code;
+ for ( prdep = newdep.value.refs, i = 0; i < fdep_size; i++, prdep++ )
+ { const ref *pdict = pfont_dict(pdep[i]);
+ ref_assign_new(prdep, pdict);
+ }
+ return dict_put_string(pfont_dict(pfont), "FDepVector", &newdep);
+}
+private int
+ztype0_define_font(gs_font_dir *pdir, gs_font *pfont)
+{
+#define pfont0 ((gs_font_type0 *)pfont)
+ gs_font **pdep = pfont0->data.FDepVector;
+ int code = gs_type0_define_font(pdir, pfont);
+ if ( code < 0 || pfont0->data.FDepVector == pdep )
+ return code;
+ return ztype0_adjust_FDepVector(pfont0);
+#undef pfont0
+}
+private int
+ztype0_make_font(gs_font_dir *pdir, const gs_font *pfont,
+ const gs_matrix *pmat, gs_font **ppfont)
+{
+#define ppfont0 ((gs_font_type0 **)ppfont)
+ gs_font **pdep = (*ppfont0)->data.FDepVector;
+ int code;
+ code = zdefault_make_font(pdir, pfont, pmat, ppfont);
+ if ( code < 0 )
+ return code;
+ code = gs_type0_make_font(pdir, pfont, pmat, ppfont);
+ if ( code < 0 )
+ return code;
+ if ( (*ppfont0)->data.FDepVector == pdep )
+ return 0;
+ return ztype0_adjust_FDepVector(*ppfont0);
+#undef ppfont0
+}
+
+/* ------ Internal routines ------ */
+
+/* Find or add a character entry in a font dictionary. */
+private int
+ensure_char_entry(os_ptr op, const char _ds *kstr,
+ byte *pvalue, int default_value)
+{ ref *pentry;
+ if ( dict_find_string(op, kstr, &pentry) <= 0 )
+ { ref ent;
+ make_int(&ent, default_value);
+ *pvalue = (byte)default_value;
+ return dict_put_string(op, kstr, &ent);
+ }
+ else
+ { check_int_leu_only(*pentry, 255);
+ *pvalue = (byte)pentry->value.intval;
+ return 0;
+ }
+}
+
+/* ------ Initialization procedure ------ */
+
+BEGIN_OP_DEFS(zfont0_op_defs) {
+ {"2.buildfont0", zbuildfont0},
+END_OP_DEFS(0) }
diff --git a/pstoraster/zfont1.c b/pstoraster/zfont1.c
new file mode 100644
index 000000000..8d62cfdca
--- /dev/null
+++ b/pstoraster/zfont1.c
@@ -0,0 +1,192 @@
+/* Copyright (C) 1991, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* zfont1.c */
+/* Type 1 and Type 4 font creation operator */
+#include "ghost.h"
+#include "errors.h"
+#include "oper.h"
+#include "gxfixed.h"
+#include "gsmatrix.h"
+#include "gxdevice.h"
+#include "gschar.h"
+#include "gxfont.h"
+#include "gxfont1.h"
+#include "bfont.h"
+#include "ialloc.h"
+#include "idict.h"
+#include "idparam.h"
+#include "store.h"
+
+/* Type 1 auxiliary procedures (defined in zchar1.c) */
+extern int z1_subr_proc(P3(gs_font_type1 *, int, gs_const_string *));
+extern int z1_seac_proc(P3(gs_font_type1 *, int, gs_const_string *));
+extern int z1_push_proc(P3(gs_font_type1 *, const fixed *, int));
+extern int z1_pop_proc(P2(gs_font_type1 *, fixed *));
+
+/* Default value of lenIV */
+#define default_lenIV 4
+
+/* Build a Type 1 or Type 4 font. */
+private int
+buildfont1or4(os_ptr op, build_proc_refs *pbuild, font_type ftype)
+{ gs_type1_data data1;
+ ref *pothersubrs;
+ ref *psubrs;
+ ref *pprivate;
+ static ref no_subrs;
+ gs_font_type1 *pfont;
+ font_data *pdata;
+ int code;
+
+ check_type(*op, t_dictionary);
+ if ( dict_find_string(op, "Private", &pprivate) <= 0 ||
+ !r_has_type(pprivate, t_dictionary)
+ )
+ return_error(e_invalidfont);
+ make_empty_array(&no_subrs, 0);
+ if ( dict_find_string(pprivate, "OtherSubrs", &pothersubrs) > 0 )
+ { if ( !r_is_array(pothersubrs) ) /* allow packed array */
+ return_error(e_invalidfont);
+ }
+ else
+ pothersubrs = &no_subrs;
+ if ( dict_find_string(pprivate, "Subrs", &psubrs) > 0 )
+ { if ( !r_is_array(psubrs) ) /* allow packed array */
+ return_error(e_invalidfont);
+ }
+ else
+ psubrs = &no_subrs;
+ /* Get the rest of the information from the Private dictionary. */
+ if ( (code = dict_int_param(pprivate, "lenIV", 0, 255,
+ default_lenIV, &data1.lenIV)) < 0 ||
+ (code = dict_int_param(pprivate, "BlueFuzz", 0, 1999, 1,
+ &data1.BlueFuzz)) < 0 ||
+ (code = dict_float_param(pprivate, "BlueScale", 0.039625,
+ &data1.BlueScale)) < 0 ||
+ (code = dict_float_param(pprivate, "BlueShift", 7.0,
+ &data1.BlueShift)) < 0 ||
+ (code = data1.BlueValues.count = dict_float_array_param(pprivate,
+ "BlueValues", max_BlueValues * 2,
+ &data1.BlueValues.values[0], NULL)) < 0 ||
+ (code = dict_float_param(pprivate, "ExpansionFactor", 0.06,
+ &data1.ExpansionFactor)) < 0 ||
+ (code = data1.FamilyBlues.count = dict_float_array_param(pprivate,
+ "FamilyBlues", max_FamilyBlues * 2,
+ &data1.FamilyBlues.values[0], NULL)) < 0 ||
+ (code = data1.FamilyOtherBlues.count = dict_float_array_param(pprivate,
+ "FamilyOtherBlues", max_FamilyOtherBlues * 2,
+ &data1.FamilyOtherBlues.values[0], NULL)) < 0 ||
+ (code = dict_bool_param(pprivate, "ForceBold", false,
+ &data1.ForceBold)) < 0 ||
+ (code = dict_int_param(pprivate, "LanguageGroup", 0, 1, 0,
+ &data1.LanguageGroup)) < 0 ||
+ (code = data1.OtherBlues.count = dict_float_array_param(pprivate,
+ "OtherBlues", max_OtherBlues * 2,
+ &data1.OtherBlues.values[0], NULL)) < 0 ||
+ (code = dict_bool_param(pprivate, "RndStemUp", true,
+ &data1.RndStemUp)) < 0 ||
+ (code = data1.StdHW.count = dict_float_array_param(pprivate,
+ "StdHW", 1, &data1.StdHW.values[0], NULL)) < 0 ||
+ (code = data1.StdVW.count = dict_float_array_param(pprivate,
+ "StdVW", 1, &data1.StdVW.values[0], NULL)) < 0 ||
+ (code = data1.StemSnapH.count = dict_float_array_param(pprivate,
+ "StemSnapH", max_StemSnap,
+ &data1.StemSnapH.values[0], NULL)) < 0 ||
+ (code = data1.StemSnapV.count = dict_float_array_param(pprivate,
+ "StemSnapV", max_StemSnap,
+ &data1.StemSnapV.values[0], NULL)) < 0 ||
+ /* The WeightVector is in the font dictionary, not Private. */
+ (code = dict_float_array_param(op, "WeightVector",
+ max_WeightVector, &data1.WeightVector[0], NULL)) < 0
+ )
+ return code;
+ /*
+ * According to section 5.6 of the "Adobe Type 1 Font Format",
+ * there is a requirement that BlueScale times the maximum
+ * alignment zone height must be less than 1. Some fonts
+ * produced by Fontographer have ridiculously large BlueScale
+ * values, so we force BlueScale back into range here.
+ */
+ { float max_zone_height = 1.0;
+ float zone_height;
+ int i;
+#define scan_zone(z)\
+ for ( i = 0; i < data1.z.count; i += 2 )\
+ if ( (zone_height = data1.z.values[i+1] - data1.z.values[i]) > max_zone_height )\
+ max_zone_height = zone_height
+ scan_zone(BlueValues);
+ scan_zone(OtherBlues);
+ scan_zone(FamilyBlues);
+ scan_zone(FamilyOtherBlues);
+ if ( data1.BlueScale * max_zone_height > 1.0 )
+ data1.BlueScale = 1.0 / max_zone_height;
+ }
+ /* Do the work common to primitive font types. */
+ code = build_gs_primitive_font(op, (gs_font_base **)&pfont, ftype,
+ &st_gs_font_type1, pbuild);
+ if ( code != 0 )
+ return code;
+ /* This is a new font, fill it in. */
+ pdata = pfont_data(pfont);
+ pfont->data = data1;
+ ref_assign(&pdata->u.type1.OtherSubrs, pothersubrs);
+ ref_assign(&pdata->u.type1.Subrs, psubrs);
+ pfont->data.subr_proc = z1_subr_proc;
+ pfont->data.seac_proc = z1_seac_proc;
+ pfont->data.push_proc = z1_push_proc;
+ pfont->data.pop_proc = z1_pop_proc;
+ pfont->data.proc_data = (char *)pdata;
+ return define_gs_font((gs_font *)pfont);
+}
+
+/* <string|name> <font_dict> .buildfont1 <string|name> <font> */
+/* Build a type 1 (Adobe encrypted) font. */
+private int
+zbuildfont1(os_ptr op)
+{ build_proc_refs build;
+ int code =
+ build_proc_name_refs(&build,
+ "%Type1BuildChar", "%Type1BuildGlyph");
+ if ( code < 0 )
+ return code;
+ return buildfont1or4(op, &build, ft_encrypted);
+}
+
+/* <string|name> <font_dict> .buildfont4 <string|name> <font> */
+/* Build a type 4 (disk-based Adobe encrypted) font. */
+private int
+zbuildfont4(os_ptr op)
+{ build_proc_refs build;
+ int code = build_gs_font_procs(op, &build);
+ if ( code < 0 )
+ return code;
+ return buildfont1or4(op, &build, ft_disk_based);
+}
+
+/* ------ Initialization procedure ------ */
+
+BEGIN_OP_DEFS(zfont1_op_defs) {
+ {"2.buildfont1", zbuildfont1},
+ {"2.buildfont4", zbuildfont4},
+END_OP_DEFS(0) }
diff --git a/pstoraster/zfont2.c b/pstoraster/zfont2.c
new file mode 100644
index 000000000..d502c4ea2
--- /dev/null
+++ b/pstoraster/zfont2.c
@@ -0,0 +1,493 @@
+/* Copyright (C) 1989, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* zfont2.c */
+/* Font creation utilities */
+#include "memory_.h"
+#include "string_.h"
+#include "ghost.h"
+#include "errors.h"
+#include "oper.h"
+#include "gxfixed.h"
+#include "gsmatrix.h"
+#include "gxdevice.h"
+#include "gschar.h"
+#include "gxfont.h"
+#include "bfont.h"
+#include "ialloc.h"
+#include "idict.h"
+#include "idparam.h"
+#include "ilevel.h"
+#include "iname.h"
+#include "ipacked.h"
+#include "istruct.h"
+#include "store.h"
+
+/* Registered encodings. See ifont.h for documentation. */
+ref registered_Encodings;
+private ref *registered_Encodings_p = &registered_Encodings;
+private gs_gc_root_t registered_Encodings_root;
+
+/* Structure descriptor */
+public_st_font_data();
+
+/* Initialize the font building operators */
+private void
+zfont2_init(void)
+{ /* Initialize the registered Encodings. */
+ { int i;
+ ialloc_ref_array(&registered_Encodings, a_readonly,
+ registered_Encodings_countof,
+ "registered_Encodings");
+ for ( i = 0; i < registered_Encodings_countof; i++ )
+ make_empty_array(&registered_Encoding(i), 0);
+ }
+ gs_register_ref_root(imemory, &registered_Encodings_root,
+ (void **)&registered_Encodings_p,
+ "registered_Encodings");
+}
+
+/* <string|name> <font_dict> .buildfont3 <string|name> <font> */
+/* Build a type 3 (user-defined) font. */
+private int
+zbuildfont3(os_ptr op)
+{ int code;
+ build_proc_refs build;
+ gs_font_base *pfont;
+
+ check_type(*op, t_dictionary);
+ code = build_gs_font_procs(op, &build);
+ if ( code < 0 )
+ return code;
+ code = build_gs_simple_font(op, &pfont, ft_user_defined,
+ &st_gs_font_base, &build);
+ if ( code < 0 )
+ return code;
+ return define_gs_font((gs_font *)pfont);
+}
+
+/* <int> <array|shortarray> .registerencoding - */
+private int
+zregisterencoding(register os_ptr op)
+{ long i;
+ if ( !r_is_array(op) )
+ return_op_typecheck(op);
+ check_read(*op);
+ check_type(op[-1], t_integer);
+ for ( i = r_size(op); i > 0; )
+ { ref cname;
+ array_get(op, --i, &cname);
+ check_type_only(cname, t_name);
+ }
+ i = op[-1].value.intval;
+ if ( i >= 0 && i < registered_Encodings_countof )
+ { ref *penc = &registered_Encoding(i);
+ ref_assign_old(&registered_Encodings, penc, op,
+ ".registerencoding");
+ }
+ pop(2);
+ return 0;
+}
+
+/* Encode a character. */
+/* (This is very inefficient right now; we can speed it up later.) */
+private gs_glyph
+zfont_encode_char(gs_show_enum *penum, gs_font *pfont, gs_char *pchr)
+{ const ref *pencoding = &pfont_data(pfont)->Encoding;
+ ulong index = *pchr; /* work around VAX widening bug */
+ ref cname;
+ int code = array_get(pencoding, (long)index, &cname);
+ if ( code < 0 || !r_has_type(&cname, t_name) )
+ return gs_no_glyph;
+ return (gs_glyph)name_index(&cname);
+}
+
+/* Encode a character in a known encoding. */
+private gs_glyph
+zfont_known_encode(gs_char chr, int encoding_index)
+{ ulong index = chr; /* work around VAX widening bug */
+ ref cname;
+ int code;
+ if ( encoding_index < 0 )
+ return gs_no_glyph;
+ code = array_get(&registered_Encoding(encoding_index),
+ (long)index, &cname);
+ if ( code < 0 || !r_has_type(&cname, t_name) )
+ return gs_no_glyph;
+ return (gs_glyph)name_index(&cname);
+}
+
+/* Get the name of a glyph. */
+/* The following typedef is needed to work around a bug in */
+/* some AIX C compilers. */
+typedef const char *const_chars;
+private const_chars
+zfont_glyph_name(gs_glyph index, uint *plen)
+{ ref nref, sref;
+ name_index_ref(index, &nref);
+ name_string_ref(&nref, &sref);
+ *plen = r_size(&sref);
+ return (const char *)sref.value.const_bytes;
+}
+
+/* ------ Initialization procedure ------ */
+
+BEGIN_OP_DEFS(zfont2_op_defs) {
+ {"2.buildfont3", zbuildfont3},
+ {"2.registerencoding", zregisterencoding},
+END_OP_DEFS(zfont2_init) }
+
+/* ------ Subroutines ------ */
+
+/* Convert strings to executable names for build_proc_refs. */
+int
+build_proc_name_refs(build_proc_refs *pbuild,
+ const char _ds *bcstr, const char _ds *bgstr)
+{ int code;
+ if ( (code = name_ref((const byte *)bcstr, strlen(bcstr), &pbuild->BuildChar, 0)) < 0 ||
+ (code = name_ref((const byte *)bgstr, strlen(bgstr), &pbuild->BuildGlyph, 0)) < 0
+ )
+ return code;
+ r_set_attrs(&pbuild->BuildChar, a_executable);
+ r_set_attrs(&pbuild->BuildGlyph, a_executable);
+ return 0;
+}
+
+/* Get the BuildChar and/or BuildGlyph routines from a (base) font. */
+int
+build_gs_font_procs(os_ptr op, build_proc_refs *pbuild)
+{ int ccode, gcode;
+ ref *pBuildChar;
+ ref *pBuildGlyph;
+
+ check_type(*op, t_dictionary);
+ ccode = dict_find_string(op, "BuildChar", &pBuildChar);
+ gcode = dict_find_string(op, "BuildGlyph", &pBuildGlyph);
+ if ( ccode <= 0 )
+ { if ( gcode <= 0 )
+ return_error(e_invalidfont);
+ make_null(&pbuild->BuildChar);
+ }
+ else
+ { check_proc(*pBuildChar);
+ pbuild->BuildChar = *pBuildChar;
+ }
+ if ( gcode <= 0 )
+ make_null(&pbuild->BuildGlyph);
+ else
+ { check_proc(*pBuildGlyph);
+ pbuild->BuildGlyph = *pBuildGlyph;
+ }
+ return 0;
+}
+
+/* Do the common work for building a primitive font -- one whose execution */
+/* algorithm is implemented in C (Type 1, Type 4, or Type 42). */
+/* The caller guarantees that *op is a dictionary. */
+int
+build_gs_primitive_font(os_ptr op, gs_font_base **ppfont, font_type ftype,
+ gs_memory_type_ptr_t pstype, const build_proc_refs *pbuild)
+{ int painttype;
+ float strokewidth;
+ ref *pcharstrings;
+ gs_font_base *pfont;
+ font_data *pdata;
+ int code;
+
+ code = dict_int_param(op, "PaintType", 0, 3, 0, &painttype);
+ if ( code < 0 )
+ return code;
+ code = dict_float_param(op, "StrokeWidth", 0.0, &strokewidth);
+ if ( code < 0 )
+ return code;
+ if ( dict_find_string(op, "CharStrings", &pcharstrings) <= 0 ||
+ !r_has_type(pcharstrings, t_dictionary)
+ )
+ return_error(e_invalidfont);
+ code = build_gs_simple_font(op, &pfont, ftype, pstype, pbuild);
+ if ( code != 0 )
+ return code;
+ pfont->PaintType = painttype;
+ pfont->StrokeWidth = strokewidth;
+ pdata = pfont_data(pfont);
+ ref_assign(&pdata->CharStrings, pcharstrings);
+ /* Check that the UniqueIDs match. This is part of the */
+ /* Adobe protection scheme, but we may as well emulate it. */
+ if ( uid_is_valid(&pfont->UID) &&
+ !dict_check_uid_param(op, &pfont->UID)
+ )
+ uid_set_invalid(&pfont->UID);
+ *ppfont = pfont;
+ return 0;
+}
+
+/* Do the common work for building a font of any non-composite FontType. */
+/* The caller guarantees that *op is a dictionary. */
+int
+build_gs_simple_font(os_ptr op, gs_font_base **ppfont, font_type ftype,
+ gs_memory_type_ptr_t pstype, const build_proc_refs *pbuild)
+{ float bbox[4];
+ gs_uid uid;
+ int code;
+ gs_font_base *pfont;
+
+ code = font_bbox_param(op, bbox);
+ if ( code < 0 ) return code;
+ code = dict_uid_param(op, &uid, 0, imemory);
+ if ( code < 0 ) return code;
+ code = build_gs_font(op, (gs_font **)ppfont, ftype, pstype, pbuild);
+ if ( code != 0 ) return code; /* invalid or scaled font */
+ pfont = *ppfont;
+ pfont->procs.init_fstack = gs_default_init_fstack;
+ pfont->procs.next_char = gs_default_next_char;
+ pfont->procs.define_font = gs_no_define_font;
+ pfont->procs.make_font = zbase_make_font;
+ pfont->FontBBox.p.x = bbox[0];
+ pfont->FontBBox.p.y = bbox[1];
+ pfont->FontBBox.q.x = bbox[2];
+ pfont->FontBBox.q.y = bbox[3];
+ pfont->UID = uid;
+ lookup_gs_simple_font_encoding(pfont);
+ return 0;
+}
+
+/* Compare the encoding of a simple font with the registered encodings. */
+void
+lookup_gs_simple_font_encoding(gs_font_base *pfont)
+{ const ref *pfe = &pfont_data(pfont)->Encoding;
+ int index;
+ for ( index = registered_Encodings_countof; --index >= 0; )
+ if ( obj_eq(pfe, &registered_Encoding(index)) )
+ break;
+ pfont->encoding_index = index;
+ if ( index < 0 )
+ { /* Look for an encoding that's "close". */
+ int near_index = -1;
+ uint esize = r_size(pfe);
+ uint best = esize / 3; /* must match at least this many */
+ for ( index = registered_Encodings_countof; --index >= 0; )
+ { const ref *pre = &registered_Encoding(index);
+ bool r_packed = r_has_type(pre, t_shortarray);
+ bool f_packed = !r_has_type(pfe, t_array);
+ uint match = esize;
+ int i;
+ ref fchar, rchar;
+ const ref *pfchar = &fchar;
+ if ( r_size(pre) != esize )
+ continue;
+ for ( i = esize; --i >= 0; )
+ { uint rnidx;
+ if ( r_packed )
+ rnidx = packed_name_index(pre->value.packed + i);
+ else
+ { array_get(pre, (long)i, &rchar);
+ rnidx = name_index(&rchar);
+ }
+ if ( f_packed )
+ array_get(pfe, (long)i, &fchar);
+ else
+ pfchar = pfe->value.const_refs + i;
+ if ( !r_has_type(pfchar, t_name) ||
+ name_index(pfchar) != rnidx
+ )
+ if ( --match <= best )
+ break;
+ }
+ if ( match > best )
+ best = match,
+ near_index = index;
+ }
+ index = near_index;
+ }
+ pfont->nearest_encoding_index = index;
+}
+
+/* Do the common work for building a font of any FontType. */
+/* The caller guarantees that *op is a dictionary. */
+/* op[-1] must be the key under which the font is being registered */
+/* in FontDirectory, normally a name or string. */
+/* Return 0 for a new font, 1 for a font made by makefont or scalefont, */
+/* or a negative error code. */
+private void get_font_name(P2(ref *, const ref *));
+private void copy_font_name(P2(gs_font_name *, const ref *));
+int
+build_gs_font(os_ptr op, gs_font **ppfont, font_type ftype,
+ gs_memory_type_ptr_t pstype, const build_proc_refs *pbuild)
+{ ref kname, fname; /* t_string */
+ ref *pftype;
+ ref *pfontname;
+ ref *pmatrix;
+ gs_matrix mat;
+ ref *pencoding;
+ bool bitmapwidths;
+ int exactsize, inbetweensize, transformedchar;
+ int wmode;
+ int code;
+ gs_font *pfont;
+ ref *pfid;
+ ref *aop = dict_access_ref(op);
+ get_font_name(&kname, op - 1);
+ if ( dict_find_string(op, "FontType", &pftype) <= 0 ||
+ !r_has_type(pftype, t_integer) ||
+ pftype->value.intval != (int)ftype ||
+ dict_find_string(op, "FontMatrix", &pmatrix) <= 0 ||
+ read_matrix(pmatrix, &mat) < 0 ||
+ dict_find_string(op, "Encoding", &pencoding) <= 0 ||
+ !r_is_array(pencoding)
+ )
+ return_error(e_invalidfont);
+ if ( dict_find_string(op, "FontName", &pfontname) > 0 )
+ get_font_name(&fname, pfontname);
+ else
+ make_empty_string(&fname, a_readonly);
+ if ( (code = dict_int_param(op, "WMode", 0, 1, 0, &wmode)) < 0 ||
+ (code = dict_bool_param(op, "BitmapWidths", false, &bitmapwidths)) < 0 ||
+ (code = dict_int_param(op, "ExactSize", 0, 2, fbit_use_bitmaps, &exactsize)) < 0 ||
+ (code = dict_int_param(op, "InBetweenSize", 0, 2, fbit_use_outlines, &inbetweensize)) < 0 ||
+ (code = dict_int_param(op, "TransformedChar", 0, 2, fbit_use_outlines, &transformedchar)) < 0
+ )
+ return code;
+ code = dict_find_string(op, "FID", &pfid);
+ if ( code > 0 )
+ { if ( !r_has_type(pfid, t_fontID) )
+ return_error(e_invalidfont);
+ /*
+ * If this font has a FID entry already, it might be
+ * a scaled font made by makefont or scalefont;
+ * in a Level 2 environment, it might be an existing font
+ * being registered under a second name, or a re-encoded
+ * font (which is questionable PostScript, but dvips
+ * is known to do this).
+ */
+ pfont = r_ptr(pfid, gs_font);
+ if ( pfont->base == pfont ) /* original font */
+ { if ( !level2_enabled )
+ return_error(e_invalidfont);
+ if ( obj_eq(pfont_dict(pfont), op) )
+ { *ppfont = pfont;
+ return 0;
+ }
+ /*
+ * This is a re-encoded font, or some other
+ * questionable situation in which the FID
+ * was preserved. Pretend the FID wasn't there.
+ */
+ }
+ else
+ { /* This was made by makefont or scalefont. */
+ /* Just insert the new name. */
+ code = 1;
+ goto set_name;
+ }
+ }
+ /* This is a new font. */
+ if ( !r_has_attr(aop, a_write) )
+ return_error(e_invalidaccess);
+ { font_data *pdata;
+ /* Make sure that we allocate the font data */
+ /* in the same VM as the font dictionary. */
+ uint space = ialloc_space(idmemory);
+ ialloc_set_space(idmemory, r_space(op));
+ pfont = ialloc_struct(gs_font, pstype,
+ "buildfont(font)");
+ pdata = ialloc_struct(font_data, &st_font_data,
+ "buildfont(data)");
+ if ( pfont == 0 || pdata == 0 )
+ code = gs_note_error(e_VMerror);
+ else
+ code = add_FID(op, pfont);
+ if ( code < 0 )
+ { ifree_object(pdata, "buildfont(data)");
+ ifree_object(pfont, "buildfont(font)");
+ ialloc_set_space(idmemory, space);
+ return code;
+ }
+ refset_null((ref *)pdata, sizeof(font_data) / sizeof(ref));
+ ref_assign_new(&pdata->dict, op);
+ ref_assign_new(&pdata->BuildChar, &pbuild->BuildChar);
+ ref_assign_new(&pdata->BuildGlyph, &pbuild->BuildGlyph);
+ ref_assign_new(&pdata->Encoding, pencoding);
+ pfont->base = pfont;
+ pfont->memory = imemory;
+ pfont->dir = 0;
+ pfont->client_data = pdata;
+ pfont->FontType = ftype;
+ pfont->FontMatrix = mat;
+ pfont->BitmapWidths = bitmapwidths;
+ pfont->ExactSize = exactsize;
+ pfont->InBetweenSize = inbetweensize;
+ pfont->TransformedChar = transformedchar;
+ pfont->WMode = wmode;
+ pfont->PaintType = 0;
+ pfont->StrokeWidth = 0.0;
+ pfont->procs.build_char = gs_no_build_char;
+ pfont->procs.encode_char = zfont_encode_char;
+ pfont->procs.callbacks.glyph_name = zfont_glyph_name;
+ pfont->procs.callbacks.known_encode = zfont_known_encode;
+ ialloc_set_space(idmemory, space);
+ }
+ code = 0;
+set_name:
+ copy_font_name(&pfont->key_name, &kname);
+ copy_font_name(&pfont->font_name, &fname);
+ *ppfont = pfont;
+ return code;
+}
+
+/* Get the string corresponding to a font name. */
+/* If the font name isn't a name or a string, return an empty string. */
+private void
+get_font_name(ref *pfname, const ref *op)
+{ switch ( r_type(op) )
+ {
+ case t_string:
+ *pfname = *op;
+ break;
+ case t_name:
+ name_string_ref(op, pfname);
+ break;
+ default:
+ /* This is weird, but legal.... */
+ make_empty_string(pfname, a_readonly);
+ }
+}
+
+/* Copy a font name into the gs_font structure. */
+private void
+copy_font_name(gs_font_name *pfstr, const ref *pfname)
+{ uint size = r_size(pfname);
+ if ( size > gs_font_name_max )
+ size = gs_font_name_max;
+ memcpy(&pfstr->chars[0], pfname->value.const_bytes, size);
+ /* Following is only for debugging printout. */
+ pfstr->chars[size] = 0;
+ pfstr->size = size;
+}
+
+/* Finish building a font, by calling gs_definefont if needed. */
+int
+define_gs_font(gs_font *pfont)
+{ return (pfont->base == pfont && pfont->dir == 0 ? /* i.e., unregistered original font */
+ gs_definefont(ifont_dir, pfont) :
+ 0);
+}
diff --git a/pstoraster/zfont42.c b/pstoraster/zfont42.c
new file mode 100644
index 000000000..be38f1238
--- /dev/null
+++ b/pstoraster/zfont42.c
@@ -0,0 +1,108 @@
+/* Copyright (C) 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* zfont42.c */
+/* Type 42 font creation operator */
+#include "ghost.h"
+#include "errors.h"
+#include "oper.h"
+#include "gsccode.h"
+#include "gsmatrix.h"
+#include "gxfont.h"
+#include "gxfont42.h"
+#include "bfont.h"
+#include "idict.h"
+#include "idparam.h"
+#include "store.h"
+
+/* Forward references */
+private int z42_string_proc(P4(gs_font_type42 *, ulong, uint, const byte **));
+
+/* <string|name> <font_dict> .buildfont42 <string|name> <font> */
+/* Build a type 42 (TrueType) font. */
+private int
+zbuildfont42(os_ptr op)
+{ build_proc_refs build;
+ ref *psfnts;
+ ref sfnts0;
+#define sfd (sfnts0.value.const_bytes)
+ gs_font_type42 *pfont;
+ font_data *pdata;
+ int code;
+
+ code = build_proc_name_refs(&build,
+ "%Type42BuildChar", "%Type42BuildGlyph");
+ if ( code < 0 )
+ return code;
+ check_type(*op, t_dictionary);
+ if ( dict_find_string(op, "sfnts", &psfnts) <= 0 )
+ return_error(e_invalidfont);
+ if ( (code = array_get(psfnts, 0L, &sfnts0)) < 0 )
+ return code;
+ if ( !r_has_type(&sfnts0, t_string) )
+ return_error(e_typecheck);
+ code = build_gs_primitive_font(op, (gs_font_base **)&pfont,
+ ft_TrueType,
+ &st_gs_font_type42, &build);
+ if ( code != 0 )
+ return code;
+ pdata = pfont_data(pfont);
+ ref_assign(&pdata->u.type42.sfnts, psfnts);
+ pfont->data.string_proc = z42_string_proc;
+ pfont->data.proc_data = (char *)pdata;
+ code = gs_type42_font_init(pfont);
+ if ( code < 0 )
+ return code;
+ return define_gs_font((gs_font *)pfont);
+}
+
+/* ------ Initialization procedure ------ */
+
+BEGIN_OP_DEFS(zfont42_op_defs) {
+ {"2.buildfont42", zbuildfont42},
+END_OP_DEFS(0) }
+
+/* Procedure for accessing the sfnts array. */
+private int
+z42_string_proc(gs_font_type42 *pfont, ulong offset, uint length,
+ const byte **pdata)
+{ const font_data *pfdata = pfont_data(pfont);
+ ulong left = offset;
+ uint index = 0;
+
+ for ( ; ; ++index )
+ { ref rstr;
+ int code = array_get(&pfdata->u.type42.sfnts, index, &rstr);
+ if ( code < 0 )
+ return code;
+ if ( !r_has_type(&rstr, t_string) )
+ return_error(e_typecheck);
+ if ( left < r_size(&rstr) )
+ { if ( left + length > r_size(&rstr) )
+ return_error(e_rangecheck);
+ *pdata = rstr.value.const_bytes + left;
+ return 0;
+ }
+ left -= r_size(&rstr);
+ }
+}
diff --git a/pstoraster/zfproc.c b/pstoraster/zfproc.c
new file mode 100644
index 000000000..1df5ca43d
--- /dev/null
+++ b/pstoraster/zfproc.c
@@ -0,0 +1,337 @@
+/* Copyright (C) 1994, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* zfproc.c */
+/* Procedure-based filter stream support */
+#include "memory_.h"
+#include "ghost.h"
+#include "errors.h"
+#include "oper.h" /* for ifilter.h */
+#include "estack.h"
+#include "gsstruct.h"
+#include "ialloc.h"
+#include "istruct.h" /* for gs_reloc_refs */
+#include "stream.h"
+#include "strimpl.h"
+#include "ifilter.h"
+#include "files.h"
+#include "store.h"
+
+/* ---------------- Generic ---------------- */
+
+/* GC procedures */
+#define pptr ((stream_proc_state *)vptr)
+private CLEAR_MARKS_PROC(sproc_clear_marks) {
+ r_clear_attrs(&pptr->proc, l_mark);
+ r_clear_attrs(&pptr->data, l_mark);
+}
+private ENUM_PTRS_BEGIN(sproc_enum_ptrs) return 0;
+ case 0:
+ *pep = &pptr->proc;
+ return ptr_ref_type;
+ case 1:
+ *pep = &pptr->data;
+ return ptr_ref_type;
+ENUM_PTRS_END
+private RELOC_PTRS_BEGIN(sproc_reloc_ptrs) ;
+ gs_reloc_refs((ref_packed *)&pptr->proc,
+ (ref_packed *)(&pptr->proc + 1), gcst);
+ r_clear_attrs(&pptr->proc, l_mark);
+ gs_reloc_refs((ref_packed *)&pptr->data,
+ (ref_packed *)(&pptr->data + 1), gcst);
+ r_clear_attrs(&pptr->data, l_mark);
+RELOC_PTRS_END
+#undef pptr
+/* Structure type for procedure-based streams. */
+private_st_stream_proc_state();
+
+/* Allocate and open a procedure-based filter. */
+/* The caller must have checked that *sop is a procedure. */
+private int
+s_proc_init(ref *sop, stream **psstrm, uint mode, const stream_template *temp)
+{ stream *sstrm;
+ stream_proc_state *state;
+ static const stream_procs procs =
+ { s_std_noavailable, s_std_noseek, s_std_read_reset,
+ s_std_read_flush, s_std_null, NULL
+ };
+ sstrm = file_alloc_stream(imemory, "s_proc_init(stream)");
+ state = (stream_proc_state *)s_alloc_state(imemory, &st_sproc_state,
+ "s_proc_init(state)");
+ if ( sstrm == 0 || state == 0 )
+ { ifree_object(state, "s_proc_init(state)");
+ /*ifree_object(sstrm, "s_proc_init(stream)");*/ /* just leave it on the file list */
+ return_error(e_VMerror);
+ }
+ s_std_init(sstrm, NULL, 0, &procs, mode);
+ sstrm->procs.process = temp->process;
+ state->template = temp;
+ state->memory = imemory;
+ state->eof = 0;
+ state->proc = *sop;
+ make_empty_string(&state->data, a_all);
+ state->index = 0;
+ sstrm->state = (stream_state *)state;
+ *psstrm = sstrm;
+ return 0;
+}
+
+/* Handle an interrupt during a stream operation. */
+/* This is logically unrelated to procedure streams, */
+/* but it is also associated with the interpreter stream machinery. */
+private int
+s_handle_intc(const ref *pstate, int nstate, int (*cont)(P1(os_ptr)))
+{ int npush = nstate + 2;
+
+ check_estack(npush);
+ if ( nstate )
+ memcpy(esp + 2, pstate, nstate * sizeof(ref));
+#if 0 /* **************** */
+ { int code = gs_interpret_error(e_interrupt, (ref *)(esp + npush));
+ if ( code < 0 )
+ return code;
+ }
+#else /* **************** */
+ npush--;
+#endif /* **************** */
+ make_op_estack(esp + 1, cont);
+ esp += npush;
+ return o_push_estack;
+}
+
+
+/* ---------------- Read streams ---------------- */
+
+/* Forward references */
+private stream_proc_process(s_proc_read_process);
+private int s_proc_read_continue(P1(os_ptr));
+
+/* Stream templates */
+private const stream_template s_proc_read_template =
+{ &st_sproc_state, NULL, s_proc_read_process, 1, 1, NULL
+};
+
+/* Allocate and open a procedure-based read stream. */
+/* The caller must have checked that *sop is a procedure. */
+int
+sread_proc(ref *sop, stream **psstrm)
+{ int code =
+ s_proc_init(sop, psstrm, s_mode_read, &s_proc_read_template);
+ if ( code < 0 )
+ return code;
+ (*psstrm)->end_status = CALLC;
+ return code;
+}
+
+/* Handle an input request. */
+#define ss ((stream_proc_state *)st)
+private int
+s_proc_read_process(stream_state *st, stream_cursor_read *ignore_pr,
+ stream_cursor_write *pw, bool last)
+{ /* Move data from the string returned by the procedure */
+ /* into the stream buffer, or ask for a callback. */
+ uint count = r_size(&ss->data) - ss->index;
+ if ( count > 0 )
+ { uint wcount = pw->limit - pw->ptr;
+ if ( wcount < count )
+ count = wcount;
+ memcpy(pw->ptr + 1, ss->data.value.bytes + ss->index, count);
+ pw->ptr += count;
+ ss->index += count;
+ return 1;
+ }
+ return (ss->eof ? EOFC : CALLC);
+}
+#undef ss
+
+/* Handle an exception (INTC or CALLC) from a read stream */
+/* whose buffer is empty. */
+int
+s_handle_read_exception(int status, const ref *fop, const ref *pstate,
+ int nstate, int (*cont)(P1(os_ptr)))
+{ int npush = nstate + 4;
+ stream *ps;
+
+ switch ( status )
+ {
+ case INTC: return s_handle_intc(pstate, nstate, cont);
+ case CALLC: break;
+ default: return_error(e_ioerror);
+ }
+ /* Find the stream whose buffer needs refilling. */
+ for ( ps = fptr(fop); ps->strm != 0; )
+ ps = ps->strm;
+#define psst ((stream_proc_state *)ps->state)
+ check_estack(npush);
+ if ( nstate )
+ memcpy(esp + 2, pstate, nstate * sizeof(ref));
+ make_op_estack(esp + 1, cont);
+ esp += npush;
+ make_op_estack(esp - 2, s_proc_read_continue);
+ esp[-1] = *fop;
+ r_clear_attrs(esp - 1, a_executable);
+ *esp = psst->proc;
+#undef psst
+ return o_push_estack;
+}
+/* Continue a read operation after returning from a procedure callout. */
+/* osp[0] contains the file (pushed on the e-stack by handle_read_status); */
+/* osp[-1] contains the new data string (pushed by the procedure). */
+/* The top of the e-stack contains the real continuation. */
+private int
+s_proc_read_continue(os_ptr op)
+{ os_ptr opbuf = op - 1;
+ stream *ps;
+ stream_proc_state *ss;
+
+ check_file(ps, op);
+ check_read_type(*opbuf, t_string);
+ while ( (ps->end_status = 0, ps->strm) != 0 )
+ ps = ps->strm;
+ ss = (stream_proc_state *)ps->state;
+ ss->data = *opbuf;
+ ss->index = 0;
+ if ( r_size(opbuf) == 0 )
+ ss->eof = true;
+ pop(2);
+ return 0;
+}
+
+/* ---------------- Write streams ---------------- */
+
+/* Forward references */
+private stream_proc_process(s_proc_write_process);
+private int s_proc_write_continue(P1(os_ptr));
+
+/* Stream templates */
+private const stream_template s_proc_write_template =
+{ &st_sproc_state, NULL, s_proc_write_process, 1, 1, NULL
+};
+
+/* Allocate and open a procedure-based write stream. */
+/* The caller must have checked that *sop is a procedure. */
+int
+swrite_proc(ref *sop, stream **psstrm)
+{ return s_proc_init(sop, psstrm, s_mode_write, &s_proc_write_template);
+}
+
+/* Handle an output request. */
+#define ss ((stream_proc_state *)st)
+private int
+s_proc_write_process(stream_state *st, stream_cursor_read *pr,
+ stream_cursor_write *ignore_pw, bool last)
+{ /* Move data from the stream buffer to the string */
+ /* returned by the procedure, or ask for a callback. */
+ uint rcount = pr->limit - pr->ptr;
+ if ( rcount > 0 )
+ { uint wcount = r_size(&ss->data) - ss->index;
+ uint count = min(rcount, wcount);
+ memcpy(ss->data.value.bytes + ss->index, pr->ptr + 1, count);
+ pr->ptr += count;
+ ss->index += count;
+ if ( rcount > wcount )
+ return CALLC;
+ else if ( last )
+ { ss->eof = true;
+ return CALLC;
+ }
+ else
+ return 0;
+ }
+ return ((ss->eof = last) ? EOFC : 0);
+}
+#undef ss
+
+/* Handle an exception (INTC or CALLC) from a write stream */
+/* whose buffer is full. */
+int
+s_handle_write_exception(int status, const ref *fop, const ref *pstate,
+ int nstate, int (*cont)(P1(os_ptr)))
+{ stream *ps;
+
+ switch ( status )
+ {
+ case INTC: return s_handle_intc(pstate, nstate, cont);
+ case CALLC: break;
+ default: return_error(e_ioerror);
+ }
+ /* Find the stream whose buffer needs emptying. */
+ for ( ps = fptr(fop); ps->strm != 0; )
+ ps = ps->strm;
+#define psst ((stream_proc_state *)ps->state)
+ if ( psst->eof )
+ { /* This is the final call from closing the stream. */
+ /* Don't run the continuation. */
+ check_estack(5);
+ esp += 5;
+ make_op_estack(esp - 4, zpop); /* pop the file */
+ make_op_estack(esp - 3, zpop); /* pop the string returned */
+ /* by the procedure */
+ make_false(esp - 1);
+ }
+ else
+ { int npush = nstate + 6;
+ check_estack(npush);
+ if ( nstate )
+ memcpy(esp + 2, pstate, nstate * sizeof(ref));
+ make_op_estack(esp + 1, cont);
+ esp += npush;
+ make_op_estack(esp - 4, s_proc_write_continue);
+ esp[-3] = *fop;
+ r_clear_attrs(esp - 3, a_executable);
+ make_true(esp - 1);
+ }
+ esp[-2] = psst->proc;
+ *esp = psst->data;
+ r_set_size(esp, psst->index);
+#undef psst
+ return o_push_estack;
+}
+/* Continue a write operation after returning from a procedure callout. */
+/* osp[0] contains the file (pushed on the e-stack by handle_write_status); */
+/* osp[-1] contains the new buffer string (pushed by the procedure). */
+/* The top of the e-stack contains the real continuation. */
+private int
+s_proc_write_continue(os_ptr op)
+{ os_ptr opbuf = op - 1;
+ stream *ps;
+ stream_proc_state *ss;
+
+ check_file(ps, op);
+ check_write_type(*opbuf, t_string);
+ while ( (ps->end_status = 0, ps->strm) != 0 )
+ ps = ps->strm;
+ ss = (stream_proc_state *)ps->state;
+ ss->data = *opbuf;
+ ss->index = 0;
+ pop(2);
+ return 0;
+}
+
+/* ------ Initialization procedure ------ */
+
+BEGIN_OP_DEFS(zfproc_op_defs) {
+ /* Internal operators */
+ {"2%s_proc_read_continue", s_proc_read_continue},
+ {"2%s_proc_write_continue", s_proc_write_continue},
+END_OP_DEFS(0) }
diff --git a/pstoraster/zfzlib.c b/pstoraster/zfzlib.c
new file mode 100644
index 000000000..433d5e0fe
--- /dev/null
+++ b/pstoraster/zfzlib.c
@@ -0,0 +1,101 @@
+/* Copyright (C) 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+#include <config.h>
+#ifdef HAVE_LIBZ
+
+/* zfzlib.c */
+/* zlib and Flate filter creation */
+#include "ghost.h"
+#include "errors.h"
+#include "oper.h"
+#include "idict.h"
+#include "strimpl.h"
+#include "spdiffx.h"
+#include "spngpx.h"
+#include "szlibx.h"
+#include "ifilter.h"
+
+/* Import the Predictor machinery from zfdecode.c and zfilter2.c. */
+int filter_read_predictor(P4(os_ptr op, int npop,
+ const stream_template *template, stream_state *st));
+int filter_write_predictor(P4(os_ptr op, int npop,
+ const stream_template *template, stream_state *st));
+
+/* <source> zlibEncode/filter <file> */
+/* <source> <dict_ignored> zlibEncode/filter <file> */
+private int
+zzlibE(os_ptr op)
+{ return filter_write_simple(op, &s_zlibE_template);
+}
+
+/* <target> zlibDecode/filter <file> */
+/* <target> <dict_ignored> zlibDecode/filter <file> */
+private int
+zzlibD(os_ptr op)
+{ return filter_read_simple(op, &s_zlibD_template);
+}
+
+/* <source> FlateEncode/filter <file> */
+/* <source> <dict> FlateEncode/filter <file> */
+private int
+zFlateE(os_ptr op)
+{ stream_zlib_state zls;
+ int npop;
+
+ if ( r_has_type(op, t_dictionary) )
+ { check_dict_read(*op);
+ npop = 1;
+ }
+ else
+ npop = 0;
+ return filter_write_predictor(op, npop, &s_zlibE_template,
+ (stream_state *)&zls);
+}
+
+/* <target> FlateDecode/filter <file> */
+/* <target> <dict> FlateDecode/filter <file> */
+private int
+zFlateD(os_ptr op)
+{ stream_zlib_state zls;
+ int npop;
+
+ if ( r_has_type(op, t_dictionary) )
+ { check_dict_read(*op);
+ npop = 1;
+ }
+ else
+ npop = 0;
+ return filter_read_predictor(op, npop, &s_zlibD_template,
+ (stream_state *)&zls);
+}
+
+/* ------ Initialization procedure ------ */
+
+BEGIN_OP_DEFS(zfzlib_op_defs) {
+ op_def_begin_filter(),
+ {"1zlibEncode", zzlibE},
+ {"1zlibDecode", zzlibD},
+ {"1FlateEncode", zFlateE},
+ {"1FlateDecode", zFlateD},
+END_OP_DEFS(0) }
+#endif
diff --git a/pstoraster/zgeneric.c b/pstoraster/zgeneric.c
new file mode 100644
index 000000000..cc3610fb3
--- /dev/null
+++ b/pstoraster/zgeneric.c
@@ -0,0 +1,495 @@
+/* Copyright (C) 1989, 1992, 1993, 1994 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* zgeneric.c */
+/* Array/string/dictionary generic operators for PostScript */
+#include "memory_.h"
+#include "ghost.h"
+#include "errors.h"
+#include "oper.h"
+#include "estack.h" /* for forall */
+#include "idict.h"
+#include "iname.h"
+#include "ipacked.h"
+#include "ivmspace.h"
+#include "store.h"
+
+/* This file implements copy, get, put, getinterval, putinterval, */
+/* length, and forall, which apply generically to */
+/* arrays, strings, and dictionaries. (Copy also has a special */
+/* meaning for copying the top N elements of the stack.) */
+
+/* See the comment in opdef.h for an invariant which allows */
+/* more efficient implementation of forall. */
+
+/* Imported operators */
+extern int zcopy_dict(P1(os_ptr));
+
+/* Forward references */
+private int zcopy_invalid(P1(os_ptr));
+private int zcopy_integer(P1(os_ptr));
+private int zcopy_interval(P1(os_ptr));
+private int copy_interval(P4(os_ptr, uint, os_ptr, client_name_t));
+
+/* The type dispatch table for `copy'. */
+/* We export it so that Level 2 can extend it to handle gstates. */
+op_proc_p zcopy_procs[t_next_index];
+
+/* Initialize the generic dispatch table. */
+private void
+zgeneric_init(void)
+{ int i;
+ for ( i = 0; i < t_next_index; i++ )
+ zcopy_procs[i] = zcopy_invalid;
+ /*zcopy_procs[t_integer] = zcopy_integer;*/ /* handled specially */
+ zcopy_procs[t_array] = zcopy_procs[t_string] = zcopy_interval;
+ zcopy_procs[t_dictionary] = zcopy_dict;
+}
+
+/* <various1> <various2> copy <various> */
+/* <obj1> ... <objn> <int> copy <obj1> ... <objn> <obj1> ... <objn> */
+/* Note that this implements copy for arrays and strings, */
+/* but not for dictionaries (see zcopy_dict in zdict.c). */
+private int
+zcopy(register os_ptr op)
+{ int type = r_type(op);
+ if ( type == t_integer )
+ return zcopy_integer(op);
+ if ( type >= t_next_index )
+ return_error(e_typecheck);
+ check_op(2);
+ return (*zcopy_procs[type])(op);
+}
+/* <other> copy */
+private int
+zcopy_invalid(os_ptr op)
+{ return_op_typecheck(op);
+}
+/* <obj1> ... <objn> <int> copy <obj1> ... <objn> <obj1> ... <objn> */
+private int
+zcopy_integer(register os_ptr op)
+{ os_ptr op1 = op - 1;
+ int count, i;
+ int code;
+ if ( (ulong)op->value.intval > op - osbot )
+ { /* There might be enough elements in other blocks. */
+ check_int_ltu(*op, ref_stack_count(&o_stack));
+ count = op->value.intval;
+ }
+ else if ( op1 + (count = op->value.intval) <= ostop )
+ { /* Fast case. */
+ memcpy((char *)op, (char *)(op - count), count * sizeof(ref));
+ push(count - 1);
+ return 0;
+ }
+ /* Do it the slow, general way. */
+ code = ref_stack_push(&o_stack, count - 1);
+ if ( code < 0 )
+ return code;
+ for ( i = 0; i < count; i++ )
+ *ref_stack_index(&o_stack, i) =
+ *ref_stack_index(&o_stack, i + count);
+ return 0;
+}
+/* <array1> <array2> copy <subarray2> */
+/* <string1> <string2> copy <substring2> */
+private int
+zcopy_interval(register os_ptr op)
+{ os_ptr op1 = op - 1;
+ int code = copy_interval(op, 0, op1, "copy");
+ if ( code < 0 ) return code;
+ r_set_size(op, r_size(op1));
+ *op1 = *op;
+ pop(1);
+ return 0;
+}
+
+/* <array|dict|name|packedarray|string> length <int> */
+private int
+zlength(register os_ptr op)
+{ switch ( r_type(op) )
+ {
+ case t_array:
+ case t_string:
+ case t_mixedarray:
+ case t_shortarray:
+ check_read(*op);
+ make_int(op, r_size(op));
+ return 0;
+ case t_dictionary:
+ check_dict_read(*op);
+ make_int(op, dict_length(op));
+ return 0;
+ case t_name:
+ { ref str;
+ name_string_ref(op, &str);
+ make_int(op, r_size(&str));
+ }
+ return 0;
+ default:
+ return_op_typecheck(op);
+ }
+}
+
+/* <array|packedarray|string> <index> get <obj> */
+/* <dict> <key> get <obj> */
+private int
+zget(register os_ptr op)
+{ os_ptr op1 = op - 1;
+ ref *pvalue;
+ switch ( r_type(op1) )
+ {
+ case t_dictionary:
+ check_dict_read(*op1);
+ if ( dict_find(op1, op, &pvalue) <= 0 )
+ return_error(e_undefined);
+ op[-1] = *pvalue;
+ break;
+ case t_string:
+ check_read(*op1);
+ check_int_ltu(*op, r_size(op1));
+ make_int(op1, op1->value.bytes[(uint)op->value.intval]);
+ break;
+ default:
+ { int code;
+ check_type(*op, t_integer);
+ check_read(*op1);
+ code = array_get(op1, op->value.intval, op1);
+ if ( code < 0 )
+ { /* Might be a stackunderflow reported as typecheck. */
+ if ( code == e_typecheck )
+ return_op_typecheck(op1);
+ else
+ return code;
+ }
+ }
+ }
+ pop(1);
+ return 0;
+}
+
+/* <array> <index> <obj> put - */
+/* <dict> <key> <value> put - */
+/* <string> <index> <int> put - */
+private int
+zput(register os_ptr op)
+{ os_ptr op1 = op - 1;
+ os_ptr op2 = op1 - 1;
+ switch ( r_type(op2) )
+ {
+ case t_dictionary:
+ check_dict_write(*op2);
+ { int code = dict_put(op2, op1, op);
+ if ( code < 0 ) return code; /* error */
+ }
+ break;
+ case t_array:
+ check_write(*op2);
+ check_int_ltu(*op1, r_size(op2));
+ store_check_dest(op2, op);
+ { ref *eltp = op2->value.refs + (uint)op1->value.intval;
+ ref_assign_old(op2, eltp, op, "put");
+ } break;
+ case t_mixedarray: /* packed arrays are read-only */
+ case t_shortarray:
+ return_error(e_invalidaccess);
+ case t_string:
+ check_write(*op2);
+ check_int_ltu(*op1, r_size(op2));
+ check_int_leu(*op, 0xff);
+ op2->value.bytes[(uint)op1->value.intval] = (byte)op->value.intval;
+ break;
+ default:
+ return_op_typecheck(op2);
+ }
+ pop(3);
+ return 0;
+}
+
+/* <seq:array|packedarray|string> <index> <count> getinterval <subseq> */
+private int
+zgetinterval(register os_ptr op)
+{ os_ptr op1 = op - 1;
+ os_ptr op2 = op1 - 1;
+ uint index;
+ uint count;
+ switch ( r_type(op2) )
+ {
+ default:
+ return_op_typecheck(op2);
+ case t_array: case t_string:
+ case t_mixedarray:
+ case t_shortarray: ;
+ }
+ check_read(*op2);
+ check_int_leu(*op1, r_size(op2));
+ index = op1->value.intval;
+ check_int_leu(*op, r_size(op2) - index);
+ count = op->value.intval;
+ switch ( r_type(op2) )
+ {
+ case t_array: op2->value.refs += index; break;
+ case t_string: op2->value.bytes += index; break;
+ case t_mixedarray:
+ { const ref_packed *packed = op2->value.packed;
+ for ( ; index--; ) packed = packed_next(packed);
+ op2->value.packed = packed;
+ } break;
+ case t_shortarray: op2->value.packed += index; break;
+ }
+ r_set_size(op2, count);
+ pop(2);
+ return 0;
+}
+
+/* <array1> <index> <array2|packedarray2> putinterval - */
+/* <string1> <index> <string2> putinterval - */
+private int
+zputinterval(register os_ptr op)
+{ os_ptr opindex = op - 1;
+ os_ptr opto = opindex - 1;
+ int code;
+ switch ( r_type(opto) )
+ {
+ default:
+ return_op_typecheck(opto);
+ case t_mixedarray:
+ case t_shortarray:
+ return_error(e_invalidaccess);
+ case t_array:
+ case t_string:
+ ;
+ }
+ check_write(*opto);
+ check_int_leu(*opindex, r_size(opto));
+ code = copy_interval(opto, (uint)(opindex->value.intval), op, "putinterval");
+ if ( code >= 0 ) pop(3);
+ return code;
+}
+
+/* <array|packedarray|string> <<element> proc> forall - */
+/* <dict> <<key> <value> proc> forall - */
+private int
+ array_continue(P1(os_ptr)),
+ dict_continue(P1(os_ptr)),
+ string_continue(P1(os_ptr)),
+ packedarray_continue(P1(os_ptr));
+private int forall_cleanup(P1(os_ptr));
+private int
+zforall(register os_ptr op)
+{ os_ptr obj = op - 1;
+ register es_ptr ep = esp;
+ uint index = 0; /* only used for dictionaries */
+ check_estack(6);
+#define cproc (ep + 5)
+ switch ( r_type(obj) )
+ {
+ default:
+ return_op_typecheck(obj);
+ case t_array:
+ check_read(*obj);
+ make_op_estack(cproc, array_continue);
+ break;
+ case t_dictionary:
+ check_dict_read(*obj);
+ make_op_estack(cproc, dict_continue);
+ index = dict_first(obj);
+ break;
+ case t_string:
+ check_read(*obj);
+ make_op_estack(cproc, string_continue);
+ break;
+ case t_mixedarray:
+ case t_shortarray:
+ check_read(*obj);
+ make_op_estack(cproc, packedarray_continue);
+ break;
+ }
+ check_proc(*op);
+ /* Push a mark, the composite object, the iteration index, */
+ /* and the procedure, and invoke the continuation operator. */
+ make_mark_estack(ep + 1, es_for, forall_cleanup);
+ ep[2] = *obj;
+ make_int(ep + 3, index);
+ ep[4] = *op;
+ esp += 4;
+ pop(2); op -= 2;
+ return (*real_opproc(cproc))(op);
+#undef cproc
+}
+/* Continuation operator for arrays */
+private int
+array_continue(register os_ptr op)
+{ es_ptr obj = esp - 2;
+ if ( r_size(obj) ) /* continue */
+ { push(1);
+ r_dec_size(obj, 1);
+ *op = *obj->value.refs;
+ obj->value.refs++;
+ esp += 2;
+ *esp = obj[2];
+ return o_push_estack;
+ }
+ else /* done */
+ { esp -= 4; /* pop mark, object, index, proc */
+ return o_pop_estack;
+ }
+}
+/* Continuation operator for dictionaries */
+private int
+dict_continue(register os_ptr op)
+{ es_ptr obj = esp - 2;
+ int index = (int)esp[-1].value.intval;
+ push(2); /* make room for key and value */
+ if ( (index = dict_next(obj, index, op - 1)) >= 0 ) /* continue */
+ { esp[-1].value.intval = index;
+ esp += 2;
+ *esp = obj[2];
+ return o_push_estack;
+ }
+ else /* done */
+ { pop(2); /* undo push */
+ esp -= 4; /* pop mark, object, index, proc */
+ return o_pop_estack;
+ }
+}
+/* Continuation operator for strings */
+private int
+string_continue(register os_ptr op)
+{ es_ptr obj = esp - 2;
+ if ( r_size(obj) ) /* continue */
+ { r_dec_size(obj, 1);
+ push(1);
+ make_int(op, *obj->value.bytes);
+ obj->value.bytes++;
+ esp += 2;
+ *esp = obj[2];
+ return o_push_estack;
+ }
+ else /* done */
+ { esp -= 4; /* pop mark, object, index, proc */
+ return o_pop_estack;
+ }
+}
+/* Continuation operator for packed arrays */
+private int
+packedarray_continue(register os_ptr op)
+{ es_ptr obj = esp - 2;
+ if ( r_size(obj) ) /* continue */
+ { const ref_packed *packed = obj->value.packed;
+ r_dec_size(obj, 1);
+ push(1);
+ packed_get(packed, op);
+ obj->value.packed = packed_next(packed);
+ esp += 2;
+ *esp = obj[2];
+ return o_push_estack;
+ }
+ else /* done */
+ { esp -= 4; /* pop mark, object, index, proc */
+ return o_pop_estack;
+ }
+}
+/* Vacuous cleanup procedure */
+private int
+forall_cleanup(os_ptr op)
+{ return 0;
+}
+
+/* ------ Initialization procedure ------ */
+
+BEGIN_OP_DEFS(zgeneric_op_defs) {
+ {"1copy", zcopy},
+ {"2forall", zforall},
+ {"2get", zget},
+ {"3getinterval", zgetinterval},
+ {"1length", zlength},
+ {"3put", zput},
+ {"3putinterval", zputinterval},
+ /* Internal operators */
+ {"0%array_continue", array_continue},
+ {"0%dict_continue", dict_continue},
+ {"0%packedarray_continue", packedarray_continue},
+ {"0%string_continue", string_continue},
+END_OP_DEFS(zgeneric_init) }
+
+/* ------ Shared routines ------ */
+
+/* Copy an interval from one operand to another. */
+/* This is used by both putinterval and string/array copy. */
+/* The destination is known to be an array or string, */
+/* and the starting index is known to be less than or equal to */
+/* its length; nothing else has been checked. */
+private int
+copy_interval(os_ptr prto, uint index, os_ptr prfrom, client_name_t cname)
+{ int fromtype = r_type(prfrom);
+ uint fromsize = r_size(prfrom);
+ if ( !(fromtype == r_type(prto) ||
+ ((fromtype == t_shortarray || fromtype == t_mixedarray) &&
+ r_type(prto) == t_array))
+ )
+ return_op_typecheck(prfrom);
+ check_read(*prfrom);
+ check_write(*prto);
+ if ( fromsize > r_size(prto) - index )
+ return_error(e_rangecheck);
+ switch ( fromtype )
+ {
+ case t_array:
+ { /* We have to worry about aliasing, */
+ /* but refcpy_to_old takes care of it for us. */
+ return refcpy_to_old(prto, index, prfrom->value.refs,
+ fromsize, cname);
+ }
+ case t_string:
+ { /* We have to worry about aliasing. */
+ const byte *from = prfrom->value.bytes;
+ byte *to = prto->value.bytes + index;
+ uint i;
+ if ( from + fromsize <= to || to + fromsize <= from )
+ memcpy(to, from, fromsize);
+ else if ( to < from )
+ for ( i = fromsize; i != 0; i--, from++, to++ )
+ *to = *from;
+ else
+ for ( i = fromsize, from += i, to += i; i != 0; i-- )
+ *--to = *--from;
+ } break;
+ case t_mixedarray:
+ case t_shortarray:
+ { /* We don't have to worry about aliasing, because */
+ /* packed arrays are read-only and hence the destination */
+ /* can't be a packed array. */
+ int i;
+ const ref_packed *packed = prfrom->value.packed;
+ ref *pdest = prto->value.refs + index;
+ ref elt;
+ for ( i = 0; i < fromsize; i++, pdest++ )
+ { packed_get(packed, &elt);
+ ref_assign_old(prto, pdest, &elt, cname);
+ packed = packed_next(packed);
+ }
+ } break;
+ }
+ return 0;
+}
diff --git a/pstoraster/zgstate.c b/pstoraster/zgstate.c
new file mode 100644
index 000000000..32a8522f0
--- /dev/null
+++ b/pstoraster/zgstate.c
@@ -0,0 +1,329 @@
+/* Copyright (C) 1989, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* zgstate.c */
+/* Graphics state operators */
+#include "ghost.h"
+#include "errors.h"
+#include "oper.h"
+#include "ialloc.h"
+#include "idict.h"
+#include "istruct.h"
+#include "igstate.h"
+#include "gsmatrix.h"
+#include "store.h"
+
+/* Forward references */
+private int near num_param(P2(const_os_ptr, int (*)(P2(gs_state *, floatp))));
+
+/* Structure descriptors */
+private_st_int_gstate();
+
+/* ------ Operations on the entire graphics state ------ */
+
+/* The current graphics state */
+gs_state *igs;
+private gs_gc_root_t igs_root;
+
+/* An empty dictionary for the pagedevice member of the int_gstate. */
+ref i_null_pagedevice;
+private ref *npd_p;
+private gs_gc_root_t npd_root;
+
+/* "Client" procedures */
+private void *gs_istate_alloc(P1(gs_memory_t *mem));
+private int gs_istate_copy(P2(void *to, const void *from));
+private void gs_istate_free(P2(void *old, gs_memory_t *mem));
+private const gs_state_client_procs istate_procs = {
+ gs_istate_alloc,
+ gs_istate_copy,
+ gs_istate_free
+};
+
+/* Initialize the graphics stack. */
+void
+igs_init(void)
+{ int_gstate *iigs;
+ ref proc0;
+ igs = gs_state_alloc(imemory);
+ iigs = gs_alloc_struct(imemory, int_gstate, &st_int_gstate, "igs_init");
+ int_gstate_map_refs(iigs, make_null);
+ make_empty_array(&iigs->dash_pattern, a_all);
+ ialloc_ref_array(&proc0, a_readonly + a_executable, 2,
+ "igs_init");
+ make_oper(proc0.value.refs, 0, zpop);
+ make_real(proc0.value.refs + 1, 0.0);
+ iigs->black_generation = proc0;
+ iigs->undercolor_removal = proc0;
+ dict_create(0, &i_null_pagedevice);
+ r_clear_attrs(&i_null_pagedevice, a_write);
+ iigs->pagedevice = i_null_pagedevice;
+ npd_p = &i_null_pagedevice;
+ gs_register_ref_root(imemory, &npd_root, (void **)&npd_p, "igs(npd)");
+ gs_state_set_client(igs, iigs, &istate_procs);
+ gs_register_struct_root(imemory, &igs_root, (void **)&igs, "igs");
+ /*
+ * gsave and grestore only work properly
+ * if there are always at least 2 entries on the stack.
+ * We count on the PostScript initialization code to do a gsave.
+ */
+}
+
+/* - gsave - */
+int
+zgsave(register os_ptr op)
+{ return gs_gsave(igs);
+}
+
+/* - grestore - */
+int
+zgrestore(register os_ptr op)
+{ return gs_grestore(igs);
+}
+
+/* - grestoreall - */
+int
+zgrestoreall(register os_ptr op)
+{ return gs_grestoreall(igs);
+}
+
+/* - initgraphics - */
+private int
+zinitgraphics(register os_ptr op)
+{ return gs_initgraphics(igs);
+}
+
+/* ------ Operations on graphics state elements ------ */
+
+/* <num> setlinewidth - */
+private int
+zsetlinewidth(register os_ptr op)
+{ return num_param(op, gs_setlinewidth);
+}
+
+/* - currentlinewidth <num> */
+private int
+zcurrentlinewidth(register os_ptr op)
+{ push(1);
+ make_real(op, gs_currentlinewidth(igs));
+ return 0;
+}
+
+/* <cap_int> setlinecap - */
+private int
+zsetlinecap(register os_ptr op)
+{ int param;
+ int code = int_param(op, 2, &param);
+ if ( code < 0 || (code = gs_setlinecap(igs, (gs_line_cap)param)) < 0 )
+ return code;
+ pop(1);
+ return 0;
+}
+
+/* - currentlinecap <cap_int> */
+private int
+zcurrentlinecap(register os_ptr op)
+{ push(1);
+ make_int(op, (int)gs_currentlinecap(igs));
+ return 0;
+}
+
+/* <join_int> setlinejoin - */
+private int
+zsetlinejoin(register os_ptr op)
+{ int param;
+ int code = int_param(op, 2, &param);
+ if ( code < 0 || (code = gs_setlinejoin(igs, (gs_line_join)param)) < 0 )
+ return code;
+ pop(1);
+ return 0;
+}
+
+/* - currentlinejoin <join_int> */
+private int
+zcurrentlinejoin(register os_ptr op)
+{ push(1);
+ make_int(op, (int)gs_currentlinejoin(igs));
+ return 0;
+}
+
+/* <num> setmiterlimit - */
+private int
+zsetmiterlimit(register os_ptr op)
+{ return num_param(op, gs_setmiterlimit);
+}
+
+/* - currentmiterlimit <num> */
+private int
+zcurrentmiterlimit(register os_ptr op)
+{ push(1);
+ make_real(op, gs_currentmiterlimit(igs));
+ return 0;
+}
+
+/* <array> <offset> setdash - */
+private int
+zsetdash(register os_ptr op)
+{ os_ptr op1 = op - 1;
+ float offset;
+ int code = real_param(op, &offset);
+ uint i, n;
+ gs_memory_t *mem = imemory;
+ float *pattern;
+
+ if ( code < 0 )
+ return_op_typecheck(op);
+ if ( !r_is_array(op1) )
+ return_op_typecheck(op1);
+ /* Adobe interpreters apparently don't check the array for */
+ /* read access, so we won't either. */
+ /*check_read(*op1);*/
+ /* Unpack the dash pattern and check it */
+ n = r_size(op1);
+ pattern =
+ (float *)gs_alloc_byte_array(mem, n, sizeof(float), "setdash");
+ if ( pattern == 0 )
+ return_error(e_VMerror);
+ for ( i = 0, code = 0; i < n && code >= 0; ++i )
+ { ref element;
+ array_get(op1, (long)i, &element);
+ code = real_param(&element, &pattern[i]);
+ }
+ if ( code >= 0 )
+ code = gs_setdash(igs, pattern, n, offset);
+ gs_free_object(mem, pattern, "setdash"); /* gs_setdash copies this */
+ if ( code < 0 )
+ return code;
+ ref_assign(&istate->dash_pattern, op1);
+ pop(2);
+ return code;
+}
+
+/* - currentdash <array> <offset> */
+private int
+zcurrentdash(register os_ptr op)
+{ push(2);
+ ref_assign(op - 1, &istate->dash_pattern);
+ make_real(op, gs_currentdash_offset(igs));
+ return 0;
+}
+
+/* <num> setflat - */
+private int
+zsetflat(register os_ptr op)
+{ return num_param(op, gs_setflat);
+}
+
+/* - currentflat <num> */
+private int
+zcurrentflat(register os_ptr op)
+{ push(1);
+ make_real(op, gs_currentflat(igs));
+ return 0;
+}
+
+/* ------ Extensions ------ */
+
+/* <adjust.x> <adjust.y> .setfilladjust2 - */
+private int
+zsetfilladjust2(register os_ptr op)
+{ float adjust[2];
+ int code = num_params(op, 2, adjust);
+
+ if ( code < 0 )
+ return code;
+ code = gs_setfilladjust(igs, adjust[0], adjust[1]);
+ if ( code < 0 )
+ return code;
+ pop(2);
+ return 0;
+}
+
+/* - .currentfilladjust2 <adjust.x> <adjust.y> */
+private int
+zcurrentfilladjust2(register os_ptr op)
+{ gs_point adjust;
+
+ push(2);
+ gs_currentfilladjust(igs, &adjust);
+ make_real(op - 1, adjust.x);
+ make_real(op, adjust.y);
+ return 0;
+}
+
+/* ------ Initialization procedure ------ */
+
+BEGIN_OP_DEFS(zgstate_op_defs) {
+ {"0currentdash", zcurrentdash},
+ {"0.currentfilladjust2", zcurrentfilladjust2},
+ {"0currentflat", zcurrentflat},
+ {"0currentlinecap", zcurrentlinecap},
+ {"0currentlinejoin", zcurrentlinejoin},
+ {"0currentlinewidth", zcurrentlinewidth},
+ {"0currentmiterlimit", zcurrentmiterlimit},
+ {"0grestore", zgrestore},
+ {"0grestoreall", zgrestoreall},
+ {"0gsave", zgsave},
+ {"0initgraphics", zinitgraphics},
+ {"2setdash", zsetdash},
+ {"2.setfilladjust2", zsetfilladjust2},
+ {"1setflat", zsetflat},
+ {"1setlinecap", zsetlinecap},
+ {"1setlinejoin", zsetlinejoin},
+ {"1setlinewidth", zsetlinewidth},
+ {"1setmiterlimit", zsetmiterlimit},
+END_OP_DEFS(0) }
+
+/* ------ Internal routines ------ */
+
+/* Allocate the interpreter's part of a graphics state. */
+private void *
+gs_istate_alloc(gs_memory_t *mem)
+{ return gs_alloc_struct(mem, int_gstate, &st_int_gstate, "int_gsave");
+}
+
+/* Copy the interpreter's part of a graphics state. */
+private int
+gs_istate_copy(void *to, const void *from)
+{ *(int_gstate *)to = *(const int_gstate *)from;
+ return 0;
+}
+
+/* Free the interpreter's part of a graphics state. */
+private void
+gs_istate_free(void *old, gs_memory_t *mem)
+{ gs_free_object(mem, old, "int_grestore");
+}
+
+/* Get a numeric parameter */
+private int near
+num_param(const_os_ptr op, int (*pproc)(P2(gs_state *, floatp)))
+{ float param;
+ int code = real_param(op, &param);
+
+ if ( code < 0 )
+ return_op_typecheck(op);
+ code = (*pproc)(igs, param);
+ if ( !code ) pop(1);
+ return code;
+}
diff --git a/pstoraster/zhsb.c b/pstoraster/zhsb.c
new file mode 100644
index 000000000..f3cfe338d
--- /dev/null
+++ b/pstoraster/zhsb.c
@@ -0,0 +1,62 @@
+/* Copyright (C) 1994 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* zhsb.c */
+/* HSB color operators */
+#include "ghost.h"
+#include "errors.h"
+#include "oper.h"
+#include "igstate.h"
+#include "store.h"
+#include "gshsb.h"
+
+/* - currenthsbcolor <hue> <saturation> <brightness> */
+private int
+zcurrenthsbcolor(register os_ptr op)
+{ float par[3];
+ gs_currenthsbcolor(igs, par);
+ push(3);
+ make_reals(op - 2, par, 3);
+ return 0;
+}
+
+/* <hue> <saturation> <brightness> sethsbcolor - */
+private int
+zsethsbcolor(register os_ptr op)
+{ float par[3];
+ int code;
+ if ( (code = num_params(op, 3, par)) < 0 ||
+ (code = gs_sethsbcolor(igs, par[0], par[1], par[2])) < 0
+ )
+ return code;
+ make_null(&istate->colorspace.array);
+ pop(3);
+ return 0;
+}
+
+/* ------ Initialization procedure ------ */
+
+BEGIN_OP_DEFS(zhsb_op_defs) {
+ {"0currenthsbcolor", zcurrenthsbcolor},
+ {"3sethsbcolor", zsethsbcolor},
+END_OP_DEFS(0) }
diff --git a/pstoraster/zht.c b/pstoraster/zht.c
new file mode 100644
index 000000000..cff3b915c
--- /dev/null
+++ b/pstoraster/zht.c
@@ -0,0 +1,238 @@
+/* Copyright (C) 1989, 1991, 1993, 1994 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* zht.c */
+/* Halftone definition operators */
+#include "ghost.h"
+#include "memory_.h"
+#include "errors.h"
+#include "oper.h"
+#include "estack.h"
+#include "gsstruct.h" /* must precede igstate.h, */
+ /* because of #ifdef in gsht.h */
+#include "ialloc.h"
+#include "igstate.h"
+#include "gsmatrix.h"
+#include "gxdevice.h" /* for gzht.h */
+#include "gzht.h"
+#include "gsstate.h"
+#include "store.h"
+
+/* Forward references */
+int zscreen_params(P2(os_ptr, gs_screen_halftone *)); /* exported for zht1.c */
+private int screen_sample(P1(os_ptr));
+private int set_screen_continue(P1(os_ptr));
+private int screen_cleanup(P1(os_ptr));
+
+/* - .currenthalftone <dict> 0 */
+/* - .currenthalftone <frequency> <angle> <proc> 1 */
+/* - .currenthalftone <red_freq> ... <gray_proc> 2 */
+private int
+zcurrenthalftone(register os_ptr op)
+{ gs_halftone ht;
+ gs_currenthalftone(igs, &ht);
+ switch ( ht.type )
+ {
+ case ht_type_screen:
+ push(4);
+ make_real(op - 3, ht.params.screen.frequency);
+ make_real(op - 2, ht.params.screen.angle);
+ op[-1] = istate->screen_procs.colored.gray;
+ make_int(op, 1);
+ break;
+ case ht_type_colorscreen:
+ push(13);
+ { int i;
+ for ( i = 0; i < 4; i++ )
+ { os_ptr opc = op - 12 + i * 3;
+ gs_screen_halftone *pht =
+ &ht.params.colorscreen.screens.indexed[i];
+ make_real(opc, pht->frequency);
+ make_real(opc + 1, pht->angle);
+ opc[2] = istate->screen_procs.indexed[i];
+ }
+ }
+ make_int(op, 2);
+ break;
+ default: /* Screen was set by sethalftone. */
+ push(2);
+ op[-1] = istate->halftone;
+ make_int(op, 0);
+ break;
+ }
+ return 0;
+}
+
+/* - .currentscreenlevels <int> */
+private int
+zcurrentscreenlevels(register os_ptr op)
+{ push(1);
+ make_int(op, gs_currentscreenlevels(igs));
+ return 0;
+}
+
+/* The setscreen operator is complex because it has to sample */
+/* each pixel in the pattern cell, calling a procedure, and then */
+/* sort the result into a whitening order. */
+
+/* Layout of stuff pushed on estack: */
+/* Control mark, */
+/* [other stuff for other screen-setting operators], */
+/* finishing procedure (or 0), */
+/* spot procedure, */
+/* enumeration structure (as bytes). */
+#define snumpush 4
+#define sproc esp[-1]
+#define senum r_ptr(esp, gs_screen_enum)
+
+/* Forward references */
+int zscreen_enum_init(P6(os_ptr, const gx_ht_order *, gs_screen_halftone *,
+ ref *, int, int (*)(P1(os_ptr))));
+private int setscreen_finish(P1(os_ptr));
+
+/* <frequency> <angle> <proc> setscreen - */
+private int
+zsetscreen(register os_ptr op)
+{ gs_screen_halftone screen;
+ gx_ht_order order;
+ int code = zscreen_params(op, &screen);
+ if ( code < 0 )
+ return code;
+ code = gs_screen_order_init(&order, igs, &screen,
+ gs_currentaccuratescreens());
+ if ( code < 0 )
+ return code;
+ return zscreen_enum_init(op, &order, &screen, op, 3,
+ setscreen_finish);
+}
+/* We break out the body of this operator so it can be shared with */
+/* the code for Type 1 halftones in sethalftone. */
+int
+zscreen_enum_init(os_ptr op, const gx_ht_order *porder,
+ gs_screen_halftone *psp, ref *pproc, int npop,
+ int (*finish_proc)(P1(os_ptr)))
+{ gs_screen_enum *penum;
+ int code;
+ check_estack(snumpush + 1);
+ penum = gs_screen_enum_alloc(imemory, "setscreen");
+ if ( penum == 0 )
+ return_error(e_VMerror);
+ make_istruct(esp + snumpush, 0, penum); /* do early for screen_cleanup in case of error */
+ code = gs_screen_enum_init(penum, porder, igs, psp);
+ if ( code < 0 )
+ { screen_cleanup(op);
+ return code;
+ }
+ /* Push everything on the estack */
+ make_mark_estack(esp + 1, es_other, screen_cleanup);
+ esp += snumpush;
+ make_op_estack(esp - 2, finish_proc);
+ sproc = *pproc;
+ push_op_estack(screen_sample);
+ pop(npop);
+ return o_push_estack;
+}
+/* Set up the next sample */
+private int
+screen_sample(register os_ptr op)
+{ gs_screen_enum *penum = senum;
+ gs_point pt;
+ int code = gs_screen_currentpoint(penum, &pt);
+ ref proc;
+ switch ( code )
+ {
+ default:
+ return code;
+ case 1:
+ /* All done */
+ if ( real_opproc(esp - 2) != 0 )
+ code = (*real_opproc(esp - 2))(op);
+ esp -= snumpush;
+ screen_cleanup(op);
+ return (code < 0 ? code : o_pop_estack);
+ case 0:
+ ;
+ }
+ push(2);
+ make_real(op - 1, pt.x);
+ make_real(op, pt.y);
+ proc = sproc;
+ push_op_estack(set_screen_continue);
+ *++esp = proc;
+ return o_push_estack;
+}
+/* Continuation procedure for processing sampled pixels. */
+private int
+set_screen_continue(register os_ptr op)
+{ float value;
+ int code = num_params(op, 1, &value);
+ if ( code < 0 ) return code;
+ code = gs_screen_next(senum, value);
+ if ( code < 0 ) return code;
+ pop(1); op--;
+ return screen_sample(op);
+}
+/* Finish setscreen. */
+private int
+setscreen_finish(os_ptr op)
+{ gs_screen_install(senum);
+ istate->screen_procs.colored.red = sproc;
+ istate->screen_procs.colored.green = sproc;
+ istate->screen_procs.colored.blue = sproc;
+ istate->screen_procs.colored.gray = sproc;
+ make_null(&istate->halftone);
+ return 0;
+}
+/* Clean up after screen enumeration */
+private int
+screen_cleanup(os_ptr op)
+{ ifree_object(esp[snumpush].value.pstruct, "screen_cleanup");
+ return 0;
+}
+
+/* ------ Utility procedures ------ */
+
+/* Get parameters for a single screen. */
+int
+zscreen_params(os_ptr op, gs_screen_halftone *phs)
+{ float fa[2];
+ int code = num_params(op - 1, 2, fa);
+ if ( code < 0 )
+ return code;
+ check_proc(*op);
+ phs->frequency = fa[0];
+ phs->angle = fa[1];
+ return 0;
+}
+
+/* ------ Initialization procedure ------ */
+
+BEGIN_OP_DEFS(zht_op_defs) {
+ {"0.currenthalftone", zcurrenthalftone},
+ {"0.currentscreenlevels", zcurrentscreenlevels},
+ {"3setscreen", zsetscreen},
+ /* Internal operators */
+ {"0%screen_sample", screen_sample},
+ {"1%set_screen_continue", set_screen_continue},
+ {"0%setscreen_finish", setscreen_finish},
+END_OP_DEFS(0) }
diff --git a/pstoraster/zht1.c b/pstoraster/zht1.c
new file mode 100644
index 000000000..8876ef73d
--- /dev/null
+++ b/pstoraster/zht1.c
@@ -0,0 +1,143 @@
+/* Copyright (C) 1994 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* zht1.c */
+/* setcolorscreen operator */
+#include "ghost.h"
+#include "memory_.h"
+#include "errors.h"
+#include "oper.h"
+#include "estack.h"
+#include "gsstruct.h" /* must precede igstate.h, */
+ /* because of #ifdef in gsht.h */
+#include "ialloc.h"
+#include "igstate.h"
+#include "gsmatrix.h"
+#include "gxdevice.h" /* for gzht.h */
+#include "gzht.h"
+#include "gsstate.h"
+#include "store.h"
+
+/* Imported from zht.c */
+int zscreen_params(P2(os_ptr, gs_screen_halftone *));
+int zscreen_enum_init(P6(os_ptr, const gx_ht_order *, gs_screen_halftone *,
+ ref *, int, int (*)(P1(os_ptr))));
+
+/* Dummy spot function */
+float
+spot_dummy(floatp x, floatp y)
+{ return (x + y) / 2;
+}
+
+/* <red_freq> ... <gray_proc> setcolorscreen - */
+private int setcolorscreen_finish(P1(os_ptr));
+private int setcolorscreen_cleanup(P1(os_ptr));
+private int
+zsetcolorscreen(register os_ptr op)
+{ gs_colorscreen_halftone cscreen;
+ ref sprocs[4];
+ gs_halftone *pht;
+ gx_device_halftone *pdht;
+ int i;
+ int code = 0;
+ for ( i = 0; i < 4; i++ )
+ { os_ptr op1 = op - 9 + i * 3;
+ int code = zscreen_params(op1, &cscreen.screens.indexed[i]);
+ if ( code < 0 )
+ return code;
+ cscreen.screens.indexed[i].spot_function = spot_dummy;
+ sprocs[i] = *op1;
+ }
+ check_estack(8); /* for sampling screens */
+ pht = ialloc_struct(gs_halftone, &st_halftone, "setcolorscreen");
+ pdht = ialloc_struct(gx_device_halftone, &st_device_halftone,
+ "setcolorscreen");
+ if ( pht == 0 || pdht == 0 )
+ code = gs_note_error(e_VMerror);
+ else
+ { pht->type = ht_type_colorscreen;
+ pht->params.colorscreen = cscreen;
+ code = gs_sethalftone_prepare(igs, pht, pdht);
+ }
+ if ( code >= 0 )
+ { /* Schedule the sampling of the screens. */
+ es_ptr esp0 = esp; /* for backing out */
+ esp += 8;
+ make_mark_estack(esp - 7, es_other, setcolorscreen_cleanup);
+ memcpy(esp - 6, sprocs, sizeof(ref) * 4); /* procs */
+ make_istruct(esp - 2, 0, pht);
+ make_istruct(esp - 1, 0, pdht);
+ make_op_estack(esp, setcolorscreen_finish);
+ for ( i = 0; i < 4; i++ )
+ { /* Shuffle the indices to correspond to */
+ /* the component order. */
+ code = zscreen_enum_init(op,
+ &pdht->components[(i + 1) & 3].corder,
+ &pht->params.colorscreen.screens.indexed[i],
+ &sprocs[i], 0, 0);
+ if ( code < 0 )
+ { esp = esp0;
+ break;
+ }
+ }
+ }
+ if ( code < 0 )
+ { ifree_object(pdht, "setcolorscreen");
+ ifree_object(pht, "setcolorscreen");
+ return code;
+ }
+ pop(12);
+ return o_push_estack;
+}
+/* Install the color screen after sampling. */
+private int
+setcolorscreen_finish(os_ptr op)
+{ gx_device_halftone *pdht = r_ptr(esp, gx_device_halftone);
+ int code;
+ pdht->order = pdht->components[0].corder;
+ code = gx_ht_install(igs, r_ptr(esp - 1, gs_halftone), pdht);
+ if ( code < 0 )
+ return code;
+ memcpy(istate->screen_procs.indexed, esp - 5, sizeof(ref) * 4);
+ make_null(&istate->halftone);
+ esp -= 7;
+ setcolorscreen_cleanup(op);
+ return o_pop_estack;
+}
+/* Clean up after installing the color screen. */
+private int
+setcolorscreen_cleanup(os_ptr op)
+{ ifree_object(esp[7].value.pstruct,
+ "setcolorscreen_cleanup(device halftone)");
+ ifree_object(esp[6].value.pstruct,
+ "setcolorscreen_cleanup(halftone)");
+ return 0;
+}
+
+/* ------ Initialization procedure ------ */
+
+BEGIN_OP_DEFS(zht1_op_defs) {
+ {"<setcolorscreen", zsetcolorscreen},
+ /* Internal operators */
+ {"0%setcolorscreen_finish", setcolorscreen_finish},
+END_OP_DEFS(0) }
diff --git a/pstoraster/zht2.c b/pstoraster/zht2.c
new file mode 100644
index 000000000..c4f466c1c
--- /dev/null
+++ b/pstoraster/zht2.c
@@ -0,0 +1,328 @@
+/* Copyright (C) 1992, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* zht2.c */
+/* Level 2 sethalftone operator */
+#include "ghost.h"
+#include "errors.h"
+#include "oper.h"
+#include "gsstruct.h"
+#include "gxdevice.h" /* for gzht.h */
+#include "gzht.h"
+#include "estack.h"
+#include "ialloc.h"
+#include "idict.h"
+#include "idparam.h"
+#include "igstate.h"
+#include "icolor.h"
+#include "store.h"
+
+/* Imported from zht.c */
+int zscreen_enum_init(P6(os_ptr, const gx_ht_order *, gs_screen_halftone *,
+ ref *, int, int (*)(P1(os_ptr))));
+
+/* Forward references */
+private int dict_spot_params(P4(const ref *, gs_spot_halftone *,
+ ref *, ref *));
+private int dict_spot_results(P2(ref *, const gs_spot_halftone *));
+private int dict_threshold_params(P3(const ref *,
+ gs_threshold_halftone *, ref *));
+
+/* <dict> <dict5> .sethalftone5 - */
+float spot_dummy(P2(floatp, floatp)); /* in zht1.c */
+int spot_sample_finish(P1(os_ptr)); /* in zht.c */
+private int sethalftone_finish(P1(os_ptr));
+private int sethalftone_cleanup(P1(os_ptr));
+private int
+zsethalftone5(register os_ptr op)
+{ uint count;
+ gs_halftone_component *phtc;
+ gs_halftone_component *pc;
+ int code = 0;
+ int i, j;
+ gs_halftone *pht;
+ gx_device_halftone *pdht;
+ static const char _ds *color_names[] =
+ { gs_ht_separation_name_strings };
+ ref sprocs[countof(color_names)];
+ ref tprocs[countof(color_names)];
+ int npop = 2;
+
+ check_type(*op, t_dictionary);
+ check_dict_read(*op);
+ check_type(op[-1], t_dictionary);
+ check_dict_read(op[-1]);
+ count = 0;
+ for ( i = 0; i < countof(color_names); i++ )
+ { ref *pvalue;
+ if ( dict_find_string(op, color_names[i], &pvalue) > 0 )
+ count++;
+ else if ( i == gs_ht_separation_Default )
+ return_error(e_typecheck);
+ }
+ check_estack(5); /* for sampling Type 1 screens */
+ refset_null(sprocs, countof(sprocs));
+ refset_null(tprocs, countof(tprocs));
+ pht = ialloc_struct(gs_halftone, &st_halftone, ".sethalftone5");
+ phtc = ialloc_struct_array(count, gs_halftone_component,
+ &st_ht_component_element,
+ ".sethalftone5");
+ pdht = ialloc_struct(gx_device_halftone, &st_device_halftone,
+ ".sethalftone5");
+ if ( pht == 0 || phtc == 0 || pdht == 0 )
+ code = gs_note_error(e_VMerror);
+ else
+ for ( i = 0, j = 0, pc = phtc; i < countof(color_names); i++ )
+ { int type;
+ ref *pvalue;
+ if ( dict_find_string(op, color_names[i], &pvalue) > 0 )
+ { check_type_only(*pvalue, t_dictionary);
+ check_dict_read(*pvalue);
+ if ( dict_int_param(pvalue, "HalftoneType", 1, 5, 0,
+ &type) < 0
+ )
+ { code = gs_note_error(e_typecheck);
+ break;
+ }
+ switch ( type )
+ {
+ default:
+ code = gs_note_error(e_rangecheck);
+ break;
+ case 1:
+ code = dict_spot_params(pvalue,
+ &pc->params.spot, sprocs + j,
+ tprocs + j);
+ pc->params.spot.screen.spot_function =
+ spot_dummy;
+ pc->type = ht_type_spot;
+ break;
+ case 3:
+ code = dict_threshold_params(pvalue,
+ &pc->params.threshold, tprocs + j);
+ pc->type = ht_type_threshold;
+ break;
+ }
+ if ( code < 0 )
+ break;
+ pc->cname = i;
+ pc++, j++;
+ }
+ }
+ if ( code >= 0 )
+ { /* We think that Type 2 and Type 4 halftones, like */
+ /* screens set by setcolorscreen, adapt automatically to */
+ /* the device color space, so we need to mark them */
+ /* with a different internal halftone type. */
+ int type = 0;
+
+ dict_int_param(op - 1, "HalftoneType", 1, 5, 0, &type);
+ pht->type =
+ (type == 2 || type == 4 ? ht_type_multiple_colorscreen :
+ ht_type_multiple);
+ pht->params.multiple.components = phtc;
+ pht->params.multiple.num_comp = count;
+ code = gs_sethalftone_prepare(igs, pht, pdht);
+ }
+ if ( code >= 0 )
+ for ( j = 0, pc = phtc; j < count; j++, pc++ )
+ { if ( pc->type == ht_type_spot )
+ { ref *pvalue;
+ dict_find_string(op, color_names[pc->cname], &pvalue);
+ code = dict_spot_results(pvalue, &pc->params.spot);
+ if ( code < 0 )
+ break;
+ }
+ }
+ if ( code >= 0 )
+ { /* Schedule the sampling of any Type 1 screens, */
+ /* and any (Type 1 or Type 3) TransferFunctions. */
+ /* Save the stack depths in case we have to back out. */
+ uint edepth = ref_stack_count(&e_stack);
+ uint odepth = ref_stack_count(&o_stack);
+ ref odict, odict5;
+
+ odict = op[-1];
+ odict5 = *op;
+ pop(2); op = osp;
+ esp += 5;
+ make_mark_estack(esp - 4, es_other, sethalftone_cleanup);
+ esp[-3] = odict;
+ make_istruct(esp - 2, 0, pht);
+ make_istruct(esp - 1, 0, pdht);
+ make_op_estack(esp, sethalftone_finish);
+ for ( j = 0; j < count; j++ )
+ { gx_ht_order *porder =
+ (pdht->components == 0 ? &pdht->order :
+ &pdht->components[j].corder);
+ switch ( phtc[j].type )
+ {
+ case ht_type_spot:
+ code = zscreen_enum_init(op, porder,
+ &phtc[j].params.spot.screen,
+ &sprocs[j], 0, 0);
+ if ( code < 0 )
+ break;
+ /* falls through */
+ case ht_type_threshold:
+ if ( !r_has_type(tprocs + j, t__invalid) )
+ { /* Schedule TransferFunction sampling. */
+ /****** check_xstack IS WRONG ******/
+ check_ostack(zcolor_remap_one_ostack);
+ check_estack(zcolor_remap_one_estack);
+ code = zcolor_remap_one(tprocs + j,
+ op, porder->transfer, igs,
+ zcolor_remap_one_finish);
+ op = osp;
+ }
+ break;
+ default: /* not possible here, but to keep */
+ /* the compilers happy.... */
+ ;
+ }
+ if ( code < 0 )
+ { /* Restore the stack. */
+ ref_stack_pop_to(&o_stack, odepth);
+ ref_stack_pop_to(&e_stack, edepth);
+ op = osp;
+ op[-1] = odict;
+ *op = odict5;
+ break;
+ }
+ npop = 0;
+ }
+ }
+ if ( code < 0 )
+ { ifree_object(pdht, ".sethalftone5");
+ ifree_object(phtc, ".sethalftone5");
+ ifree_object(pht, ".sethalftone5");
+ return code;
+ }
+ pop(npop);
+ return o_push_estack;
+}
+/* Install the halftone after sampling. */
+private int
+sethalftone_finish(os_ptr op)
+{ gx_device_halftone *pdht = r_ptr(esp, gx_device_halftone);
+ int code;
+ if ( pdht->components )
+ pdht->order = pdht->components[0].corder;
+ code = gx_ht_install(igs, r_ptr(esp - 1, gs_halftone), pdht);
+ if ( code < 0 )
+ return code;
+ istate->halftone = esp[-2];
+ esp -= 4;
+ sethalftone_cleanup(op);
+ return o_pop_estack;
+}
+/* Clean up after installing the halftone. */
+private int
+sethalftone_cleanup(os_ptr op)
+{ ifree_object(esp[4].value.pstruct,
+ "sethalftone_cleanup(device halftone)");
+ ifree_object(esp[3].value.pstruct,
+ "sethalftone_cleanup(halftone)");
+ return 0;
+}
+
+/* ------ Initialization procedure ------ */
+
+BEGIN_OP_DEFS(zht2_l2_op_defs) {
+ op_def_begin_level2(),
+ {"2.sethalftone5", zsethalftone5},
+ /* Internal operators */
+ {"0%sethalftone_finish", sethalftone_finish},
+END_OP_DEFS(0) }
+
+/* ------ Internal routines ------ */
+
+/* Extract frequency, angle, spot function, and accurate screens flag */
+/* from a dictionary. */
+private int
+dict_spot_params(const ref *pdict, gs_spot_halftone *psp,
+ ref *psproc, ref *ptproc)
+{ int code;
+ check_dict_read(*pdict);
+ if ( (code = dict_float_param(pdict, "Frequency", 0.0,
+ &psp->screen.frequency)) != 0 ||
+ (code = dict_float_param(pdict, "Angle", 0.0,
+ &psp->screen.angle)) != 0 ||
+ (code = dict_proc_param(pdict, "SpotFunction", psproc, false)) != 0 ||
+ (code = dict_bool_param(pdict, "AccurateScreens",
+ gs_currentaccuratescreens(),
+ &psp->accurate_screens)) < 0 ||
+ (code = dict_proc_param(pdict, "TransferFunction", ptproc, false)) < 0
+ )
+ return (code < 0 ? code : e_undefined);
+ psp->transfer = (code > 0 ? (gs_mapping_proc)0 : gs_mapped_transfer);
+ return 0;
+}
+
+/* Set actual frequency and angle in a dictionary. */
+private int
+dict_real_result(ref *pdict, const char _ds *kstr, floatp val)
+{ int code = 0;
+ ref *ignore;
+ if ( dict_find_string(pdict, kstr, &ignore) > 0 )
+ { ref rval;
+ check_dict_write(*pdict);
+ make_real(&rval, val);
+ code = dict_put_string(pdict, kstr, &rval);
+ }
+ return code;
+}
+private int
+dict_spot_results(ref *pdict, const gs_spot_halftone *psp)
+{ int code;
+ code = dict_real_result(pdict, "ActualFrequency",
+ psp->screen.actual_frequency);
+ if ( code < 0 )
+ return code;
+ return dict_real_result(pdict, "ActualAngle",
+ psp->screen.actual_angle);
+}
+
+/* Extract width, height, and thresholds from a dictionary. */
+private int
+dict_threshold_params(const ref *pdict, gs_threshold_halftone *ptp,
+ ref *ptproc)
+{ int code;
+ ref *tstring;
+ check_dict_read(*pdict);
+ if ( (code = dict_int_param(pdict, "Width", 1, 0x7fff, -1,
+ &ptp->width)) < 0 ||
+ (code = dict_int_param(pdict, "Height", 1, 0x7fff, -1,
+ &ptp->height)) < 0 ||
+ (code = dict_find_string(pdict, "Thresholds", &tstring)) <= 0 ||
+ (code = dict_proc_param(pdict, "TransferFunction", ptproc, false)) < 0
+ )
+ return (code < 0 ? code : e_undefined);
+ check_read_type_only(*tstring, t_string);
+ if ( r_size(tstring) != (long)ptp->width * ptp->height )
+ return_error(e_rangecheck);
+ ptp->thresholds.data = tstring->value.const_bytes;
+ ptp->thresholds.size = r_size(tstring);
+ ptp->transfer = (code > 0 ? (gs_mapping_proc)0 : gs_mapped_transfer);
+ return 0;
+}
diff --git a/pstoraster/zimage2.c b/pstoraster/zimage2.c
new file mode 100644
index 000000000..d166532bf
--- /dev/null
+++ b/pstoraster/zimage2.c
@@ -0,0 +1,166 @@
+/* Copyright (C) 1992, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* zimage2.c */
+/* image operator extensions for Level 2 PostScript */
+#include "math_.h"
+#include "memory_.h"
+#include "ghost.h"
+#include "errors.h"
+#include "oper.h"
+#include "gscolor.h"
+#include "gscspace.h"
+#include "gscolor2.h"
+#include "gsmatrix.h"
+#include "gsimage.h"
+#include "idict.h"
+#include "idparam.h"
+#include "iimage.h"
+#include "ilevel.h"
+#include "igstate.h" /* for igs */
+
+/* Define a structure for acquiring image parameters. */
+typedef struct image_params_s {
+ gs_image_t image;
+ bool MultipleDataSources;
+ ref DataSource[4];
+ const float *pDecode;
+} image_params;
+
+/* Common code for unpacking an image dictionary. */
+/* Assume *op is a dictionary. */
+private int
+image_dict_unpack(os_ptr op, image_params *pip, int max_bits_per_component)
+{ int code;
+ int num_components;
+ int decode_size;
+ ref *pds;
+
+ check_dict_read(*op);
+ num_components =
+ gs_color_space_num_components(gs_currentcolorspace(igs));
+ if ( num_components < 1 )
+ return_error(e_rangecheck); /* Pattern space not allowed */
+ if ( max_bits_per_component == 1 ) /* imagemask */
+ num_components = 1; /* for Decode */
+#define pim (&pip->image)
+ if ( (code = dict_int_param(op, "ImageType", 1, 1, 1,
+ &code)) < 0 ||
+ (code = dict_int_param(op, "Width", 0, 0x7fff, -1,
+ &pim->Width)) < 0 ||
+ (code = dict_int_param(op, "Height", 0, 0x7fff, -1,
+ &pim->Height)) < 0 ||
+ (code = dict_matrix_param(op, "ImageMatrix",
+ &pim->ImageMatrix)) < 0 ||
+ (code = dict_bool_param(op, "MultipleDataSources", false,
+ &pip->MultipleDataSources)) < 0 ||
+ (code = dict_int_param(op, "BitsPerComponent", 0,
+ max_bits_per_component, -1,
+ &pim->BitsPerComponent)) < 0 ||
+ (code = decode_size = dict_float_array_param(op, "Decode",
+ num_components * 2,
+ &pim->Decode[0], NULL)) < 0 ||
+ (code = dict_bool_param(op, "Interpolate", false,
+ &pim->Interpolate)) < 0 ||
+ (code = dict_bool_param(op, "CombineWithColor", false,
+ &pim->CombineWithColor)) < 0
+ )
+ return code;
+ if ( decode_size == 0 )
+ pip->pDecode = 0;
+ else if ( decode_size != num_components * 2 )
+ return_error(e_rangecheck);
+ else
+ pip->pDecode = &pim->Decode[0];
+ /* Extract and check the data sources. */
+ if ( (code = dict_find_string(op, "DataSource", &pds)) < 0 )
+ return code;
+ if ( pip->MultipleDataSources )
+ { check_type_only(*pds, t_array);
+ if ( r_size(pds) != num_components )
+ return_error(e_rangecheck);
+ memcpy(&pip->DataSource[0], pds->value.refs, sizeof(ref) * num_components);
+ }
+ else
+ pip->DataSource[0] = *pds;
+#undef pim
+ return 0;
+}
+
+/* (<width> <height> <bits/sample> <matrix> <datasrc> image -) */
+/* <dict> image - */
+private int
+z2image(register os_ptr op)
+{ if ( level2_enabled )
+ { check_op(1);
+ if ( r_has_type(op, t_dictionary) )
+ { image_params ip;
+ int code;
+
+ gs_image_t_init_color(&ip.image);
+ code = image_dict_unpack(op, &ip, 12);
+ if ( code < 0 )
+ return code;
+ ip.image.ColorSpace = gs_currentcolorspace(igs);
+ return zimage_setup(&ip.image, ip.MultipleDataSources,
+ &ip.DataSource[0], 1);
+ }
+ }
+ /* Level 1 image operator */
+ check_op(5);
+ return zimage(op);
+}
+
+/* (<width> <height> <paint_1s> <matrix> <datasrc> imagemask -) */
+/* <dict> imagemask - */
+private int
+z2imagemask(register os_ptr op)
+{ if ( level2_enabled )
+ { check_op(1);
+ if ( r_has_type(op, t_dictionary) )
+ { image_params ip;
+ int code;
+
+ gs_image_t_init_mask(&ip.image, false);
+ code = image_dict_unpack(op, &ip, 1);
+ if ( code < 0 )
+ return code;
+ if ( ip.MultipleDataSources )
+ return_error(e_rangecheck);
+ return zimage_setup(&ip.image, false,
+ &ip.DataSource[0], 1);
+ }
+ }
+ /* Level 1 imagemask operator */
+ check_op(5);
+ return zimagemask(op);
+}
+
+/* ------ Initialization procedure ------ */
+
+/* Note that these override the definitions in zpaint.c. */
+BEGIN_OP_DEFS(zimage2_l2_op_defs) {
+ op_def_begin_level2(),
+ {"1image", z2image},
+ {"1imagemask", z2imagemask},
+END_OP_DEFS(0) }
diff --git a/pstoraster/ziodev.c b/pstoraster/ziodev.c
new file mode 100644
index 000000000..c5b3718cd
--- /dev/null
+++ b/pstoraster/ziodev.c
@@ -0,0 +1,401 @@
+/* Copyright (C) 1993, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* ziodev.c */
+/* Standard IODevice implementation */
+#include "memory_.h"
+#include "stdio_.h"
+#include "string_.h"
+#include "ghost.h"
+#include "gp.h"
+#include "gpcheck.h"
+#include "gsstruct.h" /* for registering root */
+#include "errors.h"
+#include "oper.h"
+#include "stream.h"
+#include "ialloc.h"
+#include "ivmspace.h"
+#include "gxiodev.h" /* must come after stream.h */
+ /* and before files.h */
+#include "files.h"
+#include "store.h"
+
+/* Complete the definition of the %os% device. */
+/* The open_file routine is exported for pipes and for %null. */
+int
+iodev_os_open_file(gx_io_device *iodev, const char *fname, uint len,
+ const char *file_access, stream **ps, gs_memory_t *mem)
+{ return file_open_stream(fname, len, file_access,
+ file_default_buffer_size, ps,
+ iodev->procs.fopen);
+}
+
+/* Define the special devices. */
+#define iodev_special(dname, init, open)\
+ { dname, "Special",\
+ { init, open, iodev_no_open_file, iodev_no_fopen, iodev_no_fclose,\
+ iodev_no_delete_file, iodev_no_rename_file, iodev_no_file_status,\
+ iodev_no_enumerate_files, NULL, NULL,\
+ iodev_no_get_params, iodev_no_put_params\
+ }\
+ }
+
+#define stdin_buf_size 128
+private ref ref_stdin;
+bool gs_stdin_is_interactive; /* exported for command line only */
+private iodev_proc_init(stdin_init);
+private iodev_proc_open_device(stdin_open);
+gx_io_device gs_iodev_stdin =
+ iodev_special("%stdin%", stdin_init, stdin_open);
+
+#define stdout_buf_size 128
+private ref ref_stdout;
+private iodev_proc_init(stdout_init);
+private iodev_proc_open_device(stdout_open);
+gx_io_device gs_iodev_stdout =
+ iodev_special("%stdout%", stdout_init, stdout_open);
+
+#define stderr_buf_size 128
+private ref ref_stderr;
+private iodev_proc_init(stderr_init);
+private iodev_proc_open_device(stderr_open);
+gx_io_device gs_iodev_stderr =
+ iodev_special("%stderr%", stderr_init, stderr_open);
+
+#define lineedit_buf_size 20 /* initial size, not fixed size */
+private iodev_proc_open_device(lineedit_open);
+gx_io_device gs_iodev_lineedit =
+ iodev_special("%lineedit%", iodev_no_init, lineedit_open);
+
+private iodev_proc_open_device(statementedit_open);
+gx_io_device gs_iodev_statementedit =
+ iodev_special("%statementedit%", iodev_no_init, statementedit_open);
+
+/* ------ Operators ------ */
+
+/* <int> .getiodevice <string> */
+private int
+zgetiodevice(register os_ptr op)
+{ gx_io_device *iodev;
+ const byte *dname;
+ check_type(*op, t_integer);
+ if ( op->value.intval != (int)op->value.intval )
+ return_error(e_rangecheck);
+ iodev = gs_getiodevice((int)(op->value.intval));
+ if ( iodev == 0 ) /* index out of range */
+ return_error(e_rangecheck);
+ dname = (const byte *)iodev->dname;
+ make_const_string(op, a_readonly | avm_foreign,
+ strlen((const char *)dname), dname);
+ return 0;
+}
+
+/* ------- %stdin, %stdout, and %stderr ------ */
+
+/*
+ * According to Adobe, it is legal to close the %std... files and then
+ * re-open them later. However, the re-opened file object is not 'eq' to
+ * the original file object (in our implementation, it has a different
+ * read_id or write_id).
+ */
+
+private gs_gc_root_t stdin_root, stdout_root, stderr_root;
+
+private int
+ s_stdin_read_process(P4(stream_state *, stream_cursor_read *,
+ stream_cursor_write *, bool));
+
+private int
+stdin_init(gx_io_device *iodev, gs_memory_t *mem)
+{ static ref *pstdin = &ref_stdin;
+ make_file(&ref_stdin, a_readonly | avm_system, 1, invalid_file_entry);
+ gs_stdin_is_interactive = true;
+ gs_register_ref_root(mem, &stdin_root,
+ (void **)&pstdin, "ref_stdin");
+ return 0;
+}
+
+/* Read from stdin into the buffer. */
+/* If interactive, only read one character. */
+private int
+s_stdin_read_process(stream_state *st, stream_cursor_read *ignore_pr,
+ stream_cursor_write *pw, bool last)
+{ FILE *file = ((stream *)st)->file; /* hack for file streams */
+ int wcount = (int)(pw->limit - pw->ptr);
+ int count;
+
+ if ( wcount > 0 )
+ { if ( gs_stdin_is_interactive )
+ wcount = 1;
+ count = fread(pw->ptr + 1, 1, wcount, file);
+ if ( count < 0 )
+ count = 0;
+ pw->ptr += count;
+ }
+ else
+ count = 0; /* return 1 if no error/EOF */
+ process_interrupts();
+ return (ferror(file) ? ERRC : feof(file) ? EOFC : count == wcount ? 1 : 0);
+}
+
+int
+iodev_stdin_open(gx_io_device *iodev, const char *access, stream **ps,
+ gs_memory_t *mem)
+{ stream *s;
+ if ( !streq1(access, 'r') )
+ return_error(e_invalidfileaccess);
+ if ( !file_is_valid(s, &ref_stdin) )
+ {
+ /****** stdin SHOULD NOT LINE-BUFFER ******/
+
+ gs_memory_t *mem = imemory_system;
+ byte *buf;
+ s = file_alloc_stream(mem, "stdin_open(stream)");
+ /* We want stdin to read only one character at a time, */
+ /* but it must have a substantial buffer, in case it is used */
+ /* by a stream that requires more than one input byte */
+ /* to make progress. */
+ buf = gs_alloc_bytes(mem, stdin_buf_size,
+ "stdin_open(buffer)");
+ if ( s == 0 || buf == 0 )
+ return_error(e_VMerror);
+ sread_file(s, gs_stdin, buf, stdin_buf_size);
+ s->procs.process = s_stdin_read_process;
+ s->save_close = s_std_null;
+ s->procs.close = file_close_file;
+ make_file(&ref_stdin, a_readonly | avm_system,
+ s->read_id, s);
+ *ps = s;
+ return 1;
+ }
+ *ps = s;
+ return 0;
+}
+private int
+stdin_open(gx_io_device *iodev, const char *access, stream **ps,
+ gs_memory_t *mem)
+{ int code = iodev_stdin_open(iodev, access, ps, mem);
+ return min(code, 0);
+}
+/* This is the public routine for getting the stdin stream. */
+int
+zget_stdin(stream **ps)
+{ stream *s;
+ if ( file_is_valid(s, &ref_stdin) )
+ { *ps = s;
+ return 0;
+ }
+ return (*gs_iodev_stdin.procs.open_device)(&gs_iodev_stdin,
+ "r", ps, imemory_system);
+}
+
+private int
+stdout_init(gx_io_device *iodev, gs_memory_t *mem)
+{ static ref *pstdout = &ref_stdout;
+ make_file(&ref_stdout, a_all | avm_system, 1, invalid_file_entry);
+ gs_register_ref_root(mem, &stdout_root,
+ (void **)&pstdout, "ref_stdout");
+ return 0;
+}
+
+int
+iodev_stdout_open(gx_io_device *iodev, const char *access, stream **ps,
+ gs_memory_t *mem)
+{ stream *s;
+ if ( !streq1(access, 'w') )
+ return_error(e_invalidfileaccess);
+ if ( !file_is_valid(s, &ref_stdout) )
+ { gs_memory_t *mem = imemory_system;
+ byte *buf;
+ s = file_alloc_stream(mem, "stdout_open(stream)");
+ buf = gs_alloc_bytes(mem, stdout_buf_size,
+ "stdout_open(buffer)");
+ if ( s == 0 || buf == 0 )
+ return_error(e_VMerror);
+ swrite_file(s, gs_stdout, buf, stdout_buf_size);
+ s->save_close = s_std_null;
+ s->procs.close = file_close_file;
+ make_file(&ref_stdout, a_write | avm_system, s->write_id, s);
+ *ps = s;
+ return 1;
+ }
+ *ps = s;
+ return 0;
+}
+private int
+stdout_open(gx_io_device *iodev, const char *access, stream **ps,
+ gs_memory_t *mem)
+{ int code = iodev_stdout_open(iodev, access, ps, mem);
+ return min(code, 0);
+}
+/* This is the public routine for getting the stdout stream. */
+int
+zget_stdout(stream **ps)
+{ stream *s;
+ if ( file_is_valid(s, &ref_stdout) )
+ { *ps = s;
+ return 0;
+ }
+ return (*gs_iodev_stdout.procs.open_device)(&gs_iodev_stdout,
+ "w", ps, imemory_system);
+}
+
+private int
+stderr_init(gx_io_device *iodev, gs_memory_t *mem)
+{ static ref *pstderr = &ref_stderr;
+ make_file(&ref_stderr, a_all | avm_system, 1, invalid_file_entry);
+ gs_register_ref_root(mem, &stderr_root,
+ (void **)&pstderr, "ref_stderr");
+ return 0;
+}
+
+int
+iodev_stderr_open(gx_io_device *iodev, const char *access, stream **ps,
+ gs_memory_t *mem)
+{ stream *s;
+ if ( !streq1(access, 'w') )
+ return_error(e_invalidfileaccess);
+ if ( !file_is_valid(s, &ref_stderr) )
+ { gs_memory_t *mem = imemory_system;
+ byte *buf;
+ s = file_alloc_stream(mem, "stderr_open(stream)");
+ buf = gs_alloc_bytes(mem, stderr_buf_size,
+ "stderr_open(buffer)");
+ if ( s == 0 || buf == 0 )
+ return_error(e_VMerror);
+ swrite_file(s, gs_stderr, buf, stderr_buf_size);
+ s->save_close = s_std_null;
+ s->procs.close = file_close_file;
+ make_file(&ref_stderr, a_write | avm_system, s->write_id, s);
+ *ps = s;
+ return 1;
+ }
+ *ps = s;
+ return 0;
+}
+private int
+stderr_open(gx_io_device *iodev, const char *access, stream **ps,
+ gs_memory_t *mem)
+{ int code = iodev_stderr_open(iodev, access, ps, mem);
+ return min(code, 0);
+}
+/* This is the public routine for getting the stderr stream. */
+int
+zget_stderr(stream **ps)
+{ stream *s;
+ if ( file_is_valid(s, &ref_stderr) )
+ { *ps = s;
+ return 0;
+ }
+ return (*gs_iodev_stderr.procs.open_device)(&gs_iodev_stderr,
+ "w", ps, imemory_system);
+}
+
+/* ------ %lineedit and %statementedit ------ */
+
+private int
+lineedit_open(gx_io_device *iodev, const char *access, stream **ps,
+ gs_memory_t *mem)
+{ uint count = 0;
+ bool in_eol = false;
+ int code;
+ stream *s;
+ stream *ins;
+ byte *buf;
+ uint buf_size = lineedit_buf_size;
+
+ if ( strcmp(access, "r") )
+ return_error(e_invalidfileaccess);
+ s = file_alloc_stream(mem, "lineedit_open(stream)");
+ if ( s == 0 )
+ return_error(e_VMerror);
+ code = (gs_iodev_stdin.procs.open_device)(&gs_iodev_stdin, access,
+ &ins, mem);
+ if ( code < 0 )
+ return code;
+ buf = gs_alloc_string(mem, buf_size, "lineedit_open(buffer)");
+ if ( buf == 0 )
+ return_error(e_VMerror);
+rd: code = zreadline_from(ins, buf, buf_size, &count, &in_eol);
+ switch ( code )
+ {
+ case EOFC:
+ code = gs_note_error(e_undefinedfilename);
+ /* falls through */
+ case 0:
+ break;
+ default:
+ code = gs_note_error(e_ioerror);
+ break;
+ case 1: /* filled buffer */
+ { uint nsize;
+ byte *nbuf;
+
+#if arch_ints_are_short
+ if ( nsize == max_uint )
+ { code = gs_note_error(e_limitcheck);
+ break;
+ }
+ else if ( nsize >= max_uint / 2 )
+ nsize = max_uint;
+ else
+#endif
+ nsize = buf_size * 2;
+ nbuf = gs_resize_string(mem, buf, buf_size, nsize,
+ "lineedit_open(grow buffer)");
+ if ( nbuf == 0 )
+ { code = gs_note_error(e_VMerror);
+ break;
+ }
+ buf = nbuf;
+ buf_size = nsize;
+ goto rd;
+ }
+ }
+ if ( code != 0 )
+ { gs_free_string(mem, buf, buf_size, "lineedit_open(buffer)");
+ return code;
+ }
+ buf = gs_resize_string(mem, buf, buf_size, count,
+ "lineedit_open(resize buffer)");
+ if ( buf == 0 )
+ return_error(e_VMerror);
+ sread_string(s, buf, count);
+ s->save_close = s->procs.close;
+ s->procs.close = file_close_disable;
+ *ps = s;
+ return 0;
+}
+
+private int
+statementedit_open(gx_io_device *iodev, const char *access, stream **ps,
+ gs_memory_t *mem)
+{ /* NOT IMPLEMENTED PROPERLY YET */
+ return lineedit_open(iodev, access, ps, mem);
+}
+
+/* ------ Initialization procedure ------ */
+
+BEGIN_OP_DEFS(ziodev_op_defs) {
+ {"1.getiodevice", zgetiodevice},
+END_OP_DEFS(0) }
diff --git a/pstoraster/ziodev2.c b/pstoraster/ziodev2.c
new file mode 100644
index 000000000..21c847869
--- /dev/null
+++ b/pstoraster/ziodev2.c
@@ -0,0 +1,133 @@
+/* Copyright (C) 1993, 1994 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* ziodev2.c */
+/* (Level 2) IODevice operators */
+#include "string_.h"
+#include "ghost.h"
+#include "gp.h"
+#include "errors.h"
+#include "oper.h"
+#include "stream.h"
+#include "gxiodev.h"
+#include "files.h" /* for file_open_stream */
+#include "iparam.h"
+#include "iutil2.h"
+#include "store.h"
+
+/* ------ %null% ------ */
+
+/* This represents the null output file. */
+private iodev_proc_open_device(null_open);
+gx_io_device gs_iodev_null =
+ { "%null%", "FileSystem",
+ { iodev_no_init, null_open, iodev_no_open_file,
+ iodev_os_fopen, iodev_os_fclose,
+ iodev_no_delete_file, iodev_no_rename_file, iodev_no_file_status,
+ iodev_no_enumerate_files, NULL, NULL,
+ iodev_no_get_params, iodev_no_put_params
+ }
+ };
+
+private int
+null_open(gx_io_device *iodev, const char *access, stream **ps,
+ gs_memory_t *mem)
+{ if ( !streq1(access, 'w') )
+ return_error(e_invalidfileaccess);
+ return file_open_stream(gp_null_file_name,
+ strlen(gp_null_file_name),
+ access, 256 /* arbitrary */, ps,
+ iodev->procs.fopen);
+}
+
+/* ------ %ram% ------ */
+
+/* This is an IODevice with no interesting parameters for the moment. */
+gx_io_device gs_iodev_ram =
+ { "%ram%", "Special",
+ { iodev_no_init, iodev_no_open_device, iodev_no_open_file,
+ iodev_no_fopen, iodev_no_fclose,
+ iodev_no_delete_file, iodev_no_rename_file, iodev_no_file_status,
+ iodev_no_enumerate_files, NULL, NULL,
+ iodev_no_get_params, iodev_no_put_params
+ }
+ };
+
+/* ------ Operators ------ */
+
+/* <iodevice> .getdevparams <mark> <name> <value> ... */
+private int
+zgetdevparams(os_ptr op)
+{ gx_io_device *iodev;
+ stack_param_list list;
+ int code;
+ ref *pmark;
+
+ check_read_type(*op, t_string);
+ iodev = gs_findiodevice(op->value.bytes, r_size(op));
+ if ( iodev == 0 )
+ return_error(e_undefinedfilename);
+ stack_param_list_write(&list, &o_stack, NULL);
+#define plist ((gs_param_list *)&list)
+ if ( (code = gs_getdevparams(iodev, plist)) < 0 )
+ { ref_stack_pop(&o_stack, list.count * 2);
+ return code;
+ }
+#undef plist
+ pmark = ref_stack_index(&o_stack, list.count * 2);
+ make_mark(pmark);
+ return 0;
+}
+
+/* <mark> <name> <value> ... <iodevice> .putdevparams */
+private int
+zputdevparams(os_ptr op)
+{ gx_io_device *iodev;
+ stack_param_list list;
+ int code;
+ check_read_type(*op, t_string);
+ iodev = gs_findiodevice(op->value.bytes, r_size(op));
+ if ( iodev == 0 )
+ return_error(e_undefinedfilename);
+ code = stack_param_list_read(&list, &o_stack, 1, NULL, false);
+ if ( code < 0 )
+ return code;
+#define plist ((gs_param_list *)&list)
+ code = param_check_password(plist, &SystemParamsPassword);
+ if ( code != 0 )
+ return_error(code < 0 ? code : e_invalidaccess);
+ code = gs_putdevparams(iodev, plist);
+ if ( code < 0 )
+ return code;
+#undef plist
+ ref_stack_pop(&o_stack, list.count * 2 + 2);
+ return 0;
+}
+
+/* ------ Initialization procedure ------ */
+
+BEGIN_OP_DEFS(ziodev2_l2_op_defs) {
+ op_def_begin_level2(),
+ {"1.getdevparams", zgetdevparams},
+ {"2.putdevparams", zputdevparams},
+END_OP_DEFS(0) }
diff --git a/pstoraster/zmath.c b/pstoraster/zmath.c
new file mode 100644
index 000000000..5da1898b5
--- /dev/null
+++ b/pstoraster/zmath.c
@@ -0,0 +1,247 @@
+/* Copyright (C) 1989, 1992, 1993 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* zmath.c */
+/* Mathematical operators */
+#include "math_.h"
+#include "ghost.h"
+#include "gxfarith.h"
+#include "errors.h"
+#include "oper.h"
+#include "store.h"
+
+/* Current state of random number generator. */
+/* We have to implement this ourselves because */
+/* the Unix rand doesn't provide anything equivalent to rrand. */
+/* Note that the value always lies in the range [0..0x7ffffffe], */
+/* even if longs are longer than 32 bits. */
+private long rand_state;
+
+/* Initialize the random number generator. */
+private void
+zmath_init(void)
+{ rand_state = 1;
+}
+
+/****** NOTE: none of these operators currently ******/
+/****** check for floating over- or underflow. ******/
+
+/* <num> sqrt <real> */
+private int
+zsqrt(register os_ptr op)
+{ float num;
+ int code = num_params(op, 1, &num);
+ if ( code < 0 )
+ return code;
+ if ( num < 0.0 )
+ return_error(e_rangecheck);
+ make_real(op, sqrt(num));
+ return 0;
+}
+
+/* <num> arccos <real> */
+private int
+zarccos(register os_ptr op)
+{ float num, result;
+ int code = num_params(op, 1, &num);
+ if ( code < 0 )
+ return code;
+ result = acos(num) * radians_to_degrees;
+ make_real(op, result);
+ return 0;
+}
+
+/* <num> arcsin <real> */
+private int
+zarcsin(register os_ptr op)
+{ float num, result;
+ int code = num_params(op, 1, &num);
+ if ( code < 0 )
+ return code;
+ result = asin(num) * radians_to_degrees;
+ make_real(op, result);
+ return 0;
+}
+
+/* <num> <denom> atan <real> */
+private int
+zatan(register os_ptr op)
+{ float args[2];
+ float result;
+ int code = num_params(op, 2, args);
+ if ( code < 0 ) return code;
+ if ( args[0] == 0 ) /* on X-axis, special case */
+ { if ( args[1] == 0 )
+ return_error(e_undefinedresult);
+ result = (args[1] < 0 ? 180 : 0);
+ }
+ else
+ { result = atan2(args[0], args[1]) * radians_to_degrees;
+ if ( result < 0 )
+ result += 360;
+ }
+ make_real(op - 1, result);
+ pop(1);
+ return 0;
+}
+
+/* <num> cos <real> */
+private int
+zcos(register os_ptr op)
+{ float angle;
+ int code = num_params(op, 1, &angle);
+ if ( code < 0 )
+ return code;
+ make_real(op, gs_cos_degrees(angle));
+ return 0;
+}
+
+/* <num> sin <real> */
+private int
+zsin(register os_ptr op)
+{ float angle;
+ int code = num_params(op, 1, &angle);
+ if ( code < 0 )
+ return code;
+ make_real(op, gs_sin_degrees(angle));
+ return 0;
+}
+
+/* <base> <exponent> exp <real> */
+private int
+zexp(register os_ptr op)
+{ float args[2];
+ float result;
+ double ipart;
+ int code = num_params(op, 2, args);
+ if ( code < 0 ) return code;
+ if ( args[0] == 0.0 && args[1] == 0.0 )
+ return_error(e_undefinedresult);
+ if ( args[0] < 0.0 && modf(args[1], &ipart) != 0.0 )
+ return_error(e_undefinedresult);
+ result = pow(args[0], args[1]);
+ make_real(op - 1, result);
+ pop(1);
+ return 0;
+}
+
+/* <posnum> ln <real> */
+private int
+zln(register os_ptr op)
+{ float num;
+ int code = num_params(op, 1, &num);
+ if ( code < 0 )
+ return code;
+ if ( num <= 0.0 )
+ return_error(e_rangecheck);
+ make_real(op, log(num));
+ return 0;
+}
+
+/* <posnum> log <real> */
+private int
+zlog(register os_ptr op)
+{ float num;
+ int code = num_params(op, 1, &num);
+ if ( code < 0 )
+ return code;
+ if ( num <= 0.0 )
+ return_error(e_rangecheck);
+ make_real(op, log10(num));
+ return 0;
+}
+
+/* - rand <int> */
+private int
+zrand(register os_ptr op)
+{ /*
+ * We use an algorithm from CACM 31 no. 10, pp. 1192-1201,
+ * October 1988. According to a posting by Ed Taft on
+ * comp.lang.postscript, Level 2 (Adobe) PostScript interpreters
+ * use this algorithm too:
+ * x[n+1] = (16807 * x[n]) mod (2^31 - 1)
+ */
+#define A 16807
+#define M 0x7fffffff
+#define Q 127773 /* M / A */
+#define R 2836 /* M % A */
+ rand_state = A * (rand_state % Q) - R * (rand_state / Q);
+ /* Note that rand_state cannot be 0 here. */
+ if ( rand_state <= 0 )
+ rand_state += M;
+#undef A
+#undef M
+#undef Q
+#undef R
+ push(1);
+ make_int(op, rand_state);
+ return 0;
+}
+
+/* <int> srand - */
+private int
+zsrand(register os_ptr op)
+{ long state;
+ check_type(*op, t_integer);
+ state = op->value.intval;
+#if arch_sizeof_long > 4
+ /* Trim the state back to 32 bits. */
+ state = (int)state;
+#endif
+ /*
+ * The following somewhat bizarre adjustments are according to
+ * public information from Adobe describing their implementation.
+ */
+ if ( state < 1 )
+ state = -(state % 0x7ffffffe) + 1;
+ else if ( state > 0x7ffffffe )
+ state = 0x7ffffffe;
+ rand_state = state;
+ pop(1);
+ return 0;
+}
+
+/* - rrand <int> */
+private int
+zrrand(register os_ptr op)
+{ push(1);
+ make_int(op, rand_state);
+ return 0;
+}
+
+/* ------ Initialization procedure ------ */
+
+BEGIN_OP_DEFS(zmath_op_defs) {
+ {"1arccos", zarccos}, /* extension */
+ {"1arcsin", zarcsin}, /* extension */
+ {"2atan", zatan},
+ {"1cos", zcos},
+ {"2exp", zexp},
+ {"1ln", zln},
+ {"1log", zlog},
+ {"0rand", zrand},
+ {"0rrand", zrrand},
+ {"1sin", zsin},
+ {"1sqrt", zsqrt},
+ {"1srand", zsrand},
+END_OP_DEFS(zmath_init) }
diff --git a/pstoraster/zmatrix.c b/pstoraster/zmatrix.c
new file mode 100644
index 000000000..d4cea8735
--- /dev/null
+++ b/pstoraster/zmatrix.c
@@ -0,0 +1,325 @@
+/* Copyright (C) 1989, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* zmatrix.c */
+/* Matrix operators */
+#include "ghost.h"
+#include "errors.h"
+#include "oper.h"
+#include "igstate.h"
+#include "gsmatrix.h"
+#include "gscoord.h"
+#include "store.h"
+
+/* Forward references */
+private int near common_transform(P3(os_ptr,
+ int (*)(P4(gs_state *, floatp, floatp, gs_point *)),
+ int (*)(P4(floatp, floatp, const gs_matrix *, gs_point *))));
+
+/* - initmatrix - */
+private int
+zinitmatrix(os_ptr op)
+{ return gs_initmatrix(igs);
+}
+
+/* <matrix> defaultmatrix <matrix> */
+private int
+zdefaultmatrix(register os_ptr op)
+{ gs_matrix mat;
+ gs_defaultmatrix(igs, &mat);
+ return write_matrix(op, &mat);
+}
+
+/* <matrix> currentmatrix <matrix> */
+private int
+zcurrentmatrix(register os_ptr op)
+{ gs_matrix mat;
+ gs_currentmatrix(igs, &mat);
+ return write_matrix(op, &mat);
+}
+
+/* <matrix> setmatrix - */
+private int
+zsetmatrix(register os_ptr op)
+{ gs_matrix mat;
+ int code = read_matrix(op, &mat);
+ if ( code < 0 )
+ return code;
+ if ( (code = gs_setmatrix(igs, &mat)) < 0 )
+ return code;
+ pop(1);
+ return 0;
+}
+
+/* <matrix|null> .setdefaultmatrix - */
+private int
+zsetdefaultmatrix(register os_ptr op)
+{ int code;
+ if ( r_has_type(op, t_null) )
+ code = gs_setdefaultmatrix(igs, NULL);
+ else
+ { gs_matrix mat;
+ code = read_matrix(op, &mat);
+ if ( code < 0 )
+ return code;
+ code = gs_setdefaultmatrix(igs, &mat);
+ }
+ if ( code < 0 )
+ return code;
+ pop(1);
+ return 0;
+}
+
+/* <tx> <ty> translate - */
+/* <tx> <ty> <matrix> translate <matrix> */
+private int
+ztranslate(register os_ptr op)
+{ int code;
+ float trans[2];
+ if ( (code = num_params(op, 2, trans)) >= 0 )
+ { code = gs_translate(igs, trans[0], trans[1]);
+ if ( code < 0 )
+ return code;
+ }
+ else /* matrix operand */
+ { gs_matrix mat;
+ /* The num_params failure might be a stack underflow. */
+ check_op(2);
+ if ( (code = num_params(op - 1, 2, trans)) < 0 ||
+ (code = gs_make_translation(trans[0], trans[1], &mat)) < 0 ||
+ (code = write_matrix(op, &mat)) < 0
+ )
+ { /* Might be a stack underflow. */
+ check_op(3);
+ return code;
+ }
+ op[-2] = *op;
+ }
+ pop(2);
+ return code;
+}
+
+/* <sx> <sy> scale - */
+/* <sx> <sy> <matrix> scale <matrix> */
+private int
+zscale(register os_ptr op)
+{ int code;
+ float scale[2];
+ if ( (code = num_params(op, 2, scale)) >= 0 )
+ { code = gs_scale(igs, scale[0], scale[1]);
+ if ( code < 0 )
+ return code;
+ }
+ else /* matrix operand */
+ { gs_matrix mat;
+ /* The num_params failure might be a stack underflow. */
+ check_op(2);
+ if ( (code = num_params(op - 1, 2, scale)) < 0 ||
+ (code = gs_make_scaling(scale[0], scale[1], &mat)) < 0 ||
+ (code = write_matrix(op, &mat)) < 0
+ )
+ { /* Might be a stack underflow. */
+ check_op(3);
+ return code;
+ }
+ op[-2] = *op;
+ }
+ pop(2);
+ return code;
+}
+
+/* <angle> rotate - */
+/* <angle> <matrix> rotate <matrix> */
+private int
+zrotate(register os_ptr op)
+{ int code;
+ float ang;
+ if ( (code = num_params(op, 1, &ang)) >= 0 )
+ { code = gs_rotate(igs, ang);
+ if ( code < 0 )
+ return code;
+ }
+ else /* matrix operand */
+ { gs_matrix mat;
+ /* The num_params failure might be a stack underflow. */
+ check_op(1);
+ if ( (code = num_params(op - 1, 1, &ang)) < 0 ||
+ (code = gs_make_rotation(ang, &mat)) < 0 ||
+ (code = write_matrix(op, &mat)) < 0
+ )
+ { /* Might be a stack underflow. */
+ check_op(2);
+ return code;
+ }
+ op[-1] = *op;
+ }
+ pop(1);
+ return code;
+}
+
+/* <matrix> concat - */
+private int
+zconcat(register os_ptr op)
+{ gs_matrix mat;
+ int code = read_matrix(op, &mat);
+ if ( code < 0 ) return code;
+ code = gs_concat(igs, &mat);
+ if ( code < 0 ) return code;
+ pop(1);
+ return 0;
+}
+
+/* <matrix1> <matrix2> <matrix> concatmatrix <matrix> */
+private int
+zconcatmatrix(register os_ptr op)
+{ gs_matrix m1, m2, mp;
+ int code;
+ if ( (code = read_matrix(op - 2, &m1)) < 0 ||
+ (code = read_matrix(op - 1, &m2)) < 0 ||
+ (code = gs_matrix_multiply(&m1, &m2, &mp)) < 0 ||
+ (code = write_matrix(op, &mp)) < 0
+ )
+ return code;
+ op[-2] = *op;
+ pop(2);
+ return code;
+}
+
+/* <x> <y> transform <xt> <yt> */
+/* <x> <y> <matrix> transform <xt> <yt> */
+private int
+ztransform(register os_ptr op)
+{ return common_transform(op, gs_transform, gs_point_transform);
+}
+
+/* <dx> <dy> dtransform <dxt> <dyt> */
+/* <dx> <dy> <matrix> dtransform <dxt> <dyt> */
+private int
+zdtransform(register os_ptr op)
+{ return common_transform(op, gs_dtransform, gs_distance_transform);
+}
+
+/* <xt> <yt> itransform <x> <y> */
+/* <xt> <yt> <matrix> itransform <x> <y> */
+private int
+zitransform(register os_ptr op)
+{ return common_transform(op, gs_itransform, gs_point_transform_inverse);
+}
+
+/* <dxt> <dyt> idtransform <dx> <dy> */
+/* <dxt> <dyt> <matrix> idtransform <dx> <dy> */
+private int
+zidtransform(register os_ptr op)
+{ return common_transform(op, gs_idtransform, gs_distance_transform_inverse);
+}
+
+/* Common logic for [i][d]transform */
+private int near
+common_transform(register os_ptr op,
+ int (*ptproc)(P4(gs_state *, floatp, floatp, gs_point *)),
+ int (*matproc)(P4(floatp, floatp, const gs_matrix *, gs_point *)))
+{ float opxy[2];
+ gs_point pt;
+ int code;
+ /* Optimize for the non-matrix case */
+ switch ( r_type(op) )
+ {
+ case t_real:
+ opxy[1] = op->value.realval;
+ break;
+ case t_integer:
+ opxy[1] = op->value.intval;
+ break;
+ case t_array: /* might be a matrix */
+ case t_shortarray:
+ case t_mixedarray:
+ { gs_matrix mat;
+ gs_matrix *pmat = &mat;
+ if ( (code = read_matrix(op, pmat)) < 0 ||
+ (code = num_params(op - 1, 2, opxy)) < 0 ||
+ (code = (*matproc)(opxy[0], opxy[1], pmat, &pt)) < 0
+ )
+ { /* Might be a stack underflow. */
+ check_op(3);
+ return code;
+ }
+ op--;
+ pop(1);
+ goto out;
+ }
+ default:
+ return_op_typecheck(op);
+ }
+ switch ( r_type(op - 1) )
+ {
+ case t_real:
+ opxy[0] = (op - 1)->value.realval;
+ break;
+ case t_integer:
+ opxy[0] = (op - 1)->value.intval;
+ break;
+ default:
+ return_op_typecheck(op - 1);
+ }
+ if ( (code = (*ptproc)(igs, opxy[0], opxy[1], &pt)) < 0 )
+ return code;
+out: make_real(op - 1, pt.x);
+ make_real(op, pt.y);
+ return 0;
+}
+
+/* <matrix> <inv_matrix> invertmatrix <inv_matrix> */
+private int
+zinvertmatrix(register os_ptr op)
+{ gs_matrix m;
+ int code;
+ if ( (code = read_matrix(op - 1, &m)) < 0 ||
+ (code = gs_matrix_invert(&m, &m)) < 0 ||
+ (code = write_matrix(op, &m)) < 0
+ )
+ return code;
+ op[-1] = *op;
+ pop(1);
+ return code;
+}
+
+/* ------ Initialization procedure ------ */
+
+BEGIN_OP_DEFS(zmatrix_op_defs) {
+ {"1concat", zconcat},
+ {"2dtransform", zdtransform},
+ {"3concatmatrix", zconcatmatrix},
+ {"1currentmatrix", zcurrentmatrix},
+ {"1defaultmatrix", zdefaultmatrix},
+ {"2idtransform", zidtransform},
+ {"0initmatrix", zinitmatrix},
+ {"2invertmatrix", zinvertmatrix},
+ {"2itransform", zitransform},
+ {"1rotate", zrotate},
+ {"2scale", zscale},
+ {"1setmatrix", zsetmatrix},
+ {"1.setdefaultmatrix", zsetdefaultmatrix},
+ {"2transform", ztransform},
+ {"2translate", ztranslate},
+END_OP_DEFS(0) }
+
diff --git a/pstoraster/zmedia2.c b/pstoraster/zmedia2.c
new file mode 100644
index 000000000..f05017746
--- /dev/null
+++ b/pstoraster/zmedia2.c
@@ -0,0 +1,442 @@
+/*
+ Copyright 1993-1999 by Easy Software Products.
+ Copyright (C) 1993, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* zmedia.c */
+/* Media matching for setpagedevice */
+#include "math_.h"
+#include "memory_.h"
+#include "ghost.h"
+#include "gsmatrix.h"
+#include "errors.h"
+#include "oper.h"
+#include "idict.h"
+#include "idparam.h"
+#include "iname.h"
+#include "store.h"
+
+/* <pagedict> <attrdict> <policydict> <keys> .matchmedia <key> true */
+/* <pagedict> <attrdict> <policydict> <keys> .matchmedia false */
+/* <pagedict> null <policydict> <keys> .matchmedia null true */
+private int zmatch_page_size(P7(const ref *pvreq, const ref *pvmed,
+ int policy, int orient, float *best_mismatch, gs_matrix *pmat,
+ gs_point *pmsize));
+private int
+zmatchmedia(register os_ptr op)
+{ os_ptr preq = op - 3;
+ os_ptr pattr = op - 2;
+ os_ptr ppol = op - 1;
+#define pkeys op
+ int policy_default;
+ float best_mismatch = (float)max_long; /* adhoc */
+ ref mmkey, nmkey;
+ float mbest = best_mismatch;
+ uint matched_priority;
+ ref no_priority;
+ ref *ppriority;
+ int mepos, orient;
+ int code;
+ int ai;
+ ref aelt[2];
+#define mkey aelt[0]
+#define mdict aelt[1]
+
+ if ( r_has_type(pattr, t_null) )
+ { check_op(4);
+ make_null(op - 3);
+ make_true(op - 2);
+ pop(2);
+ return 0;
+ }
+ check_type(*preq, t_dictionary);
+ check_dict_read(*preq);
+ check_type(*pattr, t_dictionary);
+ check_dict_read(*pattr);
+ check_type(*ppol, t_dictionary);
+ check_dict_read(*ppol);
+ check_array(*pkeys);
+ check_read(*pkeys);
+/**** MRS - This code 1) doesn't work, and 2) if it did work, why do we want it? ****/
+#if 0
+ switch ( code = dict_int_null_param(preq, "MediaPosition", 0, 0x7fff,
+ 0, &mepos) )
+ {
+ default: return code;
+ case 2:
+ case 1: mepos = -1;
+ case 0: ;
+ }
+ switch ( code = dict_int_null_param(preq, "Orientation", 0, 3,
+ 0, &orient) )
+ {
+ default: return code;
+ case 2:
+ case 1: orient = -1;
+ case 0: ;
+ }
+#else
+ mepos = -1;
+ orient = -1;
+#endif /* 0 */
+
+ code = dict_int_param(ppol, "PolicyNotFound", 0, 7, 0,
+ &policy_default);
+ if ( code < 0 )
+ return code;
+ if ( dict_find_string(pattr, "Priority", &ppriority) > 0 )
+ { check_array_only(*ppriority);
+ check_read(*ppriority);
+ }
+ else
+ { make_empty_array(&no_priority, a_readonly);
+ ppriority = &no_priority;
+ }
+#define reset_match()\
+ matched_priority = r_size(ppriority),\
+ make_null(&mmkey),\
+ make_null(&nmkey)
+ reset_match();
+ for ( ai = dict_first(pattr); (ai = dict_next(pattr, ai, aelt)) >= 0; )
+ { if ( r_has_type(&mdict, t_dictionary) &&
+ r_has_attr(dict_access_ref(&mdict), a_read) &&
+ r_has_type(&mkey, t_integer) &&
+ (mepos < 0 || mkey.value.intval == mepos)
+ )
+ { bool match_all;
+ uint ki, pi;
+
+ code = dict_bool_param(&mdict, "MatchAll", false,
+ &match_all);
+ if ( code < 0 )
+ return code;
+ for ( ki = 0; ki < r_size(pkeys); ki++ )
+ { ref key;
+ ref kstr;
+ ref *prvalue;
+ ref *pmvalue;
+ ref *ppvalue;
+ int policy;
+
+ array_get(pkeys, ki, &key);
+ if ( dict_find(&mdict, &key, &pmvalue) <= 0 )
+ continue;
+ if ( dict_find(preq, &key, &prvalue) <= 0 ||
+ r_has_type(prvalue, t_null)
+ )
+ { if ( match_all )
+ goto no;
+ else
+ continue;
+ }
+ /* Look for the Policies entry for this key. */
+ if ( dict_find(ppol, &key, &ppvalue) > 0 )
+ { check_type_only(*ppvalue, t_integer);
+ policy = ppvalue->value.intval;
+ }
+ else
+ policy = policy_default;
+ /*
+ * Match a requested attribute value with the attribute value in the
+ * description of a medium. For all attributes except PageSize,
+ * matching means equality. PageSize is special; see match_page_size
+ * below.
+ */
+ if ( r_has_type(&key, t_name) &&
+ (name_string_ref(&key, &kstr),
+ r_size(&kstr) == 8 &&
+ !memcmp(kstr.value.bytes, "PageSize", 8))
+ )
+ { gs_matrix ignore_mat;
+ gs_point ignore_msize;
+ if ( zmatch_page_size(prvalue, pmvalue,
+ policy, orient,
+ &best_mismatch,
+ &ignore_mat,
+ &ignore_msize)
+ <= 0 )
+ goto no;
+ }
+ else
+ if ( !obj_eq(prvalue, pmvalue) )
+ goto no;
+ }
+ /* We have a match. If it is a better match */
+ /* than the current best one, it supersedes it */
+ /* regardless of priority. */
+ if ( best_mismatch < mbest )
+ { mbest = best_mismatch;
+ reset_match();
+ }
+ /* In case of a tie, see if the new match has */
+ /* priority. */
+ for ( pi = matched_priority; pi > 0; )
+ { ref pri;
+ pi--;
+ array_get(ppriority, pi, &pri);
+ if ( obj_eq(&mkey, &pri) )
+ { /* Yes, higher priority. */
+ mmkey = mkey;
+ matched_priority = pi;
+ break;
+ }
+ }
+ /* Save the match in case no match has priority. */
+ nmkey = mkey;
+no: ;
+ }
+ }
+#undef mkey
+#undef mdict
+#undef pkeys
+ if ( r_has_type(&nmkey, t_null) )
+ { make_false(op - 3);
+ pop(3);
+ }
+ else
+ { if ( r_has_type(&mmkey, t_null) )
+ op[-3] = nmkey;
+ else
+ op[-3] = mmkey;
+ make_true(op - 2);
+ pop(2);
+ }
+ return 0;
+}
+
+/* [<req_x> <req_y>] [<med_x0> <med_y0> (<med_x1> <med_y1> | )]
+ * <policy> <matrix|null> <orient|null> .matchpagesize
+ * <matrix|null> <med_x> <med_y> true -or- false
+ */
+private int
+zmatchpagesize(register os_ptr op)
+{ gs_matrix mat;
+ float ignore_mismatch = (float)max_long;
+ gs_point media_size;
+ int orient, code;
+
+ check_type(op[-2], t_integer);
+ if ( r_has_type(op - 1, t_null) )
+ orient = -1;
+ else
+ { check_int_leu(op[-1], 3);
+ orient = (int)op[-1].value.intval;
+ }
+ code = zmatch_page_size(op - 4, op - 3, (int)op[-2].value.intval,
+ orient,
+ &ignore_mismatch, &mat, &media_size);
+ switch ( code )
+ {
+ default:
+ return code;
+ case 0:
+ make_false(op - 4);
+ pop(4);
+ break;
+ case 1:
+ code = write_matrix(op, &mat);
+ if ( code < 0 && !r_has_type(op, t_null) )
+ return code;
+ op[-4] = *op;
+ make_real(op - 3, media_size.x);
+ make_real(op - 2, media_size.y);
+ make_true(op - 1);
+ pop(1);
+ break;
+ }
+ return 0;
+}
+/* Match the PageSize. See below for details. */
+private bool match_page_size(P7(const gs_point *request,
+ const gs_rect *medium, int policy, int orient,
+ float *best_mismatch, gs_matrix *pmat, gs_point *pmsize));
+private int
+zmatch_page_size(const ref *pvreq, const ref *pvmed, int policy, int orient,
+ float *best_mismatch, gs_matrix *pmat, gs_point *pmsize)
+{ uint nm;
+ check_array(*pvreq);
+ check_array(*pvmed);
+ if ( !(r_size(pvreq) == 2 && ((nm = r_size(pvmed)) == 2 || nm == 4)) )
+ return_error(e_rangecheck);
+ { ref rv[6];
+ uint i;
+ float v[6];
+ int code;
+
+ array_get(pvreq, 0, &rv[0]);
+ array_get(pvreq, 1, &rv[1]);
+ for ( i = 0; i < 4; ++i )
+ array_get(pvmed, i % nm, &rv[i + 2]);
+ if ( (code = num_params(rv + 5, 6, v)) < 0 )
+ return code;
+ { gs_point request;
+ gs_rect medium;
+
+ request.x = v[0], request.y = v[1];
+ medium.p.x = v[2], medium.p.y = v[3],
+ medium.q.x = v[4], medium.q.y = v[5];
+ return match_page_size(&request, &medium, policy, orient,
+ best_mismatch, pmat, pmsize);
+ }
+ }
+}
+/*
+ * Match a requested PageSize with the PageSize of a medium. The medium
+ * may specify either a single value [mx my] or a range
+ * [mxmin mymin mxmax mymax]; matching means equality or inclusion
+ * to within a tolerance of 5, possibly swapping the requested X and Y.
+ * Take the Policies value into account, keeping track of the discrepancy
+ * if needed. When a match is found, also return the matrix to be
+ * concatenated after setting up the default matrix, and the actual
+ * media size.
+ *
+ * NOTE: The algorithm here doesn't work properly for variable-size media
+ * when the match isn't exact. We'll fix it if we ever need to.
+ */
+private void make_adjustment_matrix(P5(const gs_point *request,
+ const gs_rect *medium, gs_matrix *pmat, bool scale, int rotate));
+private bool
+match_page_size(const gs_point *request, const gs_rect *medium, int policy,
+ int orient, float *best_mismatch, gs_matrix *pmat, gs_point *pmsize)
+{ double rx = request->x, ry = request->y;
+
+ if ( rx - medium->p.x >= -5 && rx - medium->q.x <= 5 &&
+ ry - medium->p.y >= -5 && ry - medium->q.y <= 5 &&
+ (orient < 0 || !(orient & 1))
+ )
+ { *best_mismatch = 0;
+ make_adjustment_matrix(request, medium, pmat, false,
+ (orient >= 0 ? orient : 0));
+ }
+ else if ( rx - medium->p.y >= -5 && rx - medium->q.y <= 5 &&
+ ry - medium->p.x >= -5 && ry - medium->q.x <= 5 &&
+ (orient < 0 || (orient & 1))
+ )
+ { *best_mismatch = 0;
+ make_adjustment_matrix(request, medium, pmat, false,
+ (orient >= 0 ? orient :
+ rx < ry ? -1 : 1));
+ }
+ else
+ { int rotate =
+ (orient >= 0 ? orient :
+ rx < ry ?
+ (medium->q.x > medium->q.y ? -1 : 0) :
+ (medium->q.x < medium->q.y ? 1 : 0));
+ bool larger =
+ (rotate ? medium->q.y >= rx && medium->q.x >= ry :
+ medium->q.x >= rx && medium->q.y >= ry);
+ bool adjust = false;
+ float mismatch = medium->q.x * medium->q.y - rx * ry;
+
+ switch ( policy )
+ {
+ default: /* exact match only */
+ return false;
+ case 3: /* nearest match, adjust */
+ adjust = true;
+ case 5: /* nearest match, don't adjust */
+ if ( fabs(mismatch) >= fabs(*best_mismatch) )
+ return false;
+ break;
+ case 4: /* next larger match, adjust */
+ adjust = true;
+ case 6: /* next larger match, don't adjust */
+ if ( !larger || mismatch >= *best_mismatch )
+ return false;
+ break;
+ }
+ if ( adjust )
+ make_adjustment_matrix(request, medium, pmat, !larger, rotate);
+ else
+ { gs_rect req_rect;
+ req_rect.p = *request;
+ req_rect.q = *request;
+ make_adjustment_matrix(request, &req_rect, pmat, false, rotate);
+ }
+ *best_mismatch = mismatch;
+ }
+ if ( pmat->xx == 0 )
+ { /* Swap request X and Y. */
+ double temp = rx;
+ rx = ry, ry = temp;
+ }
+#define adjust_into(req, mmin, mmax)\
+ (req < mmin ? mmin : req > mmax ? mmax : req)
+ pmsize->x = adjust_into(rx, medium->p.x, medium->q.x);
+ pmsize->y = adjust_into(ry, medium->p.y, medium->q.y);
+ return true;
+}
+/*
+ * Compute the adjustment matrix for scaling and/or rotating the page
+ * to match the medium. If the medium is completely flexible in a given
+ * dimension (e.g., roll media in one dimension, or displays in both),
+ * we must adjust its size in that dimension to match the request.
+ * We recognize this by medium->p.{x,y} = 0.
+ */
+private void
+make_adjustment_matrix(const gs_point *request, const gs_rect *medium,
+ gs_matrix *pmat, bool scale, int rotate)
+{ double rx = request->x, ry = request->y;
+ double mx = medium->q.x, my = medium->q.y;
+
+ /* Rotate the request if necessary. */
+ if ( rotate & 1 )
+ { double temp = rx;
+ rx = ry, ry = temp;
+ }
+
+ /* Adjust the medium size if flexible. */
+ if ( medium->p.x == 0 && mx > rx )
+ mx = rx;
+ if ( medium->p.y == 0 && my > ry )
+ my = ry;
+
+ /* Translate to align the centers. */
+ gs_make_translation(mx / 2, my / 2, pmat);
+
+ /* Rotate if needed. */
+ if ( rotate )
+ gs_matrix_rotate(pmat, 90.0 * rotate, pmat);
+
+ /* Scale if needed. */
+ if ( scale )
+ { double xfactor = mx / rx;
+ double yfactor = my / ry;
+ double factor = min(xfactor, yfactor);
+ if ( factor < 1 )
+ gs_matrix_scale(pmat, factor, factor, pmat);
+ }
+
+ /* Now translate the origin back, */
+ /* using the original, unswapped request. */
+ gs_matrix_translate(pmat, -request->x / 2, -request->y / 2, pmat);
+}
+
+/* ------ Initialization procedure ------ */
+
+BEGIN_OP_DEFS(zmedia2_l2_op_defs) {
+ op_def_begin_level2(),
+ {"3.matchmedia", zmatchmedia},
+ {"5.matchpagesize", zmatchpagesize},
+END_OP_DEFS(0) }
diff --git a/pstoraster/zmisc.c b/pstoraster/zmisc.c
new file mode 100644
index 000000000..f57bac27d
--- /dev/null
+++ b/pstoraster/zmisc.c
@@ -0,0 +1,313 @@
+/* Copyright (C) 1989, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* zmisc.c */
+/* Miscellaneous operators */
+#include "errno_.h"
+#include "memory_.h"
+#include "string_.h"
+#include "ghost.h"
+#include "gscdefs.h" /* for gs_serialnumber */
+#include "gp.h"
+#include "errors.h"
+#include "oper.h"
+#include "ialloc.h"
+#include "idict.h"
+#include "dstack.h" /* for name lookup in bind */
+#include "iname.h"
+#include "ipacked.h"
+#include "ivmspace.h"
+#include "store.h"
+
+/* Import the C getenv function. */
+extern char *getenv(P1(const char *));
+
+/* <proc> bind <proc> */
+private int
+zbind(os_ptr op)
+{ uint depth = 1;
+ ref defn;
+ register os_ptr bsp;
+ switch ( r_type(op) )
+ {
+ case t_array:
+ case t_mixedarray:
+ case t_shortarray:
+ defn = *op;
+ break;
+ case t_oparray:
+ defn = *op->value.const_refs;
+ break;
+ default:
+ return_op_typecheck(op);
+ }
+ push(1);
+ *op = defn;
+ bsp = op;
+ /*
+ * We must not make the top-level procedure read-only,
+ * but we must bind it even if it is read-only already.
+ *
+ * Here are the invariants for the following loop:
+ * `depth' elements have been pushed on the ostack;
+ * For i < depth, p = ref_stack_index(&o_stack, i):
+ * *p is an array (or packedarray) ref. */
+#define r_is_ex_oper(rp)\
+ ((r_btype(rp) == t_operator || r_type(rp) == t_oparray) &&\
+ r_has_attr(rp, a_executable))
+ while ( depth )
+ { while ( r_size(bsp) )
+ { ref *tp = bsp->value.refs;
+ r_dec_size(bsp, 1);
+ if ( r_is_packed(tp) )
+ { /* Check for a packed executable name */
+ ushort elt = *(ushort *)tp;
+ if ( r_packed_is_exec_name(&elt) )
+ { ref nref;
+ ref *pvalue;
+ name_index_ref(packed_name_index(&elt),
+ &nref);
+ if ( (pvalue = dict_find_name(&nref)) != 0 &&
+ r_is_ex_oper(pvalue)
+ )
+ /* Note: can't undo this by restore! */
+ *(ushort *)tp =
+ pt_tag(pt_executable_operator) +
+ op_index(pvalue);
+ }
+ bsp->value.refs = (ref *)((ref_packed *)tp + 1);
+ }
+ else
+ switch ( bsp->value.refs++, r_type(tp) )
+ {
+ case t_name: /* bind the name if an operator */
+ if ( r_has_attr(tp, a_executable) )
+ { ref *pvalue;
+ if ( (pvalue = dict_find_name(tp)) != 0 &&
+ r_is_ex_oper(pvalue)
+ )
+ ref_assign_old(bsp, tp, pvalue, "bind");
+ }
+ break;
+ case t_array: /* push into array if writable */
+ if ( !r_has_attr(tp, a_write) ) break;
+ case t_mixedarray:
+ case t_shortarray:
+ if ( r_has_attr(tp, a_executable) )
+ { /* Make reference read-only */
+ r_clear_attrs(tp, a_write);
+ if ( bsp >= ostop )
+ { /* Push a new stack block. */
+ ref temp;
+ int code;
+ temp = *tp;
+ osp = bsp;
+ code = ref_stack_push(&o_stack, 1);
+ if ( code < 0 )
+ { ref_stack_pop(&o_stack, depth);
+ return_error(code);
+ }
+ bsp = osp;
+ *bsp = temp;
+ }
+ else
+ *++bsp = *tp;
+ depth++;
+ }
+ }
+ }
+ bsp--; depth--;
+ if ( bsp < osbot )
+ { /* Pop back to the previous stack block. */
+ osp = bsp;
+ ref_stack_pop_block(&o_stack);
+ bsp = osp;
+ }
+ }
+ osp = bsp;
+ return 0;
+}
+
+/* - serialnumber <int> */
+private int
+zserialnumber(register os_ptr op)
+{ push(1);
+ make_int(op, gs_serialnumber);
+ return 0;
+}
+
+/* - realtime <int> */
+private int
+zrealtime(register os_ptr op)
+{ long secs_ns[2];
+ gp_get_realtime(secs_ns);
+ push(1);
+ make_int(op, secs_ns[0] * 1000 + secs_ns[1] / 1000000);
+ return 0;
+}
+
+/* - usertime <int> */
+private int
+zusertime(register os_ptr op)
+{ long secs_ns[2];
+ gp_get_usertime(secs_ns);
+ push(1);
+ make_int(op, secs_ns[0] * 1000 + secs_ns[1] / 1000000);
+ return 0;
+}
+
+/* ---------------- Non-standard operators ---------------- */
+
+/* <string> getenv <value_string> true */
+/* <string> getenv false */
+private int
+zgetenv(register os_ptr op)
+{ char *str, *value;
+ int code;
+ check_read_type(*op, t_string);
+ str = ref_to_string(op, imemory, "getenv name");
+ if ( str == 0 )
+ return_error(e_VMerror);
+ value = getenv(str);
+ ifree_string((byte *)str, r_size(op) + 1, "getenv name");
+ if ( value == 0 ) /* not found */
+ { make_bool(op, 0);
+ return 0;
+ }
+ code = string_to_ref(value, op, iimemory, "getenv value");
+ if ( code < 0 ) return code;
+ push(1);
+ make_bool(op, 1);
+ return 0;
+}
+
+/* <name> <proc> .makeoperator <oper> */
+private int
+zmakeoperator(register os_ptr op)
+{ op_array_table *opt;
+ uint count;
+ ref *tab;
+
+ check_type(op[-1], t_name);
+ check_proc(*op);
+ switch ( r_space(op) )
+ {
+ case avm_global: opt = &op_array_table_global; break;
+ case avm_local: opt = &op_array_table_local; break;
+ default: return_error(e_invalidaccess);
+ }
+ count = opt->count;
+ tab = opt->table.value.refs;
+ /*
+ * restore doesn't reset op_array_table.count, but it does
+ * remove entries from op_array_table.table. Since we fill
+ * the table in order, we can detect that a restore has occurred
+ * by checking whether what should be the most recent entry
+ * is occupied. If not, we scan backwards over the vacated entries
+ * to find the true end of the table.
+ */
+ while ( count > 0 && r_has_type(&tab[count - 1], t_null) )
+ --count;
+ if ( count == r_size(&opt->table) )
+ return_error(e_limitcheck);
+ ref_assign_old(&opt->table, &tab[count], op, "makeoperator");
+ opt->nx_table[count] = name_index(op - 1);
+ op_index_ref(opt->base_index + count, op - 1);
+ opt->count = count + 1;
+ pop(1);
+ return 0;
+}
+
+/* - .oserrno <int> */
+private int
+zoserrno(register os_ptr op)
+{ push(1);
+ make_int(op, errno);
+ return 0;
+}
+
+/* <int> .setoserrno - */
+private int
+zsetoserrno(register os_ptr op)
+{ check_type(*op, t_integer);
+ errno = op->value.intval;
+ pop(1);
+ return 0;
+}
+
+/* <int> .oserrorstring <string> true */
+/* <int> .oserrorstring false */
+private int
+zoserrorstring(register os_ptr op)
+{ const char *str;
+ int code;
+ uint len;
+ byte ch;
+ check_type(*op, t_integer);
+ str = gp_strerror((int)op->value.intval);
+ if ( str == 0 || (len = strlen(str)) == 0 )
+ { make_false(op);
+ return 0;
+ }
+ check_ostack(1);
+ code = string_to_ref(str, op, iimemory, ".oserrorstring");
+ if ( code < 0 )
+ return code;
+ /* Strip trailing end-of-line characters. */
+ while ( (len = r_size(op)) != 0 &&
+ ((ch = op->value.bytes[--len]) == '\r' || ch == '\n')
+ )
+ r_dec_size(op, 1);
+ push(1);
+ make_true(op);
+ return 0;
+}
+
+/* <string> <bool> .setdebug - */
+private int
+zsetdebug(register os_ptr op)
+{ check_read_type(op[-1], t_string);
+ check_type(*op, t_boolean);
+ { int i;
+ for ( i = 0; i < r_size(op - 1); i++ )
+ gs_debug[op[-1].value.bytes[i] & 127] =
+ op->value.boolval;
+ }
+ pop(2);
+ return 0;
+}
+
+/* ------ Initialization procedure ------ */
+
+BEGIN_OP_DEFS(zmisc_op_defs) {
+ {"1bind", zbind},
+ {"1getenv", zgetenv},
+ {"2.makeoperator", zmakeoperator},
+ {"0.oserrno", zoserrno},
+ {"1.oserrorstring", zoserrorstring},
+ {"0realtime", zrealtime},
+ {"1serialnumber", zserialnumber},
+ {"2.setdebug", zsetdebug},
+ {"1.setoserrno", zsetoserrno},
+ {"0usertime", zusertime},
+END_OP_DEFS(0) }
diff --git a/pstoraster/zmisc1.c b/pstoraster/zmisc1.c
new file mode 100644
index 000000000..c9642bb04
--- /dev/null
+++ b/pstoraster/zmisc1.c
@@ -0,0 +1,127 @@
+/* Copyright (C) 1994 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* zmisc1.c */
+/* Miscellaneous Type 1 font operators */
+#include "memory_.h"
+#include "ghost.h"
+#include "errors.h"
+#include "oper.h"
+#include "gscrypt1.h"
+#include "stream.h" /* for getting state of PFBD stream */
+#include "strimpl.h"
+#include "sfilter.h"
+#include "ifilter.h"
+
+/* <state> <from_string> <to_string> .type1encrypt <new_state> <substring> */
+/* <state> <from_string> <to_string> .type1decrypt <new_state> <substring> */
+private int type1crypt(P2(os_ptr,
+ int (*)(P4(byte *, const byte *, uint, ushort *))));
+private int
+ztype1encrypt(os_ptr op)
+{ return type1crypt(op, gs_type1_encrypt);
+}
+private int
+ztype1decrypt(os_ptr op)
+{ return type1crypt(op, gs_type1_decrypt);
+}
+private int
+type1crypt(register os_ptr op, int (*proc)(P4(byte *, const byte *, uint, ushort *)))
+{ crypt_state state;
+ uint ssize;
+ check_type(op[-2], t_integer);
+ state = op[-2].value.intval;
+ if ( op[-2].value.intval != state )
+ return_error(e_rangecheck); /* state value was truncated */
+ check_read_type(op[-1], t_string);
+ check_write_type(*op, t_string);
+ ssize = r_size(op - 1);
+ if ( r_size(op) < ssize )
+ return_error(e_rangecheck);
+ discard((*proc)(op->value.bytes, op[-1].value.const_bytes, ssize,
+ &state)); /* can't fail */
+ op[-2].value.intval = state;
+ op[-1] = *op;
+ r_set_size(op - 1, ssize);
+ pop(1);
+ return 0;
+}
+
+/* Get the seed parameter for eexecEncode/Decode. */
+/* Return npop if OK. */
+private int
+eexec_param(os_ptr op, ushort *pcstate)
+{ int npop = 1;
+ if ( r_has_type(op, t_dictionary) )
+ ++npop, --op;
+ check_type(*op, t_integer);
+ *pcstate = op->value.intval;
+ if ( op->value.intval != *pcstate )
+ return_error(e_rangecheck); /* state value was truncated */
+ return npop;
+}
+
+/* <target> <seed> eexecEncode/filter <file> */
+/* <target> <seed> <dict_ignored> eexecEncode/filter <file> */
+private int
+zexE(register os_ptr op)
+{ stream_exE_state state;
+ int code = eexec_param(op, &state.cstate);
+
+ if ( code < 0 )
+ return code;
+ return filter_write(op, code, &s_exE_template, (stream_state *)&state,
+ 0);
+}
+
+/* <source> <seed> eexecDecode/filter <file> */
+/* <source> <seed> <dict_ignored> eexecDecode/filter <file> */
+private int
+zexD(register os_ptr op)
+{ stream_exD_state state;
+ int code = eexec_param(op, &state.cstate);
+
+ if ( code < 0 )
+ return code;
+ /* If we're reading a .PFB file, let the filter know about it, */
+ /* so it can read recklessly to the end of the binary section. */
+ state.pfb_state = 0;
+ if ( r_has_type(op - 1, t_file) )
+ { stream *s = (op - 1)->value.pfile;
+ if ( s->state != 0 && s->state->template == &s_PFBD_template )
+ state.pfb_state = (stream_PFBD_state *)s->state;
+ }
+ state.binary = -1;
+ return filter_read(op, code, &s_exD_template, (stream_state *)&state,
+ 0);
+}
+
+/* ------ Initialization procedure ------ */
+
+BEGIN_OP_DEFS(zmisc1_op_defs) {
+ {"3.type1encrypt", ztype1encrypt},
+ {"3.type1decrypt", ztype1decrypt},
+ op_def_begin_filter(),
+ {"2eexecEncode", zexE},
+ {"2eexecDecode", zexD},
+END_OP_DEFS(0) }
diff --git a/pstoraster/zmisc2.c b/pstoraster/zmisc2.c
new file mode 100644
index 000000000..7ae758134
--- /dev/null
+++ b/pstoraster/zmisc2.c
@@ -0,0 +1,244 @@
+/* Copyright (C) 1992, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* zmisc2.c */
+/* Miscellaneous Level 2 operators */
+#include "memory_.h"
+#include "string_.h"
+#include "ghost.h"
+#include "errors.h"
+#include "oper.h"
+#include "estack.h"
+#include "idict.h"
+#include "idparam.h"
+#include "iparam.h"
+#include "dstack.h"
+#include "ilevel.h"
+#include "iname.h"
+#include "iutil2.h"
+#include "ivmspace.h"
+#include "store.h"
+
+/* Forward references */
+private int set_language_level(P1(int));
+
+/* ------ Language level operators ------ */
+
+/* - .languagelevel <1 or 2> */
+private int
+zlanguagelevel(register os_ptr op)
+{ push(1);
+ ref_assign(op, &ref_language_level);
+ return 0;
+}
+
+/* <1 or 2> .setlanguagelevel - */
+private int
+zsetlanguagelevel(register os_ptr op)
+{ int code = 0;
+ check_type(*op, t_integer);
+ if ( op->value.intval < 1 || op->value.intval > 2 )
+ return_error(e_rangecheck);
+ if ( op->value.intval != ref_language_level.value.intval )
+ { code = set_language_level((int)op->value.intval);
+ if ( code < 0 ) return code;
+ }
+ pop(1);
+ ref_assign_old(NULL, &ref_language_level, op, "setlanguagelevel");
+ return code;
+}
+
+/* ------ The 'where' hack ------ */
+
+private int
+z2where(register os_ptr op)
+{ /*
+ * Aldus Freehand versions 2.x check for the presence of the
+ * setcolor operator, and if it is missing, substitute a procedure.
+ * Unfortunately, the procedure takes different parameters from
+ * the operator. As a result, files produced by this application
+ * cause an error if the setcolor operator is actually defined
+ * and 'bind' is ever used. Aldus fixed this bug in Freehand 3.0,
+ * but there are a lot of files created by the older versions
+ * still floating around. Therefore, at Adobe's suggestion,
+ * we implement the following dreadful hack in the 'where' operator:
+ * If the key is /setcolor, and
+ * there is a dictionary named FreeHandDict, and
+ * currentdict is that dictionary,
+ * then "where" consults only that dictionary and not any other
+ * dictionaries on the dictionary stack.
+ */
+ ref rkns, rfh;
+ const ref *pdref = dsp;
+ ref *pvalue;
+ if ( !r_has_type(op, t_name) ||
+ (name_string_ref(op, &rkns), r_size(&rkns)) != 8 ||
+ memcmp(rkns.value.bytes, "setcolor", 8) != 0 ||
+ name_ref((const byte *)"FreeHandDict", 12, &rfh, -1) < 0 ||
+ (pvalue = dict_find_name(&rfh)) == 0 ||
+ !obj_eq(pvalue, pdref)
+ )
+ return zwhere(op);
+ check_dict_read(*pdref);
+ if ( dict_find(pdref, op, &pvalue) > 0 )
+ { ref_assign(op, pdref);
+ push(1);
+ make_true(op);
+ }
+ else
+ make_false(op);
+ return 0;
+}
+
+/* ------ Initialization procedure ------ */
+
+/* The level setting ops are recognized even in Level 1 mode. */
+BEGIN_OP_DEFS(zmisc2_op_defs) {
+ {"0.languagelevel", zlanguagelevel},
+ {"1.setlanguagelevel", zsetlanguagelevel},
+ /* The rest of the operators are defined only in Level 2. */
+ op_def_begin_level2(),
+/* Note that this overrides the definition in zdict.c. */
+ {"1where", z2where},
+END_OP_DEFS(0) }
+
+/* ------ Internal procedures ------ */
+
+/* Adjust the interpreter for a change in language level. */
+/* This is used for the .setlanguagelevel operator, */
+/* and after a restore. */
+private int swap_entry(P3(ref elt[2], ref *pdict, ref *pdict2));
+private int
+set_language_level(int level)
+{ ref *pgdict = /* globaldict, if present */
+ ref_stack_index(&d_stack, ref_stack_count(&d_stack) - 2);
+ ref *level2dict;
+ if (dict_find_string(systemdict, "level2dict", &level2dict) <= 0)
+ return_error(e_undefined);
+ /* As noted in dstack.h, we allocate the extra d-stack entry */
+ /* for globaldict even in Level 1 mode; in Level 1 mode, */
+ /* this entry holds an extra copy of systemdict, and */
+ /* [count]dictstack omit the very bottommost entry. */
+ if ( level == 2 ) /* from Level 1 to Level 2 */
+ { /* Put globaldict in the dictionary stack. */
+ ref *pdict;
+ int code = dict_find_string(level2dict, "globaldict", &pdict);
+ if ( code <= 0 )
+ return_error(e_undefined);
+ if ( !r_has_type(pdict, t_dictionary) )
+ return_error(e_typecheck);
+ *pgdict = *pdict;
+ /* Set other flags for Level 2 operation. */
+ dict_auto_expand = true;
+ }
+ else /* from Level 2 to Level 1 */
+ { /* Clear the cached definition pointers of all names */
+ /* defined in globaldict. This will slow down */
+ /* future lookups, but we don't care. */
+ int index = dict_first(pgdict);
+ ref elt[2];
+ while ( (index = dict_next(pgdict, index, &elt[0])) >= 0 )
+ if ( r_has_type(&elt[0], t_name) )
+ name_invalidate_value_cache(&elt[0]);
+ /* Overwrite globaldict in the dictionary stack. */
+ *pgdict = *systemdict;
+ /* Set other flags for Level 1 operation. */
+ dict_auto_expand = false;
+ }
+ /* Swap the contents of level2dict and systemdict. */
+ /* If a value in level2dict is a dictionary, and it contains */
+ /* a key/value pair referring to itself, swap its contents */
+ /* with the contents of the same dictionary in systemdict. */
+ /* (This is a hack to swap the contents of statusdict.) */
+ { int index = dict_first(level2dict);
+ ref elt[2]; /* key, value */
+ ref *subdict;
+ while ( (index = dict_next(level2dict, index, &elt[0])) >= 0 )
+ if ( r_has_type(&elt[1], t_dictionary) &&
+ dict_find(&elt[1], &elt[0], &subdict) > 0 &&
+ obj_eq(&elt[1], subdict)
+ )
+ {
+#define sub2dict &elt[1]
+ int isub = dict_first(sub2dict);
+ ref subelt[2];
+ int found = dict_find(systemdict, &elt[0], &subdict);
+ if ( found <= 0 )
+ continue;
+ while ( (isub = dict_next(sub2dict, isub, &subelt[0])) >= 0 )
+ if ( !obj_eq(&subelt[0], &elt[0]) ) /* don't swap dict itself */
+ { int code = swap_entry(subelt, subdict, sub2dict);
+ if ( code < 0 )
+ return code;
+ }
+#undef sub2dict
+ }
+ else
+ { int code = swap_entry(elt, systemdict, level2dict);
+ if ( code < 0 )
+ return code;
+ }
+ }
+ dict_set_top(); /* reload dict stack cache */
+ return 0;
+}
+
+/* Swap an entry from a Level 2 dictionary into a base dictionary. */
+/* elt[0] is the key, elt[1] is the value in the Level 2 dictionary. */
+private int
+swap_entry(ref elt[2], ref *pdict, ref *pdict2)
+{ ref *pvalue;
+ ref old_value;
+ int found = dict_find(pdict, &elt[0], &pvalue);
+ switch ( found )
+ {
+ default: /* <0, error */
+ return found;
+ case 0: /* missing */
+ make_null(&old_value);
+ break;
+ case 1: /* present */
+ old_value = *pvalue;
+ }
+ /*
+ * Temporarily flag the dictionaries as local, so that we don't
+ * get invalidaccess errors. (We know that they are both
+ * referenced from systemdict, so they are allowed to reference
+ * local objects even if they are global.)
+ */
+ { uint space2 = r_space(pdict2);
+ int code;
+ r_set_space(pdict2, avm_local);
+ dict_put(pdict2, &elt[0], &old_value);
+ if ( r_has_type(&elt[1], t_null) )
+ code = dict_undef(pdict, &elt[0]);
+ else
+ { uint space = r_space(pdict);
+ r_set_space(pdict, avm_local);
+ code = dict_put(pdict, &elt[0], &elt[1]);
+ r_set_space(pdict, space);
+ }
+ r_set_space(pdict2, space2);
+ return code;
+ }
+}
diff --git a/pstoraster/zpacked.c b/pstoraster/zpacked.c
new file mode 100644
index 000000000..74e5e4266
--- /dev/null
+++ b/pstoraster/zpacked.c
@@ -0,0 +1,237 @@
+/* Copyright (C) 1990, 1992, 1993 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* zpacked.c */
+/* Packed array operators */
+#include "ghost.h"
+#include "errors.h"
+#include "ialloc.h"
+#include "idict.h"
+#include "iname.h"
+#include "istack.h" /* for iparray.h */
+#include "ipacked.h"
+#include "iparray.h"
+#include "ivmspace.h"
+#include "oper.h"
+#include "store.h"
+
+/* Import the array packing flag */
+extern ref ref_array_packing;
+
+/* - currentpacking <bool> */
+private int
+zcurrentpacking(register os_ptr op)
+{ push(1);
+ ref_assign(op, &ref_array_packing);
+ return 0;
+}
+
+/* <obj_0> ... <obj_n-1> <n> packedarray <packedarray> */
+int
+zpackedarray(register os_ptr op)
+{ int code;
+ ref parr;
+ check_type(*op, t_integer);
+ if ( op->value.intval < 0 ||
+ (op->value.intval > op - osbot &&
+ op->value.intval >= ref_stack_count(&o_stack))
+ )
+ return_error(e_rangecheck);
+ osp--;
+ code = make_packed_array(&parr, &o_stack, (uint)op->value.intval,
+ "packedarray");
+ osp++;
+ if ( code >= 0 )
+ *osp = parr;
+ return code;
+}
+
+/* <bool> setpacking - */
+private int
+zsetpacking(register os_ptr op)
+{ check_type(*op, t_boolean);
+ ref_assign_old(NULL, &ref_array_packing, op, "setpacking");
+ pop(1);
+ return 0;
+}
+
+/* ------ Non-operator routines ------ */
+
+/* Make a packed array. See the comment in packed.h about */
+/* ensuring that refs in mixed arrays are properly aligned. */
+int
+make_packed_array(ref *parr, ref_stack *pstack, uint size, client_name_t cname)
+{ uint i;
+ const ref *pref;
+ uint idest = 0, ishort = 0;
+ ref_packed *pbody, *pdest;
+ ref_packed *pshort; /* points to start of */
+ /* last run of short elements */
+ uint space = ialloc_space(idmemory);
+ int skip = 0, pad;
+ ref rtemp;
+ int code;
+
+ /* Do a first pass to calculate the size of the array, */
+ /* and to detect local-into-global stores. */
+
+ for ( i = size; i != 0; i-- )
+ { pref = ref_stack_index(pstack, i - 1);
+ switch ( r_btype(pref) ) /* not r_type, opers are special */
+ {
+ case t_name:
+ if ( name_index(pref) >= packed_name_max_index )
+ break; /* can't pack */
+ idest++;
+ continue;
+ case t_integer:
+ if ( pref->value.intval < packed_min_intval ||
+ pref->value.intval > packed_max_intval
+ )
+ break;
+ idest++;
+ continue;
+ case t_oparray:
+ /* Check for local-into-global store. */
+ store_check_space(space, pref);
+ /* falls through */
+ case t_operator:
+ { uint oidx;
+ if ( !r_has_attr(pref, a_executable) )
+ break;
+ oidx = op_index(pref);
+ if ( oidx == 0 || oidx > packed_int_mask )
+ break;
+ } idest++;
+ continue;
+ default:
+ /* Check for local-into-global store. */
+ store_check_space(space, pref);
+ }
+ /* Can't pack this element, use a full ref. */
+ /* We may have to unpack up to align_packed_per_ref - 1 */
+ /* preceding short elements. */
+ /* If we are at the beginning of the array, however, */
+ /* we can just move the elements up. */
+ { int i = (idest - ishort) & (align_packed_per_ref - 1);
+ if ( ishort == 0 ) /* first time */
+ idest += skip = -i & (align_packed_per_ref - 1);
+ else
+ idest += (packed_per_ref - 1) * i;
+ }
+ ishort = idest += packed_per_ref;
+ }
+ pad = -idest & (packed_per_ref - 1); /* padding at end */
+
+ /* Now we can allocate the array. */
+
+ code = ialloc_ref_array(&rtemp, 0, (idest + pad) / packed_per_ref,
+ cname);
+ if ( code < 0 )
+ return code;
+ pbody = (ref_packed *)rtemp.value.refs;
+
+ /* Make sure any initial skipped elements contain legal packed */
+ /* refs, so that the garbage collector can scan storage. */
+
+ pshort = pbody;
+ for ( ; skip; skip-- )
+ *pbody++ = pt_tag(pt_integer);
+ pdest = pbody;
+
+ for ( i = size; i != 0; i-- )
+ { pref = ref_stack_index(pstack, i - 1);
+ switch ( r_btype(pref) ) /* not r_type, opers are special */
+ {
+ case t_name:
+ { uint nidx = name_index(pref);
+ if ( nidx >= packed_name_max_index )
+ break; /* can't pack */
+ *pdest++ = nidx +
+ (r_has_attr(pref, a_executable) ?
+ pt_tag(pt_executable_name) :
+ pt_tag(pt_literal_name));
+ } continue;
+ case t_integer:
+ if ( pref->value.intval < packed_min_intval ||
+ pref->value.intval > packed_max_intval
+ )
+ break;
+ *pdest++ = pt_tag(pt_integer) +
+ ((short)pref->value.intval - packed_min_intval);
+ continue;
+ case t_oparray:
+ case t_operator:
+ { uint oidx;
+ if ( !r_has_attr(pref, a_executable) )
+ break;
+ oidx = op_index(pref);
+ if ( oidx == 0 || oidx > packed_int_mask )
+ break;
+ *pdest++ = pt_tag(pt_executable_operator) + oidx;
+ } continue;
+ }
+ /* Can't pack this element, use a full ref. */
+ /* We may have to unpack up to align_packed_per_ref - 1 */
+ /* preceding short elements. */
+ /* Note that if we are at the beginning of the array, */
+ /* 'skip' already ensures that we don't need to do this. */
+ { int i = (pdest - pshort) & (align_packed_per_ref - 1);
+ const ref_packed *psrc = pdest;
+ ref *pmove =
+ (ref *)(pdest += (packed_per_ref - 1) * i);
+ ref_assign_new(pmove, pref);
+ while ( --i >= 0 )
+ { --psrc;
+ --pmove;
+ packed_get(psrc, pmove);
+ }
+ }
+ pshort = pdest += packed_per_ref;
+ }
+
+ { int atype =
+ (pdest == pbody + size ? t_shortarray : t_mixedarray);
+
+ /* Pad with legal packed refs so that the garbage collector */
+ /* can scan storage. */
+
+ for ( ; pad; pad-- )
+ *pdest++ = pt_tag(pt_integer);
+
+ /* Finally, make the array. */
+
+ ref_stack_pop(pstack, size);
+ make_tasv_new(parr, atype, a_readonly | space, size,
+ packed, pbody + skip);
+ }
+ return 0;
+}
+
+/* ------ Initialization procedure ------ */
+
+BEGIN_OP_DEFS(zpacked_op_defs) {
+ {"0currentpacking", zcurrentpacking},
+ {"1packedarray", zpackedarray},
+ {"1setpacking", zsetpacking},
+END_OP_DEFS(0) }
diff --git a/pstoraster/zpaint.c b/pstoraster/zpaint.c
new file mode 100644
index 000000000..75b1f02b2
--- /dev/null
+++ b/pstoraster/zpaint.c
@@ -0,0 +1,484 @@
+/* Copyright (C) 1989, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* zpaint.c */
+/* Painting operators */
+#include "ghost.h"
+#include "errors.h"
+#include "oper.h"
+#include "estack.h" /* for image[mask] */
+#include "gsstruct.h"
+#include "ialloc.h"
+#include "igstate.h"
+#include "ilevel.h"
+#include "store.h"
+#include "gscspace.h"
+#include "gsmatrix.h"
+#include "gsimage.h"
+#include "gspaint.h"
+#include "stream.h"
+#include "ifilter.h" /* for stream exception handling */
+#include "iimage.h"
+
+/* Forward references */
+private int image_setup(P5(gs_image_t *pim, bool multi, os_ptr op, const gs_color_space *pcs, int npop));
+private int image_proc_continue(P1(os_ptr));
+private int image_file_continue(P1(os_ptr));
+private int image_file_buffered_continue(P1(os_ptr));
+private int image_string_process(P3(os_ptr, gs_image_enum *, int));
+private int image_cleanup(P1(os_ptr));
+
+/* - fill - */
+private int
+zfill(register os_ptr op)
+{ return gs_fill(igs);
+}
+
+/* - .fillpage - */
+private int
+zfillpage(register os_ptr op)
+{ return gs_fillpage(igs);
+}
+
+/* - eofill - */
+private int
+zeofill(register os_ptr op)
+{ return gs_eofill(igs);
+}
+
+/* - stroke - */
+private int
+zstroke(register os_ptr op)
+{ return gs_stroke(igs);
+}
+
+/* <width> <height> <bits/sample> <matrix> <datasrc> image - */
+int
+zimage(register os_ptr op)
+{ return zimage_opaque_setup(op, false, NULL, 5);
+}
+
+/* <width> <height> <paint_1s> <matrix> <datasrc> imagemask - */
+int
+zimagemask(register os_ptr op)
+{ gs_image_t image;
+
+ check_type(op[-2], t_boolean);
+ gs_image_t_init_mask(&image, op[-2].value.boolval);
+ return image_setup(&image, false, op, NULL, 5);
+}
+
+/* Common setup for image and colorimage. */
+/* Fills in MultipleDataSources, BitsPerComponent. */
+int
+zimage_opaque_setup(register os_ptr op, bool multi,
+ const gs_color_space *pcs, int npop)
+{ gs_image_t image;
+
+ check_int_leu(op[-2], (level2_enabled ? 12 : 8)); /* bits/sample */
+ gs_image_t_init_color(&image);
+ image.BitsPerComponent = (int)op[-2].value.intval;
+ return image_setup(&image, multi, op, pcs, npop);
+}
+
+/* Common setup for [color]image and imagemask. */
+/* Fills in Width, Height, ImageMatrix, ColorSpace. */
+private int
+image_setup(gs_image_t *pim, bool multi, register os_ptr op,
+ const gs_color_space *pcs, int npop)
+{ int code;
+
+ check_type(op[-4], t_integer); /* width */
+ check_type(op[-3], t_integer); /* height */
+ if ( op[-4].value.intval < 0 || op[-3].value.intval < 0 )
+ return_error(e_rangecheck);
+ if ( (code = read_matrix(op - 1, &pim->ImageMatrix)) < 0 )
+ return code;
+ pim->ColorSpace = pcs;
+ pim->Width = (int)op[-4].value.intval;
+ pim->Height = (int)op[-3].value.intval;
+ return zimage_setup(pim, multi, op, npop);
+}
+
+/* Common setup for Level 1 image/imagemask/colorimage and */
+/* the Level 2 dictionary form of image/imagemask. */
+int
+zimage_setup(const gs_image_t *pim, bool multi, const ref *sources, int npop)
+{ int code;
+ gs_image_enum *penum;
+ int px;
+ const ref *pp;
+ int num_sources =
+ (multi ? gs_color_space_num_components(pim->ColorSpace) : 1);
+ bool must_buffer = false;
+
+ /*
+ * We push the following on the estack. "Optional" values are
+ * set to null if not used, so the offsets will be constant.
+ * Control mark,
+ * 4 data sources (1-4 actually used),
+ * 4 row buffers (only if must_buffer),
+ * current plane index,
+ * current byte in row (only if must_buffer, otherwise 0),
+ * enumeration structure.
+ */
+#define inumpush 12
+ check_estack(inumpush + 2); /* stuff above, + continuation + proc */
+ /*
+ * Note that the data sources may be procedures, strings, or (Level
+ * 2 only) files. (The Level 1 reference manual says that Level 1
+ * requires procedures, but Adobe Level 1 interpreters also accept
+ * strings.) The sources must all be of the same type.
+ *
+ * If the sources are files, and two or more are the same file,
+ * we must buffer data for each row; otherwise, we can deliver the
+ * data directly out of the stream buffers.
+ */
+ for ( px = 0, pp = sources; px < num_sources; px++, pp++ )
+ { switch ( r_type(pp) )
+ {
+ case t_file:
+ if ( !level2_enabled )
+ return_error(e_typecheck);
+ /* Check for aliasing. */
+ { int pi;
+ for ( pi = 0; pi < px; ++pi )
+ if ( sources[pi].value.pfile == pp->value.pfile )
+ must_buffer = true;
+ }
+ /* falls through */
+ case t_string:
+ if ( r_type(pp) != r_type(sources) )
+ return_error(e_typecheck);
+ check_read(*pp);
+ break;
+ default:
+ if ( !r_is_proc(sources) )
+ return_error(e_typecheck);
+ check_proc(*pp);
+ }
+ }
+ if ( (penum = gs_image_enum_alloc(imemory, "image_setup")) == 0 )
+ return_error(e_VMerror);
+ code = gs_image_init(penum, pim, multi, igs);
+ if ( code != 0 ) /* error, or empty image */
+ { ifree_object(penum, "image_setup");
+ if ( code >= 0 ) /* empty image */
+ pop(npop);
+ return code;
+ }
+ push_mark_estack(es_other, image_cleanup);
+ ++esp;
+ for ( px = 0, pp = sources; px < 4; esp++, px++, pp++ )
+ { if ( px < num_sources )
+ *esp = *pp;
+ else
+ make_null(esp);
+ make_null(esp + 4); /* buffer */
+ }
+ esp += 6;
+ make_int(esp - 2, 0); /* current plane */
+ make_int(esp - 1, 0); /* current byte in row */
+ make_istruct(esp, 0, penum);
+ switch ( r_type(sources) )
+ {
+ case t_file:
+ if ( must_buffer )
+ { /* Allocate a buffer for each row. */
+ uint size = gs_image_bytes_per_row(penum);
+ for ( px = 0; px < num_sources; ++px )
+ { byte *sbody = ialloc_string(size, "image_setup");
+ if ( sbody == 0 )
+ { esp -= inumpush;
+ image_cleanup(osp);
+ return_error(e_VMerror);
+ }
+ make_string(esp - 6 + px, icurrent_space, size, sbody);
+ }
+ push_op_estack(image_file_buffered_continue);
+ }
+ else
+ { push_op_estack(image_file_continue);
+ }
+ break;
+ case t_string:
+ pop(npop);
+ return image_string_process(osp, penum, num_sources);
+ default: /* procedure */
+ push_op_estack(image_proc_continue);
+ *++esp = sources[0];
+ break;
+ }
+ pop(npop);
+ return o_push_estack;
+}
+/* Continuation for procedure data source. */
+private int
+image_proc_continue(register os_ptr op)
+{ gs_image_enum *penum = r_ptr(esp, gs_image_enum);
+ uint size, used;
+ int code;
+ int px;
+ const ref *pproc;
+
+ if ( !r_has_type_attrs(op, t_string, a_read) )
+ { check_op(1);
+ /* Procedure didn't return a (readable) string. Quit. */
+ esp -= inumpush;
+ image_cleanup(op);
+ return_error(!r_has_type(op, t_string) ? e_typecheck : e_invalidaccess);
+ }
+ size = r_size(op);
+ if ( size == 0 )
+ code = 1;
+ else
+ code = gs_image_next(penum, op->value.bytes, size, &used);
+ if ( code )
+ { /* Stop now. */
+ esp -= inumpush;
+ pop(1); op = osp;
+ image_cleanup(op);
+ return (code < 0 ? code : o_pop_estack);
+ }
+ pop(1);
+ px = (int)++(esp[-2].value.intval);
+ pproc = esp - (inumpush - 2);
+ if ( px == 4 || r_has_type(pproc + px, t_null) )
+ esp[-2].value.intval = px = 0;
+ push_op_estack(image_proc_continue);
+ *++esp = pproc[px];
+ return o_push_estack;
+}
+/* Continue processing data from an image with file data sources */
+/* and no file buffering. */
+private int
+image_file_continue(os_ptr op)
+{ gs_image_enum *penum = r_ptr(esp, gs_image_enum);
+ const ref *pproc = esp - (inumpush - 2);
+
+ for ( ; ; )
+ { uint size = max_uint;
+ int code;
+ int pn, px;
+ const ref *pp;
+
+ /*
+ * Do a first pass through the files to ensure that they all
+ * have data available in their buffers, and compute the min
+ * of the available amounts.
+ */
+
+ for ( pn = 0, pp = pproc; pn < 4 && !r_has_type(pp, t_null);
+ ++pn, ++pp
+ )
+ { stream *s = pp->value.pfile;
+ int min_left = sbuf_min_left(s);
+ uint avail;
+
+ while ( (avail = sbufavailable(s)) <= min_left )
+ { int next = sgetc(s);
+
+ if ( next >= 0 )
+ { sputback(s);
+ if ( s->end_status == EOFC || s->end_status == ERRC )
+ min_left = 0;
+ continue;
+ }
+ switch ( next )
+ {
+ case EOFC:
+ break; /* with avail = 0 */
+ case INTC:
+ case CALLC:
+ return
+ s_handle_read_exception(next, pp,
+ NULL, 0, image_file_continue);
+ default:
+ /* case ERRC: */
+ return_error(e_ioerror);
+ }
+ break; /* for EOFC */
+ }
+ avail -= min_left;
+ if ( avail < size )
+ size = avail;
+ }
+
+ /* Now pass the min of the available buffered data to */
+ /* the image processor. */
+
+ if ( size == 0 )
+ code = 1;
+ else
+ { int pi;
+ uint used; /* only set for the last plane */
+
+ for ( px = 0, pp = pproc, code = 0; px < pn && !code;
+ ++px, ++pp
+ )
+ code = gs_image_next(penum, sbufptr(pp->value.pfile),
+ size, &used);
+ /* Now that used has been set, update the streams. */
+ for ( pi = 0, pp = pproc; pi < px; ++pi, ++pp )
+ sbufskip(pp->value.pfile, used);
+ }
+ if ( code )
+ { esp -= inumpush;
+ image_cleanup(op);
+ return (code < 0 ? code : o_pop_estack);
+ }
+ }
+}
+/* Continue processing data from an image with file data sources */
+/* and file buffering. This is similar to the procedure case. */
+private int
+image_file_buffered_continue(os_ptr op)
+{ gs_image_enum *penum = r_ptr(esp, gs_image_enum);
+ const ref *pproc = esp - (inumpush - 2);
+ int px = esp[-2].value.intval;
+ int dpos = esp[-1].value.intval;
+ uint size = gs_image_bytes_per_row(penum);
+ int code = 0;
+
+ while ( !code )
+ { const ref *pp;
+ uint avail = size;
+ uint used;
+ int pi;
+
+ /* Accumulate data until we have a full set of planes. */
+ while ( px < 4 && !r_has_type((pp = pproc + px), t_null) )
+ { const ref *pb = pp + 4;
+ uint used;
+ int status = sgets(pp->value.pfile, pb->value.bytes,
+ size - dpos, &used);
+
+ if ( (dpos += used) == size )
+ dpos = 0, ++px;
+ else switch ( status )
+ {
+ case EOFC:
+ if ( dpos < avail )
+ avail = dpos;
+ dpos = 0, ++px;
+ break;
+ case INTC:
+ case CALLC:
+ /* Call out to read from a procedure-based stream. */
+ esp[-2].value.intval = px;
+ esp[-1].value.intval = dpos;
+ return s_handle_read_exception(status, pp,
+ NULL, 0, image_file_buffered_continue);
+ default:
+ /*case ERRC:*/
+ return_error(e_ioerror);
+ }
+ }
+ /* Pass the data to the image processor. */
+ if ( avail == 0 )
+ { code = 1;
+ break;
+ }
+ for ( pi = 0, pp = pproc + 4; pi < px && !code; ++pi, ++pp )
+ code = gs_image_next(penum, pp->value.bytes, avail, &used);
+ /* Reinitialize for the next row. */
+ px = dpos = 0;
+ }
+ esp -= inumpush;
+ image_cleanup(op);
+ return (code < 0 ? code : o_pop_estack);
+}
+/* Process data from an image with string data sources. */
+/* This never requires callbacks, so it's simpler. */
+private int
+image_string_process(os_ptr op, gs_image_enum *penum, int num_sources)
+{ int px = 0;
+
+ for ( ; ; )
+ { const ref *psrc = esp - (inumpush - 2) + px;
+ uint size = r_size(psrc);
+ uint used;
+ int code;
+
+ if ( size == 0 )
+ code = 1;
+ else
+ code = gs_image_next(penum, psrc->value.bytes, size, &used);
+ if ( code )
+ { /* Stop now. */
+ esp -= inumpush;
+ image_cleanup(op);
+ return (code < 0 ? code : o_pop_estack);
+ }
+ if ( ++px == num_sources )
+ px = 0;
+ }
+}
+/* Clean up after enumerating an image */
+private int
+image_cleanup(os_ptr op)
+{ gs_image_enum *penum = r_ptr(esp + inumpush, gs_image_enum);
+ const ref *pb;
+
+ /* Free any row buffers, in LIFO order as usual. */
+ for ( pb = esp + 9; pb >= esp + 6; --pb )
+ if ( r_has_type(pb, t_string) )
+ gs_free_string(imemory, pb->value.bytes, r_size(pb),
+ "image_cleanup");
+ gs_image_cleanup(penum);
+ ifree_object(penum, "image_cleanup");
+ return 0;
+}
+
+/* ------ Non-standard operators ------ */
+
+/* <width> <height> <data> .imagepath - */
+private int
+zimagepath(register os_ptr op)
+{ int code;
+ check_type(op[-2], t_integer);
+ check_type(op[-1], t_integer);
+ check_read_type(*op, t_string);
+ if ( r_size(op) < ((op[-2].value.intval + 7) >> 3) * op[-1].value.intval )
+ return_error(e_rangecheck);
+ code = gs_imagepath(igs,
+ (int)op[-2].value.intval, (int)op[-1].value.intval,
+ op->value.const_bytes);
+ if ( code == 0 ) pop(3);
+ return code;
+}
+
+/* ------ Initialization procedure ------ */
+
+BEGIN_OP_DEFS(zpaint_op_defs) {
+ {"0eofill", zeofill},
+ {"0fill", zfill},
+ {"0.fillpage", zfillpage},
+ {"5image", zimage},
+ {"5imagemask", zimagemask},
+ {"3.imagepath", zimagepath},
+ {"0stroke", zstroke},
+ /* Internal operators */
+ {"1%image_proc_continue", image_proc_continue},
+ {"0%image_file_continue", image_file_continue},
+END_OP_DEFS(0) }
diff --git a/pstoraster/zpath.c b/pstoraster/zpath.c
new file mode 100644
index 000000000..4489eff2b
--- /dev/null
+++ b/pstoraster/zpath.c
@@ -0,0 +1,179 @@
+/* Copyright (C) 1989, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* zpath.c */
+/* Basic path operators */
+#include "math_.h"
+#include "ghost.h"
+#include "errors.h"
+#include "oper.h"
+#include "igstate.h"
+#include "gsmatrix.h"
+#include "gspath.h"
+#include "store.h"
+
+/* Forward references */
+private int near common_to(P2(os_ptr,
+ int (*)(P3(gs_state *, floatp, floatp))));
+private int near common_curve(P2(os_ptr,
+ int (*)(P7(gs_state *, floatp, floatp, floatp, floatp, floatp, floatp))));
+
+/* - newpath - */
+private int
+znewpath(register os_ptr op)
+{ return gs_newpath(igs);
+}
+
+/* - currentpoint <x> <y> */
+private int
+zcurrentpoint(register os_ptr op)
+{ gs_point pt;
+ int code = gs_currentpoint(igs, &pt);
+ if ( code < 0 ) return code;
+ push(2);
+ make_real(op - 1, pt.x);
+ make_real(op, pt.y);
+ return 0;
+}
+
+/* <x> <y> moveto - */
+int
+zmoveto(os_ptr op)
+{ return common_to(op, gs_moveto);
+}
+
+/* <dx> <dy> rmoveto - */
+int
+zrmoveto(os_ptr op)
+{ return common_to(op, gs_rmoveto);
+}
+
+/* <x> <y> lineto - */
+int
+zlineto(os_ptr op)
+{ return common_to(op, gs_lineto);
+}
+
+/* <dx> <dy> rlineto - */
+int
+zrlineto(os_ptr op)
+{ return common_to(op, gs_rlineto);
+}
+
+/* Common code for [r](move/line)to */
+private int near
+common_to(os_ptr op, int (*add_proc)(P3(gs_state *, floatp, floatp)))
+{ float opxy[2];
+ int code;
+ if ( (code = num_params(op, 2, opxy)) < 0 ||
+ (code = (*add_proc)(igs, opxy[0], opxy[1])) < 0
+ ) return code;
+ pop(2);
+ return 0;
+}
+
+/* <x1> <y1> <x2> <y2> <x3> <y3> curveto - */
+int
+zcurveto(register os_ptr op)
+{ return common_curve(op, gs_curveto);
+}
+
+/* <dx1> <dy1> <dx2> <dy2> <dx3> <dy3> rcurveto - */
+int
+zrcurveto(register os_ptr op)
+{ return common_curve(op, gs_rcurveto);
+}
+
+/* Common code for [r]curveto */
+private int near
+common_curve(os_ptr op,
+ int (*add_proc)(P7(gs_state *, floatp, floatp, floatp, floatp, floatp, floatp)))
+{ float opxy[6];
+ int code;
+ if ( (code = num_params(op, 6, opxy)) < 0 ) return code;
+ code = (*add_proc)(igs, opxy[0], opxy[1], opxy[2], opxy[3], opxy[4], opxy[5]);
+ if ( code >= 0 ) pop(6);
+ return code;
+}
+
+/* - closepath - */
+int
+zclosepath(register os_ptr op)
+{ return gs_closepath(igs);
+}
+
+/* - initclip - */
+private int
+zinitclip(register os_ptr op)
+{ return gs_initclip(igs);
+}
+
+/* - clip - */
+private int
+zclip(register os_ptr op)
+{ return gs_clip(igs);
+}
+
+/* - eoclip - */
+private int
+zeoclip(register os_ptr op)
+{ return gs_eoclip(igs);
+}
+
+/* <bool> .setclipoutside - */
+private int
+zsetclipoutside(register os_ptr op)
+{ int code;
+ check_type(*op, t_boolean);
+ code = gs_setclipoutside(igs, op->value.boolval);
+ if ( code >= 0 )
+ pop(1);
+ return code;
+}
+
+/* - .currentclipoutside <bool> */
+private int
+zcurrentclipoutside(register os_ptr op)
+{ push(1);
+ make_bool(op, gs_currentclipoutside(igs));
+ return 0;
+}
+
+/* ------ Initialization procedure ------ */
+
+BEGIN_OP_DEFS(zpath_op_defs) {
+ {"0clip", zclip},
+ {"0closepath", zclosepath},
+ {"0.currentclipoutside", zcurrentclipoutside},
+ {"0currentpoint", zcurrentpoint},
+ {"6curveto", zcurveto},
+ {"0eoclip", zeoclip},
+ {"0initclip", zinitclip},
+ {"2lineto", zlineto},
+ {"2moveto", zmoveto},
+ {"0newpath", znewpath},
+ {"6rcurveto", zrcurveto},
+ {"2rlineto", zrlineto},
+ {"2rmoveto", zrmoveto},
+ {"1.setclipoutside", zsetclipoutside},
+END_OP_DEFS(0) }
diff --git a/pstoraster/zpath1.c b/pstoraster/zpath1.c
new file mode 100644
index 000000000..cdc3516c5
--- /dev/null
+++ b/pstoraster/zpath1.c
@@ -0,0 +1,244 @@
+/* Copyright (C) 1989, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* zpath1.c */
+/* PostScript Level 1 additional path operators */
+#include "memory_.h"
+#include "ghost.h"
+#include "oper.h"
+#include "errors.h"
+#include "estack.h" /* for pathforall */
+#include "ialloc.h"
+#include "igstate.h"
+#include "gsstruct.h"
+#include "gspath.h"
+#include "store.h"
+
+/* Forward references */
+private int near common_arc(P2(os_ptr,
+ int (*)(P6(gs_state *, floatp, floatp, floatp, floatp, floatp))));
+private int near common_arct(P2(os_ptr, float *));
+
+/* <x> <y> <r> <ang1> <ang2> arc - */
+int
+zarc(os_ptr op)
+{ return common_arc(op, gs_arc);
+}
+
+/* <x> <y> <r> <ang1> <ang2> arcn - */
+int
+zarcn(os_ptr op)
+{ return common_arc(op, gs_arcn);
+}
+
+/* Common code for arc[n] */
+private int near
+common_arc(os_ptr op,
+ int (*aproc)(P6(gs_state *, floatp, floatp, floatp, floatp, floatp)))
+{ float xyra[5]; /* x, y, r, ang1, ang2 */
+ int code;
+ if ( (code = num_params(op, 5, xyra)) < 0 ) return code;
+ code = (*aproc)(igs, xyra[0], xyra[1], xyra[2], xyra[3], xyra[4]);
+ if ( code >= 0 ) pop(5);
+ return code;
+}
+
+/* <x1> <y1> <x2> <y2> <r> arct - */
+int
+zarct(register os_ptr op)
+{ int code = common_arct(op, (float *)0);
+ if ( code < 0 ) return code;
+ pop(5);
+ return 0;
+}
+
+/* <x1> <y1> <x2> <y2> <r> arcto <xt1> <yt1> <xt2> <yt2> */
+private int
+zarcto(register os_ptr op)
+{ float tanxy[4]; /* xt1, yt1, xt2, yt2 */
+ int code = common_arct(op, tanxy);
+ if ( code < 0 ) return code;
+ make_real(op - 4, tanxy[0]);
+ make_real(op - 3, tanxy[1]);
+ make_real(op - 2, tanxy[2]);
+ make_real(op - 1, tanxy[3]);
+ pop(1);
+ return 0;
+}
+
+/* Common code for arct[o] */
+private int near
+common_arct(os_ptr op, float *tanxy)
+{ float args[5]; /* x1, y1, x2, y2, r */
+ int code;
+ if ( (code = num_params(op, 5, args)) < 0 ) return code;
+ return gs_arcto(igs, args[0], args[1], args[2], args[3], args[4], tanxy);
+}
+
+/* - flattenpath - */
+private int
+zflattenpath(register os_ptr op)
+{ return gs_flattenpath(igs);
+}
+
+/* - reversepath - */
+private int
+zreversepath(register os_ptr op)
+{ return gs_reversepath(igs);
+}
+
+/* - strokepath - */
+private int
+zstrokepath(register os_ptr op)
+{ return gs_strokepath(igs);
+}
+
+/* - clippath - */
+private int
+zclippath(register os_ptr op)
+{ return gs_clippath(igs);
+}
+
+/* <bool> .pathbbox <llx> <lly> <urx> <ury> */
+private int
+zpathbbox(register os_ptr op)
+{ gs_rect box;
+ int code;
+
+ check_type(*op, t_boolean);
+ code = gs_upathbbox(igs, &box, op->value.boolval);
+ if ( code < 0 )
+ return code;
+ push(3);
+ make_real(op - 3, box.p.x);
+ make_real(op - 2, box.p.y);
+ make_real(op - 1, box.q.x);
+ make_real(op, box.q.y);
+ return 0;
+}
+
+/* <moveproc> <lineproc> <curveproc> <closeproc> pathforall - */
+private int path_continue(P1(os_ptr));
+private int path_cleanup(P1(os_ptr));
+private int
+zpathforall(register os_ptr op)
+{ gs_path_enum *penum;
+ int code;
+
+ check_proc(op[-3]);
+ check_proc(op[-2]);
+ check_proc(op[-1]);
+ check_proc(*op);
+ check_estack(8);
+ if ( (penum = gs_path_enum_alloc(imemory, "pathforall")) == 0 )
+ return_error(e_VMerror);
+ code = gs_path_enum_init(penum, igs);
+ if ( code < 0 )
+ { ifree_object(penum, "path_cleanup");
+ return code;
+ }
+ /* Push a mark, the four procedures, and the path enumerator. */
+ push_mark_estack(es_for, path_cleanup); /* iterator */
+ memcpy(esp + 1, op - 3, 4 * sizeof(ref)); /* 4 procs */
+ esp += 5;
+ make_istruct(esp, 0, penum);
+ push_op_estack(path_continue);
+ pop(4); op -= 4;
+ return o_push_estack;
+}
+/* Continuation procedure for pathforall */
+private void pf_push(P3(gs_point *, int, os_ptr));
+private int
+path_continue(register os_ptr op)
+{ gs_path_enum *penum = r_ptr(esp, gs_path_enum);
+ gs_point ppts[3];
+ int code;
+ /* Make sure we have room on the o-stack for the worst case */
+ /* before we enumerate the next path element. */
+ check_ostack(6); /* 3 points for curveto */
+ code = gs_path_enum_next(penum, ppts);
+ switch ( code )
+ {
+ case 0: /* all done */
+ { esp -= 6;
+ path_cleanup(op);
+ return o_pop_estack;
+ }
+ default: /* error */
+ return code;
+ case gs_pe_moveto:
+ esp[2] = esp[-4]; /* moveto proc */
+ pf_push(ppts, 1, op);
+ break;
+ case gs_pe_lineto:
+ esp[2] = esp[-3]; /* lineto proc */
+ pf_push(ppts, 1, op);
+ break;
+ case gs_pe_curveto:
+ esp[2] = esp[-2]; /* curveto proc */
+ pf_push(ppts, 3, op);
+ break;
+ case gs_pe_closepath:
+ esp[2] = esp[-1]; /* closepath proc */
+ break;
+ }
+ push_op_estack(path_continue);
+ ++esp; /* include pushed procedure */
+ return o_push_estack;
+}
+/* Internal procedure to push one or more points */
+private void
+pf_push(gs_point *ppts, int n, os_ptr op)
+{ while ( n-- )
+ { op += 2;
+ make_real(op - 1, ppts->x);
+ make_real(op, ppts->y);
+ ppts++;
+ }
+ osp = op;
+}
+/* Clean up after a pathforall */
+private int
+path_cleanup(os_ptr op)
+{ gs_path_enum *penum = r_ptr(esp + 6, gs_path_enum);
+ gs_path_enum_cleanup(penum);
+ ifree_object(penum, "path_cleanup");
+ return 0;
+}
+
+/* ------ Initialization procedure ------ */
+
+BEGIN_OP_DEFS(zpath1_op_defs) {
+ {"5arc", zarc},
+ {"5arcn", zarcn},
+ {"5arct", zarct},
+ {"5arcto", zarcto},
+ {"0clippath", zclippath},
+ {"0flattenpath", zflattenpath},
+ {"4pathforall", zpathforall},
+ {"0reversepath", zreversepath},
+ {"0strokepath", zstrokepath},
+ {"0.pathbbox", zpathbbox},
+ /* Internal operators */
+ {"0%path_continue", path_continue},
+END_OP_DEFS(0) }
diff --git a/pstoraster/zpcolor.c b/pstoraster/zpcolor.c
new file mode 100644
index 000000000..e9e33a042
--- /dev/null
+++ b/pstoraster/zpcolor.c
@@ -0,0 +1,253 @@
+/* Copyright (C) 1994, 1995, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* zpcolor.c */
+/* Pattern color */
+#include "ghost.h"
+#include "errors.h"
+#include "oper.h"
+#include "gscolor.h"
+#include "gsmatrix.h"
+#include "gsstruct.h"
+#include "gxcspace.h"
+#include "gxfixed.h" /* for gxcolor2.h */
+#include "gxcolor2.h"
+#include "gxdcolor.h" /* for gxpcolor.h */
+#include "gxdevice.h"
+#include "gxdevmem.h" /* for gxpcolor.h */
+#include "gxpcolor.h"
+#include "estack.h"
+#include "ialloc.h"
+#include "istruct.h"
+#include "idict.h"
+#include "idparam.h"
+#include "igstate.h"
+#include "store.h"
+
+/* Imported from gspcolor.c */
+extern const gs_color_space_type gs_color_space_type_Pattern;
+
+/* Imported from zcolor2.c */
+extern gs_memory_type_ptr_t zcolor2_st_pattern_instance_p;
+
+/* Forward references */
+private int zPaintProc(P2(const gs_client_color *, gs_state *));
+private int pattern_paint_prepare(P1(os_ptr));
+private int pattern_paint_finish(P1(os_ptr));
+
+/*
+ * Define the structure for remembering the pattern dictionary.
+ * This is the "client data" in the template.
+ * See zgstate.c (int_gstate) or zfont2.c (font_data) for information
+ * as to why we define this as a structure rather than a ref array.
+ */
+typedef struct int_pattern_s {
+ ref dict;
+} int_pattern;
+gs_private_st_ref_struct(st_int_pattern, int_pattern, "int_pattern");
+
+/* Initialize the Pattern cache and the Pattern instance type. */
+private void
+zpcolor_init(void)
+{ gstate_set_pattern_cache(igs,
+ gx_pattern_alloc_cache(imemory_system,
+ gx_pat_cache_default_tiles(),
+ gx_pat_cache_default_bits()));
+ zcolor2_st_pattern_instance_p = &st_pattern_instance;
+}
+
+/* <pattern> <matrix> .buildpattern <pattern> <instance> */
+private int
+zbuildpattern(os_ptr op)
+{ os_ptr op1 = op - 1;
+ int code;
+ gs_matrix mat;
+ int PatternType;
+ float BBox[4];
+ gs_client_pattern template;
+ int_pattern *pdata;
+ gs_client_color cc_instance;
+ ref *pPaintProc;
+ check_type(*op1, t_dictionary);
+ check_dict_read(*op1);
+ if ( (code = read_matrix(op, &mat)) < 0 ||
+ (code = dict_uid_param(op1, &template.uid, 1, imemory)) != 1 ||
+ (code = dict_int_param(op1, "PatternType", 1, 1, 0, &PatternType)) < 0 ||
+ (code = dict_int_param(op1, "PaintType", 1, 2, 0, &template.PaintType)) < 0 ||
+ (code = dict_int_param(op1, "TilingType", 1, 3, 0, &template.TilingType)) < 0 ||
+ (code = dict_float_array_param(op1, "BBox", 4, BBox, NULL)) != 4 ||
+ (code = dict_float_param(op1, "XStep", 0.0, &template.XStep)) != 0 ||
+ (code = dict_float_param(op1, "YStep", 0.0, &template.YStep)) != 0 ||
+ (code = dict_find_string(op1, "PaintProc", &pPaintProc)) <= 0
+ )
+ return_error((code < 0 ? code : e_rangecheck));
+ check_proc(*pPaintProc);
+ template.BBox.p.x = BBox[0];
+ template.BBox.p.y = BBox[1];
+ template.BBox.q.x = BBox[2];
+ template.BBox.q.y = BBox[3];
+ template.PaintProc = zPaintProc;
+ pdata = ialloc_struct(int_pattern, &st_int_pattern, "int_pattern");
+ if ( pdata == 0 )
+ return_error(e_VMerror);
+ template.client_data = pdata;
+ pdata->dict = *op1;
+ code = gs_makepattern(&cc_instance, &template, &mat, igs, imemory);
+ if ( code < 0 )
+ { ifree_object(pdata, "int_pattern");
+ return code;
+ }
+ make_istruct(op, a_readonly, cc_instance.pattern);
+ return code;
+}
+
+/* <array> .setpatternspace - */
+/* In the case of uncolored patterns, the current color space is */
+/* the base space for the pattern space. */
+private int
+zsetpatternspace(register os_ptr op)
+{ gs_color_space cs;
+ uint edepth = ref_stack_count(&e_stack);
+ int code;
+
+ check_read_type(*op, t_array);
+ cs = *gs_currentcolorspace(igs);
+ if ( cs.type->num_components < 0 ) /* i.e., Pattern space */
+ return_error(e_rangecheck);
+ switch ( r_size(op) )
+ {
+ case 1: /* no base space */
+ cs.params.pattern.has_base_space = false;
+ break;
+ default:
+ return_error(e_rangecheck);
+ case 2:
+ /* We can't count on C compilers to recognize the aliasing */
+ /* that would be involved in a direct assignment, so.... */
+ { gs_paint_color_space cs_paint;
+ cs_paint = *(gs_paint_color_space *)&cs;
+ cs.params.pattern.base_space = cs_paint;
+ }
+ cs.params.pattern.has_base_space = true;
+ }
+ cs.type = &gs_color_space_type_Pattern;
+ code = gs_setcolorspace(igs, &cs);
+ if ( code < 0 )
+ { ref_stack_pop_to(&e_stack, edepth);
+ return code;
+ }
+ pop(1);
+ return (ref_stack_count(&e_stack) == edepth ? 0 : o_push_estack); /* installation will load the caches */
+}
+
+/* ------ Initialization procedure ------ */
+
+BEGIN_OP_DEFS(zpcolor_l2_op_defs) {
+ op_def_begin_level2(),
+ {"2.buildpattern", zbuildpattern},
+ {"1.setpatternspace", zsetpatternspace},
+ /* Internal operators */
+ {"0%pattern_paint_prepare", pattern_paint_prepare},
+ {"0%pattern_paint_finish", pattern_paint_finish},
+END_OP_DEFS(zpcolor_init) }
+
+/* ------ Internal procedures ------ */
+
+/* Set up the pattern pointer in a client color for setcolor */
+/* with a Pattern space. */
+
+
+/* Render the pattern by calling the PaintProc. */
+private int pattern_paint_cleanup(P1(os_ptr));
+private int
+zPaintProc(const gs_client_color *pcc, gs_state *pgs)
+{ /* Just schedule a call on the real PaintProc. */
+ check_estack(2);
+ esp++;
+ push_op_estack(pattern_paint_prepare);
+ return e_InsertProc;
+}
+/* Prepare to run the PaintProc. */
+private int
+pattern_paint_prepare(os_ptr op)
+{ gs_state *pgs = igs;
+ gs_pattern_instance *pinst = gs_currentcolor(pgs)->pattern;
+ ref *pdict = &((int_pattern *)pinst->template.client_data)->dict;
+ gx_device_pattern_accum *pdev;
+ int code;
+ ref *ppp;
+ check_estack(5);
+ pdev = gx_pattern_accum_alloc(imemory, "pattern_paint_prepare");
+ if ( pdev == 0 )
+ return_error(e_VMerror);
+ pdev->instance = pinst;
+ pdev->bitmap_memory = gstate_pattern_cache(pgs)->memory;
+ code = (*dev_proc(pdev, open_device))((gx_device *)pdev);
+ if ( code < 0 )
+ { ifree_object(pdev, "pattern_paint_prepare");
+ return code;
+ }
+ code = gs_gsave(pgs);
+ if ( code < 0 )
+ return code;
+ code = gs_setgstate(pgs, pinst->saved);
+ if ( code < 0 )
+ { gs_grestore(pgs);
+ return code;
+ }
+ gx_set_device_only(pgs, (gx_device *)pdev);
+ push_mark_estack(es_other, pattern_paint_cleanup);
+ ++esp;
+ make_istruct(esp, 0, pdev);
+ push_op_estack(pattern_paint_finish);
+ dict_find_string(pdict, "PaintProc", &ppp); /* can't fail */
+ *++esp = *ppp;
+ *++esp = *pdict; /* (push on ostack) */
+ return o_push_estack;
+}
+/* Save the rendered pattern. */
+private int
+pattern_paint_finish(os_ptr op)
+{ gx_device_pattern_accum *pdev = r_ptr(esp, gx_device_pattern_accum);
+ gx_color_tile *ctile;
+ int code = gx_pattern_cache_add_entry(igs, pdev, &ctile);
+ if ( code < 0 )
+ return code;
+ if ( ctile->tbits.data != 0 )
+ pdev->bits->bitmap_memory = 0; /* don't free the bits */
+ if ( ctile->tmask.data != 0 )
+ pdev->mask->bitmap_memory = 0; /* ditto */
+ esp -= 2;
+ pattern_paint_cleanup(op);
+ return o_pop_estack;
+}
+/* Clean up after rendering a pattern. Note that iff the rendering */
+/* succeeded, closing the accumulator won't free the bits. */
+private int
+pattern_paint_cleanup(os_ptr op)
+{ gx_device_pattern_accum *pdev = r_ptr(esp + 2, gx_device_pattern_accum);
+ gs_grestore(igs);
+ (*dev_proc(pdev, close_device))((gx_device *)pdev);
+ ifree_object(pdev, "pattern_paint_cleanup");
+ return 0;
+}
diff --git a/pstoraster/zrelbit.c b/pstoraster/zrelbit.c
new file mode 100644
index 000000000..fd78f62a8
--- /dev/null
+++ b/pstoraster/zrelbit.c
@@ -0,0 +1,314 @@
+/* Copyright (C) 1989, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* zrelbit.c */
+/* Relational, boolean, and bit operators */
+#include "ghost.h"
+#include "errors.h"
+#include "oper.h"
+#include "gsutil.h"
+#include "idict.h"
+#include "store.h"
+
+/* ------ Standard operators ------ */
+
+/* Forward references */
+private int near obj_le(P2(os_ptr, os_ptr));
+
+/* <obj1> <obj2> eq <bool> */
+private int
+zeq(register os_ptr op)
+{ register os_ptr op1 = op - 1;
+#define eq_check_read(opp, dflt)\
+ switch ( r_type(opp) )\
+ { case t_string: check_read(*opp); break;\
+ default: dflt; break;\
+ }
+ eq_check_read(op1, check_op(2));
+ eq_check_read(op, DO_NOTHING);
+ make_bool(op1, (obj_eq(op1, op) ? 1 : 0));
+ pop(1);
+ return 0;
+}
+
+/* <obj1> <obj2> ne <bool> */
+private int
+zne(register os_ptr op)
+{ /* We'll just be lazy and use eq. */
+ int code = zeq(op);
+ if ( !code ) op[-1].value.boolval ^= 1;
+ return code;
+}
+
+/* <num1> <num2> ge <bool> */
+/* <str1> <str2> ge <bool> */
+private int
+zge(register os_ptr op)
+{ int code = obj_le(op, op - 1);
+ if ( code < 0 ) return code;
+ make_bool(op - 1, code);
+ pop(1);
+ return 0;
+}
+
+/* <num1> <num2> gt <bool> */
+/* <str1> <str2> gt <bool> */
+private int
+zgt(register os_ptr op)
+{ int code = obj_le(op - 1, op);
+ if ( code < 0 ) return code;
+ make_bool(op - 1, code ^ 1);
+ pop(1);
+ return 0;
+}
+
+/* <num1> <num2> le <bool> */
+/* <str1> <str2> le <bool> */
+private int
+zle(register os_ptr op)
+{ int code = obj_le(op - 1, op);
+ if ( code < 0 ) return code;
+ make_bool(op - 1, code);
+ pop(1);
+ return 0;
+}
+
+/* <num1> <num2> lt <bool> */
+/* <str1> <str2> lt <bool> */
+private int
+zlt(register os_ptr op)
+{ int code = obj_le(op, op - 1);
+ if ( code < 0 ) return code;
+ make_bool(op - 1, code ^ 1);
+ pop(1);
+ return 0;
+}
+
+/* <num1> <num2> .max <num> */
+/* <str1> <str2> .max <str> */
+private int
+zmax(register os_ptr op)
+{ int code = obj_le(op - 1, op);
+ if ( code < 0 ) return code;
+ if ( code )
+ { ref_assign(op - 1, op);
+ }
+ pop(1);
+ return 0;
+}
+
+/* <num1> <num2> .min <num> */
+/* <str1> <str2> .min <str> */
+private int
+zmin(register os_ptr op)
+{ int code = obj_le(op - 1, op);
+ if ( code < 0 ) return code;
+ if ( !code )
+ { ref_assign(op - 1, op);
+ }
+ pop(1);
+ return 0;
+}
+
+/* <bool1> <bool2> and <bool> */
+/* <int1> <int2> and <int> */
+private int
+zand(register os_ptr op)
+{ switch ( r_type(op) )
+ {
+ case t_boolean:
+ check_type(op[-1], t_boolean);
+ op[-1].value.boolval &= op->value.boolval;
+ break;
+ case t_integer:
+ check_type(op[-1], t_integer);
+ op[-1].value.intval &= op->value.intval;
+ break;
+ default:
+ return_op_typecheck(op);
+ }
+ pop(1);
+ return 0;
+}
+
+/* <bool> not <bool> */
+/* <int> not <int> */
+private int
+znot(register os_ptr op)
+{ switch ( r_type(op) )
+ {
+ case t_boolean:
+ op->value.boolval = !op->value.boolval;
+ break;
+ case t_integer:
+ op->value.intval = ~op->value.intval;
+ break;
+ default:
+ return_op_typecheck(op);
+ }
+ return 0;
+}
+
+/* <bool1> <bool2> or <bool> */
+/* <int1> <int2> or <int> */
+private int
+zor(register os_ptr op)
+{ switch ( r_type(op) )
+ {
+ case t_boolean:
+ check_type(op[-1], t_boolean);
+ op[-1].value.boolval |= op->value.boolval;
+ break;
+ case t_integer:
+ check_type(op[-1], t_integer);
+ op[-1].value.intval |= op->value.intval;
+ break;
+ default:
+ return_op_typecheck(op);
+ }
+ pop(1);
+ return 0;
+}
+
+/* <bool1> <bool2> xor <bool> */
+/* <int1> <int2> xor <int> */
+private int
+zxor(register os_ptr op)
+{ switch ( r_type(op) )
+ {
+ case t_boolean:
+ check_type(op[-1], t_boolean);
+ op[-1].value.boolval ^= op->value.boolval;
+ break;
+ case t_integer:
+ check_type(op[-1], t_integer);
+ op[-1].value.intval ^= op->value.intval;
+ break;
+ default:
+ return_op_typecheck(op);
+ }
+ pop(1);
+ return 0;
+}
+
+/* <int> <shift> bitshift <int> */
+private int
+zbitshift(register os_ptr op)
+{ int shift;
+ check_type(*op, t_integer);
+ check_type(op[-1], t_integer);
+#define max_shift (arch_sizeof_long * 8 - 1)
+ if ( op->value.intval < -max_shift || op->value.intval > max_shift )
+ op[-1].value.intval = 0;
+#undef max_shift
+ else if ( (shift = op->value.intval) < 0 )
+ op[-1].value.intval = ((ulong)(op[-1].value.intval)) >> -shift;
+ else
+ op[-1].value.intval <<= shift;
+ pop(1);
+ return 0;
+}
+
+/* ------ Extensions ------ */
+
+/* <obj1> <obj2> .identeq <bool> */
+private int
+zidenteq(register os_ptr op)
+{ register os_ptr op1 = op - 1;
+ eq_check_read(op1, check_op(2));
+ eq_check_read(op, DO_NOTHING);
+ make_bool(op1, (obj_ident_eq(op1, op) ? 1 : 0));
+ pop(1);
+ return 0;
+
+}
+
+/* <obj1> <obj2> .identne <bool> */
+private int
+zidentne(register os_ptr op)
+{ /* We'll just be lazy and use .identeq. */
+ int code = zidenteq(op);
+ if ( !code ) op[-1].value.boolval ^= 1;
+ return code;
+}
+
+/* ------ Initialization procedure ------ */
+
+BEGIN_OP_DEFS(zrelbit_op_defs) {
+ {"2and", zand},
+ {"2bitshift", zbitshift},
+ {"2eq", zeq},
+ {"2ge", zge},
+ {"2gt", zgt},
+ {"2le", zle},
+ {"2lt", zlt},
+ {"2.max", zmax},
+ {"2.min", zmin},
+ {"2ne", zne},
+ {"1not", znot},
+ {"2or", zor},
+ {"2xor", zxor},
+ /* Extensions */
+ {"2.identeq", zidenteq},
+ {"2.identne", zidentne},
+END_OP_DEFS(0) }
+
+/* ------ Internal routines ------ */
+
+/* Compare two operands (both numeric, or both strings). */
+/* Return 1 if op[-1] <= op[0], 0 if op[-1] > op[0], */
+/* or a (negative) error code. */
+#define bcval(v1, rel, v2) (op1->value.v1 rel op->value.v2 ? 1 : 0)
+private int near
+obj_le(register os_ptr op1, register os_ptr op)
+{ switch ( r_type(op1) )
+ {
+ case t_integer:
+ switch ( r_type(op) )
+ {
+ case t_integer:
+ return bcval(intval, <=, intval);
+ case t_real:
+ return bcval(intval, <=, realval);
+ default:
+ return_op_typecheck(op);
+ }
+ case t_real:
+ switch ( r_type(op) )
+ {
+ case t_real:
+ return bcval(realval, <=, realval);
+ case t_integer:
+ return bcval(realval, <=, intval);
+ default:
+ return_op_typecheck(op);
+ }
+ case t_string:
+ check_read(*op1);
+ check_read_type(*op, t_string);
+ return (bytes_compare(op1->value.bytes, r_size(op1),
+ op->value.bytes, r_size(op)) <= 0 ? 1 : 0);
+ default:
+ return_op_typecheck(op1);
+ }
+}
diff --git a/pstoraster/zstack.c b/pstoraster/zstack.c
new file mode 100644
index 000000000..87499de24
--- /dev/null
+++ b/pstoraster/zstack.c
@@ -0,0 +1,267 @@
+/* Copyright (C) 1989, 1991, 1992, 1994 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* zstack.c */
+/* Operand stack operators */
+#include "memory_.h"
+#include "ghost.h"
+#include "errors.h"
+#include "ialloc.h"
+#include "istack.h"
+#include "oper.h"
+#include "store.h"
+
+/* <obj> pop - */
+int
+zpop(register os_ptr op)
+{ check_op(1);
+ pop(1);
+ return 0;
+}
+
+/* <obj1> <obj2> exch <obj2> <obj1> */
+int
+zexch(register os_ptr op)
+{ ref next;
+ check_op(2);
+ ref_assign_inline(&next, op - 1);
+ ref_assign_inline(op - 1, op);
+ ref_assign_inline(op, &next);
+ return 0;
+}
+
+/* <obj> dup <obj> <obj> */
+int
+zdup(register os_ptr op)
+{ check_op(1);
+ push(1);
+ ref_assign_inline(op, op - 1);
+ return 0;
+}
+
+/* <obj_n> ... <obj_0> <n> index <obj_n> ... <obj_0> <obj_n> */
+int
+zindex(register os_ptr op)
+{ register os_ptr opn;
+ check_type(*op, t_integer);
+ if ( (ulong)op->value.intval >= op - osbot )
+ { /* Might be in an older stack block. */
+ ref *elt;
+ if ( op->value.intval < 0 )
+ return_error(e_rangecheck);
+ elt = ref_stack_index(&o_stack, op->value.intval + 1);
+ if ( elt == 0 )
+ return_error(e_rangecheck);
+ ref_assign(op, elt);
+ return 0;
+ }
+ opn = op + ~(int)op->value.intval;
+ ref_assign_inline(op, opn);
+ return 0;
+}
+
+/* <obj_n-1> ... <obj_0> <n> <i> roll */
+/* <obj_(i-1)_mod_ n> ... <obj_0> <obj_n-1> ... <obj_i_mod_n> */
+int
+zroll(register os_ptr op)
+{ os_ptr op1 = op - 1;
+ int count, mod;
+ register os_ptr from, to;
+ register int n;
+ check_type(*op1, t_integer);
+ check_type(*op, t_integer);
+ if ( (ulong)op1->value.intval > op1 - osbot )
+ { /*
+ * The data might span multiple stack blocks.
+ * There are efficient ways to handle this situation,
+ * but they're more complicated than seems worth implementing;
+ * for now, do something very simple and inefficient.
+ */
+ int left, i;
+ if ( op1->value.intval < 0 ||
+ op1->value.intval + 2 > ref_stack_count(&o_stack)
+ )
+ return_error(e_rangecheck);
+ count = op1->value.intval;
+ if ( count <= 1 )
+ { pop(2);
+ return 0;
+ }
+ mod = op->value.intval;
+ if ( mod >= count )
+ mod %= count;
+ else if ( mod < 0 )
+ { mod %= count;
+ if ( mod < 0 ) mod += count; /* can't assume % means mod! */
+ }
+ /* Use the chain rotation algorithm mentioned below. */
+ for ( i = 0, left = count; left; i++ )
+ { ref *elt = ref_stack_index(&o_stack, i + 2);
+ ref save;
+ int j, k;
+ ref *next;
+ save = *elt;
+ for ( j = i, left--; ; j = k, elt = next, left-- )
+ { k = (j + mod) % count;
+ if ( k == i )
+ break;
+ next = ref_stack_index(&o_stack, k + 2);
+ ref_assign(elt, next);
+ }
+ *elt = save;
+ }
+ pop(2);
+ return 0;
+ }
+ count = op1->value.intval;
+ if ( count <= 1 )
+ { pop(2);
+ return 0;
+ }
+ mod = op->value.intval;
+ /*
+ * The elegant approach, requiring no extra space, would be to
+ * rotate the elements in chains separated by mod elements.
+ * Instead, we simply check to make sure there is enough space
+ * above op to do the roll in two block moves.
+ * Unfortunately, we can't count on memcpy doing the right thing
+ * in *either* direction.
+ */
+ switch ( mod )
+ {
+ case 1: /* common special case */
+ pop(2); op -= 2;
+ { ref top;
+ ref_assign_inline(&top, op);
+ for ( from = op, n = count; --n; from-- )
+ ref_assign_inline(from, from - 1);
+ ref_assign_inline(from, &top);
+ } return 0;
+ case -1: /* common special case */
+ pop(2); op -= 2;
+ { ref bot;
+ to = op - count + 1;
+ ref_assign_inline(&bot, to);
+ for ( n = count; --n; to++ )
+ ref_assign(to, to + 1);
+ ref_assign_inline(to, &bot);
+ } return 0;
+ }
+ if ( mod < 0 )
+ { mod += count;
+ if ( mod < 0 )
+ { mod %= count;
+ if ( mod < 0 )
+ mod += count; /* can't assume % means mod! */
+ }
+ }
+ else if ( mod >= count )
+ mod %= count;
+ if ( mod <= count >> 1)
+ { /* Move everything up, then top elements down. */
+ if ( mod >= ostop - op )
+ { o_stack.requested = mod;
+ return_error(e_stackoverflow);
+ }
+ pop(2); op -= 2;
+ for ( to = op + mod, from = op, n = count; n--; to--, from-- )
+ ref_assign(to, from);
+ memcpy((char *)(from + 1), (char *)(op + 1), mod * sizeof(ref));
+ }
+ else
+ { /* Move bottom elements up, then everything down. */
+ mod = count - mod;
+ if ( mod >= ostop - op )
+ { o_stack.requested = mod;
+ return_error(e_stackoverflow);
+ }
+ pop(2); op -= 2;
+ to = op - count + 1;
+ memcpy((char *)(op + 1), (char *)to, mod * sizeof(ref));
+ for ( from = to + mod, n = count; n--; to++, from++ )
+ ref_assign(to, from);
+ }
+ return 0;
+}
+
+/* |- ... clear |- */
+/* The function name is changed, because the IRIS library has */
+/* a function called zclear. */
+private int
+zclear_stack(os_ptr op)
+{ ref_stack_clear(&o_stack);
+ return 0;
+}
+
+/* |- <obj_n-1> ... <obj_0> count <obj_n-1> ... <obj_0> <n> */
+private int
+zcount(register os_ptr op)
+{ push(1);
+ make_int(op, ref_stack_count(&o_stack) - 1);
+ return 0;
+}
+
+/* - mark <mark> */
+private int
+zmark(register os_ptr op)
+{ push(1);
+ make_mark(op);
+ return 0;
+}
+
+/* <mark> ... cleartomark */
+int
+zcleartomark(register os_ptr op)
+{ uint count = ref_stack_counttomark(&o_stack);
+ if ( count == 0 )
+ return_error(e_unmatchedmark);
+ ref_stack_pop(&o_stack, count);
+ return 0;
+}
+
+/* <mark> <obj_n-1> ... <obj_0> counttomark */
+/* <mark> <obj_n-1> ... <obj_0> <n> */
+private int
+zcounttomark(os_ptr op)
+{ uint count = ref_stack_counttomark(&o_stack);
+ if ( count == 0 )
+ return_error(e_unmatchedmark);
+ push(1);
+ make_int(op, count - 1);
+ return 0;
+}
+
+/* ------ Initialization procedure ------ */
+
+BEGIN_OP_DEFS(zstack_op_defs) {
+ {"0clear", zclear_stack},
+ {"0cleartomark", zcleartomark},
+ {"0count", zcount},
+ {"0counttomark", zcounttomark},
+ {"1dup", zdup},
+ {"2exch", zexch},
+ {"2index", zindex},
+ {"0mark", zmark},
+ {"1pop", zpop},
+ {"2roll", zroll},
+END_OP_DEFS(0) }
diff --git a/pstoraster/zstring.c b/pstoraster/zstring.c
new file mode 100644
index 000000000..8ca0439e2
--- /dev/null
+++ b/pstoraster/zstring.c
@@ -0,0 +1,161 @@
+/* Copyright (C) 1989, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* zstring.c */
+/* String operators */
+#include "memory_.h"
+#include "ghost.h"
+#include "errors.h"
+#include "gsutil.h"
+#include "ialloc.h"
+#include "iname.h"
+#include "ivmspace.h"
+#include "oper.h"
+#include "store.h"
+
+/* The generic operators (copy, get, put, getinterval, putinterval, */
+/* length, and forall) are implemented in zgeneric.c. */
+
+/* <int> string <string> */
+int
+zstring(register os_ptr op)
+{ byte *sbody;
+ uint size;
+ check_int_leu(*op, max_string_size);
+ size = op->value.intval;
+ sbody = ialloc_string(size, "string");
+ if ( sbody == 0 )
+ return_error(e_VMerror);
+ make_string(op, a_all | icurrent_space, size, sbody);
+ memset(sbody, 0, size);
+ return 0;
+}
+
+/* <name> .namestring <string> */
+private int
+znamestring(register os_ptr op)
+{ check_type(*op, t_name);
+ name_string_ref(op, op);
+ return 0;
+}
+
+/* <string> <pattern> anchorsearch <post> <match> -true- */
+/* <string> <pattern> anchorsearch <string> -false- */
+private int
+zanchorsearch(register os_ptr op)
+{ os_ptr op1 = op - 1;
+ uint size = r_size(op);
+ check_read_type(*op1, t_string);
+ check_read_type(*op, t_string);
+ if ( size <= r_size(op1) && !memcmp(op1->value.bytes, op->value.bytes, size) )
+ { os_ptr op0 = op;
+ push(1);
+ *op0 = *op1;
+ r_set_size(op0, size);
+ op1->value.bytes += size;
+ r_dec_size(op1, size);
+ make_true(op);
+ }
+ else
+ make_false(op);
+ return 0;
+}
+
+/* <string> <pattern> search <post> <match> <pre> -true- */
+/* <string> <pattern> search <string> -false- */
+private int
+zsearch(register os_ptr op)
+{ os_ptr op1 = op - 1;
+ uint size = r_size(op);
+ uint count;
+ byte *pat;
+ byte *ptr;
+ byte ch;
+
+ check_read_type(*op1, t_string);
+ check_read_type(*op, t_string);
+ if ( size > r_size(op1) ) /* can't match */
+ { make_false(op);
+ return 0;
+ }
+ count = r_size(op1) - size;
+ ptr = op1->value.bytes;
+ if ( size == 0 )
+ goto found;
+ pat = op->value.bytes;
+ ch = pat[0];
+ do
+ { if ( *ptr == ch && (size == 1 || !memcmp(ptr, pat, size)) )
+ goto found;
+ ptr++;
+ }
+ while ( count-- );
+ /* No match */
+ make_false(op);
+ return 0;
+found: op->tas.type_attrs = op1->tas.type_attrs;
+ op->value.bytes = ptr;
+ r_set_size(op, size);
+ push(2);
+ op[-1] = *op1;
+ r_set_size(op - 1, ptr - op[-1].value.bytes);
+ op1->value.bytes = ptr + size;
+ r_set_size(op1, count);
+ make_true(op);
+ return 0;
+}
+
+/* <obj> <pattern> .stringmatch <bool> */
+private int
+zstringmatch(register os_ptr op)
+{ os_ptr op1 = op - 1;
+ bool result;
+ check_read_type(*op, t_string);
+ switch ( r_type(op1) )
+ {
+ case t_string:
+ check_read(*op1);
+ goto cmp;
+ case t_name:
+ name_string_ref(op1, op1); /* can't fail */
+cmp: result = string_match(op1->value.const_bytes, r_size(op1),
+ op->value.const_bytes, r_size(op),
+ NULL);
+ break;
+ default:
+ result = (r_size(op) == 1 && *op->value.bytes == '*');
+ }
+ make_bool(op1, result);
+ pop(1);
+ return 0;
+}
+
+/* ------ Initialization procedure ------ */
+
+BEGIN_OP_DEFS(zstring_op_defs) {
+ {"2anchorsearch", zanchorsearch},
+ {"1.namestring", znamestring},
+ {"2search", zsearch},
+ {"1string", zstring},
+ {"2.stringmatch", zstringmatch},
+END_OP_DEFS(0) }
diff --git a/pstoraster/zsysvm.c b/pstoraster/zsysvm.c
new file mode 100644
index 000000000..7b0ef4463
--- /dev/null
+++ b/pstoraster/zsysvm.c
@@ -0,0 +1,146 @@
+/* Copyright (C) 1994, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* zsysvm.c */
+/* System VM and VM-specific operators */
+#include "ghost.h"
+#include "oper.h"
+#include "ialloc.h"
+#include "ivmspace.h"
+#include "store.h" /* for make_bool */
+
+/*
+ * These operators allow creation of objects in a specific VM --
+ * local, global, or system. System VM, which is not a standard PostScript
+ * facility, is not subject to save and restore; objects in system VM
+ * may only refer to simple objects or to other (composite) objects
+ * in system VM.
+ */
+
+/* Execute an operator with a specific VM selected as current VM. */
+private int
+specific_vm_op(os_ptr op, int (*opproc)(P1(os_ptr)), uint space)
+{ uint save_space = icurrent_space;
+ int code;
+ ialloc_set_space(idmemory, space);
+ code = (*opproc)(op);
+ ialloc_set_space(idmemory, save_space);
+ return code;
+}
+
+/* <int> .globalvmarray <array> */
+private int
+zglobalvmarray(os_ptr op)
+{ return specific_vm_op(op, zarray, avm_global);
+}
+
+/* <int> .globalvmdict <dict> */
+private int
+zglobalvmdict(os_ptr op)
+{ return specific_vm_op(op, zdict, avm_global);
+}
+
+/* <obj_0> ... <obj_n-1> <n> .globalvmpackedarray <packedarray> */
+private int
+zglobalvmpackedarray(os_ptr op)
+{ return specific_vm_op(op, zpackedarray, avm_global);
+}
+
+/* <int> .globalvmstring <string> */
+private int
+zglobalvmstring(os_ptr op)
+{ return specific_vm_op(op, zstring, avm_global);
+}
+
+/* <int> .localvmarray <array> */
+private int
+zlocalvmarray(os_ptr op)
+{ return specific_vm_op(op, zarray, avm_local);
+}
+
+/* <int> .localvmdict <dict> */
+private int
+zlocalvmdict(os_ptr op)
+{ return specific_vm_op(op, zdict, avm_local);
+}
+
+/* <obj_0> ... <obj_n-1> <n> .localvmpackedarray <packedarray> */
+private int
+zlocalvmpackedarray(os_ptr op)
+{ return specific_vm_op(op, zpackedarray, avm_local);
+}
+
+/* <int> .localvmstring <string> */
+private int
+zlocalvmstring(os_ptr op)
+{ return specific_vm_op(op, zstring, avm_local);
+}
+
+/* <int> .systemvmarray <array> */
+private int
+zsystemvmarray(os_ptr op)
+{ return specific_vm_op(op, zarray, avm_system);
+}
+
+/* <int> .systemvmdict <dict> */
+private int
+zsystemvmdict(os_ptr op)
+{ return specific_vm_op(op, zdict, avm_system);
+}
+
+/* <obj_0> ... <obj_n-1> <n> .systemvmpackedarray <packedarray> */
+private int
+zsystemvmpackedarray(os_ptr op)
+{ return specific_vm_op(op, zpackedarray, avm_system);
+}
+
+/* <int> .systemvmstring <string> */
+private int
+zsystemvmstring(os_ptr op)
+{ return specific_vm_op(op, zstring, avm_system);
+}
+
+/* <any> .systemvmcheck <bool> */
+private int
+zsystemvmcheck(register os_ptr op)
+{ make_bool(op, (r_space(op) == avm_system ? true : false));
+ return 0;
+}
+
+/* ------ Initialization procedure ------ */
+
+BEGIN_OP_DEFS(zsysvm_op_defs) {
+ {"1.globalvmarray", zglobalvmarray},
+ {"1.globalvmdict", zglobalvmdict},
+ {"1.globalvmpackedarray", zglobalvmpackedarray},
+ {"1.globalvmstring", zglobalvmstring},
+ {"1.localvmarray", zlocalvmarray},
+ {"1.localvmdict", zlocalvmdict},
+ {"1.localvmpackedarray", zlocalvmpackedarray},
+ {"1.localvmstring", zlocalvmstring},
+ {"1.systemvmarray", zsystemvmarray},
+ {"1.systemvmcheck", zsystemvmcheck},
+ {"1.systemvmdict", zsystemvmdict},
+ {"1.systemvmpackedarray", zsystemvmpackedarray},
+ {"1.systemvmstring", zsystemvmstring},
+END_OP_DEFS(0) }
diff --git a/pstoraster/ztoken.c b/pstoraster/ztoken.c
new file mode 100644
index 000000000..d69f0ffd9
--- /dev/null
+++ b/pstoraster/ztoken.c
@@ -0,0 +1,232 @@
+/* Copyright (C) 1994 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* ztoken.c */
+/* Token reading operators */
+#include "ghost.h"
+#include "errors.h"
+#include "oper.h"
+#include "estack.h"
+#include "gsstruct.h" /* for iscan.h */
+#include "stream.h"
+#include "files.h"
+#include "store.h"
+#include "strimpl.h" /* for sfilter.h */
+#include "sfilter.h" /* for iscan.h */
+#include "iscan.h"
+
+/* <file> token <obj> -true- */
+/* <string> token <post> <obj> -true- */
+/* <string|file> token -false- */
+private int ztoken_continue(P1(os_ptr));
+private int token_continue(P4(os_ptr, stream *, scanner_state *, bool));
+int
+ztoken(register os_ptr op)
+{ switch ( r_type(op) )
+ {
+ default:
+ return_op_typecheck(op);
+ case t_file:
+ { stream *s;
+ scanner_state state;
+ check_read_file(s, op);
+ check_ostack(1);
+ scanner_state_init(&state, false);
+ return token_continue(op, s, &state, true);
+ }
+ case t_string:
+ { ref token;
+ int code = scan_string_token(op, &token);
+ switch ( code )
+ {
+ case scan_EOF: /* no tokens */
+ make_false(op);
+ return 0;
+ default:
+ if ( code < 0 )
+ return code;
+ }
+ push(2);
+ op[-1] = token;
+ make_true(op);
+ return 0;
+ }
+ }
+}
+/* Continue reading a token after an interrupt or callout. */
+/* *op is the scanner state; op[-1] is the file. */
+private int
+ztoken_continue(os_ptr op)
+{ stream *s;
+ scanner_state *pstate;
+ check_read_file(s, op - 1);
+ check_stype(*op, st_scanner_state);
+ pstate = r_ptr(op, scanner_state);
+ pop(1);
+ return token_continue(osp, s, pstate, false);
+}
+/* Common code for token reading. */
+private int
+token_continue(os_ptr op, stream *s, scanner_state *pstate, bool save)
+{ int code;
+ ref token;
+ /* Note that scan_token may change osp! */
+ /* Also, we must temporarily remove the file from the o-stack */
+ /* when calling scan_token, in case we are scanning a procedure. */
+ ref fref;
+ ref_assign(&fref, op);
+again: pop(1);
+ code = scan_token(s, &token, pstate);
+ op = osp;
+ switch ( code )
+ {
+ default: /* error */
+ push(1);
+ ref_assign(op, &fref);
+ break;
+ case scan_BOS:
+ code = 0;
+ case 0: /* read a token */
+ push(2);
+ ref_assign(op - 1, &token);
+ make_true(op);
+ break;
+ case scan_EOF: /* no tokens */
+ push(1);
+ make_false(op);
+ code = 0;
+ break;
+ case scan_Refill: /* need more data */
+ push(1);
+ ref_assign(op, &fref);
+ code = scan_handle_refill(op, pstate, save, false,
+ ztoken_continue);
+ switch ( code )
+ {
+ case 0: /* state is not copied to the heap */
+ goto again;
+ case o_push_estack:
+ return code;
+ }
+ break; /* error */
+ }
+ if ( code <= 0 && !save )
+ { /* Deallocate the scanner state record. */
+ ifree_object(pstate, "token_continue");
+ }
+ return code;
+}
+
+/* <file> .tokenexec - */
+/* Read a token and do what the interpreter would do with it. */
+/* This is different from token + exec because literal procedures */
+/* are not executed (although binary object sequences ARE executed). */
+int ztokenexec_continue(P1(os_ptr)); /* export for interpreter */
+private int tokenexec_continue(P4(os_ptr, stream *, scanner_state *, bool));
+int
+ztokenexec(register os_ptr op)
+{ stream *s;
+ scanner_state state;
+ check_read_file(s, op);
+ check_estack(1);
+ scanner_state_init(&state, false);
+ return tokenexec_continue(op, s, &state, true);
+}
+/* Continue reading a token for execution after an interrupt or callout. */
+/* *op is the scanner state; op[-1] is the file. */
+/* We export this because this is how the interpreter handles a */
+/* scan_Refill for an executable file. */
+int
+ztokenexec_continue(os_ptr op)
+{ stream *s;
+ scanner_state *pstate;
+ check_read_file(s, op - 1);
+ check_stype(*op, st_scanner_state);
+ pstate = r_ptr(op, scanner_state);
+ pop(1);
+ return tokenexec_continue(osp, s, pstate, false);
+}
+/* Common code for token reading + execution. */
+private int
+tokenexec_continue(os_ptr op, stream *s, scanner_state *pstate, bool save)
+{ int code;
+ /* Note that scan_token may change osp! */
+ /* Also, we must temporarily remove the file from the o-stack */
+ /* when calling scan_token, in case we are scanning a procedure. */
+ ref fref;
+ ref_assign(&fref, op);
+ pop(1);
+again: code = scan_token(s, (ref *)(esp + 1), pstate);
+ op = osp;
+ switch ( code )
+ {
+ case 0:
+ if ( r_is_proc(esp + 1) )
+ { /* Treat procedure as a literal. */
+ push(1);
+ ref_assign(op, esp + 1);
+ code = 0;
+ break;
+ }
+ /* falls through */
+ case scan_BOS:
+ ++esp;
+ code = o_push_estack;
+ break;
+ case scan_EOF: /* no tokens */
+ code = 0;
+ break;
+ case scan_Refill: /* need more data */
+ code = scan_handle_refill(&fref, pstate, save, true,
+ ztokenexec_continue);
+ switch ( code )
+ {
+ case 0: /* state is not copied to the heap */
+ goto again;
+ case o_push_estack:
+ return code;
+ }
+ /* falls through */
+ default: /* error */
+ break;
+ }
+ if ( code < 0 )
+ { /* Push the operand back on the stack. */
+ push(1);
+ ref_assign(op, &fref);
+ }
+ if ( !save )
+ { /* Deallocate the scanner state record. */
+ ifree_object(pstate, "token_continue");
+ }
+ return code;
+}
+
+/* ------ Initialization procedure ------ */
+
+BEGIN_OP_DEFS(ztoken_op_defs) {
+ {"1token", ztoken},
+ {"1.tokenexec", ztokenexec},
+ /* Internal operators */
+ {"2%ztokenexec_continue", ztokenexec_continue},
+END_OP_DEFS(0) }
diff --git a/pstoraster/ztype.c b/pstoraster/ztype.c
new file mode 100644
index 000000000..8850521fa
--- /dev/null
+++ b/pstoraster/ztype.c
@@ -0,0 +1,459 @@
+/* Copyright (C) 1989, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* ztype.c */
+/* Type, attribute, and conversion operators */
+#include "math_.h"
+#include "memory_.h"
+#include "string_.h"
+#include "ghost.h"
+#include "errors.h"
+#include "oper.h"
+#include "imemory.h" /* for register_ref_root */
+#include "idict.h"
+#include "iname.h"
+#include "stream.h" /* for iscan.h */
+#include "strimpl.h" /* for sfilter.h for picky compilers */
+#include "sfilter.h" /* ditto */
+#include "iscan.h"
+#include "iutil.h"
+#include "dstack.h" /* for dict_set_top */
+#include "store.h"
+
+/* Forward references */
+private int near access_check(P3(os_ptr, int, bool));
+private int convert_to_string(P2(os_ptr, os_ptr));
+
+/*
+ * Max and min integer values expressed as reals.
+ * Note that these are biased by 1 to allow for truncation.
+ * They should be #defines rather than static consts, but
+ * several of the SCO Unix compilers apparently can't handle this.
+ * On the other hand, the DEC compiler can't handle casts in
+ * constant expressions, so we can't use min_long and max_long.
+ * What a nuisance!
+ */
+#define alt_min_long (-1L << (arch_sizeof_long * 8 - 1))
+#define alt_max_long (~(alt_min_long))
+private const double min_int_real = (alt_min_long * 1.0 - 1);
+private const double max_int_real = (alt_max_long * 1.0 + 1);
+#define real_can_be_int(v)\
+ ((v) > min_int_real && (v) < max_int_real)
+#define zcvi_possible(v) real_can_be_int(v)
+
+/* Get the pointer to the access flags for a ref. */
+#define access_ref(opp)\
+ (r_has_type(opp, t_dictionary) ? dict_access_ref(opp) : opp)
+
+/* Initialize the table of type names. */
+/* We export the type names just in case they might be useful. */
+ref type_names; /* t_array */
+private ref *type_names_p = &type_names;
+private gs_gc_root_t type_names_root;
+private void
+ztype_init(void)
+{ static const char _ds *tnames[] = { type_name_strings };
+ int i;
+ ialloc_ref_array(&type_names, a_readonly, t_next_index,
+ "type names");
+ for ( i = 0; i < t_next_index; i++ )
+ { if ( tnames[i] == 0 )
+ make_null(&type_names.value.refs[i]);
+ else
+ { name_enter_string(tnames[i],
+ &type_names.value.refs[i]);
+ r_set_attrs(&type_names.value.refs[i], a_executable);
+ }
+ }
+ gs_register_ref_root(imemory, &type_names_root,
+ (void **)&type_names_p, "type_names");
+}
+
+/* <obj> type <name> */
+private int
+ztype(register os_ptr op)
+{ ref *ptref = &type_names.value.refs[r_btype(op)];
+ if ( !r_has_type(ptref, t_name) )
+ { /* Must be either a stack underflow or a t_[a]struct. */
+ check_op(1);
+ { /* Get the type name from the structure. */
+ const char *sname =
+ gs_struct_type_name_string(gs_object_type(imemory,
+ op->value.pstruct));
+ int code = name_ref((const byte *)sname, strlen(sname),
+ (ref *)op, 0);
+ if ( code < 0 )
+ return code;
+ }
+ r_set_attrs(op, a_executable);
+ }
+ else
+ { ref_assign(op, ptref);
+ }
+ return 0;
+}
+
+/* <obj> cvlit <obj> */
+private int
+zcvlit(register os_ptr op)
+{ ref *aop;
+ check_op(1);
+ aop = access_ref(op);
+ r_clear_attrs(aop, a_executable);
+ return 0;
+}
+
+/* <obj> cvx <obj> */
+int
+zcvx(register os_ptr op)
+{ ref *aop;
+ uint opidx;
+ check_op(1);
+ /*
+ * If the object is an internal operator, we can't allow it to
+ * exist in executable form anywhere outside the e-stack.
+ */
+ if ( r_has_type(op, t_operator) &&
+ ((opidx = op_index(op)) == 0 ||
+ op_def_is_internal(op_def_table[opidx]))
+ )
+ return_error(e_rangecheck);
+ aop = access_ref(op);
+ r_set_attrs(aop, a_executable);
+ return 0;
+}
+
+/* <obj> xcheck <bool> */
+private int
+zxcheck(register os_ptr op)
+{ check_op(1);
+ make_bool(op, (r_has_attr(access_ref(op), a_executable) ? 1 : 0));
+ return 0;
+}
+
+/* <obj:array|packedarray|file|string> executeonly <obj> */
+private int
+zexecuteonly(register os_ptr op)
+{ check_op(1);
+ if ( r_has_type(op, t_dictionary) )
+ return_error(e_typecheck);
+ return access_check(op, a_execute, true);
+}
+
+/* <obj:array|packedarray|dict|file|string> noaccess <obj> */
+private int
+znoaccess(register os_ptr op)
+{ check_op(1);
+ /* Don't allow removing read access to permanent dictionaries. */
+ if ( r_has_type(op, t_dictionary) && dict_is_permanent_on_dstack(op) )
+ return_error(e_invalidaccess);
+ return access_check(op, 0, true);
+}
+
+/* <obj:array|packedarray|dict|file|string> readonly <obj> */
+int
+zreadonly(register os_ptr op)
+{ return access_check(op, a_readonly, true);
+}
+
+/* <array|packedarray|dict|file|string> rcheck <bool> */
+private int
+zrcheck(register os_ptr op)
+{ int code = access_check(op, a_read, false);
+ if ( code >= 0 ) make_bool(op, code), code = 0;
+ return code;
+}
+
+/* <array|packedarray|dict|file|string> wcheck <bool> */
+private int
+zwcheck(register os_ptr op)
+{ int code = access_check(op, a_write, false);
+ if ( code >= 0 ) make_bool(op, code), code = 0;
+ return code;
+}
+
+/* <num> cvi <int> */
+/* <string> cvi <int> */
+private int
+zcvi(register os_ptr op)
+{ float fval;
+ switch ( r_type(op) )
+ {
+ case t_integer:
+ return 0;
+ case t_real:
+ fval = op->value.realval;
+ break;
+ default:
+ return_op_typecheck(op);
+ case t_string:
+ { ref str, token;
+ int code;
+ ref_assign(&str, op);
+ code = scan_string_token(&str, &token);
+ switch ( code )
+ {
+ case scan_EOF: /* no tokens */
+ case scan_BOS: /* not allowed */
+ code = gs_note_error(e_syntaxerror);
+ default:
+ if ( code < 0 )
+ return code;
+ }
+ switch ( r_type(&token) )
+ {
+ case t_integer:
+ *op = token;
+ return 0;
+ case t_real:
+ fval = token.value.realval;
+ break;
+ default:
+ return_error(e_typecheck);
+ }
+ }
+ }
+ /* Check if a real will fit into an integer value */
+ if ( !zcvi_possible(fval) )
+ return_error(e_rangecheck);
+ make_int(op, (long)fval); /* truncates towards 0 */
+ return 0;
+}
+
+/* <string> cvn <name> */
+private int
+zcvn(register os_ptr op)
+{ check_read_type(*op, t_string);
+ return name_from_string(op, op);
+}
+
+/* <num> cvr <real> */
+/* <string> cvr <real> */
+private int
+zcvr(register os_ptr op)
+{ switch ( r_type(op) )
+ {
+ case t_integer:
+ make_real(op, op->value.intval);
+ case t_real:
+ return 0;
+ default:
+ return_op_typecheck(op);
+ case t_string:
+ { ref str, token;
+ int code;
+ ref_assign(&str, op);
+ code = scan_string_token(&str, &token);
+ switch ( code )
+ {
+ case scan_EOF: /* no tokens */
+ case scan_BOS: /* not allowed */
+ code = gs_note_error(e_syntaxerror);
+ default:
+ if ( code < 0 )
+ return code;
+ }
+ switch ( r_type(&token) )
+ {
+ case t_integer:
+ make_real(op, token.value.intval);
+ return 0;
+ case t_real:
+ *op = token;
+ return 0;
+ default:
+ return_error(e_typecheck);
+ }
+ }
+ }
+}
+
+/* <num> <radix_int> <string> cvrs <substring> */
+private int
+zcvrs(register os_ptr op)
+{ int radix;
+ check_type(op[-1], t_integer);
+ if ( op[-1].value.intval < 2 || op[-1].value.intval > 36 )
+ return_error(e_rangecheck);
+ radix = op[-1].value.intval;
+ check_write_type(*op, t_string);
+ if ( radix == 10 )
+ { switch ( r_type(op - 2) )
+ {
+ case t_integer: case t_real:
+ { int code = convert_to_string(op - 2, op);
+ if ( code < 0 ) return code;
+ pop(2);
+ return 0;
+ }
+ default:
+ return_op_typecheck(op - 2);
+ }
+ }
+ else
+ { ulong ival;
+ byte digits[sizeof(ulong) * 8];
+ byte *endp = &digits[countof(digits)];
+ byte *dp = endp;
+ switch ( r_type(op - 2) )
+ {
+ case t_integer:
+ ival = (ulong)op[-2].value.intval;
+ break;
+ case t_real:
+ { float fval = op[-2].value.realval;
+ if ( !real_can_be_int(fval) )
+ return_error(e_rangecheck);
+ ival = (ulong)(long)fval;
+ } break;
+ default:
+ return_op_typecheck(op - 2);
+ }
+ do
+ { int dit = ival % radix;
+ *--dp = dit + (dit < 10 ? '0' : ('A' - 10));
+ ival /= radix;
+ }
+ while ( ival );
+ if ( endp - dp > r_size(op) ) return_error(e_rangecheck);
+ memcpy(op->value.bytes, dp, (uint)(endp - dp));
+ r_set_size(op, endp - dp);
+ }
+ op[-2] = *op;
+ pop(2);
+ return 0;
+}
+
+/* <any> <string> cvs <substring> */
+private int
+zcvs(register os_ptr op)
+{ int code;
+ check_op(2);
+ check_write_type(*op, t_string);
+ code = convert_to_string(op - 1, op);
+ if ( code >= 0 ) pop(1);
+ return code;
+}
+
+/* ------ Initialization procedure ------ */
+
+BEGIN_OP_DEFS(ztype_op_defs) {
+ {"1cvi", zcvi},
+ {"1cvlit", zcvlit},
+ {"1cvn", zcvn},
+ {"1cvr", zcvr},
+ {"3cvrs", zcvrs},
+ {"2cvs", zcvs},
+ {"1cvx", zcvx},
+ {"1executeonly", zexecuteonly},
+ {"1noaccess", znoaccess},
+ {"1rcheck", zrcheck},
+ {"1readonly", zreadonly},
+ {"1type", ztype},
+ {"1wcheck", zwcheck},
+ {"1xcheck", zxcheck},
+END_OP_DEFS(ztype_init) }
+
+/* ------ Internal routines ------ */
+
+/* Test or modify the access of an object. */
+/* If modify = true, restrict to the selected access and return 0; */
+/* if modify = false, do not change the access, and return 1 */
+/* if the object had the access. */
+/* Return an error code if the object is not of appropriate type, */
+/* or if the object did not have the access already when modify=1. */
+private int near
+access_check(os_ptr op,
+ int access, /* mask for attrs */
+ bool modify) /* if true, reduce access */
+{ ref *aop;
+ switch ( r_type(op) )
+ {
+ case t_dictionary:
+ aop = dict_access_ref(op);
+ if ( modify )
+ { if ( !r_has_attrs(aop, access) )
+ return_error(e_invalidaccess);
+ ref_save(op, aop, "access_check(modify)");
+ r_clear_attrs(aop, a_all);
+ r_set_attrs(aop, access);
+ dict_set_top();
+ return 0;
+ }
+ break;
+ case t_array: case t_file: case t_string:
+ case t_mixedarray: case t_shortarray:
+ case t_astruct: case t_device: ;
+ if ( modify )
+ { if ( !r_has_attrs(op, access) )
+ return_error(e_invalidaccess);
+ r_clear_attrs(op, a_all);
+ r_set_attrs(op, access);
+ return 0;
+ }
+ aop = op;
+ break;
+ default:
+ return_op_typecheck(op);
+ }
+ return (r_has_attrs(aop, access)? 1 : 0);
+}
+
+/* Do all the work of cvs. The destination has been checked, but not */
+/* the source. This is a separate procedure so that */
+/* cvrs can use it when the radix is 10. */
+private int
+convert_to_string(os_ptr op1, os_ptr op)
+{ uint len;
+ const byte *pstr = 0;
+ int code = obj_cvs(op1, op->value.bytes, r_size(op), &len, &pstr);
+
+ if ( code < 0 )
+ { /* Some common downloaded error handlers assume that */
+ /* operator names don't exceed a certain fixed size. */
+ /* To work around this bit of bad design, we implement */
+ /* a special hack here: if we got a rangecheck, and */
+ /* the object is an operator whose name begins with */
+ /* %, ., or @, we just truncate the name. */
+ if ( code == e_rangecheck )
+ switch ( r_btype(op1) )
+ {
+ case t_oparray:
+ case t_operator:
+ if ( pstr != 0 )
+ switch ( *pstr )
+ {
+ case '%':
+ case '.':
+ case '@':
+ len = r_size(op);
+ memcpy(op->value.bytes, pstr, len);
+ goto ok;
+ }
+ }
+ return code;
+ }
+ok: *op1 = *op;
+ r_set_size(op1, len);
+ return 0;
+}
diff --git a/pstoraster/zupath.c b/pstoraster/zupath.c
new file mode 100644
index 000000000..e9faebfd5
--- /dev/null
+++ b/pstoraster/zupath.c
@@ -0,0 +1,526 @@
+/* Copyright (C) 1990, 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* zupath.c */
+/* Operators related to user paths */
+#include "ghost.h"
+#include "errors.h"
+#include "oper.h"
+#include "idict.h"
+#include "dstack.h"
+#include "igstate.h"
+#include "iutil.h"
+#include "store.h"
+#include "stream.h"
+#include "ibnum.h"
+#include "gsmatrix.h"
+#include "gsstate.h"
+#include "gscoord.h"
+#include "gspaint.h"
+#include "gxfixed.h"
+#include "gxdevice.h"
+#include "gspath.h"
+#include "gzpath.h" /* for saving path */
+#include "gzstate.h" /* for accessing path */
+
+/* Forward references */
+private int upath_append(P2(os_ptr, os_ptr));
+private int upath_stroke(P1(os_ptr));
+
+/* ------ Insideness testing ------ */
+
+/* Forward references */
+private int in_test(P2(os_ptr, int (*)(P1(gs_state *))));
+private int in_path(P3(os_ptr, os_ptr, gx_device *));
+private int in_path_result(P3(os_ptr, int, int));
+private int in_utest(P2(os_ptr, int (*)(P1(gs_state *))));
+private int in_upath(P2(os_ptr, gx_device *));
+private int in_upath_result(P3(os_ptr, int, int));
+
+/* We use invalidexit, which the painting procedures cannot generate, */
+/* as an "error" to indicate that the hit detection device found a hit. */
+#define e_hit e_invalidexit
+
+/* <x> <y> ineofill <bool> */
+/* <userpath> ineofill <bool> */
+private int
+zineofill(os_ptr op)
+{ return in_test(op, gs_eofill);
+}
+
+/* <x> <y> infill <bool> */
+/* <userpath> infill <bool> */
+private int
+zinfill(os_ptr op)
+{ return in_test(op, gs_fill);
+}
+
+/* <x> <y> instroke <bool> */
+/* <userpath> instroke <bool> */
+private int
+zinstroke(os_ptr op)
+{ return in_test(op, gs_stroke);
+}
+
+/* <x> <y> <userpath> inueofill <bool> */
+/* <userpath1> <userpath2> inueofill <bool> */
+private int
+zinueofill(os_ptr op)
+{ return in_utest(op, gs_eofill);
+}
+
+/* <x> <y> <userpath> inufill <bool> */
+/* <userpath1> <userpath2> inufill <bool> */
+private int
+zinufill(os_ptr op)
+{ return in_utest(op, gs_fill);
+}
+
+/* <x> <y> <userpath> inustroke <bool> */
+/* <userpath1> <userpath2> inustroke <bool> */
+private int
+zinustroke(os_ptr op)
+{ /* This is different because of the optional matrix operand. */
+ int code = gs_gsave(igs);
+ int spop, npop;
+ gx_device hdev;
+
+ if ( code < 0 ) return code;
+ if ( (spop = upath_stroke(op)) < 0 ||
+ (npop = in_path(op - spop, op, &hdev)) < 0
+ )
+ { gs_grestore(igs);
+ return code;
+ }
+ code = gs_stroke(igs);
+ return in_upath_result(op, npop + spop, code);
+}
+
+/* ------ Internal routines ------ */
+
+/* Define a minimal device for insideness testing. */
+/* It returns e_hit whenever it is asked to actually paint any pixels. */
+private dev_proc_fill_rectangle(hit_fill_rectangle);
+private gx_device hit_device =
+{ std_device_std_body(gx_device, 0, "hit detector",
+ 0, 0, 1, 1),
+ { NULL, /* open_device */
+ NULL, /* get_initial_matrix */
+ NULL, /* sync_output */
+ NULL, /* output_page */
+ NULL, /* close_device */
+ gx_default_map_rgb_color,
+ gx_default_map_color_rgb,
+ hit_fill_rectangle
+ }
+};
+/* Test for a hit when filling a rectangle. */
+private int
+hit_fill_rectangle(gx_device *dev, int x, int y, int w, int h,
+ gx_color_index color)
+{ return (w > 0 && h > 0 ? e_hit : 0);
+}
+
+/* Do the work of the non-user-path insideness operators. */
+private int
+in_test(os_ptr op, int (*paintproc)(P1(gs_state *)))
+{ gx_device hdev;
+ int npop = in_path(op, op, &hdev);
+ int code;
+
+ if ( npop < 0 ) return npop;
+ code = (*paintproc)(igs);
+ return in_path_result(op, npop, code);
+}
+
+/* Set up a clipping path and device for insideness testing. */
+private int
+in_path(os_ptr oppath, os_ptr op, gx_device *phdev)
+{ int code = gs_gsave(igs);
+ int npop;
+ float uxy[2];
+
+ if ( code < 0 )
+ return code;
+ code = num_params(oppath, 2, uxy);
+ if ( code >= 0 )
+ { /* Aperture is a single pixel. */
+ gs_point dxy;
+ gs_fixed_rect fr;
+ gs_transform(igs, uxy[0], uxy[1], &dxy);
+ fr.p.x = fixed_floor(float2fixed(dxy.x));
+ fr.p.y = fixed_floor(float2fixed(dxy.y));
+ fr.q.x = fr.p.x + fixed_1;
+ fr.q.y = fr.p.y + fixed_1;
+ code = gx_clip_to_rectangle(igs, &fr);
+ npop = 2;
+ }
+ else
+ { /* Aperture is a user path. */
+ /* We have to set the clipping path without disturbing */
+ /* the current path. */
+ gx_path save;
+ save = *igs->path;
+ gx_path_reset(igs->path); /* prevent newpath from */
+ /* releasing path */
+ code = upath_append(oppath, op);
+ if ( code >= 0 )
+ code = gx_clip_to_path(igs);
+ gs_newpath(igs); /* release upath */
+ *igs->path = save;
+ npop = 1;
+ }
+ if ( code < 0 )
+ { gs_grestore(igs);
+ return code;
+ }
+ /* Install the hit detection device. */
+ gx_set_device_color_1(igs);
+ *phdev = hit_device;
+ gx_device_fill_in_procs(phdev);
+ gx_set_device_only(igs, phdev);
+ return npop;
+}
+
+/* Finish an insideness test. */
+private int
+in_path_result(os_ptr op, int npop, int code)
+{ int result;
+ gs_grestore(igs); /* matches gsave in in_path */
+ switch ( code )
+ {
+ case e_hit: /* found a hit */
+ result = 1;
+ break;
+ case 0: /* completed painting without a hit */
+ result = 0;
+ break;
+ default: /* error */
+ return code;
+ }
+ npop--;
+ pop(npop); op -= npop;
+ make_bool(op, result);
+ return 0;
+
+}
+
+/* Do the work of the user-path insideness operators. */
+private int
+in_utest(os_ptr op, int (*paintproc)(P1(gs_state *)))
+{ gx_device hdev;
+ int npop = in_upath(op, &hdev);
+ int code;
+
+ if ( npop < 0 ) return npop;
+ code = (*paintproc)(igs);
+ return in_upath_result(op, npop, code);
+}
+
+/* Set up a clipping path and device for insideness testing */
+/* with a user path. */
+private int
+in_upath(os_ptr op, gx_device *phdev)
+{ int code = gs_gsave(igs);
+ int npop;
+
+ if ( code < 0 ) return code;
+ if ( (code = upath_append(op, op)) < 0 ||
+ (npop = in_path(op - 1, op, phdev)) < 0
+ )
+ { gs_grestore(igs);
+ return code;
+ }
+ return npop + 1;
+}
+
+/* Finish an insideness test with a user path. */
+private int
+in_upath_result(os_ptr op, int npop, int code)
+{ gs_grestore(igs); /* matches gsave in in_upath */
+ return in_path_result(op, npop, code);
+}
+
+/* ------ User paths ------ */
+
+/* User path operator codes */
+typedef enum {
+ upath_setbbox = 0,
+ upath_moveto = 1,
+ upath_rmoveto = 2,
+ upath_lineto = 3,
+ upath_rlineto = 4,
+ upath_curveto = 5,
+ upath_rcurveto = 6,
+ upath_arc = 7,
+ upath_arcn = 8,
+ upath_arct = 9,
+ upath_closepath = 10,
+ upath_ucache = 11
+} upath_op;
+#define upath_op_max 11
+#define upath_repeat 32
+static byte up_nargs[upath_op_max + 1] =
+ { 4, 2, 2, 2, 2, 6, 6, 5, 5, 5, 0, 0 };
+/* Declare operator procedures not declared in opextern.h. */
+int zsetbbox(P1(os_ptr));
+int zarc(P1(os_ptr));
+int zarcn(P1(os_ptr));
+int zarct(P1(os_ptr));
+private int zucache(P1(os_ptr));
+#undef zp
+static op_proc_p up_ops[upath_op_max + 1] =
+ { zsetbbox, zmoveto, zrmoveto, zlineto, zrlineto,
+ zcurveto, zrcurveto, zarc, zarcn, zarct,
+ zclosepath, zucache
+ };
+
+/* - ucache - */
+private int
+zucache(os_ptr op)
+{ /* A no-op for now. */
+ return 0;
+}
+
+/* <userpath> uappend - */
+private int
+zuappend(register os_ptr op)
+{ int code = gs_gsave(igs);
+ if ( code < 0 ) return code;
+ if ( (code = upath_append(op, op)) >= 0 )
+ code = gs_upmergepath(igs);
+ gs_grestore(igs);
+ if ( code < 0 ) return code;
+ pop(1);
+ return 0;
+}
+
+/* <userpath> ueofill - */
+private int
+zueofill(register os_ptr op)
+{ int code = gs_gsave(igs);
+ if ( code < 0 ) return code;
+ if ( (code = upath_append(op, op)) >= 0 )
+ code = gs_eofill(igs);
+ gs_grestore(igs);
+ if ( code < 0 ) return code;
+ pop(1);
+ return 0;
+}
+
+/* <userpath> ufill - */
+private int
+zufill(register os_ptr op)
+{ int code = gs_gsave(igs);
+ if ( code < 0 ) return code;
+ if ( (code = upath_append(op, op)) >= 0 )
+ code = gs_fill(igs);
+ gs_grestore(igs);
+ if ( code < 0 ) return code;
+ pop(1);
+ return 0;
+}
+
+/* <userpath> ustroke - */
+/* <userpath> <matrix> ustroke - */
+private int
+zustroke(register os_ptr op)
+{ int code = gs_gsave(igs);
+ int npop;
+ if ( code < 0 ) return code;
+ if ( (code = npop = upath_stroke(op)) >= 0 )
+ code = gs_stroke(igs);
+ gs_grestore(igs);
+ if ( code < 0 ) return code;
+ pop(npop);
+ return 0;
+}
+
+/* <userpath> ustrokepath - */
+/* <userpath> <matrix> ustrokepath - */
+private int
+zustrokepath(register os_ptr op)
+{ int code = gs_gsave(igs);
+ int npop;
+ if ( code < 0 ) return code;
+ if ( (code = npop = upath_stroke(op)) < 0 ||
+ (code = gs_strokepath(igs)) < 0 ||
+ (code = gs_upmergepath(igs)) < 0
+ )
+ DO_NOTHING;
+ gs_grestore(igs);
+ if ( code < 0 ) return code;
+ pop(npop);
+ return 0;
+}
+
+/* --- Internal routines --- */
+
+/* Append a user path to the current path. */
+private int
+upath_append(os_ptr oppath, os_ptr op)
+{ check_read(*oppath);
+ gs_newpath(igs);
+ /****** ROUND tx AND ty ******/
+ if ( r_has_type(oppath, t_array) && r_size(oppath) == 2 &&
+ r_has_type(oppath->value.refs + 1, t_string)
+ )
+ { /* 1st element is operators, 2nd is operands */
+ const ref *operands = oppath->value.refs;
+ int code, format;
+ int repcount = 1;
+ const byte *opp;
+ uint ocount, i = 0;
+
+ code = num_array_format(operands);
+ if ( code < 0 )
+ return code;
+ format = code;
+ opp = oppath->value.refs[1].value.bytes;
+ ocount = r_size(&oppath->value.refs[1]);
+ while ( ocount-- )
+ { byte opx = *opp++;
+ if ( opx > 32 )
+ repcount = opx - 32;
+ else if ( opx > upath_op_max )
+ return_error(e_rangecheck);
+ else /* operator */
+ { do
+ { byte opargs = up_nargs[opx];
+ while ( opargs-- )
+ { push(1);
+ code = num_array_get(operands, format, i++, op);
+ switch ( code )
+ {
+ case t_integer:
+ r_set_type_attrs(op, t_integer, 0);
+ break;
+ case t_real:
+ r_set_type_attrs(op, t_real, 0);
+ break;
+ default:
+ return_error(e_typecheck);
+ }
+ }
+ code = (*up_ops[opx])(op);
+ if ( code < 0 ) return code;
+ op = osp; /* resync */
+ }
+ while ( --repcount );
+ repcount = 1;
+ }
+ }
+ }
+ else if ( r_is_array(oppath) )
+ { /* Ordinary executable array. */
+ const ref *arp = oppath;
+ uint ocount = r_size(oppath);
+ long index = 0;
+ int argcount = 0;
+ int (*oproc)(P1(os_ptr));
+ int opx, code;
+
+ for ( ; index < ocount; index++ )
+ { ref rup;
+ ref *defp;
+
+ array_get(arp, index, &rup);
+ switch ( r_type(&rup) )
+ {
+ case t_integer:
+ case t_real:
+ argcount++;
+ push(1);
+ *op = rup;
+ break;
+ case t_name:
+ if ( !r_has_attr(&rup, a_executable) )
+ return_error(e_typecheck);
+ if ( dict_find(systemdict, &rup, &defp) <= 0 )
+ return_error(e_undefined);
+ if ( r_btype(defp) != t_operator )
+ return_error(e_typecheck);
+ goto xop;
+ case t_operator:
+ defp = &rup;
+xop: if ( !r_has_attr(defp, a_executable) )
+ return_error(e_typecheck);
+ oproc = real_opproc(defp);
+ for ( opx = 0; opx <= upath_op_max; opx++ )
+ if ( oproc == up_ops[opx] ) break;
+ if ( opx > upath_op_max || argcount != up_nargs[opx] )
+ return_error(e_typecheck);
+ code = (*oproc)(op);
+ if ( code < 0 ) return code;
+ op = osp; /* resync ostack pointer */
+ argcount = 0;
+ break;
+ default:
+ return_error(e_typecheck);
+ }
+ }
+ if ( argcount )
+ return_error(e_typecheck); /* leftover args */
+ }
+ else
+ return_error(e_typecheck);
+ return 0;
+}
+
+/* Append a user path to the current path, and then apply */
+/* a transformation if one is supplied. */
+private int
+upath_stroke(register os_ptr op)
+{ int code, npop;
+ gs_matrix mat;
+ if ( (code = read_matrix(op, &mat)) >= 0 )
+ { if ( (code = upath_append(op - 1, op)) >= 0 )
+ code = gs_concat(igs, &mat);
+ npop = 2;
+ }
+ else
+ { code = upath_append(op, op);
+ npop = 1;
+ }
+ return (code < 0 ? code : npop);
+}
+
+/* ------ Initialization procedure ------ */
+
+BEGIN_OP_DEFS(zupath_l2_op_defs) {
+ op_def_begin_level2(),
+ /* Insideness testing */
+ {"1ineofill", zineofill},
+ {"1infill", zinfill},
+ {"1instroke", zinstroke},
+ {"2inueofill", zinueofill},
+ {"2inufill", zinufill},
+ {"2inustroke", zinustroke},
+ /* User paths */
+ {"1uappend", zuappend},
+ {"1ueofill", zueofill},
+ {"1ufill", zufill},
+ {"1ustroke", zustroke},
+ {"1ustrokepath", zustrokepath},
+ {"0ucache", zucache},
+END_OP_DEFS(0) }
diff --git a/pstoraster/zusparam.c b/pstoraster/zusparam.c
new file mode 100644
index 000000000..fb51399a1
--- /dev/null
+++ b/pstoraster/zusparam.c
@@ -0,0 +1,503 @@
+/* Copyright (C) 1996 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* zusparam.c */
+/* User and system parameter operators */
+#include "memory_.h"
+#include "string_.h"
+#include "ghost.h"
+#include "errors.h"
+#include "oper.h"
+#include "gscdefs.h"
+#include "gsstruct.h" /* for gxht.h */
+#include "gsfont.h" /* for user params */
+#include "gxht.h" /* for user params */
+#include "gsutil.h"
+#include "estack.h"
+#include "ialloc.h" /* for imemory for status */
+#include "idict.h"
+#include "idparam.h"
+#include "iparam.h"
+#include "dstack.h"
+#include "iname.h"
+#include "iutil2.h"
+#include "store.h"
+
+/* The (global) font directory */
+extern gs_font_dir *ifont_dir; /* in zfont.c */
+
+/* Import the GC parameters from zvmem2.c. */
+extern int set_vm_reclaim(P1(long));
+extern int set_vm_threshold(P1(long));
+
+/* The system passwords. */
+private password StartJobPassword = NULL_PASSWORD;
+password SystemParamsPassword = NULL_PASSWORD; /* exported for ziodev2.c. */
+
+/* Define an individual user or system parameter. */
+/* Eventually this will be made public. */
+#define param_def_common\
+ const char _ds *pname
+typedef struct param_def_s {
+ param_def_common;
+} param_def;
+typedef struct long_param_def_s {
+ param_def_common;
+ long min_value, max_value;
+ long (*current)(P0());
+ int (*set)(P1(long));
+} long_param_def;
+#if arch_sizeof_long > arch_sizeof_int
+# define max_uint_param max_uint
+#else
+# define max_uint_param max_long
+#endif
+typedef struct bool_param_def_s {
+ param_def_common;
+ bool (*current)(P0());
+ int (*set)(P1(bool));
+} bool_param_def;
+typedef struct string_param_def_s {
+ param_def_common;
+ void (*current)(P1(gs_param_string *));
+ int (*set)(P1(gs_param_string *));
+} string_param_def;
+/* Define a parameter set (user or system). */
+typedef struct param_set_s {
+ const long_param_def *long_defs;
+ uint long_count;
+ const bool_param_def *bool_defs;
+ uint bool_count;
+ const string_param_def *string_defs;
+ uint string_count;
+} param_set;
+
+/* Forward references */
+private int setparams(P2(gs_param_list *, const param_set _ds *));
+private int currentparams(P2(os_ptr, const param_set _ds *));
+
+/* ------ Passwords ------ */
+
+#define plist ((gs_param_list *)&list)
+
+/* <string|int> .checkpassword <0|1|2> */
+private int
+zcheckpassword(register os_ptr op)
+{ ref params[2];
+ array_param_list list;
+ int result = 0;
+ int code = name_ref((const byte *)"Password", 8, &params[0], 0);
+ if ( code < 0 )
+ return code;
+ params[1] = *op;
+ array_param_list_read(&list, params, 2, NULL, false);
+ if ( param_check_password(plist, &StartJobPassword) == 0 )
+ result = 1;
+ if ( param_check_password(plist, &SystemParamsPassword) == 0 )
+ result = 2;
+ make_int(op, result);
+ return 0;
+}
+
+#undef plist
+
+/* ------ System parameters ------ */
+
+/* Integer values */
+private long
+current_BuildTime(void)
+{ return gs_buildtime;
+}
+private long
+current_MaxFontCache(void)
+{ return gs_currentcachesize(ifont_dir);
+}
+private int
+set_MaxFontCache(long val)
+{ return gs_setcachesize(ifont_dir,
+ (uint)(val < 0 ? 0 : val > max_uint ? max_uint :
+ val));
+}
+private long
+current_CurFontCache(void)
+{ uint cstat[7];
+ gs_cachestatus(ifont_dir, cstat);
+ return cstat[0];
+}
+private long
+current_MaxGlobalVM(void)
+{ gs_memory_gc_status_t stat;
+ gs_memory_gc_status(iimemory_global, &stat);
+ return stat.max_vm;
+}
+private int
+set_MaxGlobalVM(long val)
+{ gs_memory_gc_status_t stat;
+ gs_memory_gc_status(iimemory_global, &stat);
+ stat.max_vm = max(val, 0);
+ gs_memory_set_gc_status(iimemory_global, &stat);
+ return 0;
+}
+private long
+current_Revision(void)
+{ return gs_revision;
+}
+private const long_param_def system_long_params[] = {
+ {"BuildTime", 0, max_uint_param, current_BuildTime, NULL},
+ {"MaxFontCache", 0, max_uint_param, current_MaxFontCache, set_MaxFontCache},
+ {"CurFontCache", 0, max_uint_param, current_CurFontCache, NULL},
+ {"Revision", 0, max_uint_param, current_Revision, NULL},
+ /* Extensions */
+ {"MaxGlobalVM", 0, max_uint_param, current_MaxGlobalVM, set_MaxGlobalVM}
+};
+/* Boolean values */
+private bool
+current_ByteOrder(void)
+{ return !arch_is_big_endian;
+}
+private const bool_param_def system_bool_params[] = {
+ {"ByteOrder", current_ByteOrder, NULL}
+};
+/* String values */
+private void
+current_RealFormat(gs_param_string *pval)
+{
+#if arch_floats_are_IEEE
+ static const char *rfs = "IEEE";
+#else
+ static const char *rfs = "not IEEE";
+#endif
+ pval->data = (const byte *)rfs;
+ pval->size = strlen(rfs);
+ pval->persistent = true;
+}
+private const string_param_def system_string_params[] = {
+ {"RealFormat", current_RealFormat, NULL}
+};
+
+/* The system parameter set */
+private const param_set system_param_set = {
+ system_long_params, countof(system_long_params),
+ system_bool_params, countof(system_bool_params),
+ system_string_params, countof(system_string_params)
+};
+
+
+/* <dict> setsystemparams - */
+private int
+zsetsystemparams(register os_ptr op)
+{ int code;
+ dict_param_list list;
+ password pass;
+
+ check_type(*op, t_dictionary);
+ code = dict_param_list_read(&list, op, NULL, false);
+ if ( code < 0 )
+ return code;
+#define plist ((gs_param_list *)&list)
+ code = param_check_password(plist, &SystemParamsPassword);
+ if ( code != 0 )
+ return_error(code < 0 ? code : e_invalidaccess);
+ code = param_read_password(plist, "StartJobPassword", &pass);
+ switch ( code )
+ {
+ default: /* invalid */
+ return code;
+ case 1: /* missing */
+ break;
+ case 0:
+ StartJobPassword = pass;
+ }
+ code = param_read_password(plist, "SystemParamsPassword", &pass);
+ switch ( code )
+ {
+ default: /* invalid */
+ return code;
+ case 1: /* missing */
+ break;
+ case 0:
+ SystemParamsPassword = pass;
+ }
+ code = setparams(plist, &system_param_set);
+ if ( code < 0 )
+ return code;
+#undef plist
+ pop(1);
+ return 0;
+}
+
+/* - .currentsystemparams <name1> <value1> ... */
+private int
+zcurrentsystemparams(os_ptr op)
+{ return currentparams(op, &system_param_set);
+}
+
+/* ------ User parameters ------ */
+
+/* Integer values */
+private long
+current_JobTimeout(void)
+{ return 0;
+}
+private int
+set_JobTimeout(long val)
+{ return 0;
+}
+private long
+current_MaxFontItem(void)
+{ return gs_currentcacheupper(ifont_dir);
+}
+private int
+set_MaxFontItem(long val)
+{ return gs_setcacheupper(ifont_dir, val);
+}
+private long
+current_MinFontCompress(void)
+{ return gs_currentcachelower(ifont_dir);
+}
+private int
+set_MinFontCompress(long val)
+{ return gs_setcachelower(ifont_dir, val);
+}
+private long
+current_MaxOpStack(void)
+{ return ref_stack_max_count(&o_stack);
+}
+private int
+set_MaxOpStack(long val)
+{ return ref_stack_set_max_count(&o_stack, val);
+}
+private long
+current_MaxDictStack(void)
+{ return ref_stack_max_count(&d_stack);
+}
+private int
+set_MaxDictStack(long val)
+{ return ref_stack_set_max_count(&d_stack, val);
+}
+private long
+current_MaxExecStack(void)
+{ return ref_stack_max_count(&e_stack);
+}
+private int
+set_MaxExecStack(long val)
+{ return ref_stack_set_max_count(&e_stack, val);
+}
+private long
+current_MaxLocalVM(void)
+{ gs_memory_gc_status_t stat;
+ gs_memory_gc_status(iimemory_local, &stat);
+ return stat.max_vm;
+}
+private int
+set_MaxLocalVM(long val)
+{ gs_memory_gc_status_t stat;
+ gs_memory_gc_status(iimemory_local, &stat);
+ stat.max_vm = max(val, 0);
+ gs_memory_set_gc_status(iimemory_local, &stat);
+ return 0;
+}
+private long
+current_VMReclaim(void)
+{ gs_memory_gc_status_t gstat, lstat;
+ gs_memory_gc_status(iimemory_global, &gstat);
+ gs_memory_gc_status(iimemory_local, &lstat);
+ return (!gstat.enabled ? -2 : !lstat.enabled ? -1 : 0);
+}
+private long
+current_VMThreshold(void)
+{ gs_memory_gc_status_t stat;
+ gs_memory_gc_status(iimemory_local, &stat);
+ return stat.vm_threshold;
+}
+#define current_WaitTimeout current_JobTimeout
+#define set_WaitTimeout set_JobTimeout
+private const long_param_def user_long_params[] = {
+ {"JobTimeout", 0, max_uint_param,
+ current_JobTimeout, set_JobTimeout},
+ {"MaxFontItem", 0, max_uint_param,
+ current_MaxFontItem, set_MaxFontItem},
+ {"MinFontCompress", 0, max_uint_param,
+ current_MinFontCompress, set_MinFontCompress},
+ {"MaxOpStack", 0, max_uint_param,
+ current_MaxOpStack, set_MaxOpStack},
+ {"MaxDictStack", 0, max_uint_param,
+ current_MaxDictStack, set_MaxDictStack},
+ {"MaxExecStack", 0, max_uint_param,
+ current_MaxExecStack, set_MaxExecStack},
+ {"MaxLocalVM", 0, max_uint_param,
+ current_MaxLocalVM, set_MaxLocalVM},
+ {"VMReclaim", -2, 0,
+ current_VMReclaim, set_vm_reclaim},
+ {"VMThreshold", 0, max_uint_param,
+ current_VMThreshold, set_vm_threshold},
+ {"WaitTimeout", 0, max_uint_param,
+ current_WaitTimeout, set_WaitTimeout}
+};
+/* Boolean values */
+private bool
+current_AccurateScreens(void)
+{ return gs_currentaccuratescreens();
+}
+private int
+set_AccurateScreens(bool val)
+{ gs_setaccuratescreens(val);
+ return 0;
+}
+private const bool_param_def user_bool_params[] = {
+ {"AccurateScreens", current_AccurateScreens, set_AccurateScreens}
+};
+/* String values */
+private void
+current_JobName(gs_param_string *pval)
+{ pval->data = 0;
+ pval->size = 0;
+ pval->persistent = true;
+}
+private int
+set_JobName(gs_param_string *val)
+{ return 0;
+}
+private const string_param_def user_string_params[] = {
+ {"JobName", current_JobName, set_JobName}
+};
+
+/* The user parameter set */
+private const param_set user_param_set = {
+ user_long_params, countof(user_long_params),
+ user_bool_params, countof(user_bool_params),
+ user_string_params, countof(user_string_params)
+};
+
+/* <dict> setuserparams - */
+private int
+zsetuserparams(register os_ptr op)
+{ dict_param_list list;
+ int code;
+
+ check_type(*op, t_dictionary);
+ code = dict_param_list_read(&list, op, NULL, false);
+ if ( code < 0 )
+ return code;
+ code = setparams((gs_param_list *)&list, &user_param_set);
+ if ( code < 0 )
+ return code;
+ pop(1);
+ return 0;
+}
+
+/* - .currentuserparams <name1> <value1> ... */
+private int
+zcurrentuserparams(os_ptr op)
+{ return currentparams(op, &user_param_set);
+}
+
+/* ------ Initialization procedure ------ */
+
+BEGIN_OP_DEFS(zusparam_op_defs) {
+ /* User and system parameters are readable even in Level 1. */
+ {"0.currentsystemparams", zcurrentsystemparams},
+ {"0.currentuserparams", zcurrentuserparams},
+ /* The rest of the operators are defined only in Level 2. */
+ op_def_begin_level2(),
+ {"1.checkpassword", zcheckpassword},
+ {"1setsystemparams", zsetsystemparams},
+ {"1setuserparams", zsetuserparams},
+END_OP_DEFS(0) }
+
+/* ------ Internal procedures ------ */
+
+/* Set the values of a parameter set from a parameter list. */
+/* We don't attempt to back out if anything fails. */
+private int
+setparams(gs_param_list *plist, const param_set _ds *pset)
+{ int i, code;
+ for ( i = 0; i < pset->long_count; i++ )
+ { const long_param_def *pdef = &pset->long_defs[i];
+ long val;
+ if ( pdef->set == NULL )
+ continue;
+ code = param_read_long(plist, pdef->pname, &val);
+ switch ( code )
+ {
+ default: /* invalid */
+ return code;
+ case 1: /* missing */
+ break;
+ case 0:
+ if ( val < pdef->min_value || val > pdef->max_value )
+ return_error(e_rangecheck);
+ code = (*pdef->set)(val);
+ if ( code < 0 )
+ return code;
+ }
+ }
+ for ( i = 0; i < pset->bool_count; i++ )
+ { const bool_param_def *pdef = &pset->bool_defs[i];
+ bool val;
+ if ( pdef->set == NULL )
+ continue;
+ code = param_read_bool(plist, pdef->pname, &val);
+ if ( code == 0 )
+ code = (*pdef->set)(val);
+ if ( code < 0 )
+ return code;
+ }
+ /****** WE SHOULD DO STRINGS AND STRING ARRAYS, BUT WE DON'T YET ******/
+ return 0;
+}
+
+/* Get the current values of a parameter set to the stack. */
+private int
+currentparams(os_ptr op, const param_set _ds *pset)
+{ stack_param_list list;
+ int i;
+ stack_param_list_write(&list, &o_stack, NULL);
+ for ( i = 0; i < pset->long_count; i++ )
+ { long val = (*pset->long_defs[i].current)();
+ int code = param_write_long((gs_param_list *)&list,
+ pset->long_defs[i].pname, &val);
+
+ if ( code < 0 )
+ return code;
+ }
+ for ( i = 0; i < pset->bool_count; i++ )
+ { bool val = (*pset->bool_defs[i].current)();
+ int code = param_write_bool((gs_param_list *)&list,
+ pset->bool_defs[i].pname, &val);
+
+ if ( code < 0 )
+ return code;
+ }
+ for ( i = 0; i < pset->string_count; i++ )
+ { gs_param_string val;
+ int code;
+
+ (*pset->string_defs[i].current)(&val);
+ code = param_write_string((gs_param_list *)&list,
+ pset->string_defs[i].pname, &val);
+ if ( code < 0 )
+ return code;
+ }
+ return 0;
+}
diff --git a/pstoraster/zvmem.c b/pstoraster/zvmem.c
new file mode 100644
index 000000000..55bac2737
--- /dev/null
+++ b/pstoraster/zvmem.c
@@ -0,0 +1,336 @@
+/* Copyright (C) 1989, 1995 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* zvmem.c */
+/* "Virtual memory" operators */
+#include "ghost.h"
+#include "gsstruct.h"
+#include "errors.h"
+#include "oper.h"
+#include "estack.h" /* for checking in restore */
+#include "ialloc.h"
+#include "idict.h" /* ditto */
+#include "igstate.h"
+#include "isave.h"
+#include "dstack.h"
+#include "store.h"
+#include "gsmatrix.h" /* for gsstate.h */
+#include "gsstate.h"
+
+/* Make an invalid file object. */
+extern void make_invalid_file(P1(ref *)); /* in zfile.c */
+
+/* 'Save' structure */
+typedef struct vm_save_s vm_save_t;
+struct vm_save_s {
+ gs_state *gsave; /* old graphics state */
+};
+gs_private_st_ptrs1(st_vm_save, vm_save_t, "savetype",
+ vm_save_enum_ptrs, vm_save_reloc_ptrs, gsave);
+
+/* - save <save> */
+int
+zsave(register os_ptr op)
+{ uint space = icurrent_space;
+ vm_save_t *vmsave;
+ ulong sid;
+ int code;
+ gs_state *prev;
+
+ /*ivalidate_spaces();*/ /****** DOESN'T WORK ******/
+ ialloc_set_space(idmemory, avm_local);
+ vmsave = ialloc_struct(vm_save_t, &st_vm_save, "zsave");
+ ialloc_set_space(idmemory, space);
+ if ( vmsave == 0 )
+ return_error(e_VMerror);
+ sid = alloc_save_state(idmemory, vmsave);
+ if ( sid == 0 )
+ { ifree_object(vmsave, "zsave");
+ return_error(e_VMerror);
+ }
+ if_debug2('u', "[u]vmsave 0x%lx, id = %lu\n",
+ (ulong)vmsave, (ulong)sid);
+ code = zgsave(op);
+ if ( code < 0 )
+ return code;
+ /* Cut the chain so we can't grestore past here. */
+ prev = gs_state_swap_saved(igs, (gs_state *)0);
+ code = zgsave(op);
+ if ( code < 0 )
+ return code;
+ vmsave->gsave = prev;
+ push(1);
+ make_tav(op, t_save, 0, saveid, sid);
+ /*ivalidate_spaces();*/ /****** DOESN'T WORK ******/
+ return 0;
+}
+
+/* <save> restore - */
+private int restore_check_operand(P2(os_ptr, alloc_save_t **));
+private int restore_check_stack(P3(const ref_stack *, const alloc_save_t *, bool));
+private void restore_fix_stack(P3(ref_stack *, const alloc_save_t *, bool));
+int
+zrestore(register os_ptr op)
+{ alloc_save_t *asave;
+ bool last;
+ vm_save_t *vmsave;
+ int code = restore_check_operand(op, &asave);
+
+ if ( code < 0 )
+ return code;
+ /*ivalidate_spaces();*/ /****** DOESN'T WORK ******/
+ if_debug2('u', "[u]vmrestore 0x%lx, id = %lu\n",
+ (ulong)alloc_save_client_data(asave),
+ (ulong)op->value.saveid);
+ /* Check the contents of the stacks. */
+ osp--;
+ { int code;
+ if ( (code = restore_check_stack(&o_stack, asave, false)) < 0 ||
+ (code = restore_check_stack(&e_stack, asave, true)) < 0 ||
+ (code = restore_check_stack(&d_stack, asave, false)) < 0
+ )
+ { osp++;
+ return code;
+ }
+ }
+ /* Reset l_new in all stack entries if the new save level is zero. */
+ /* Also do some special fixing on the e-stack. */
+ restore_fix_stack(&o_stack, asave, false);
+ restore_fix_stack(&e_stack, asave, true);
+ restore_fix_stack(&d_stack, asave, false);
+ /* Iteratively restore the state of memory, */
+ /* also doing a grestoreall at each step. */
+ do
+ { vmsave = alloc_save_client_data(alloc_save_current(idmemory));
+ /* Restore the graphics state. */
+ gs_grestoreall(igs);
+ gs_state_swap_saved(gs_state_saved(igs), vmsave->gsave);
+ gs_grestore(igs);
+ gs_grestore(igs);
+ /*
+ * If alloc_save_space decided to do a second save, the vmsave
+ * object was allocated one save level less deep than the
+ * current level, so ifree_object won't actually free it;
+ * however, it points to a gsave object that definitely
+ * *has* been freed. In order not to trip up the garbage
+ * collector, we clear the gsave pointer now.
+ */
+ vmsave->gsave = 0;
+ /* Now it's safe to restore the state of memory. */
+ last = alloc_restore_state_step(asave);
+ }
+ while ( !last );
+ { uint space = icurrent_space;
+ ialloc_set_space(idmemory, avm_local);
+ ifree_object(vmsave, "zrestore");
+ ialloc_set_space(idmemory, space);
+ }
+ dict_set_top(); /* reload dict stack cache */
+ /*ivalidate_spaces();*/ /****** DOESN'T WORK ******/
+ return 0;
+}
+/* Check the operand of a restore. */
+private int
+restore_check_operand(os_ptr op, alloc_save_t **pasave)
+{ vm_save_t *vmsave;
+ ulong sid;
+ alloc_save_t *asave;
+ check_type(*op, t_save);
+ vmsave = r_ptr(op, vm_save_t);
+ if ( vmsave == 0 ) /* invalidated save */
+ return_error(e_invalidrestore);
+ sid = op->value.saveid;
+ asave = alloc_find_save(idmemory, sid);
+ if ( asave == 0 )
+ return_error(e_invalidrestore);
+ *pasave = asave;
+ return 0;
+}
+/* Check a stack to make sure all its elements are older than a save. */
+private int
+restore_check_stack(const ref_stack *pstack, const alloc_save_t *asave,
+ bool is_estack)
+{ STACK_LOOP_BEGIN(pstack, bot, size)
+ { const ref *stkp;
+ for ( stkp = bot; size; stkp++, size-- )
+ { const void *ptr;
+ switch ( r_type(stkp) )
+ {
+ case t_array:
+ ptr = stkp->value.refs; break;
+ case t_dictionary:
+ ptr = stkp->value.pdict; break;
+ case t_file:
+ /* Don't check executable files on the e-stack. */
+ if ( r_has_attr(stkp, a_executable) && is_estack )
+ continue;
+ ptr = stkp->value.pfile; break;
+ case t_name:
+ /* Names are special because of how they are allocated. */
+ if ( alloc_name_is_since_save(stkp, asave) )
+ return_error(e_invalidrestore);
+ continue;
+ case t_string:
+ /* Don't check empty executable strings */
+ /* on the e-stack. */
+ if ( r_size(stkp) == 0 &&
+ r_has_attr(stkp, a_executable) && is_estack
+ )
+ continue;
+ ptr = stkp->value.bytes; break;
+ case t_mixedarray:
+ case t_shortarray:
+ ptr = stkp->value.packed; break;
+ case t_device:
+ ptr = stkp->value.pdevice; break;
+ case t_fontID:
+ case t_struct:
+ case t_astruct:
+ ptr = stkp->value.pstruct; break;
+ default:
+ continue;
+ }
+ if ( alloc_is_since_save(ptr, asave) )
+ return_error(e_invalidrestore);
+ }
+ }
+ STACK_LOOP_END(bot, size)
+ return 0; /* OK */
+}
+/*
+ * If the new save level is zero, fix up the contents of a stack
+ * by clearing the l_new bit in all the entries (since we can't tolerate
+ * values with l_new set if the save level is zero).
+ * Also, in any case, fix up the e-stack by replacing empty executable
+ * strings and closed executable files that are newer than the save
+ * with canonical ones that aren't.
+ *
+ * Note that this procedure is only called if restore_check_stack succeeded.
+ */
+private void
+restore_fix_stack(ref_stack *pstack, const alloc_save_t *asave,
+ bool is_estack)
+{ STACK_LOOP_BEGIN(pstack, bot, size)
+ { ref *stkp;
+ for ( stkp = bot; size; stkp++, size-- )
+ { r_clear_attrs(stkp, l_new); /* always do it, no harm */
+ if ( is_estack )
+ { ref ofile;
+ ref_assign(&ofile, stkp);
+ switch ( r_type(stkp) )
+ {
+ case t_string:
+ if ( r_size(stkp) == 0 &&
+ alloc_is_since_save(stkp->value.bytes,
+ asave)
+ )
+ { make_empty_const_string(stkp,
+ avm_foreign);
+ break;
+ }
+ continue;
+ case t_file:
+ if ( alloc_is_since_save(stkp->value.pfile,
+ asave)
+ )
+ { make_invalid_file(stkp);
+ break;
+ }
+ continue;
+ default:
+ continue;
+ }
+ r_copy_attrs(stkp, a_all | a_executable,
+ &ofile);
+ }
+ }
+ }
+ STACK_LOOP_END(bot, size)
+}
+
+/* - vmstatus <save_level> <vm_used> <vm_maximum> */
+private int
+zvmstatus(register os_ptr op)
+{ gs_memory_status_t mstat, dstat;
+ gs_memory_status(imemory, &mstat);
+ if ( imemory == imemory_global )
+ { gs_memory_status_t sstat;
+ gs_memory_status(imemory_system, &sstat);
+ mstat.allocated += sstat.allocated;
+ mstat.used += sstat.used;
+ }
+ gs_memory_status(&gs_memory_default, &dstat);
+ push(3);
+ make_int(op - 2, alloc_save_level(idmemory));
+ make_int(op - 1, mstat.used);
+ make_int(op, mstat.allocated + dstat.allocated - dstat.used);
+ return 0;
+}
+
+/* ------ Non-standard extensions ------ */
+
+/* <save> .forgetsave - */
+private int
+zforgetsave(register os_ptr op)
+{ alloc_save_t *asave;
+ vm_save_t *vmsave;
+ int code = restore_check_operand(op, &asave);
+ if ( code < 0 )
+ return 0;
+ vmsave = alloc_save_client_data(asave);
+ /* Reset l_new in all stack entries if the new save level is zero. */
+ restore_fix_stack(&o_stack, asave, false);
+ restore_fix_stack(&e_stack, asave, false);
+ restore_fix_stack(&d_stack, asave, false);
+ /* Forget the gsaves, by deleting the bottom gstate on */
+ /* the current stack and the top one on the saved stack and then */
+ /* concatenating the stacks together. */
+ { gs_state *pgs = igs;
+ gs_state *last;
+ while ( gs_state_saved(last = gs_state_saved(pgs)) != 0 )
+ pgs = last;
+ gs_state_swap_saved(last, vmsave->gsave);
+ gs_grestore(last);
+ gs_grestore(last);
+ }
+ /* Forget the save in the memory manager. */
+ alloc_forget_save(asave);
+ { uint space = icurrent_space;
+ ialloc_set_space(idmemory, avm_local);
+ /* See above for why we clear the gsave pointer here. */
+ vmsave->gsave = 0;
+ ifree_object(vmsave, "zrestore");
+ ialloc_set_space(idmemory, space);
+ }
+ pop(1);
+ return 0;
+}
+
+/* ------ Initialization procedure ------ */
+
+BEGIN_OP_DEFS(zvmem_op_defs) {
+ {"1.forgetsave", zforgetsave},
+ {"1restore", zrestore},
+ {"0save", zsave},
+ {"0vmstatus", zvmstatus},
+END_OP_DEFS(0) }
diff --git a/pstoraster/zvmem2.c b/pstoraster/zvmem2.c
new file mode 100644
index 000000000..51381bfc9
--- /dev/null
+++ b/pstoraster/zvmem2.c
@@ -0,0 +1,156 @@
+/* Copyright (C) 1992, 1993, 1994 Aladdin Enterprises. All rights reserved.
+
+ This file is part of GNU Ghostscript.
+
+ GNU Ghostscript is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to
+ anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer to
+ the GNU General Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute GNU
+ Ghostscript, but only under the conditions described in the GNU General
+ Public License. A copy of this license is supposed to have been given to
+ you along with GNU Ghostscript so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+
+ Aladdin Enterprises is not affiliated with the Free Software Foundation or
+ the GNU Project. GNU Ghostscript, as distributed by Aladdin Enterprises,
+ does not depend on any other GNU software.
+*/
+
+/* zvmem2.c */
+/* Level 2 "Virtual memory" operators */
+#include "ghost.h"
+#include "errors.h"
+#include "oper.h"
+#include "estack.h"
+#include "ialloc.h" /* for ivmspace.h */
+#include "ivmspace.h"
+#include "store.h"
+
+/* Garbage collector control parameters. */
+#define default_vm_threshold_SMALL 20000
+#define default_vm_threshold_LARGE 250000
+#if arch_small_memory
+# define default_vm_threshold default_vm_threshold_SMALL
+#else
+# define default_vm_threshold\
+ (gs_if_debug_c('.') ? default_vm_threshold_SMALL :\
+ default_vm_threshold_LARGE)
+#endif
+#define min_vm_threshold 1
+#define max_vm_threshold max_long
+
+/* ------ Local/global VM control ------ */
+
+/* <bool> setglobal/setshared - */
+private int
+zsetglobal(register os_ptr op)
+{ check_type(*op, t_boolean);
+ ialloc_set_space(idmemory,
+ (op->value.boolval ? avm_global : avm_local));
+ pop(1);
+ return 0;
+}
+
+/* <bool> currentglobal/currentshared - */
+private int
+zcurrentglobal(register os_ptr op)
+{ push(1);
+ make_bool(op, ialloc_space(idmemory) != avm_local);
+ return 0;
+}
+
+/* <any> gcheck/scheck <bool> */
+private int
+zgcheck(register os_ptr op)
+{ check_op(1);
+ make_bool(op, (r_is_local(op) ? false : true));
+ return 0;
+}
+
+/* ------ Garbage collector control ------ */
+
+/* These routines are exported for setuserparams. */
+
+/* <int> setvmthreshold - */
+int
+set_vm_threshold(long val)
+{ gs_memory_gc_status_t stat;
+ if ( val < -1 )
+ return_error(e_rangecheck);
+ else if ( val == -1 )
+ val = default_vm_threshold;
+ else if ( val < min_vm_threshold )
+ val = min_vm_threshold;
+ else if ( val > max_vm_threshold )
+ val = max_vm_threshold;
+ gs_memory_gc_status(idmemory->space_global, &stat);
+ stat.vm_threshold = val;
+ gs_memory_set_gc_status(idmemory->space_global, &stat);
+ gs_memory_gc_status(idmemory->space_local, &stat);
+ stat.vm_threshold = val;
+ gs_memory_set_gc_status(idmemory->space_local, &stat);
+ return 0;
+}
+private int
+zsetvmthreshold(register os_ptr op)
+{ int code;
+ check_type(*op, t_integer);
+ code = set_vm_threshold(op->value.intval);
+ if ( code >= 0 )
+ pop(1);
+ return code;
+}
+
+/* <int> vmreclaim - */
+int
+set_vm_reclaim(long val)
+{ if ( val >= -2 && val <= 0 )
+ { gs_memory_gc_status_t stat;
+ gs_memory_gc_status(idmemory->space_system, &stat);
+ stat.enabled = val >= -1;
+ gs_memory_set_gc_status(idmemory->space_system, &stat);
+ gs_memory_gc_status(idmemory->space_global, &stat);
+ stat.enabled = val >= -1;
+ gs_memory_set_gc_status(idmemory->space_global, &stat);
+ gs_memory_gc_status(idmemory->space_local, &stat);
+ stat.enabled = val == 0;
+ gs_memory_set_gc_status(idmemory->space_local, &stat);
+ return 0;
+ }
+ else
+ return_error(e_rangecheck);
+}
+private int
+zvmreclaim(register os_ptr op)
+{ check_type(*op, t_integer);
+ if ( op->value.intval == 1 || op->value.intval == 2 )
+ { /* Force the interpreter to store its state and exit. */
+ /* The interpreter's caller will do the actual GC. */
+ return_error(e_VMreclaim);
+ }
+ else
+ { int code = set_vm_reclaim(op->value.intval);
+ if ( code >= 0 )
+ pop(1);
+ return code;
+ }
+}
+
+/* ------ Initialization procedure ------ */
+
+/* The VM operators are defined even if the initial language level is 1, */
+/* because we need them during initialization. */
+BEGIN_OP_DEFS(zvmem2_op_defs) {
+ {"0.currentglobal", zcurrentglobal},
+ {"1.gcheck", zgcheck},
+ {"1.setglobal", zsetglobal},
+ /* The rest of the operators are defined only in Level 2. */
+ op_def_begin_level2(),
+ {"1setvmthreshold", zsetvmthreshold},
+ {"1vmreclaim", zvmreclaim},
+END_OP_DEFS(0) }
diff --git a/scheduler/Makefile b/scheduler/Makefile
new file mode 100644
index 000000000..91c6ca7f2
--- /dev/null
+++ b/scheduler/Makefile
@@ -0,0 +1,82 @@
+#
+# "$Id$"
+#
+# Scheduler Makefile for the Common UNIX Printing System (CUPS).
+#
+# Copyright 1997-1999 by Easy Software Products, all rights reserved.
+#
+# These coded instructions, statements, and computer programs are the
+# property of Easy Software Products and are protected by Federal
+# copyright law. Distribution and use rights are outlined in the file
+# "LICENSE.txt" which should have been included with this file. If this
+# file is missing or damaged please contact Easy Software Products
+# at:
+#
+# Attn: CUPS Licensing Information
+# Easy Software Products
+# 44141 Airport View Drive, Suite 204
+# Hollywood, Maryland 20636-3111 USA
+#
+# Voice: (301) 373-9603
+# EMail: cups-info@cups.org
+# WWW: http://www.cups.org
+#
+
+include ../Makedefs
+
+CUPSDOBJS = auth.o classes.o client.o conf.o dirsvc.o main.o ipp.o \
+ listen.o job.o printers.o
+OBJS = $(CUPSDOBJS) testspeed.o
+
+#
+# Make everything...
+#
+
+all: cupsd testspeed
+
+#
+# Clean all object files...
+#
+
+clean:
+ rm -f $(OBJS) cupsd testspeed
+
+#
+# Install the scheduler...
+#
+
+install:
+ -$(MKDIR) $(SBINDIR)
+ $(CP) cupsd $(SBINDIR)
+ -$(MKDIR) $(SERVERROOT)/interfaces
+ -$(MKDIR) $(SERVERROOT)/logs
+ -$(MKDIR) $(SERVERROOT)/ppd
+ -$(MKDIR) $(SERVERROOT)/requests
+
+#
+# Make the scheduler executable, "cupsd".
+#
+
+cupsd: $(CUPSDOBJS) ../cups/$(LIBCUPS)
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o cupsd $(CUPSDOBJS) $(LIBS)
+
+$(CUPSDOBJS): auth.h classes.h client.h conf.h cupsd.h dirsvc.h job.h \
+ printers.h ../cups/cups.h ../cups/http.h ../cups/ipp.h \
+ ../cups/language.h ../cups/mime.h ../cups/string.h
+
+#
+# Make the test program, "testspeed".
+#
+
+testspeed: testspeed.o ../cups/$(LIBCUPS)
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o testspeed testspeed.o $(LIBS)
+
+testspeed.o: ../cups/cups.h ../cups/http.h ../cups/ipp.h ../cups/language.h
+
+$(OBJS): ../config.h ../Makedefs
+
+#
+# End of "$Id$".
+#
diff --git a/scheduler/auth.c b/scheduler/auth.c
new file mode 100644
index 000000000..49c21d8c5
--- /dev/null
+++ b/scheduler/auth.c
@@ -0,0 +1,602 @@
+/*
+ * "$Id$"
+ *
+ * Authorization routines for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-1999 by Easy Software Products, all rights reserved.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * Contents:
+ *
+ * AddLocation() - Add a location for authorization.
+ * AllowHost() - Add a host name that is allowed to access the
+ * location.
+ * AllowIP() - Add an IP address or network that is allowed to
+ * access the location.
+ * DeleteAllLocations() - Free all memory used for location authorization.
+ * DenyHost() - Add a host name that is not allowed to access the
+ * location.
+ * DenyIP() - Add an IP address or network that is not allowed
+ * to access the location.
+ * IsAuthorized() - Check to see if the user is authorized...
+ * add_allow() - Add an allow mask to the location.
+ * add_deny() - Add a deny mask to the location.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "cupsd.h"
+#include <pwd.h>
+#include <grp.h>
+#ifdef HAVE_SHADOW_H
+# include <shadow.h>
+#endif /* HAVE_SHADOW_H */
+#ifdef HAVE_CRYPT_H
+# include <crypt.h>
+#endif /* HAVE_CRYPT_H */
+
+/*
+ * Local functions...
+ */
+
+static authmask_t *add_allow(location_t *loc);
+static authmask_t *add_deny(location_t *loc);
+static int check_auth(unsigned ip, char *name, int namelen,
+ int num_masks, authmask_t *masks);
+
+
+/*
+ * 'AddLocation()' - Add a location for authorization.
+ */
+
+location_t * /* O - Pointer to new location record */
+AddLocation(char *location) /* I - Location path */
+{
+ location_t *temp; /* New location */
+
+
+ /*
+ * Try to allocate memory for the new location.
+ */
+
+ if (NumLocations == 0)
+ temp = malloc(sizeof(location_t));
+ else
+ temp = realloc(Locations, sizeof(location_t) * (NumLocations + 1));
+
+ if (temp == NULL)
+ return (NULL);
+
+ Locations = temp;
+ temp += NumLocations;
+ NumLocations ++;
+
+ /*
+ * Initialize the record and copy the name over...
+ */
+
+ memset(temp, 0, sizeof(location_t));
+ strncpy(temp->location, location, sizeof(temp->location) - 1);
+ temp->length = strlen(temp->location);
+
+ LogMessage(LOG_DEBUG, "AddLocation: added location \'%s\'", location);
+
+ /*
+ * Return the new record...
+ */
+
+ return (temp);
+}
+
+
+/*
+ * 'AllowHost()' - Add a host name that is allowed to access the location.
+ */
+
+void
+AllowHost(location_t *loc, /* I - Location to add to */
+ char *name) /* I - Name of host or domain to add */
+{
+ authmask_t *temp; /* New host/domain mask */
+
+
+ if ((temp = add_allow(loc)) == NULL)
+ return;
+
+ temp->type = AUTH_NAME;
+ temp->mask.name.name = strdup(name);
+ temp->mask.name.length = strlen(name);
+
+ LogMessage(LOG_DEBUG, "AllowHost: %s allow %s", loc->location, name);
+}
+
+
+/*
+ * 'AllowIP()' - Add an IP address or network that is allowed to access the
+ * location.
+ */
+
+void
+AllowIP(location_t *loc, /* I - Location to add to */
+ unsigned address, /* I - IP address to add */
+ unsigned netmask) /* I - Netmask of address */
+{
+ authmask_t *temp; /* New host/domain mask */
+
+
+ if ((temp = add_allow(loc)) == NULL)
+ return;
+
+ temp->type = AUTH_IP;
+ temp->mask.ip.address = address;
+ temp->mask.ip.netmask = netmask;
+
+ LogMessage(LOG_DEBUG, "AllowIP: %s allow %08x/%08x", loc->location,
+ address, netmask);
+}
+
+
+/*
+ * 'DeleteAllLocations()' - Free all memory used for location authorization.
+ */
+
+void
+DeleteAllLocations(void)
+{
+ int i, j; /* Looping vars */
+ location_t *loc; /* Current location */
+ authmask_t *mask; /* Current mask */
+
+
+ /*
+ * Free all of the allow/deny records first...
+ */
+
+ for (i = NumLocations, loc = Locations; i > 0; i --, loc ++)
+ {
+ for (j = loc->num_allow, mask = loc->allow; j > 0; j --, mask ++)
+ if (mask->type == AUTH_NAME)
+ free(mask->mask.name.name);
+
+ if (loc->num_allow > 0)
+ free(loc->allow);
+
+ for (j = loc->num_deny, mask = loc->deny; j > 0; j --, mask ++)
+ if (mask->type == AUTH_NAME)
+ free(mask->mask.name.name);
+
+ if (loc->num_deny > 0)
+ free(loc->deny);
+ }
+
+ /*
+ * Then free the location array...
+ */
+
+ if (NumLocations > 0)
+ free(Locations);
+
+ Locations = NULL;
+ NumLocations = 0;
+}
+
+
+/*
+ * 'DenyHost()' - Add a host name that is not allowed to access the location.
+ */
+
+void
+DenyHost(location_t *loc, /* I - Location to add to */
+ char *name) /* I - Name of host or domain to add */
+{
+ authmask_t *temp; /* New host/domain mask */
+
+
+ if ((temp = add_deny(loc)) == NULL)
+ return;
+
+ temp->type = AUTH_NAME;
+ temp->mask.name.name = strdup(name);
+ temp->mask.name.length = strlen(name);
+
+ LogMessage(LOG_DEBUG, "DenyHost: %s deny %s", loc->location, name);
+}
+
+
+/*
+ * 'DenyIP()' - Add an IP address or network that is not allowed to access
+ * the location.
+ */
+
+void
+DenyIP(location_t *loc, /* I - Location to add to */
+ unsigned address, /* I - IP address to add */
+ unsigned netmask) /* I - Netmask of address */
+{
+ authmask_t *temp; /* New host/domain mask */
+
+
+ if ((temp = add_deny(loc)) == NULL)
+ return;
+
+ temp->type = AUTH_IP;
+ temp->mask.ip.address = address;
+ temp->mask.ip.netmask = netmask;
+
+ LogMessage(LOG_DEBUG, "DenyIP: %s deny %08x/%08x\n", loc->location,
+ address, netmask);
+}
+
+
+/*
+ * 'IsAuthorized()' - Check to see if the user is authorized...
+ */
+
+http_status_t /* O - HTTP_OK if authorized or error code */
+IsAuthorized(client_t *con) /* I - Connection */
+{
+ int i, /* Looping var */
+ auth; /* Authorization status */
+ unsigned address; /* Authorization address */
+ location_t *loc, /* Current location */
+ *best; /* Best match for location so far */
+ int bestlen, /* Length of best match */
+ hostlen; /* Length of hostname */
+ struct passwd *pw; /* User password data */
+#ifdef HAVE_SHADOW_H
+ struct spwd *spw; /* Shadow password data */
+#endif /* HAVE_SHADOW_H */
+ struct group *grp; /* Group data */
+
+
+ /*
+ * First loop through the list of locations to find a match...
+ */
+
+ best = NULL;
+ bestlen = 0;
+
+ for (i = NumLocations, loc = Locations; i > 0; i --, loc ++)
+ if (loc->length > bestlen &&
+ strncmp(con->uri, loc->location, loc->length) == 0)
+ {
+ best = loc;
+ bestlen = loc->length;
+ }
+
+ /*
+ * If there is no match, the access is not authorized...
+ */
+
+ if (best == NULL)
+ return (HTTP_FORBIDDEN);
+
+ /*
+ * Check host/ip-based accesses...
+ */
+
+ auth = best->order_type;
+ address = ntohl(con->http.hostaddr.sin_addr.s_addr);
+ hostlen = strlen(con->http.hostname);
+
+ if (address == 0x7f000001 || strcasecmp(con->http.hostname, "localhost") == 0)
+ {
+ /*
+ * Access from localhost (127.0.0.1) is always allowed...
+ */
+
+ auth = AUTH_ALLOW;
+ }
+ else
+ {
+ /*
+ * Do authorization checks on the domain/address...
+ */
+
+ switch (auth)
+ {
+ case AUTH_ALLOW : /* Order Deny,Allow */
+ if (check_auth(address, con->http.hostname, hostlen,
+ best->num_deny, best->deny))
+ auth = AUTH_DENY;
+
+ if (check_auth(address, con->http.hostname, hostlen,
+ best->num_allow, best->allow))
+ auth = AUTH_ALLOW;
+ break;
+
+ case AUTH_DENY : /* Order Allow,Deny */
+ if (check_auth(address, con->http.hostname, hostlen,
+ best->num_allow, best->allow))
+ auth = AUTH_ALLOW;
+
+ if (check_auth(address, con->http.hostname, hostlen,
+ best->num_deny, best->deny))
+ auth = AUTH_DENY;
+ break;
+ }
+ }
+
+ if (auth == AUTH_DENY)
+ return (HTTP_FORBIDDEN);
+
+ /*
+ * Now see what access level is required...
+ */
+
+ if (best->level == AUTH_ANON) /* Anonymous access - allow it */
+ return (HTTP_OK);
+
+ DEBUG_printf(("IsAuthorized: username = \"%s\", password = \"%s\"\n",
+ con->username, con->password));
+
+ if (con->username[0] == '\0' || con->password[0] == '\0')
+ return (HTTP_UNAUTHORIZED); /* Non-anonymous needed user/pass */
+
+ /*
+ * Check the user's password...
+ */
+
+ pw = getpwnam(con->username); /* Get the current password */
+ endpwent(); /* Close the password file */
+
+ if (pw == NULL) /* No such user... */
+ {
+ LogMessage(LOG_WARN, "IsAuthorized: Unknown username \"%s\"; access denied.",
+ con->username);
+ return (HTTP_UNAUTHORIZED);
+ }
+
+ if (pw->pw_passwd[0] == '\0') /* Don't allow blank passwords! */
+ {
+ LogMessage(LOG_WARN, "IsAuthorized: Username \"%s\" has no password; access denied.",
+ con->username);
+ return (HTTP_UNAUTHORIZED);
+ }
+
+ DEBUG_printf(("pw->pw_passwd = \"%s\"\n", pw->pw_passwd));
+
+#ifdef HAVE_SHADOW_H
+ spw = getspnam(con->username);
+ endspent();
+
+ if (spw == NULL && strcmp(pw->pw_passwd, "x") == 0)
+ return (HTTP_UNAUTHORIZED); /* No such user or bad shadow file */
+
+# ifdef DEBUG
+ if (spw != NULL)
+ printf("spw->sp_pwdp = \"%s\"\n", spw->sp_pwdp);
+ else
+ puts("spw = NULL");
+# endif /* DEBUG */
+
+ if (spw != NULL && spw->sp_pwdp[0] == '\0')
+ { /* Don't allow blank passwords! */
+ LogMessage(LOG_WARN, "IsAuthorized: Username \"%s\" has no password; access denied.",
+ con->username);
+ return (HTTP_UNAUTHORIZED);
+ }
+#endif /* HAVE_SHADOW_H */
+
+ /*
+ * OK, the password isn't blank, so compare with what came from the client...
+ */
+
+ DEBUG_printf(("IsAuthorized: pw_passwd = %s, crypt = %s\n",
+ pw->pw_passwd, crypt(con->password, pw->pw_passwd)));
+
+ if (strcmp(pw->pw_passwd, crypt(con->password, pw->pw_passwd)) != 0)
+ {
+#ifdef HAVE_SHADOW_H
+ if (spw != NULL)
+ {
+ DEBUG_printf(("IsAuthorized: sp_pwdp = %s, crypt = %s\n",
+ spw->sp_pwdp, crypt(con->password, spw->sp_pwdp)));
+
+ if (strcmp(spw->sp_pwdp, crypt(con->password, spw->sp_pwdp)) != 0)
+ return (HTTP_UNAUTHORIZED);
+ }
+ else
+#endif /* HAVE_SHADOW_H */
+ return (HTTP_UNAUTHORIZED);
+ }
+
+ /*
+ * OK, the password is good. See if we need normal user access, or group
+ * access...
+ */
+
+ if (best->level == AUTH_USER)
+ return (HTTP_OK);
+
+ /*
+ * Check to see if this user is in the specified group...
+ */
+
+ grp = getgrnam(best->group_name);
+ endgrent();
+
+ if (grp == NULL) /* No group by that name??? */
+ {
+ LogMessage(LOG_WARN, "IsAuthorized: group name \"%s\" does not exist!",
+ best->group_name);
+ return (HTTP_UNAUTHORIZED);
+ }
+
+ for (i = 0; grp->gr_mem[i] != NULL; i ++)
+ if (strcmp(con->username, grp->gr_mem[i]) == 0)
+ return (HTTP_OK);
+
+ /*
+ * Check to see if the default group ID matches for the user...
+ */
+
+ if (grp->gr_gid == pw->pw_gid)
+ return (HTTP_OK);
+
+ /*
+ * The user isn't part of the specified group, so deny access...
+ */
+
+ DEBUG_puts("IsAuthorized: user not in group!");
+
+ return (HTTP_UNAUTHORIZED);
+}
+
+
+/*
+ * 'add_allow()' - Add an allow mask to the location.
+ */
+
+static authmask_t * /* O - New mask record */
+add_allow(location_t *loc) /* I - Location to add to */
+{
+ authmask_t *temp; /* New mask record */
+
+
+ /*
+ * Range-check...
+ */
+
+ if (loc == NULL)
+ return (NULL);
+
+ /*
+ * Try to allocate memory for the record...
+ */
+
+ if (loc->num_allow == 0)
+ temp = malloc(sizeof(authmask_t));
+ else
+ temp = realloc(loc->allow, sizeof(authmask_t) * (loc->num_allow + 1));
+
+ if (temp == NULL)
+ return (NULL);
+
+ loc->allow = temp;
+ temp += loc->num_allow;
+ loc->num_allow ++;
+
+ /*
+ * Clear the mask record and return...
+ */
+
+ memset(temp, 0, sizeof(authmask_t));
+ return (temp);
+}
+
+
+/*
+ * 'add_deny()' - Add a deny mask to the location.
+ */
+
+static authmask_t * /* O - New mask record */
+add_deny(location_t *loc) /* I - Location to add to */
+{
+ authmask_t *temp; /* New mask record */
+
+
+ /*
+ * Range-check...
+ */
+
+ if (loc == NULL)
+ return (NULL);
+
+ /*
+ * Try to allocate memory for the record...
+ */
+
+ if (loc->num_deny == 0)
+ temp = malloc(sizeof(authmask_t));
+ else
+ temp = realloc(loc->deny, sizeof(authmask_t) * (loc->num_deny + 1));
+
+ if (temp == NULL)
+ return (NULL);
+
+ loc->deny = temp;
+ temp += loc->num_deny;
+ loc->num_deny ++;
+
+ /*
+ * Clear the mask record and return...
+ */
+
+ memset(temp, 0, sizeof(authmask_t));
+ return (temp);
+}
+
+
+/*
+ * 'check_auth()' - Check authorization masks.
+ */
+
+static int /* O - 1 if mask matches, 0 otherwise */
+check_auth(unsigned ip, /* I - Client address */
+ char *name, /* I - Client hostname */
+ int name_len, /* I - Length of hostname */
+ int num_masks,/* I - Number of masks */
+ authmask_t *masks) /* I - Masks */
+{
+ while (num_masks > 0)
+ {
+ switch (masks->type)
+ {
+ case AUTH_NAME :
+ /*
+ * Check for exact name match...
+ */
+
+ if (strcasecmp(name, masks->mask.name.name) == 0)
+ return (1);
+
+ /*
+ * Check for domain match...
+ */
+
+ if (name_len >= masks->mask.name.length &&
+ masks->mask.name.name[0] == '.' &&
+ strcasecmp(name + name_len - masks->mask.name.length,
+ masks->mask.name.name) == 0)
+ return (1);
+ break;
+
+ case AUTH_IP :
+ /*
+ * Check for IP/network address match...
+ */
+
+ if ((ip & masks->mask.ip.netmask) == masks->mask.ip.address)
+ return (1);
+ break;
+ }
+
+ masks ++;
+ num_masks --;
+ }
+
+ return (0);
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/scheduler/auth.h b/scheduler/auth.h
new file mode 100644
index 000000000..e9fdc79bf
--- /dev/null
+++ b/scheduler/auth.h
@@ -0,0 +1,108 @@
+/*
+ * "$Id$"
+ *
+ * Authorization definitions for the Common UNIX Printing System (CUPS)
+ * scheduler.
+ *
+ * Copyright 1997-1999 by Easy Software Products, all rights reserved.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ */
+
+/*
+ * HTTP authorization types and levels...
+ */
+
+#define AUTH_ANON 0 /* Anonymous access */
+#define AUTH_USER 1 /* Must have a valid username/password */
+#define AUTH_GROUP 2 /* Must also be in a named group */
+
+#define AUTH_ALLOW 0 /* Allow access */
+#define AUTH_DENY 1 /* Deny access */
+
+#define AUTH_NAME 0 /* Authorize host by name */
+#define AUTH_IP 1 /* Authorize host by IP */
+
+
+/*
+ * HTTP authorization structures...
+ */
+
+typedef struct
+{
+ unsigned address, /* IP address */
+ netmask; /* IP netmask */
+} ipmask_t;
+
+typedef struct
+{
+ int length; /* Length of name */
+ char *name; /* Name string */
+} namemask_t;
+
+typedef struct
+{
+ int type; /* Mask type */
+ union
+ {
+ namemask_t name; /* Host/Domain name */
+ ipmask_t ip; /* IP address/network */
+ } mask; /* Mask data */
+} authmask_t;
+
+typedef struct
+{
+ char location[HTTP_MAX_URI]; /* Location of resource */
+ int length, /* Length of location string */
+ order_type, /* Allow or Deny */
+ level; /* Access level required */
+ char group_name[MAX_USERPASS];/* User group name */
+ int num_allow; /* Number of Allow lines */
+ authmask_t *allow; /* Allow lines */
+ int num_deny; /* Number of Deny lines */
+ authmask_t *deny; /* Deny lines */
+} location_t;
+
+
+/*
+ * Globals...
+ */
+
+VAR int NumLocations VALUE(0);
+ /* Number of authorization locations */
+VAR location_t *Locations VALUE(NULL);
+ /* Authorization locations */
+
+
+/*
+ * Prototypes...
+ */
+
+extern location_t *AddLocation(char *location);
+extern void AllowHost(location_t *loc, char *name);
+extern void AllowIP(location_t *loc, unsigned address,
+ unsigned netmask);
+extern void DeleteAllLocations(void);
+extern void DenyHost(location_t *loc, char *name);
+extern void DenyIP(location_t *loc, unsigned address,
+ unsigned netmask);
+extern http_status_t IsAuthorized(client_t *con);
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/scheduler/classes.c b/scheduler/classes.c
new file mode 100644
index 000000000..faaeff058
--- /dev/null
+++ b/scheduler/classes.c
@@ -0,0 +1,522 @@
+/*
+ * "$Id$"
+ *
+ * Printer class routines for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-1999 by Easy Software Products, all rights reserved.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * Contents:
+ *
+ * AddClass() - Add a class to the system.
+ * AddPrinterToClass() - Add a printer to a class...
+ * DeletePrinterFromClass() - Delete a printer from a class.
+ * DeletePrinterFromClasses() - Delete a printer from all classes.
+ * DeleteAllClasses() - Remove all classes from the system.
+ * FindAvailablePrinter() - Find an available printer in a class.
+ * FindClass() - Find the named class.
+ * LoadAllClasses() - Load classes from the classes.conf file.
+ * SaveAllClasses() - Save classes to the classes.conf file.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "cupsd.h"
+
+
+/*
+ * 'AddClass()' - Add a class to the system.
+ */
+
+printer_t * /* O - New class */
+AddClass(char *name) /* I - Name of class */
+{
+ printer_t *c; /* New class */
+
+
+ /*
+ * Add the printer and set the type to "class"...
+ */
+
+ if ((c = AddPrinter(name)) != NULL)
+ {
+ c->type = CUPS_PRINTER_CLASS;
+ sprintf(c->uri, "ipp://%s:%d/classes/%s", ServerName,
+ ntohs(Listeners[0].address.sin_port), name);
+ SetPrinterAttrs(c);
+ }
+
+ return (c);
+}
+
+
+/*
+ * 'AddPrinterToClass()' - Add a printer to a class...
+ */
+
+void
+AddPrinterToClass(printer_t *c, /* I - Class to add to */
+ printer_t *p) /* I - Printer to add */
+{
+ printer_t **temp; /* Pointer to printer array */
+
+
+ /*
+ * Allocate memory as needed...
+ */
+
+ if (c->num_printers == 0)
+ temp = malloc(sizeof(printer_t *));
+ else
+ temp = realloc(c->printers, sizeof(printer_t *) * (c->num_printers + 1));
+
+ if (temp == NULL)
+ {
+ LogMessage(LOG_ERROR, "Unable to add printer %s to class %s!",
+ p->name, c->name);
+ return;
+ }
+
+ /*
+ * Add the printer to the end of the array and update the number of printers.
+ */
+
+ c->printers = temp;
+ temp += c->num_printers;
+ c->num_printers ++;
+
+ *temp = p;
+
+ /*
+ * Update the IPP attributes...
+ */
+
+ SetPrinterAttrs(c);
+}
+
+
+/*
+ * 'DeletePrinterFromClass()' - Delete a printer from a class.
+ */
+
+void
+DeletePrinterFromClass(printer_t *c, /* I - Class to delete from */
+ printer_t *p) /* I - Printer to delete */
+{
+ int i; /* Looping var */
+
+
+ /*
+ * See if the printer is in the class...
+ */
+
+ for (i = 0; i < c->num_printers; i ++)
+ if (p == c->printers[i])
+ break;
+
+ /*
+ * If it is, remove it from the list...
+ */
+
+ if (i < c->num_printers)
+ {
+ /*
+ * Yes, remove the printer...
+ */
+
+ c->num_printers --;
+ if (i < c->num_printers)
+ memcpy(c->printers + i, c->printers + i + 1,
+ (c->num_printers - i) * sizeof(printer_t *));
+ }
+
+ /*
+ * If there are no more printers in this class, delete the class...
+ */
+
+ if (c->num_printers == 0)
+ {
+ DeletePrinter(c);
+ return;
+ }
+
+ /*
+ * Recompute the printer type mask...
+ */
+
+ c->type = ~CUPS_PRINTER_REMOTE;
+
+ for (i = 0; i < c->num_printers; i ++)
+ c->type &= c->printers[i]->type;
+
+ c->type |= CUPS_PRINTER_CLASS;
+
+ /*
+ * Update the IPP attributes...
+ */
+
+ SetPrinterAttrs(c);
+}
+
+
+/*
+ * 'DeletePrinterFromClasses()' - Delete a printer from all classes.
+ */
+
+void
+DeletePrinterFromClasses(printer_t *p) /* I - Printer to delete */
+{
+ printer_t *c; /* Pointer to current class */
+
+
+ /*
+ * Loop through the printer/class list and remove the printer
+ * from each class listed...
+ */
+
+ for (c = Printers; c != NULL; c = c->next)
+ if (c->type & CUPS_PRINTER_CLASS)
+ DeletePrinterFromClass(c, p);
+}
+
+
+/*
+ * 'DeleteAllClasses()' - Remove all classes from the system.
+ */
+
+void
+DeleteAllClasses(void)
+{
+ printer_t *c, /* Pointer to current printer/class */
+ *next; /* Pointer to next printer in list */
+
+
+ for (c = Printers; c != NULL; c = next)
+ {
+ next = c->next;
+
+ if (c->type & CUPS_PRINTER_CLASS)
+ DeletePrinter(c);
+ }
+}
+
+
+/*
+ * 'FindAvailablePrinter()' - Find an available printer in a class.
+ */
+
+printer_t * /* O - Available printer or NULL */
+FindAvailablePrinter(char *name) /* I - Class to check */
+{
+ int i; /* Looping var */
+ printer_t *c; /* Printer class */
+
+
+ /*
+ * Find the class...
+ */
+
+ if ((c = FindClass(name)) == NULL)
+ return (NULL);
+
+ /*
+ * Loop through the printers in the class and return the first idle
+ * printer... [MRS - might want to rotate amongst the available
+ * printers in a future incarnation]
+ */
+
+ for (i = 0; i < c->num_printers; i ++)
+ if (c->printers[i]->state == IPP_PRINTER_IDLE)
+ return (c->printers[i]);
+
+ return (NULL);
+}
+
+
+/*
+ * 'FindClass()' - Find the named class.
+ */
+
+printer_t * /* O - Matching class or NULL */
+FindClass(char *name) /* I - Name of class */
+{
+ printer_t *c; /* Current class/printer */
+
+
+ for (c = Printers; c != NULL; c = c->next)
+ switch (strcasecmp(name, c->name))
+ {
+ case 0 : /* name == c->name */
+ if (c->type & CUPS_PRINTER_CLASS)
+ return (c);
+ case 1 : /* name > c->name */
+ break;
+ case -1 : /* name < c->name */
+ return (NULL);
+ }
+
+ return (NULL);
+}
+
+
+/*
+ * 'LoadAllClasses()' - Load classes from the classes.conf file.
+ */
+
+void
+LoadAllClasses(void)
+{
+ FILE *fp; /* classes.conf file */
+ int i; /* Looping var */
+ int linenum; /* Current line number */
+ int len; /* Length of line */
+ char line[HTTP_MAX_BUFFER], /* Line from file */
+ name[256], /* Parameter name */
+ *nameptr, /* Pointer into name */
+ *value, /* Pointer to value */
+ *lineptr; /* Pointer in line */
+ printer_t *p, /* Current printer class */
+ *temp; /* Temporary pointer to printer */
+
+
+ /*
+ * Open the classes.conf file...
+ */
+
+ sprintf(line, "%s/conf/classes.conf", ServerRoot);
+ if ((fp = fopen(line, "r")) == NULL)
+ return;
+
+ /*
+ * Read class configurations until we hit EOF...
+ */
+
+ linenum = 0;
+ p = NULL;
+
+ while (fgets(line, sizeof(line), fp) != NULL)
+ {
+ linenum ++;
+
+ /*
+ * Skip comment lines...
+ */
+
+ if (line[0] == '#')
+ continue;
+
+ /*
+ * Strip trailing newline, if any...
+ */
+
+ len = strlen(line);
+
+ if (line[len - 1] == '\n')
+ {
+ len --;
+ line[len] = '\0';
+ }
+
+ /*
+ * Extract the name from the beginning of the line...
+ */
+
+ for (value = line; isspace(*value); value ++);
+
+ for (nameptr = name; *value != '\0' && !isspace(*value);)
+ *nameptr++ = *value++;
+ *nameptr = '\0';
+
+ while (isspace(*value))
+ value ++;
+
+ if (name[0] == '\0')
+ continue;
+
+ /*
+ * Decode the directive...
+ */
+
+ if (strcmp(name, "<Class") == 0 ||
+ strcmp(name, "<DefaultClass") == 0)
+ {
+ /*
+ * <Class name> or <DefaultClass name>
+ */
+
+ if (line[len - 1] == '>' && p == NULL)
+ {
+ line[len - 1] = '\0';
+
+ p = AddClass(value);
+ p->accepting = 1;
+ p->state = IPP_PRINTER_IDLE;
+
+ if (strcmp(name, "<DefaultClass") == 0)
+ DefaultPrinter = p;
+ }
+ else
+ {
+ LogMessage(LOG_ERROR, "Syntax error on line %d of classes.conf.",
+ linenum);
+ return;
+ }
+ }
+ else if (strcmp(name, "</Class>") == 0)
+ {
+ if (p != NULL)
+ {
+ SetPrinterAttrs(p);
+ p = NULL;
+ }
+ else
+ {
+ LogMessage(LOG_ERROR, "Syntax error on line %d of classes.conf.",
+ linenum);
+ return;
+ }
+ }
+ else if (p == NULL)
+ {
+ LogMessage(LOG_ERROR, "Syntax error on line %d of classes.conf.",
+ linenum);
+ return;
+ }
+
+ else if (strcmp(name, "Info") == 0)
+ strncpy(p->info, value, sizeof(p->info) - 1);
+ else if (strcmp(name, "MoreInfo") == 0)
+ strncpy(p->more_info, value, sizeof(p->more_info) - 1);
+ else if (strcmp(name, "Location") == 0)
+ strncpy(p->location, value, sizeof(p->location) - 1);
+ else if (strcmp(name, "Printer") == 0)
+ {
+ if ((temp = FindPrinter(value)) != NULL)
+ AddPrinterToClass(p, temp);
+ else
+ LogMessage(LOG_WARN, "Unknown printer %s on line %d of classes.conf.",
+ value, linenum);
+ }
+ else if (strcmp(name, "State") == 0)
+ {
+ /*
+ * Set the initial queue state...
+ */
+
+ if (strcasecmp(value, "idle") == 0)
+ p->state = IPP_PRINTER_IDLE;
+ else if (strcasecmp(value, "stopped") == 0)
+ p->state = IPP_PRINTER_STOPPED;
+
+ p->accepting = p->state != IPP_PRINTER_STOPPED;
+ }
+ }
+
+ fclose(fp);
+}
+
+
+/*
+ * 'SaveAllClasses()' - Save classes to the classes.conf file.
+ */
+
+void
+SaveAllClasses(void)
+{
+ FILE *fp; /* classes.conf file */
+ char temp[1024]; /* Temporary string */
+ printer_t *pclass; /* Current printer class */
+ int i; /* Looping var */
+ time_t curtime; /* Current time */
+ struct tm *curdate; /* Current date */
+
+
+ /*
+ * Create the classes.conf file...
+ */
+
+ sprintf(temp, "%s/conf/classes.conf", ServerRoot);
+ if ((fp = fopen(temp, "w")) == NULL)
+ {
+ LogMessage(LOG_ERROR, "Unable to save classes.conf - %s", strerror(errno));
+ return;
+ }
+ else
+ LogMessage(LOG_INFO, "Saving classes.conf...");
+
+ /*
+ * Write a small header to the file...
+ */
+
+ curtime = time(NULL);
+ curdate = gmtime(&curtime);
+ strftime(temp, sizeof(temp) - 1, "# Written by cupsd on %c\n", curdate);
+
+ fputs("# Class configuration file for " CUPS_SVERSION "\n", fp);
+ fputs(temp, fp);
+
+ /*
+ * Write each local class known to the system...
+ */
+
+ for (pclass = Printers; pclass != NULL; pclass = pclass->next)
+ {
+ /*
+ * Skip remote destinations and regular printers...
+ */
+
+ if ((pclass->type & CUPS_PRINTER_REMOTE) ||
+ (pclass->type & CUPS_PRINTER_IMPLICIT) ||
+ !(pclass->type & CUPS_PRINTER_CLASS))
+ continue;
+
+ /*
+ * Write printers as needed...
+ */
+
+ if (pclass == DefaultPrinter)
+ fprintf(fp, "<DefaultClass %s>\n", pclass->name);
+ else
+ fprintf(fp, "<Class %s>\n", pclass->name);
+
+ if (pclass->info[0])
+ fprintf(fp, "Info %s\n", pclass->info);
+ if (pclass->more_info[0])
+ fprintf(fp, "MoreInfo %s\n", pclass->more_info);
+ if (pclass->location[0])
+ fprintf(fp, "Location %s\n", pclass->location);
+ if (pclass->state == IPP_PRINTER_STOPPED)
+ fputs("State Stopped\n", fp);
+ else
+ fputs("State Idle\n", fp);
+
+ for (i = 0; i < pclass->num_printers; i ++)
+ fprintf(fp, "Printer %s\n", pclass->printers[i]->name);
+
+ fputs("</Class>\n", fp);
+ }
+
+ fclose(fp);
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/scheduler/classes.h b/scheduler/classes.h
new file mode 100644
index 000000000..1dbd941eb
--- /dev/null
+++ b/scheduler/classes.h
@@ -0,0 +1,43 @@
+/*
+ * "$Id$"
+ *
+ * Printer class definitions for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-1999 by Easy Software Products, all rights reserved.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ */
+
+
+/*
+ * Prototypes...
+ */
+
+extern printer_t *AddClass(char *name);
+extern void AddPrinterToClass(printer_t *c, printer_t *p);
+extern void DeletePrinterFromClass(printer_t *c, printer_t *p);
+extern void DeletePrinterFromClasses(printer_t *p);
+extern void DeleteAllClasses(void);
+extern printer_t *FindAvailablePrinter(char *name);
+extern printer_t *FindClass(char *name);
+extern void LoadAllClasses(void);
+extern void SaveAllClasses(void);
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/scheduler/client.c b/scheduler/client.c
new file mode 100644
index 000000000..8b683f17e
--- /dev/null
+++ b/scheduler/client.c
@@ -0,0 +1,1507 @@
+/*
+ * "$Id$"
+ *
+ * Client routines for the Common UNIX Printing System (CUPS) scheduler.
+ *
+ * Copyright 1997-1999 by Easy Software Products, all rights reserved.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * Contents:
+ *
+ * AcceptClient() - Accept a new client.
+ * CloseAllClients() - Close all remote clients immediately.
+ * CloseClient() - Close a remote client.
+ * ReadClient() - Read data from a client.
+ * SendCommand() - Send output from a command via HTTP.
+ * SendError() - Send an error message via HTTP.
+ * SendFile() - Send a file via HTTP.
+ * SendHeader() - Send an HTTP request.
+ * WriteClient() - Write data to a client as needed.
+ * check_if_modified() - Decode an "If-Modified-Since" line.
+ * decode_basic_auth() - Decode a Basic authorization string.
+ * get_file() - Get a filename and state info.
+ * pipe_command() - Pipe the output of a command to the remote client.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "cupsd.h"
+
+
+/*
+ * Local functions...
+ */
+
+static int check_if_modified(client_t *con, struct stat *filestats);
+static void decode_basic_auth(client_t *con);
+static char *get_file(client_t *con, struct stat *filestats);
+static int pipe_command(client_t *con, int infile, int *outfile, char *command, char *options);
+
+
+/*
+ * 'AcceptClient()' - Accept a new client.
+ */
+
+void
+AcceptClient(listener_t *lis) /* I - Listener socket */
+{
+ int i; /* Looping var */
+ int val; /* Parameter value */
+ client_t *con; /* New client pointer */
+ unsigned address;/* Address of client */
+ struct hostent *host; /* Host entry for address */
+
+
+ DEBUG_printf(("AcceptClient(%08x) %d NumClients = %d\n",
+ lis, lis->fd, NumClients));
+
+ /*
+ * Get a pointer to the next available client...
+ */
+
+ con = Clients + NumClients;
+
+ memset(con, 0, sizeof(client_t));
+ con->http.activity = time(NULL);
+
+ /*
+ * Accept the client and get the remote address...
+ */
+
+ val = sizeof(struct sockaddr_in);
+
+ if ((con->http.fd = accept(lis->fd, (struct sockaddr *)&(con->http.hostaddr),
+ &val)) < 0)
+ {
+ LogMessage(LOG_ERROR, "accept() failed - %s.", strerror(errno));
+ return;
+ }
+
+ con->http.hostaddr.sin_port = lis->address.sin_port;
+
+ /*
+ * Get the hostname or format the IP address as needed...
+ */
+
+ address = ntohl(con->http.hostaddr.sin_addr.s_addr);
+
+ if (HostNameLookups)
+#ifndef __sgi
+ host = gethostbyaddr((char *)&address, sizeof(address), AF_INET);
+#else
+ host = gethostbyaddr(&address, sizeof(address), AF_INET);
+#endif /* !__sgi */
+ else
+ host = NULL;
+
+ if (host == NULL)
+ sprintf(con->http.hostname, "%d.%d.%d.%d", (address >> 24) & 255,
+ (address >> 16) & 255, (address >> 8) & 255, address & 255);
+ else
+ strncpy(con->http.hostname, host->h_name, sizeof(con->http.hostname) - 1);
+
+ LogMessage(LOG_DEBUG, "accept() %d from %s:%d.", con->http.fd,
+ con->http.hostname, ntohs(con->http.hostaddr.sin_port));
+
+ /*
+ * Add the socket to the select() input mask.
+ */
+
+ fcntl(con->http.fd, F_SETFD, fcntl(con->http.fd, F_GETFD) | FD_CLOEXEC);
+
+ DEBUG_printf(("AcceptClient: Adding fd %d to InputSet...\n", con->http.fd));
+ FD_SET(con->http.fd, &InputSet);
+
+ NumClients ++;
+
+ /*
+ * Temporarily suspend accept()'s until we lose a client...
+ */
+
+ if (NumClients == MaxClients)
+ for (i = 0; i < NumListeners; i ++)
+ {
+ DEBUG_printf(("AcceptClient: Removing fd %d from InputSet...\n", Listeners[i].fd));
+ FD_CLR(Listeners[i].fd, &InputSet);
+ }
+}
+
+
+/*
+ * 'CloseAllClients()' - Close all remote clients immediately.
+ */
+
+void
+CloseAllClients(void)
+{
+ while (NumClients > 0)
+ CloseClient(Clients);
+}
+
+
+/*
+ * 'CloseClient()' - Close a remote client.
+ */
+
+void
+CloseClient(client_t *con) /* I - Client to close */
+{
+ int i; /* Looping var */
+ int status; /* Exit status of pipe command */
+
+
+ LogMessage(LOG_DEBUG, "CloseClient() %d", con->http.fd);
+
+ /*
+ * Close the socket and clear the file from the input set for select()...
+ */
+
+ if (con->http.fd > 0)
+ {
+ DEBUG_printf(("CloseClient: Removing fd %d from InputSet...\n", con->http.fd));
+ close(con->http.fd);
+ FD_CLR(con->http.fd, &InputSet);
+ FD_CLR(con->http.fd, &OutputSet);
+ con->http.fd = 0;
+ }
+
+ for (i = 0; i < NumListeners; i ++)
+ {
+ DEBUG_printf(("CloseClient: Adding fd %d to InputSet...\n", Listeners[i].fd));
+ FD_SET(Listeners[i].fd, &InputSet);
+ }
+
+ if (con->pipe_pid != 0)
+ {
+ DEBUG_printf(("CloseClient: Removing fd %d from InputSet...\n", con->file));
+ FD_CLR(con->file, &InputSet);
+ }
+
+ /*
+ * If we have a data file open, close it...
+ */
+
+ if (con->file)
+ {
+ if (con->pipe_pid)
+ {
+ kill(con->pipe_pid, SIGKILL);
+ waitpid(con->pipe_pid, &status, WNOHANG);
+ }
+
+ FD_CLR(con->file, &InputSet);
+ close(con->file);
+ con->file = 0;
+ }
+
+ /*
+ * Compact the list of clients as necessary...
+ */
+
+ NumClients --;
+
+ if (con < (Clients + NumClients))
+ memcpy(con, con + 1, (Clients + NumClients - con) * sizeof(client_t));
+}
+
+
+/*
+ * 'ReadClient()' - Read data from a client.
+ */
+
+int /* O - 1 on success, 0 on error */
+ReadClient(client_t *con) /* I - Client to read from */
+{
+ char line[8192], /* Line from client... */
+ operation[64], /* Operation code from socket */
+ version[64]; /* HTTP version number string */
+ int major, minor; /* HTTP version numbers */
+ http_status_t status; /* Transfer status */
+ ipp_state_t ipp_state; /* State of IPP transfer */
+ int bytes; /* Number of bytes to POST */
+ char *filename; /* Name of file for GET/HEAD */
+ struct stat filestats; /* File information */
+ mime_type_t *type; /* MIME type of file */
+ char command[1024], /* Command to run */
+ *options; /* Options/CGI data */
+ printer_t *p; /* Printer */
+
+
+ status = HTTP_CONTINUE;
+
+ switch (con->http.state)
+ {
+ case HTTP_WAITING :
+ /*
+ * See if we've received a request line...
+ */
+
+ if (httpGets(line, sizeof(line) - 1, HTTP(con)) == NULL)
+ {
+ CloseClient(con);
+ return (0);
+ }
+
+ /*
+ * Ignore blank request lines...
+ */
+
+ if (line[0] == '\0')
+ break;
+
+ /*
+ * Clear other state variables...
+ */
+
+ httpClearFields(HTTP(con));
+
+ con->http.activity = time(NULL);
+ con->http.version = HTTP_1_0;
+ con->http.keep_alive = HTTP_KEEPALIVE_OFF;
+ con->http.data_encoding = HTTP_ENCODE_LENGTH;
+ con->http.data_remaining = 0;
+ con->operation = HTTP_WAITING;
+ con->bytes = 0;
+ con->file = 0;
+ con->pipe_pid = 0;
+ con->username[0] = '\0';
+ con->password[0] = '\0';
+ con->uri[0] = '\0';
+
+ if (con->language != NULL)
+ {
+ cupsLangFree(con->language);
+ con->language = NULL;
+ }
+
+ /*
+ * Grab the request line...
+ */
+
+ switch (sscanf(line, "%63s%1023s%s", operation, con->uri, version))
+ {
+ case 1 :
+ SendError(con, HTTP_BAD_REQUEST);
+ CloseClient(con);
+ return (0);
+ case 2 :
+ con->http.version = HTTP_0_9;
+ break;
+ case 3 :
+ if (sscanf(version, "HTTP/%d.%d", &major, &minor) != 2)
+ {
+ SendError(con, HTTP_BAD_REQUEST);
+ CloseClient(con);
+ return (0);
+ }
+
+ if (major < 2)
+ {
+ con->http.version = (http_version_t)(major * 100 + minor);
+ if (con->http.version == HTTP_1_1)
+ con->http.keep_alive = HTTP_KEEPALIVE_ON;
+ else
+ con->http.keep_alive = HTTP_KEEPALIVE_OFF;
+ }
+ else
+ {
+ SendError(con, HTTP_NOT_SUPPORTED);
+ CloseClient(con);
+ return (0);
+ }
+ break;
+ }
+
+ /*
+ * Process the request...
+ */
+
+ if (strcmp(operation, "GET") == 0)
+ con->http.state = HTTP_GET;
+ else if (strcmp(operation, "PUT") == 0)
+ con->http.state = HTTP_PUT;
+ else if (strcmp(operation, "POST") == 0)
+ con->http.state = HTTP_POST;
+ else if (strcmp(operation, "DELETE") == 0)
+ con->http.state = HTTP_DELETE;
+ else if (strcmp(operation, "TRACE") == 0)
+ con->http.state = HTTP_TRACE;
+ else if (strcmp(operation, "CLOSE") == 0)
+ con->http.state = HTTP_CLOSE;
+ else if (strcmp(operation, "OPTIONS") == 0)
+ con->http.state = HTTP_OPTIONS;
+ else if (strcmp(operation, "HEAD") == 0)
+ con->http.state = HTTP_HEAD;
+ else
+ {
+ SendError(con, HTTP_BAD_REQUEST);
+ CloseClient(con);
+ return (0);
+ }
+
+ con->start = time(NULL);
+ con->operation = con->http.state;
+
+ LogMessage(LOG_DEBUG, "ReadClient() %d %s %s HTTP/%d.%d", con->http.fd,
+ operation, con->uri,
+ con->http.version / 100, con->http.version % 100);
+
+ con->http.status = HTTP_OK;
+ break;
+
+ case HTTP_CLOSE :
+ case HTTP_DELETE :
+ case HTTP_GET :
+ case HTTP_HEAD :
+ case HTTP_POST :
+ case HTTP_PUT :
+ case HTTP_TRACE :
+ /*
+ * Parse incoming parameters until the status changes...
+ */
+
+ status = httpUpdate(HTTP(con));
+
+ if (status != HTTP_OK && status != HTTP_CONTINUE)
+ {
+ SendError(con, HTTP_BAD_REQUEST);
+ CloseClient(con);
+ return (0);
+ }
+ break;
+ }
+
+ /*
+ * Handle new transfers...
+ */
+
+ if (status == HTTP_OK)
+ {
+ con->language = cupsLangGet(con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE]);
+
+ decode_basic_auth(con);
+
+ if (con->http.fields[HTTP_FIELD_HOST][0] == '\0' &&
+ con->http.version >= HTTP_1_0)
+ {
+ if (!SendError(con, HTTP_BAD_REQUEST))
+ {
+ CloseClient(con);
+ return (0);
+ }
+ }
+ else if (strncmp(con->uri, "..", 2) == 0)
+ {
+ /*
+ * Protect against malicious users!
+ */
+
+ if (!SendError(con, HTTP_FORBIDDEN))
+ {
+ CloseClient(con);
+ return (0);
+ }
+ }
+ else if (con->uri[0] != '/')
+ {
+ /*
+ * Don't allow proxying (yet)...
+ */
+
+ if (!SendError(con, HTTP_METHOD_NOT_ALLOWED))
+ {
+ CloseClient(con);
+ return (0);
+ }
+ }
+ else if ((status = IsAuthorized(con)) != HTTP_OK)
+ {
+ SendError(con, status);
+ CloseClient(con);
+ return (0);
+ }
+ else switch (con->http.state)
+ {
+ case HTTP_GET_SEND :
+ if (strncmp(con->uri, "/printers/", 10) == 0 &&
+ strcmp(con->uri + strlen(con->uri) - 4, ".ppd") == 0)
+ {
+ /*
+ * Send PPD file - get the real printer name since printer
+ * names are not case sensitive but filename can be...
+ */
+
+ con->uri[strlen(con->uri) - 4] = '\0'; /* Drop ".ppd" */
+
+ if ((p = FindPrinter(con->uri + 10)) != NULL)
+ sprintf(con->uri, "/ppd/%s.ppd", p->name);
+ else
+ {
+ if (!SendError(con, HTTP_NOT_FOUND))
+ {
+ CloseClient(con);
+ return (0);
+ }
+
+ break;
+ }
+ }
+
+ if (strncmp(con->uri, "/printers", 9) == 0 ||
+ strncmp(con->uri, "/classes", 8) == 0 ||
+ strncmp(con->uri, "/jobs", 5) == 0)
+ {
+ /*
+ * Send CGI output...
+ */
+
+ if (strncmp(con->uri, "/printers", 9) == 0)
+ {
+ sprintf(command, "%s/cgi-bin/printers.cgi", ServerRoot);
+ options = con->uri + 9;
+ }
+ else if (strncmp(con->uri, "/classes", 8) == 0)
+ {
+ sprintf(command, "%s/cgi-bin/classes.cgi", ServerRoot);
+ options = con->uri + 8;
+ }
+ else
+ {
+ sprintf(command, "%s/cgi-bin/jobs.cgi", ServerRoot);
+ options = con->uri + 5;
+ }
+
+ if (*options == '/')
+ options ++;
+
+ if (!SendCommand(con, command, options))
+ {
+ if (!SendError(con, HTTP_NOT_FOUND))
+ {
+ CloseClient(con);
+ return (0);
+ }
+ }
+ else
+ LogRequest(con, HTTP_OK);
+
+ if (con->http.version <= HTTP_1_0)
+ con->http.keep_alive = HTTP_KEEPALIVE_OFF;
+ }
+ else
+ {
+ /*
+ * Serve a file...
+ */
+
+ if ((filename = get_file(con, &filestats)) == NULL)
+ {
+ if (!SendError(con, HTTP_NOT_FOUND))
+ {
+ CloseClient(con);
+ return (0);
+ }
+ }
+ else if (!check_if_modified(con, &filestats))
+ {
+ if (!SendError(con, HTTP_NOT_MODIFIED))
+ {
+ CloseClient(con);
+ return (0);
+ }
+ }
+ else
+ {
+ type = mimeFileType(MimeDatabase, filename);
+ if (type == NULL)
+ strcpy(line, "text/plain");
+ else
+ sprintf(line, "%s/%s", type->super, type->type);
+
+ if (!SendFile(con, HTTP_OK, filename, line, &filestats))
+ {
+ CloseClient(con);
+ return (0);
+ }
+ }
+ }
+ break;
+
+ case HTTP_POST_RECV :
+ /*
+ * See if the POST request includes a Content-Length field, and if
+ * so check the length against any limits that are set...
+ */
+
+ if (con->http.fields[HTTP_FIELD_CONTENT_LENGTH][0] &&
+ atoi(con->http.fields[HTTP_FIELD_CONTENT_LENGTH]) > MaxRequestSize &&
+ MaxRequestSize > 0)
+ {
+ /*
+ * Request too large...
+ */
+
+ if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
+ {
+ CloseClient(con);
+ return (0);
+ }
+
+ break;
+ }
+
+ /*
+ * See what kind of POST request this is; for IPP requests the
+ * content-type field will be "application/ipp"...
+ */
+
+ if (strcmp(con->http.fields[HTTP_FIELD_CONTENT_TYPE], "application/ipp") == 0)
+ con->request = ippNew();
+ else if (strcmp(con->http.fields[HTTP_FIELD_CONTENT_TYPE], "application/x-www-form-urlencoded") == 0 &&
+ (strncmp(con->uri, "/printers", 9) == 0 ||
+ strncmp(con->uri, "/classes", 8) == 0 ||
+ strncmp(con->uri, "/jobs", 5) == 0))
+ {
+ /*
+ * CGI request...
+ */
+
+ if (strncmp(con->uri, "/printers", 9) == 0)
+ {
+ sprintf(command, "%s/cgi-bin/printers", ServerRoot);
+ options = con->uri + 9;
+ }
+ else if (strncmp(con->uri, "/classes", 8) == 0)
+ {
+ sprintf(command, "%s/cgi-bin/classes", ServerRoot);
+ options = con->uri + 8;
+ }
+ else
+ {
+ sprintf(command, "%s/cgi-bin/jobs", ServerRoot);
+ options = con->uri + 5;
+ }
+
+ if (*options == '/')
+ options ++;
+
+ if (!SendCommand(con, command, options))
+ {
+ if (!SendError(con, HTTP_NOT_FOUND))
+ {
+ CloseClient(con);
+ return (0);
+ }
+ }
+ else
+ LogRequest(con, HTTP_OK);
+
+ if (con->http.version <= HTTP_1_0)
+ con->http.keep_alive = HTTP_KEEPALIVE_OFF;
+ }
+ else if (!SendError(con, HTTP_UNAUTHORIZED))
+ {
+ CloseClient(con);
+ return (0);
+ }
+ break;
+
+ case HTTP_PUT_RECV :
+ case HTTP_DELETE :
+ case HTTP_TRACE :
+ SendError(con, HTTP_NOT_IMPLEMENTED);
+
+ case HTTP_CLOSE :
+ CloseClient(con);
+ return (0);
+
+ case HTTP_HEAD :
+ if (strncmp(con->uri, "/printers", 9) == 0 &&
+ strcmp(con->uri + strlen(con->uri) - 4, ".ppd") == 0)
+ {
+ /*
+ * Send PPD file...
+ */
+
+ sprintf(command, "/ppd/%s", con->uri + 10);
+ strcpy(con->uri, command);
+ }
+
+ if (strncmp(con->uri, "/printers/", 10) == 0 ||
+ strncmp(con->uri, "/classes/", 9) == 0 ||
+ strncmp(con->uri, "/jobs/", 6) == 0)
+ {
+ /*
+ * CGI output...
+ */
+
+ if (!SendHeader(con, HTTP_OK, "text/html"))
+ {
+ CloseClient(con);
+ return (0);
+ }
+
+ if (httpPrintf(HTTP(con), "\r\n") < 0)
+ {
+ CloseClient(con);
+ return (0);
+ }
+
+ LogRequest(con, HTTP_OK);
+ }
+ else if ((filename = get_file(con, &filestats)) == NULL)
+ {
+ if (!SendHeader(con, HTTP_NOT_FOUND, "text/html"))
+ {
+ CloseClient(con);
+ return (0);
+ }
+
+ LogRequest(con, HTTP_NOT_FOUND);
+ }
+ else if (!check_if_modified(con, &filestats))
+ {
+ if (!SendError(con, HTTP_NOT_MODIFIED))
+ {
+ CloseClient(con);
+ return (0);
+ }
+
+ LogRequest(con, HTTP_NOT_MODIFIED);
+ }
+ else
+ {
+ /*
+ * Serve a file...
+ */
+
+ type = mimeFileType(MimeDatabase, filename);
+ if (type == NULL)
+ strcpy(line, "text/plain");
+ else
+ sprintf(line, "%s/%s", type->super, type->type);
+
+ if (!SendHeader(con, HTTP_OK, line))
+ {
+ CloseClient(con);
+ return (0);
+ }
+
+ if (httpPrintf(HTTP(con), "Last-Modified: %s\r\n",
+ httpGetDateString(filestats.st_mtime)) < 0)
+ {
+ CloseClient(con);
+ return (0);
+ }
+
+ if (httpPrintf(HTTP(con), "Content-Length: %d\r\n",
+ filestats.st_size) < 0)
+ {
+ CloseClient(con);
+ return (0);
+ }
+
+ LogRequest(con, HTTP_OK);
+ }
+
+ if (send(con->http.fd, "\r\n", 2, 0) < 0)
+ {
+ CloseClient(con);
+ return (0);
+ }
+
+ con->http.state = HTTP_WAITING;
+ break;
+ }
+ }
+
+ /*
+ * Handle any incoming data...
+ */
+
+ switch (con->http.state)
+ {
+ case HTTP_PUT_RECV :
+ break;
+
+ case HTTP_POST_RECV :
+ LogMessage(LOG_DEBUG, "ReadClient() %d con->data_encoding = %s con->data_remaining = %d",
+ con->http.fd,
+ con->http.data_encoding == HTTP_ENCODE_CHUNKED ? "chunked" : "length",
+ con->http.data_remaining);
+ DEBUG_printf(("ReadClient() %d con->data_encoding = %s con->data_remaining = %d\n",
+ con->http.fd,
+ con->http.data_encoding == HTTP_ENCODE_CHUNKED ? "chunked" : "length",
+ con->http.data_remaining));
+
+ if (con->request != NULL)
+ {
+ /*
+ * Grab any request data from the connection...
+ */
+
+ if ((ipp_state = ippRead(&(con->http), con->request)) == IPP_ERROR)
+ {
+ LogMessage(LOG_ERROR, "ReadClient() %d IPP Read Error!",
+ con->http.fd);
+ CloseClient(con);
+ return (0);
+ }
+ else if (ipp_state != IPP_DATA)
+ break;
+
+ if (con->file == 0 && con->http.state != HTTP_POST_SEND)
+ {
+ /*
+ * Create a file as needed for the request data...
+ */
+
+ sprintf(con->filename, "%s/requests/XXXXXX", ServerRoot);
+ con->file = mkstemp(con->filename);
+ fchmod(con->file, 0644);
+
+ LogMessage(LOG_DEBUG, "ReadClient() %d REQUEST %s", con->http.fd,
+ con->filename);
+
+ if (con->file < 0)
+ {
+ if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
+ {
+ CloseClient(con);
+ return (0);
+ }
+ }
+ }
+ }
+
+ if (con->http.state != HTTP_POST_SEND)
+ {
+ if ((bytes = httpRead(HTTP(con), line, sizeof(line))) < 0)
+ {
+ CloseClient(con);
+ return (0);
+ }
+ else if (bytes > 0)
+ {
+ con->bytes += bytes;
+
+ if (bytes >= 1024)
+ LogMessage(LOG_DEBUG, "ReadClient() %d writing %d bytes", bytes);
+
+ if (write(con->file, line, bytes) < bytes)
+ {
+ close(con->file);
+ con->file = 0;
+ unlink(con->filename);
+
+ if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
+ {
+ CloseClient(con);
+ return (0);
+ }
+ }
+ }
+ else if (con->http.state != HTTP_POST_SEND)
+ {
+ CloseClient(con);
+ return (0);
+ }
+ }
+
+ if (con->http.state == HTTP_POST_SEND)
+ {
+ if (con->file)
+ {
+ fstat(con->file, &filestats);
+ close(con->file);
+ con->file = 0;
+
+ if (filestats.st_size > MaxRequestSize &&
+ MaxRequestSize > 0)
+ {
+ /*
+ * Request is too big; remove it and send an error...
+ */
+
+ unlink(con->filename);
+
+ if (con->request)
+ {
+ /*
+ * Delete any IPP request data...
+ */
+
+ ippDelete(con->request);
+ con->request = NULL;
+ }
+
+ if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
+ {
+ CloseClient(con);
+ return (0);
+ }
+ }
+ }
+
+ if (con->request)
+ ProcessIPPRequest(con);
+ }
+ break;
+ }
+
+ if (!con->http.keep_alive && con->http.state == HTTP_WAITING)
+ {
+ CloseClient(con);
+ return (0);
+ }
+ else
+ return (1);
+}
+
+
+/*
+ * 'SendCommand()' - Send output from a command via HTTP.
+ */
+
+int
+SendCommand(client_t *con,
+ char *command,
+ char *options)
+{
+ con->pipe_pid = pipe_command(con, 0, &(con->file), command, options);
+
+ LogMessage(LOG_DEBUG, "SendCommand() %d command=\"%s\" file=%d pipe_pid=%d",
+ con->http.fd, command, con->file, con->pipe_pid);
+
+ if (con->pipe_pid == 0)
+ return (0);
+
+ fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
+
+ DEBUG_printf(("SendCommand: Adding fd %d to InputSet...\n", con->file));
+ FD_SET(con->file, &InputSet);
+ FD_SET(con->http.fd, &OutputSet);
+
+ if (!SendHeader(con, HTTP_OK, NULL))
+ return (0);
+
+ if (con->http.version == HTTP_1_1)
+ {
+ con->http.data_encoding = HTTP_ENCODE_CHUNKED;
+
+ if (httpPrintf(HTTP(con), "Transfer-Encoding: chunked\r\n") < 0)
+ return (0);
+ }
+
+ return (1);
+}
+
+
+/*
+ * 'SendError()' - Send an error message via HTTP.
+ */
+
+int /* O - 1 if successful, 0 otherwise */
+SendError(client_t *con, /* I - Connection */
+ http_status_t code) /* I - Error code */
+{
+ char message[1024]; /* Text version of error code */
+
+
+ /*
+ * Put the request in the access_log file...
+ */
+
+ if (con->operation > HTTP_WAITING)
+ LogRequest(con, code);
+
+ /*
+ * To work around bugs in some proxies, don't use Keep-Alive for some
+ * error messages...
+ */
+
+ if (code >= HTTP_BAD_REQUEST)
+ con->http.keep_alive = HTTP_KEEPALIVE_OFF;
+
+ /*
+ * Send an error message back to the client. If the error code is a
+ * 400 or 500 series, make sure the message contains some text, too!
+ */
+
+ if (!SendHeader(con, code, NULL))
+ return (0);
+
+ if (code == HTTP_UNAUTHORIZED)
+ {
+ if (httpPrintf(HTTP(con), "WWW-Authenticate: Basic realm=\"CUPS\"\r\n") < 0)
+ return (0);
+ }
+
+ if (con->http.version >= HTTP_1_1 && !con->http.keep_alive)
+ {
+ if (httpPrintf(HTTP(con), "Connection: close\r\n") < 0)
+ return (0);
+ }
+
+ if (code >= HTTP_BAD_REQUEST)
+ {
+ /*
+ * Send a human-readable error message.
+ */
+
+ sprintf(message, "<HTML><HEAD><TITLE>%d %s</TITLE></HEAD>"
+ "<BODY><H1>%s</H1>%s</BODY></HTML>\n",
+ code, httpStatus(code), httpStatus(code),
+ con->language ? con->language->messages[code] : httpStatus(code));
+
+ if (httpPrintf(HTTP(con), "Content-Type: text/html\r\n") < 0)
+ return (0);
+ if (httpPrintf(HTTP(con), "Content-Length: %d\r\n", strlen(message)) < 0)
+ return (0);
+ if (httpPrintf(HTTP(con), "\r\n") < 0)
+ return (0);
+ if (send(con->http.fd, message, strlen(message), 0) < 0)
+ return (0);
+ }
+ else if (httpPrintf(HTTP(con), "\r\n") < 0)
+ return (0);
+
+ con->http.state = HTTP_WAITING;
+
+ return (1);
+}
+
+
+/*
+ * 'SendFile()' - Send a file via HTTP.
+ */
+
+int
+SendFile(client_t *con,
+ http_status_t code,
+ char *filename,
+ char *type,
+ struct stat *filestats)
+{
+ con->file = open(filename, O_RDONLY);
+
+ LogMessage(LOG_DEBUG, "SendFile() %d file=%d", con->http.fd, con->file);
+
+ if (con->file < 0)
+ return (0);
+
+ fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
+
+ con->pipe_pid = 0;
+
+ if (!SendHeader(con, code, type))
+ return (0);
+
+ if (httpPrintf(HTTP(con), "Last-Modified: %s\r\n", httpGetDateString(filestats->st_mtime)) < 0)
+ return (0);
+ if (httpPrintf(HTTP(con), "Content-Length: %d\r\n", filestats->st_size) < 0)
+ return (0);
+ if (httpPrintf(HTTP(con), "\r\n") < 0)
+ return (0);
+
+ FD_SET(con->http.fd, &OutputSet);
+
+ return (1);
+}
+
+
+/*
+ * 'SendHeader()' - Send an HTTP request.
+ */
+
+int /* O - 1 on success, 0 on failure */
+SendHeader(client_t *con, /* I - Client to send to */
+ http_status_t code, /* I - HTTP status code */
+ char *type) /* I - MIME type of document */
+{
+ if (httpPrintf(HTTP(con), "HTTP/%d.%d %d %s\r\n", con->http.version / 100,
+ con->http.version % 100, code, httpStatus(code)) < 0)
+ return (0);
+ if (httpPrintf(HTTP(con), "Date: %s\r\n", httpGetDateString(time(NULL))) < 0)
+ return (0);
+ if (httpPrintf(HTTP(con), "Server: CUPS/1.0\r\n") < 0)
+ return (0);
+ if (con->http.keep_alive && con->http.version >= HTTP_1_0)
+ {
+ if (httpPrintf(HTTP(con), "Connection: Keep-Alive\r\n") < 0)
+ return (0);
+ if (httpPrintf(HTTP(con), "Keep-Alive: timeout=%d\r\n", KeepAliveTimeout) < 0)
+ return (0);
+ }
+ if (con->language != NULL)
+ {
+ if (httpPrintf(HTTP(con), "Content-Language: %s\r\n",
+ con->language->language) < 0)
+ return (0);
+
+ if (type != NULL)
+ if (httpPrintf(HTTP(con), "Content-Type: %s; charset=%s\r\n", type,
+ cupsLangEncoding(con->language)) < 0)
+ return (0);
+ }
+ else if (type != NULL)
+ if (httpPrintf(HTTP(con), "Content-Type: %s\r\n", type) < 0)
+ return (0);
+
+ return (1);
+}
+
+
+/*
+ * 'WriteClient()' - Write data to a client as needed.
+ */
+
+int /* O - 1 if success, 0 if fail */
+WriteClient(client_t *con) /* I - Client connection */
+{
+ int bytes; /* Number of bytes written */
+ char buf[HTTP_MAX_BUFFER]; /* Data buffer */
+ ipp_state_t ipp_state; /* IPP state value */
+
+
+ if (con->http.state != HTTP_GET_SEND &&
+ con->http.state != HTTP_POST_SEND)
+ return (1);
+
+ if (con->response != NULL)
+ {
+ ipp_state = ippWrite(&(con->http), con->response);
+ bytes = ipp_state != IPP_ERROR && ipp_state != IPP_DATA;
+ }
+ else if ((bytes = read(con->file, buf, sizeof(buf))) > 0)
+ {
+ if (httpWrite(HTTP(con), buf, bytes) < 0)
+ {
+ CloseClient(con);
+ return (0);
+ }
+
+ con->bytes += bytes;
+ }
+
+ if (bytes <= 0)
+ {
+ LogRequest(con, HTTP_OK);
+
+ if (con->http.data_encoding == HTTP_ENCODE_CHUNKED)
+ {
+ if (httpPrintf(HTTP(con), "0\r\n\r\n") < 0)
+ {
+ CloseClient(con);
+ return (0);
+ }
+ }
+
+ con->http.state = HTTP_WAITING;
+
+ FD_CLR(con->http.fd, &OutputSet);
+
+ if (con->file)
+ {
+ DEBUG_printf(("WriteClient: Removing fd %d from InputSet...\n", con->file));
+ FD_CLR(con->file, &InputSet);
+
+ if (con->pipe_pid)
+ kill(con->pipe_pid, SIGTERM);
+
+ close(con->file);
+ con->file = 0;
+ con->pipe_pid = 0;
+ }
+
+ if (con->request != NULL)
+ {
+ ippDelete(con->request);
+ con->request = NULL;
+ }
+
+ if (con->response != NULL)
+ {
+ ippDelete(con->response);
+ con->response = NULL;
+ }
+
+ if (!con->http.keep_alive)
+ {
+ CloseClient(con);
+ return (0);
+ }
+ }
+
+ if (bytes >= 1024)
+ LogMessage(LOG_DEBUG, "WriteClient() %d %d bytes", con->http.fd, bytes);
+
+ con->http.activity = time(NULL);
+
+ return (1);
+}
+
+
+/*
+ * 'check_if_modified()' - Decode an "If-Modified-Since" line.
+ */
+
+static int /* O - 1 if modified since */
+check_if_modified(client_t *con, /* I - Client connection */
+ struct stat *filestats) /* I - File information */
+{
+ char *ptr; /* Pointer into field */
+ time_t date; /* Time/date value */
+ int size; /* Size/length value */
+
+
+ size = 0;
+ date = 0;
+ ptr = con->http.fields[HTTP_FIELD_IF_MODIFIED_SINCE];
+
+ if (*ptr == '\0')
+ return (1);
+
+ LogMessage(LOG_DEBUG, "check_if_modified() %d If-Modified-Since=\"%s\"",
+ con->http.fd, ptr);
+
+ while (*ptr != '\0')
+ {
+ while (isspace(*ptr) || *ptr == ';')
+ ptr ++;
+
+ if (strncasecmp(ptr, "length=", 7) == 0)
+ {
+ ptr += 7;
+ size = atoi(ptr);
+
+ while (isdigit(*ptr))
+ ptr ++;
+ }
+ else if (isalpha(*ptr))
+ {
+ date = httpGetDateTime(ptr);
+ while (*ptr != '\0' && *ptr != ';')
+ ptr ++;
+ }
+ }
+
+ LogMessage(LOG_DEBUG, "check_if_modified() %d sizes=%d,%d dates=%d,%d",
+ con->http.fd, size, filestats->st_size, date, filestats->st_mtime);
+
+ return ((size != filestats->st_size && size != 0) ||
+ (date < filestats->st_mtime && date != 0) ||
+ (size == 0 && date == 0));
+}
+
+
+/*
+ * 'decode_basic_auth()' - Decode a Basic authorization string.
+ */
+
+static void
+decode_basic_auth(client_t *con) /* I - Client to decode to */
+{
+ char *s, /* Authorization string */
+ value[1024]; /* Value string */
+
+
+ /*
+ * Decode the string and pull the username and password out...
+ */
+
+ s = con->http.fields[HTTP_FIELD_AUTHORIZATION];
+ if (strncmp(s, "Basic", 5) != 0)
+ return;
+
+ s += 5;
+ while (isspace(*s))
+ s ++;
+
+ httpDecode64(value, s);
+
+ sscanf(value, "%[^:]:%[^\n]", con->username, con->password);
+
+ LogMessage(LOG_DEBUG, "decode_basic_auth() %d username=\"%s\"",
+ con->http.fd, con->username);
+}
+
+
+/*
+ * 'get_file()' - Get a filename and state info.
+ */
+
+static char * /* O - Real filename */
+get_file(client_t *con, /* I - Client connection */
+ struct stat *filestats)/* O - File information */
+{
+ int status; /* Status of filesystem calls */
+ char *params; /* Pointer to parameters in URI */
+ static char filename[1024]; /* Filename buffer */
+
+
+ /*
+ * Need to add DocumentRoot global...
+ */
+
+ if (strncmp(con->uri, "/ppd/", 5) == 0)
+ sprintf(filename, "%s%s", ServerRoot, con->uri);
+ else if (con->language != NULL)
+ sprintf(filename, "%s/%s%s", DocumentRoot, con->language->language,
+ con->uri);
+ else
+ sprintf(filename, "%s%s", DocumentRoot, con->uri);
+
+ if ((params = strchr(filename, '?')) != NULL)
+ *params = '\0';
+
+ /*
+ * Grab the status for this language; if there isn't a language-specific file
+ * then fallback to the default one...
+ */
+
+ if ((status = stat(filename, filestats)) != 0 && con->language != NULL)
+ {
+ /*
+ * Drop the language prefix and try the current directory...
+ */
+
+ if (strncmp(con->uri, "/ppd/", 5) != 0)
+ {
+ sprintf(filename, "%s%s", DocumentRoot, con->uri);
+
+ status = stat(filename, filestats);
+ }
+ }
+
+ /*
+ * If we're found a directory, get the index.html file instead...
+ */
+
+ if (!status && S_ISDIR(filestats->st_mode))
+ {
+ if (filename[strlen(filename) - 1] == '/')
+ strcat(filename, "index.html");
+ else
+ strcat(filename, "/index.html");
+
+ status = stat(filename, filestats);
+ }
+
+ LogMessage(LOG_DEBUG, "get_file() %d filename=%s size=%d",
+ con->http.fd, filename, status ? -1 : filestats->st_size);
+
+ if (status)
+ return (NULL);
+ else
+ return (filename);
+}
+
+
+/*
+ * 'pipe_command()' - Pipe the output of a command to the remote client.
+ */
+
+static int /* O - Process ID */
+pipe_command(client_t *con, /* I - Client connection */
+ int infile, /* I - Standard input for command */
+ int *outfile, /* O - Standard output for command */
+ char *command, /* I - Command to run */
+ char *options) /* I - Options for command */
+{
+ int pid; /* Process ID */
+ char *commptr; /* Command string pointer */
+ int fds[2]; /* Pipe FDs */
+ int argc; /* Number of arguments */
+ char argbuf[1024], /* Argument buffer */
+ *argv[100], /* Argument strings */
+ *envp[100]; /* Environment variables */
+ char hostname[1024]; /* Hostname string */
+ static char lang[1024]; /* LANG env variable */
+ static char content_length[1024]; /* CONTENT_LENGTH env variable */
+ static char content_type[1024]; /* CONTENT_TYPE env variable */
+ static char ipp_port[1024]; /* Default listen port */
+ static char server_port[1024]; /* Default listen port */
+ static char server_name[1024]; /* Default listen hostname */
+ static char remote_host[1024]; /* REMOTE_HOST env variable */
+ static char remote_user[1024]; /* REMOTE_HOST env variable */
+ static char tmpdir[1024]; /* TMPDIR env variable */
+
+ /*
+ * Copy the command string...
+ */
+
+ strncpy(argbuf, options, sizeof(argbuf) - 1);
+ argbuf[sizeof(argbuf) - 1] = '\0';
+
+ /*
+ * Parse the string; arguments can be separated by spaces or by ? or +...
+ */
+
+ argv[0] = argbuf;
+
+ for (commptr = argbuf, argc = 1; *commptr != '\0' && argc < 99; commptr ++)
+ if (*commptr == ' ' || *commptr == '?' || *commptr == '+')
+ {
+ *commptr++ = '\0';
+
+ while (*commptr == ' ')
+ commptr ++;
+
+ if (*commptr != '\0')
+ {
+ argv[argc] = commptr;
+ argc ++;
+ }
+
+ commptr --;
+ }
+ else if (*commptr == '%')
+ {
+ if (commptr[1] >= '0' && commptr[1] <= '9')
+ *commptr = (commptr[1] - '0') << 4;
+ else
+ *commptr = (tolower(commptr[1]) - 'a' + 10) << 4;
+
+ if (commptr[2] >= '0' && commptr[2] <= '9')
+ *commptr |= commptr[2] - '0';
+ else
+ *commptr |= tolower(commptr[2]) - 'a' + 10;
+
+ strcpy(commptr + 1, commptr + 3);
+ }
+
+ argv[argc] = NULL;
+
+ if (argv[0][0] == '\0')
+ argv[0] = strrchr(command, '/') + 1;
+
+ /*
+ * Setup the environment variables as needed...
+ */
+
+ gethostname(hostname, sizeof(hostname) - 1);
+
+ sprintf(lang, "LANG=%s", con->language ? con->language->language : "C");
+ sprintf(ipp_port, "IPP_PORT=%d", ntohs(con->http.hostaddr.sin_port));
+ sprintf(server_port, "SERVER_PORT=%d", ntohs(con->http.hostaddr.sin_port));
+ sprintf(server_name, "SERVER_NAME=%s", hostname);
+ sprintf(remote_host, "REMOTE_HOST=%s", con->http.hostname);
+ sprintf(remote_user, "REMOTE_USER=%s", con->username);
+ sprintf(tmpdir, "TMPDIR=%s", TempDir);
+
+ envp[0] = "PATH=/bin:/usr/bin";
+ envp[1] = "SERVER_SOFTWARE=CUPS/1.0";
+ envp[2] = "GATEWAY_INTERFACE=CGI/1.1";
+ envp[3] = "SERVER_PROTOCOL=HTTP/1.1";
+ envp[4] = ipp_port;
+ envp[5] = server_name;
+ envp[6] = server_port;
+ envp[7] = remote_host;
+ envp[8] = remote_user;
+ envp[9] = lang;
+ envp[10] = "TZ=GMT";
+ envp[11] = tmpdir;
+
+ if (con->operation == HTTP_GET)
+ {
+ envp[12] = "REQUEST_METHOD=GET";
+ envp[13] = NULL;
+ }
+ else
+ {
+ sprintf(content_length, "CONTENT_LENGTH=%d", con->http.data_remaining);
+ sprintf(content_type, "CONTENT_TYPE=%s",
+ con->http.fields[HTTP_FIELD_CONTENT_TYPE]);
+
+ envp[12] = "REQUEST_METHOD=POST";
+ envp[13] = content_length;
+ envp[14] = content_type;
+ envp[15] = NULL;
+ }
+
+ /*
+ * Create a pipe for the output...
+ */
+
+ if (pipe(fds))
+ return (0);
+
+ /*
+ * Then execute the pipe command...
+ */
+
+ if ((pid = fork()) == 0)
+ {
+ /*
+ * Child comes here... Close stdin if necessary and dup the pipe to stdout.
+ */
+
+ setuid(User);
+ setgid(Group);
+
+ if (infile)
+ {
+ close(0);
+ dup(infile);
+ }
+
+ close(1);
+ dup(fds[1]);
+
+ close(fds[0]);
+ close(fds[1]);
+
+ /*
+ * Execute the pipe program; if an error occurs, exit with status 1...
+ */
+
+ execve(command, argv, envp);
+ perror("execve failed");
+ exit(errno);
+ return (0);
+ }
+ else if (pid < 0)
+ {
+ /*
+ * Error - can't fork!
+ */
+
+ close(fds[0]);
+ close(fds[1]);
+ return (0);
+ }
+ else
+ {
+ /*
+ * Fork successful - return the PID...
+ */
+
+ *outfile = fds[0];
+ close(fds[1]);
+
+ return (pid);
+ }
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/scheduler/client.h b/scheduler/client.h
new file mode 100644
index 000000000..9dbd1de04
--- /dev/null
+++ b/scheduler/client.h
@@ -0,0 +1,94 @@
+/*
+ * "$Id$"
+ *
+ * Client definitions for the Common UNIX Printing System (CUPS) scheduler.
+ *
+ * Copyright 1997-1999 by Easy Software Products, all rights reserved.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ */
+
+/*
+ * HTTP client structure...
+ */
+
+typedef struct
+{
+ http_t http; /* HTTP client connection */
+ ipp_t *request, /* IPP request information */
+ *response; /* IPP response information */
+ time_t start; /* Request start time */
+ http_state_t operation; /* Request operation */
+ int bytes; /* Bytes transferred for this request */
+ char username[16], /* Username from Authorization: line */
+ password[16], /* Password from Authorization: line */
+ uri[HTTP_MAX_URI], /* Localized URL/URI for GET/PUT */
+ filename[HTTP_MAX_URI]; /* Filename of output file */
+ int file; /* Input/output file */
+ int pipe_pid; /* Pipe process ID (or 0 if not a pipe) */
+ cups_lang_t *language; /* Language to use */
+} client_t;
+
+#define HTTP(con) &((con)->http)
+
+
+/*
+ * HTTP listener structure...
+ */
+
+typedef struct
+{
+ int fd; /* File descriptor for this client */
+ struct sockaddr_in address; /* Bind address of socket */
+} listener_t;
+
+
+/*
+ * Globals...
+ */
+
+VAR int NumListeners VALUE(0);
+ /* Number of listening sockets */
+VAR listener_t Listeners[MAX_LISTENERS];
+ /* Listening sockets */
+VAR int NumClients VALUE(0);
+ /* Number of HTTP clients */
+VAR client_t *Clients VALUE(NULL);
+ /* HTTP clients */
+
+
+/*
+ * Prototypes...
+ */
+
+extern void AcceptClient(listener_t *lis);
+extern void CloseAllClients(void);
+extern void CloseClient(client_t *con);
+extern void ProcessIPPRequest(client_t *con);
+extern int ReadClient(client_t *con);
+extern int SendCommand(client_t *con, char *command, char *options);
+extern int SendError(client_t *con, http_status_t code);
+extern int SendFile(client_t *con, http_status_t code, char *filename,
+ char *type, struct stat *filestats);
+extern int SendHeader(client_t *con, http_status_t code, char *type);
+extern void StartListening(void);
+extern void StopListening(void);
+extern int WriteClient(client_t *con);
+
+/*
+ * End of "$Id$".
+ */
diff --git a/scheduler/conf.c b/scheduler/conf.c
new file mode 100644
index 000000000..e5456c2d4
--- /dev/null
+++ b/scheduler/conf.c
@@ -0,0 +1,1189 @@
+/*
+ * "$Id$"
+ *
+ * Configuration routines for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-1999 by Easy Software Products, all rights reserved.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * Contents:
+ *
+ * ReadConfiguration() - Read the cupsd.conf file.
+ * LogRequest() - Log an HTTP request in Common Log Format.
+ * LogMessage() - Log a message to the error log file.
+ * read_configuration() - Read a configuration file.
+ * read_location() - Read a <Location path> definition.
+ * get_address() - Get an address + port number from a line.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "cupsd.h"
+#include <stdarg.h>
+#include <pwd.h>
+#include <grp.h>
+#include <sys/resource.h>
+
+
+/*
+ * Possibly missing network definitions...
+ */
+
+#ifndef INADDR_NONE
+# define INADDR_NONE 0xffffffff
+#endif /* !INADDR_NONE */
+
+
+/*
+ * Configuration variable structure...
+ */
+
+typedef struct
+{
+ char *name; /* Name of variable */
+ void *ptr; /* Pointer to variable */
+ int type, /* Type (int, string, address) */
+ size; /* Size of string */
+} var_t;
+
+#define VAR_INTEGER 0
+#define VAR_STRING 1
+#define VAR_BOOLEAN 2
+
+
+/*
+ * Local globals...
+ */
+
+static var_t variables[] =
+{
+ { "ServerName", ServerName, VAR_STRING, sizeof(ServerName) },
+ { "ServerAdmin", ServerAdmin, VAR_STRING, sizeof(ServerAdmin) },
+ { "ServerRoot", ServerRoot, VAR_STRING, sizeof(ServerRoot) },
+ { "DocumentRoot", DocumentRoot, VAR_STRING, sizeof(DocumentRoot) },
+ { "SystemGroup", SystemGroup, VAR_STRING, sizeof(SystemGroup) },
+ { "AccessLog", AccessLog, VAR_STRING, sizeof(AccessLog) },
+ { "ErrorLog", ErrorLog, VAR_STRING, sizeof(ErrorLog) },
+ { "PageLog", PageLog, VAR_STRING, sizeof(PageLog) },
+ { "DefaultCharset", DefaultCharset, VAR_STRING, sizeof(DefaultCharset) },
+ { "DefaultLanguage", DefaultLanguage, VAR_STRING, sizeof(DefaultLanguage) },
+ { "RIPCache", RIPCache, VAR_STRING, sizeof(RIPCache) },
+ { "TempDir", TempDir, VAR_STRING, sizeof(TempDir) },
+ { "HostNameLookups", &HostNameLookups, VAR_BOOLEAN, 0 },
+ { "Timeout", &Timeout, VAR_INTEGER, 0 },
+ { "KeepAlive", &KeepAlive, VAR_BOOLEAN, 0 },
+ { "KeepAliveTimeout", &KeepAliveTimeout, VAR_INTEGER, 0 },
+ { "ImplicitClasses", &ImplicitClasses, VAR_BOOLEAN, 0 },
+ { "Browsing", &Browsing, VAR_BOOLEAN, 0 },
+ { "BrowsePort", &BrowsePort, VAR_INTEGER, 0 },
+ { "BrowseInterval", &BrowseInterval, VAR_INTEGER, 0 },
+ { "BrowseTimeout", &BrowseTimeout, VAR_INTEGER, 0 },
+ { "MaxClients", &MaxClients, VAR_INTEGER, 0 },
+ { "MaxLogSize", &MaxLogSize, VAR_INTEGER, 0 },
+ { "MaxRequestSize", &MaxRequestSize, VAR_INTEGER, 0 }
+};
+#define NUM_VARS (sizeof(variables) / sizeof(variables[0]))
+static char *months[12] = /* Months */
+ {
+ "Jan",
+ "Feb",
+ "Mar",
+ "Apr",
+ "May",
+ "Jun",
+ "Jul",
+ "Aug",
+ "Sep",
+ "Oct",
+ "Nov",
+ "Dec"
+ };
+
+
+/*
+ * Local functions...
+ */
+
+static int read_configuration(FILE *fp);
+static int read_location(FILE *fp, char *name, int linenum);
+static int get_address(char *value, unsigned defaddress, int defport,
+ struct sockaddr_in *address);
+
+
+/*
+ * 'ReadConfiguration()' - Read the cupsd.conf file.
+ */
+
+int /* O - 1 if file read successfully, 0 otherwise */
+ReadConfiguration(void)
+{
+ FILE *fp; /* Configuration file */
+ int status; /* Return status */
+ char directory[1024];/* Configuration directory */
+ struct rlimit limit; /* Runtime limit */
+
+
+ /*
+ * Close all network clients and stop all jobs...
+ */
+
+ CloseAllClients();
+ StopListening();
+ StopBrowsing();
+
+ if (Clients != NULL)
+ {
+ free(Clients);
+ Clients = NULL;
+ }
+
+ if (AccessFile != NULL)
+ {
+ if (AccessFile != stderr)
+ fclose(AccessFile);
+
+ AccessFile = NULL;
+ }
+
+ if (ErrorFile != NULL)
+ {
+ if (ErrorFile != stderr)
+ fclose(ErrorFile);
+
+ ErrorFile = NULL;
+ }
+
+ /*
+ * Clear the current configuration...
+ */
+
+ NeedReload = FALSE;
+
+ FD_ZERO(&InputSet);
+ FD_ZERO(&OutputSet);
+
+ DeleteAllLocations();
+
+ DeleteAllClasses();
+
+ gethostname(ServerName, sizeof(ServerName));
+ sprintf(ServerAdmin, "root@%s", ServerName);
+ strcpy(ServerRoot, CUPS_SERVERROOT);
+ strcpy(DocumentRoot, CUPS_DATADIR "/doc");
+ strcpy(AccessLog, "logs/access_log");
+ strcpy(ErrorLog, "logs/error_log");
+ strcpy(DefaultLanguage, DEFAULT_LANGUAGE);
+ strcpy(DefaultCharset, DEFAULT_CHARSET);
+ strcpy(RIPCache, "8m");
+ if (getenv("TMPDIR") == NULL)
+ strcpy(TempDir, "/var/tmp");
+ else
+ strcpy(TempDir, getenv("TMPDIR"));
+
+ /*
+ * Find the default system group: "sys", "system", or "root"...
+ */
+
+ if (getgrnam("sys") != NULL)
+ strcpy(SystemGroup, "sys");
+ else
+ {
+ endgrent();
+ if (getgrnam("system") != NULL)
+ strcpy(SystemGroup, "system");
+ else
+ strcpy(SystemGroup, "root");
+ }
+
+ endgrent();
+
+ User = DEFAULT_UID;
+ Group = DEFAULT_GID;
+ LogLevel = LOG_ERROR;
+ HostNameLookups = FALSE;
+ Timeout = DEFAULT_TIMEOUT;
+ KeepAlive = TRUE;
+ KeepAliveTimeout = DEFAULT_KEEPALIVE;
+ ImplicitClasses = TRUE;
+
+ MaxClients = 100;
+
+ MaxLogSize = 1024 * 1024;
+ MaxRequestSize = 0;
+
+ Browsing = TRUE;
+ BrowsePort = ippPort();
+ BrowseInterval = DEFAULT_INTERVAL;
+ BrowseTimeout = DEFAULT_TIMEOUT;
+ NumBrowsers = 0;
+
+ NumListeners = 0;
+
+ DefaultPrinter = NULL;
+
+ DeleteAllPrinters();
+
+ if (MimeDatabase != NULL)
+ mimeDelete(MimeDatabase);
+
+ if ((fp = fopen(ConfigurationFile, "r")) == NULL)
+ return (0);
+
+ status = read_configuration(fp);
+
+ fclose(fp);
+
+ if (!status)
+ return (0);
+
+ LogMessage(LOG_DEBUG, "ReadConfiguration() ConfigurationFile=\"%s\"",
+ ConfigurationFile);
+
+ /*
+ * Check the MaxClients setting, and then allocate memory for it...
+ */
+
+ getrlimit(RLIMIT_NOFILE, &limit);
+
+ if (MaxClients > (limit.rlim_max / 3))
+ MaxClients = limit.rlim_max / 3;
+
+ if ((Clients = calloc(sizeof(client_t), MaxClients)) == NULL)
+ {
+ LogMessage(LOG_ERROR, "ReadConfiguration() FATAL: unable to allocate memory for %d clients!",
+ MaxClients);
+ exit(1);
+ }
+ else
+ LogMessage(LOG_INFO, "ReadConfiguration() Configured for up to %d clients.",
+ MaxClients);
+
+ /*
+ * Read the MIME type and conversion database...
+ */
+
+ sprintf(directory, "%s/conf", ServerRoot);
+
+ MimeDatabase = mimeNew();
+ mimeMerge(MimeDatabase, directory);
+
+ /*
+ * Load printers and classes...
+ */
+
+ LoadAllPrinters();
+ LoadAllClasses();
+
+ /*
+ * Add a default browser if browsing is enabled and no browser addresses
+ * were defined...
+ */
+
+ if (Browsing && NumBrowsers == 0)
+ {
+ NumBrowsers ++;
+
+ memset(Browsers + 0, 0, sizeof(Browsers[0]));
+ Browsers[0].sin_addr.s_addr = htonl(INADDR_BROADCAST);
+ Browsers[0].sin_family = AF_INET;
+ Browsers[0].sin_port = htons(BrowsePort);
+ }
+
+ /*
+ * Startup all the networking stuff...
+ */
+
+ StartListening();
+ StartBrowsing();
+
+ /*
+ * Check for queued jobs...
+ */
+
+ CheckJobs();
+
+ return (1);
+}
+
+
+/*
+ * 'LogRequest()' - Log an HTTP request in Common Log Format.
+ */
+
+int /* O - 1 on success, 0 on error */
+LogRequest(client_t *con, /* I - Request to log */
+ http_status_t code) /* I - Response code */
+{
+ char filename[1024], /* Name of access log file */
+ backname[1024]; /* Backup filename */
+ struct tm *date; /* Date information */
+ static char *states[] = /* HTTP client states... */
+ {
+ "WAITING",
+ "OPTIONS",
+ "GET",
+ "GET",
+ "HEAD",
+ "POST",
+ "POST",
+ "POST",
+ "PUT",
+ "PUT",
+ "DELETE",
+ "TRACE",
+ "CLOSE",
+ "STATUS"
+ };
+
+
+ /*
+ * See if the access log is open...
+ */
+
+ if (AccessFile == NULL)
+ {
+ /*
+ * Nope, open the access log file...
+ */
+
+ if (AccessLog[0] == '\0')
+ return (1);
+ else if (AccessLog[0] != '/')
+ sprintf(filename, "%s/%s", ServerRoot, AccessLog);
+ else
+ strcpy(filename, AccessLog);
+
+ if ((AccessFile = fopen(filename, "a")) == NULL)
+ AccessFile = stderr;
+ }
+
+ /*
+ * See if we need to rotate the log file...
+ */
+
+ if (ftell(AccessFile) > MaxLogSize && MaxLogSize > 0)
+ {
+ /*
+ * Rotate access_log file...
+ */
+
+ fclose(AccessFile);
+
+ if (AccessLog[0] != '/')
+ sprintf(filename, "%s/%s", ServerRoot, AccessLog);
+ else
+ strcpy(filename, AccessLog);
+
+ strcpy(backname, filename);
+ strcat(backname, ".O");
+
+ unlink(backname);
+ rename(filename, backname);
+
+ if ((AccessFile = fopen(filename, "a")) == NULL)
+ AccessFile = stderr;
+ }
+
+ /*
+ * Write a log of the request in "common log format"...
+ */
+
+ date = localtime(&(con->start));
+
+ fprintf(AccessFile, "%s - %s [%02d/%s/%04d:%02d:%02d:%02d +0000] \"%s %s HTTP/%d.%d\" %d %d\n",
+ con->http.hostname, con->username[0] != '\0' ? con->username : "-",
+ date->tm_mday, months[date->tm_mon], 1900 + date->tm_year,
+ date->tm_hour, date->tm_min, date->tm_sec,
+ states[con->operation], con->uri,
+ con->http.version / 100, con->http.version % 100,
+ code, con->bytes);
+ fflush(AccessFile);
+
+ return (1);
+}
+
+
+/*
+ * 'LogMessage()' - Log a message to the error log file.
+ */
+
+int /* O - 1 on success, 0 on error */
+LogMessage(int level, /* I - Log level */
+ char *message, /* I - printf-style message string */
+ ...) /* I - Additional args as needed */
+{
+ char filename[1024], /* Name of error log file */
+ backname[1024]; /* Backup filename */
+ va_list ap; /* Argument pointer */
+ time_t dtime; /* Time value */
+ struct tm *date; /* Date information */
+ static char levels[] = /* Log levels... */
+ {
+ 'N',
+ 'E',
+ 'W',
+ 'I',
+ 'D'
+ };
+
+
+ /*
+ * See if we want to log this message...
+ */
+
+ if (level <= LogLevel)
+ {
+ /*
+ * See if the error log file is open...
+ */
+
+ if (ErrorFile == NULL)
+ {
+ /*
+ * Nope, open error log...
+ */
+
+ if (ErrorLog[0] == '\0')
+ return (1);
+ else if (ErrorLog[0] != '/')
+ sprintf(filename, "%s/%s", ServerRoot, ErrorLog);
+ else
+ strcpy(filename, ErrorLog);
+
+ if ((ErrorFile = fopen(filename, "a")) == NULL)
+ ErrorFile = stderr;
+ }
+
+ /*
+ * Do we need to rotate the log?
+ */
+
+ if (ftell(ErrorFile) > MaxLogSize && MaxLogSize > 0)
+ {
+ /*
+ * Rotate error_log file...
+ */
+
+ fclose(ErrorFile);
+
+ if (ErrorLog[0] != '/')
+ sprintf(filename, "%s/%s", ServerRoot, ErrorLog);
+ else
+ strcpy(filename, ErrorLog);
+
+ strcpy(backname, filename);
+ strcat(backname, ".O");
+
+ unlink(backname);
+ rename(filename, backname);
+
+ if ((ErrorFile = fopen(filename, "a")) == NULL)
+ ErrorFile = stderr;
+ }
+
+ /*
+ * Print the log level and date/time...
+ */
+
+ dtime = time(NULL);
+ date = localtime(&dtime);
+
+ fprintf(ErrorFile, "%c [%02d/%s/%04d:%02d:%02d:%02d +0000] ",
+ levels[level],
+ date->tm_mday, months[date->tm_mon], 1900 + date->tm_year,
+ date->tm_hour, date->tm_min, date->tm_sec);
+
+ /*
+ * Then the log message...
+ */
+
+ va_start(ap, message);
+ vfprintf(ErrorFile, message, ap);
+ va_end(ap);
+
+ /*
+ * Then a newline...
+ */
+
+ fputs("\n", ErrorFile);
+ fflush(ErrorFile);
+ }
+
+ return (1);
+}
+
+
+/*
+ * 'LogMessage()' - Log a message to the error log file.
+ */
+
+int /* O - 1 on success, 0 on error */
+LogPage(job_t *job, /* I - Job being printed */
+ char *page) /* I - Page being printed */
+{
+ char filename[1024], /* Name of error log file */
+ backname[1024]; /* Backup filename */
+ time_t dtime; /* Time value */
+ struct tm *date; /* Date information */
+
+
+ /*
+ * See if the page log file is open...
+ */
+
+ if (PageFile == NULL)
+ {
+ /*
+ * Nope, open page log...
+ */
+
+ if (PageLog[0] == '\0')
+ return (1);
+ else if (PageLog[0] != '/')
+ sprintf(filename, "%s/%s", ServerRoot, PageLog);
+ else
+ strcpy(filename, PageLog);
+
+ if ((PageFile = fopen(filename, "a")) == NULL)
+ PageFile = stderr;
+ }
+
+ /*
+ * Do we need to rotate the log?
+ */
+
+ if (ftell(PageFile) > MaxLogSize && MaxLogSize > 0)
+ {
+ /*
+ * Rotate page_log file...
+ */
+
+ fclose(PageFile);
+
+ if (PageLog[0] != '/')
+ sprintf(filename, "%s/%s", ServerRoot, PageLog);
+ else
+ strcpy(filename, PageLog);
+
+ strcpy(backname, filename);
+ strcat(backname, ".O");
+
+ unlink(backname);
+ rename(filename, backname);
+
+ if ((PageFile = fopen(filename, "a")) == NULL)
+ PageFile = stderr;
+ }
+
+ /*
+ * Print a page log entry of the form:
+ *
+ * printer job-id user [DD/MON/YYYY:HH:MM:SS +0000] page num-copies
+ */
+
+ dtime = time(NULL);
+ date = localtime(&dtime);
+
+ fprintf(PageFile, "%s %s %d [%02d/%s/%04d:%02d:%02d:%02d +0000] %s\n",
+ job->printer->name, job->username, job->id,
+ date->tm_mday, months[date->tm_mon], 1900 + date->tm_year,
+ date->tm_hour, date->tm_min, date->tm_sec, page);
+ fflush(PageFile);
+
+ return (1);
+}
+
+
+/*
+ * 'read_configuration()' - Read a configuration file.
+ */
+
+static int /* O - 1 on success, 0 on failure */
+read_configuration(FILE *fp) /* I - File to read from */
+{
+ int i; /* Looping var */
+ int linenum; /* Current line number */
+ int len; /* Length of line */
+ char line[HTTP_MAX_BUFFER], /* Line from file */
+ name[256], /* Parameter name */
+ *nameptr, /* Pointer into name */
+ *value; /* Pointer to value */
+ var_t *var; /* Current variable */
+
+
+ /*
+ * Loop through each line in the file...
+ */
+
+ linenum = 0;
+
+ while (fgets(line, sizeof(line), fp) != NULL)
+ {
+ linenum ++;
+
+ /*
+ * Skip comment lines...
+ */
+
+ if (line[0] == '#')
+ continue;
+
+ /*
+ * Strip trailing newline, if any...
+ */
+
+ len = strlen(line);
+
+ if (line[len - 1] == '\n')
+ {
+ len --;
+ line[len] = '\0';
+ }
+
+ /*
+ * Extract the name from the beginning of the line...
+ */
+
+ for (value = line; isspace(*value); value ++);
+
+ for (nameptr = name; *value != '\0' && !isspace(*value);)
+ *nameptr++ = *value++;
+ *nameptr = '\0';
+
+ while (isspace(*value))
+ value ++;
+
+ if (name[0] == '\0')
+ continue;
+
+ /*
+ * Decode the directive...
+ */
+
+ if (strcmp(name, "<Location") == 0)
+ {
+ /*
+ * <Location path>
+ */
+
+ if (line[len - 1] == '>')
+ {
+ line[len - 1] = '\0';
+
+ linenum = read_location(fp, value, linenum);
+ if (linenum == 0)
+ return (0);
+ }
+ else
+ {
+ LogMessage(LOG_ERROR, "ReadConfiguration() Syntax error on line %d.",
+ linenum);
+ return (0);
+ }
+ }
+ else if (strcmp(name, "Port") == 0 ||
+ strcmp(name, "Listen") == 0)
+ {
+ /*
+ * Add a listening address to the list...
+ */
+
+ if (NumListeners < MAX_BROWSERS)
+ {
+ if (get_address(value, INADDR_ANY, IPP_PORT,
+ &(Listeners[NumListeners].address)))
+ NumListeners ++;
+ else
+ LogMessage(LOG_ERROR, "Bad %s address %s at line %d.", name,
+ value, linenum);
+ }
+ else
+ LogMessage(LOG_WARN, "Too many %s directives at line %d.", name,
+ linenum);
+ }
+ else if (strcmp(name, "BrowseAddress") == 0)
+ {
+ /*
+ * Add a browse address to the list...
+ */
+
+ if (NumBrowsers < MAX_BROWSERS)
+ {
+ if (get_address(value, INADDR_NONE, BrowsePort, Browsers + NumBrowsers))
+ NumBrowsers ++;
+ else
+ LogMessage(LOG_ERROR, "Bad BrowseAddress %s at line %d.", value,
+ linenum);
+ }
+ else
+ LogMessage(LOG_WARN, "Too many BrowseAddress directives at line %d.",
+ linenum);
+ }
+ else if (strcmp(name, "User") == 0)
+ {
+ /*
+ * User ID to run as...
+ */
+
+ if (isdigit(value[0]))
+ User = atoi(value);
+ else
+ {
+ struct passwd *p; /* Password information */
+
+ endpwent();
+ p = getpwnam(value);
+
+ if (p != NULL)
+ User = p->pw_uid;
+ else
+ LogMessage(LOG_WARN, "ReadConfiguration() Unknown username \"%s\"",
+ value);
+ }
+ }
+ else if (strcmp(name, "Group") == 0)
+ {
+ /*
+ * Group ID to run as...
+ */
+
+ if (isdigit(value[0]))
+ Group = atoi(value);
+ else
+ {
+ struct group *g; /* Group information */
+
+ endgrent();
+ g = getgrnam(value);
+
+ if (g != NULL)
+ Group = g->gr_gid;
+ else
+ LogMessage(LOG_WARN, "ReadConfiguration() Unknown groupname \"%s\"",
+ value);
+ }
+ }
+ else if (strcmp(name, "LogLevel") == 0)
+ {
+ /*
+ * Amount of logging to do...
+ */
+
+ if (strcmp(value, "debug") == 0)
+ LogLevel = LOG_DEBUG;
+ else if (strcmp(value, "info") == 0)
+ LogLevel = LOG_INFO;
+ else if (strcmp(value, "warn") == 0)
+ LogLevel = LOG_WARN;
+ else if (strcmp(value, "error") == 0)
+ LogLevel = LOG_ERROR;
+ else if (strcmp(value, "none") == 0)
+ LogLevel = LOG_NONE;
+ }
+ else
+ {
+ /*
+ * Find a simple variable in the list...
+ */
+
+ for (i = NUM_VARS, var = variables; i > 0; i --, var ++)
+ if (strcmp(name, var->name) == 0)
+ break;
+
+ if (i == 0)
+ {
+ /*
+ * Unknown directive! Output an error message and continue...
+ */
+
+ LogMessage(LOG_ERROR, "Unknown directive %s on line %d.", name,
+ linenum);
+ continue;
+ }
+
+ switch (var->type)
+ {
+ case VAR_INTEGER :
+ *((int *)var->ptr) = atoi(value);
+ break;
+
+ case VAR_BOOLEAN :
+ if (strcasecmp(value, "true") == 0 ||
+ strcasecmp(value, "on") == 0 ||
+ strcasecmp(value, "enabled") == 0 ||
+ atoi(value) != 0)
+ *((int *)var->ptr) = TRUE;
+ else if (strcasecmp(value, "false") == 0 ||
+ strcasecmp(value, "off") == 0 ||
+ strcasecmp(value, "disabled") == 0 ||
+ strcasecmp(value, "0") == 0)
+ *((int *)var->ptr) = FALSE;
+ else
+ LogMessage(LOG_ERROR, "Unknown boolean value %s on line %d.",
+ value, linenum);
+ break;
+
+ case VAR_STRING :
+ strncpy((char *)var->ptr, value, var->size - 1);
+ value[var->size - 1] = '\0';
+ break;
+ }
+ }
+ }
+
+ return (1);
+}
+
+
+/*
+ * 'read_location()' - Read a <Location path> definition.
+ */
+
+static int /* O - New line number or 0 on error */
+read_location(FILE *fp, /* I - Configuration file */
+ char *location, /* I - Location name/path */
+ int linenum) /* I - Current line number */
+{
+ location_t *loc; /* New location */
+ int len; /* Length of line */
+ char line[HTTP_MAX_BUFFER], /* Line buffer */
+ name[256], /* Configuration directive */
+ *nameptr, /* Pointer into name */
+ *value; /* Value for directive */
+ unsigned address, /* Address value */
+ netmask; /* Netmask value */
+ int ip[4], /* IP address components */
+ ipcount, /* Number of components provided */
+ mask[4]; /* IP netmask components */
+ static unsigned netmasks[4] = /* Standard netmasks... */
+ {
+ 0xff000000,
+ 0xffff0000,
+ 0xffffff00,
+ 0xffffffff
+ };
+
+
+ if ((loc = AddLocation(location)) == NULL)
+ return (0);
+
+ while (fgets(line, sizeof(line), fp) != NULL)
+ {
+ linenum ++;
+
+ /*
+ * Skip comment lines...
+ */
+
+ if (line[0] == '#')
+ continue;
+
+ /*
+ * Strip trailing newline, if any...
+ */
+
+ len = strlen(line);
+
+ if (line[len - 1] == '\n')
+ {
+ len --;
+ line[len] = '\0';
+ }
+
+ /*
+ * Extract the name from the beginning of the line...
+ */
+
+ for (value = line; isspace(*value); value ++);
+
+ for (nameptr = name; *value != '\0' && !isspace(*value);)
+ *nameptr++ = *value++;
+ *nameptr = '\0';
+
+ while (isspace(*value))
+ value ++;
+
+ if (name[0] == '\0')
+ continue;
+
+ /*
+ * Decode the directive...
+ */
+
+ if (strcmp(name, "</Location>") == 0)
+ return (linenum);
+ else if (strcmp(name, "Order") == 0)
+ {
+ /*
+ * "Order Deny,Allow" or "Order Allow,Deny"...
+ */
+
+ if (strncasecmp(value, "deny", 4) == 0)
+ loc->order_type = AUTH_ALLOW;
+ else if (strncasecmp(value, "allow", 5) == 0)
+ loc->order_type = AUTH_DENY;
+ else
+ LogMessage(LOG_ERROR, "Unknown Order value %s on line %d.",
+ value, linenum);
+ }
+ else if (strcmp(name, "Allow") == 0 ||
+ strcmp(name, "Deny") == 0)
+ {
+ /*
+ * Allow [From] host/ip...
+ * Deny [From] host/ip...
+ */
+
+ if (strncasecmp(value, "from", 4) == 0)
+ {
+ /*
+ * Strip leading "from"...
+ */
+
+ value += 4;
+
+ while (isspace(*value))
+ value ++;
+ }
+
+ /*
+ * Figure out what form the allow/deny address takes:
+ *
+ * All
+ * None
+ * *.domain.com
+ * .domain.com
+ * host.domain.com
+ * nnn.*
+ * nnn.nnn.*
+ * nnn.nnn.nnn.*
+ * nnn.nnn.nnn.nnn
+ * nnn.nnn.nnn.nnn/mm
+ * nnn.nnn.nnn.nnn/mmm.mmm.mmm.mmm
+ */
+
+ if (strcasecmp(value, "all") == 0)
+ {
+ /*
+ * All hosts...
+ */
+
+ if (strcmp(name, "Allow") == 0)
+ AllowIP(loc, 0, 0);
+ else
+ DenyIP(loc, 0, 0);
+ }
+ else if (strcasecmp(value, "none") == 0)
+ {
+ /*
+ * No hosts...
+ */
+
+ if (strcmp(name, "Allow") == 0)
+ AllowIP(loc, ~0, 0);
+ else
+ DenyIP(loc, ~0, 0);
+ }
+ else if (value[0] == '*' || value[0] == '.' || !isdigit(value[0]))
+ {
+ /*
+ * Host or domain name...
+ */
+
+ if (value[0] == '*')
+ value ++;
+
+ if (strcmp(name, "Allow") == 0)
+ AllowHost(loc, value);
+ else
+ DenyHost(loc, value);
+ }
+ else
+ {
+ /*
+ * One of many IP address forms...
+ */
+
+ memset(ip, 0, sizeof(ip));
+ ipcount = sscanf(value, "%d.%d.%d.%d", ip + 0, ip + 1, ip + 2, ip + 3);
+ address = (((((ip[0] << 8) | ip[1]) << 8) | ip[2]) << 8) | ip[3];
+
+ if ((value = strchr(value, '/')) != NULL)
+ {
+ value ++;
+ memset(mask, 0, sizeof(mask));
+ switch (sscanf(value, "%d.%d.%d.%d", mask + 0, mask + 1,
+ mask + 2, mask + 3))
+ {
+ case 1 :
+ netmask = (0xffffffff << (32 - mask[0])) & 0xffffffff;
+ break;
+ case 4 :
+ netmask = (((((mask[0] << 8) | mask[1]) << 8) |
+ mask[2]) << 8) | mask[3];
+ break;
+ default :
+ LogMessage(LOG_ERROR, "Bad netmask value %s on line %d.",
+ value, linenum);
+ netmask = 0xffffffff;
+ break;
+ }
+ }
+ else
+ netmask = netmasks[ipcount - 1];
+
+ if (strcmp(name, "Allow") == 0)
+ AllowIP(loc, address, netmask);
+ else
+ DenyIP(loc, address, netmask);
+ }
+ }
+ else if (strcmp(name, "AuthType") == 0)
+ {
+ /*
+ * AuthType Basic
+ */
+
+ if (strcasecmp(value, "basic") != 0)
+ LogMessage(LOG_WARN, "Unknown authorization type %s on line %d.",
+ value, linenum);
+ }
+ else if (strcmp(name, "AuthClass") == 0)
+ {
+ /*
+ * AuthClass anonymous, user, system, group
+ */
+
+ if (strcasecmp(value, "anonymous") == 0)
+ loc->level = AUTH_ANON;
+ else if (strcasecmp(value, "user") == 0)
+ loc->level = AUTH_USER;
+ else if (strcasecmp(value, "group") == 0)
+ loc->level = AUTH_GROUP;
+ else if (strcasecmp(value, "system") == 0)
+ {
+ loc->level = AUTH_GROUP;
+ strcpy(loc->group_name, SystemGroup);
+ }
+ else
+ LogMessage(LOG_WARN, "Unknown authorization class %s on line %d.",
+ value, linenum);
+ }
+ else if (strcmp(name, "AuthGroupName") == 0)
+ strncpy(loc->group_name, value, sizeof(loc->group_name) - 1);
+ else
+ LogMessage(LOG_ERROR, "Unknown Location directive %s on line %d.",
+ name, linenum);
+ }
+
+ return (0);
+}
+
+
+/*
+ * 'get_address()' - Get an address + port number from a line.
+ */
+
+static int /* O - 1 if address good, 0 if bad */
+get_address(char *value, /* I - Value string */
+ unsigned defaddress, /* I - Default address */
+ int defport, /* I - Default port */
+ struct sockaddr_in *address) /* O - Socket address */
+{
+ char hostname[256], /* Hostname or IP */
+ portname[256]; /* Port number or name */
+ struct hostent *host; /* Host address */
+ struct servent *port; /* Port number */
+
+
+ /*
+ * Initialize the socket address to the defaults...
+ */
+
+ memset(address, 0, sizeof(struct sockaddr_in));
+ address->sin_family = AF_INET;
+ address->sin_addr.s_addr = htonl(defaddress);
+ address->sin_port = htons(defport);
+
+ /*
+ * Try to grab a hostname and port number...
+ */
+
+ switch (sscanf(value, "%[^:]:%s", hostname, portname))
+ {
+ case 1 :
+ if (strchr(hostname, '.') == NULL)
+ {
+ /*
+ * Hostname is a port number...
+ */
+
+ strcpy(portname, hostname);
+ hostname[0] = '\0';
+ }
+ break;
+ case 2 :
+ break;
+ default :
+ puts("sscanf failed!");
+ return (0);
+ }
+
+ /*
+ * Decode the hostname and port number as needed...
+ */
+
+ if (hostname[0] != '\0')
+ {
+ if (isdigit(hostname[0]))
+ address->sin_addr.s_addr = htonl(inet_addr(hostname));
+ else
+ {
+ if ((host = gethostbyname(hostname)) == NULL)
+ {
+ perror("gethostbyname");
+ return (0);
+ }
+
+ memcpy(&(address->sin_addr), host->h_addr, host->h_length);
+ address->sin_port = htons(defport);
+ }
+ }
+
+ if (portname[0] != '\0')
+ {
+ if (isdigit(portname[0]))
+ address->sin_port = htons(atoi(portname));
+ else
+ {
+ if ((port = getservbyname(portname, NULL)) == NULL)
+ return (0);
+ else
+ address->sin_port = htons(port->s_port);
+ }
+ }
+
+ return (1);
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/scheduler/conf.h b/scheduler/conf.h
new file mode 100644
index 000000000..07a035499
--- /dev/null
+++ b/scheduler/conf.h
@@ -0,0 +1,112 @@
+/*
+ * "$Id$"
+ *
+ * Configuration file definitions for the Common UNIX Printing System (CUPS)
+ * scheduler.
+ *
+ * Copyright 1997-1999 by Easy Software Products, all rights reserved.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ */
+
+/*
+ * Log levels...
+ */
+
+#define LOG_PAGE -1 /* Used internally for page logging */
+#define LOG_NONE 0
+#define LOG_ERROR 1
+#define LOG_WARN 2
+#define LOG_INFO 3
+#define LOG_DEBUG 4
+
+
+/*
+ * Globals...
+ */
+
+VAR char ConfigurationFile[256] VALUE(CUPS_SERVERROOT "/conf/cupsd.conf"),
+ /* Configuration file to use */
+ ServerName[256] VALUE(""),
+ /* FQDN for server */
+ ServerAdmin[256] VALUE(""),
+ /* Administrator's email */
+ ServerRoot[1024] VALUE(CUPS_SERVERROOT),
+ /* Root directory for scheduler */
+ DocumentRoot[1024] VALUE(CUPS_DATADIR "/doc"),
+ /* Root directory for documents */
+ SystemGroup[32] VALUE(DEFAULT_GROUP),
+ /* System group name */
+ AccessLog[1024] VALUE("logs/access_log"),
+ /* Access log filename */
+ ErrorLog[1024] VALUE("logs/error_log"),
+ /* Error log filename */
+ PageLog[1024] VALUE("logs/page_log"),
+ /* Page log filename */
+ DefaultLanguage[32] VALUE("C"),
+ /* Default language encoding */
+ DefaultCharset[32] VALUE(DEFAULT_CHARSET),
+ /* Default charset */
+ RIPCache[32] VALUE("8m"),
+ /* Amount of memory for RIPs */
+ TempDir[32] VALUE("/var/tmp");
+ /* Temporary directory */
+VAR int User VALUE(DEFAULT_UID),
+ /* User ID for server */
+ Group VALUE(DEFAULT_GID),
+ /* Group ID for server */
+ LogLevel VALUE(LOG_ERROR),
+ /* Log level */
+ MaxClients VALUE(0),
+ /* Maximum number of clients */
+ MaxLogSize VALUE(1024 * 1024),
+ /* Maximum size of log files */
+ MaxRequestSize VALUE(0),
+ /* Maximum size of IPP requests */
+ HostNameLookups VALUE(FALSE),
+ /* Do we do reverse lookups? */
+ Timeout VALUE(DEFAULT_TIMEOUT),
+ /* Timeout during requests */
+ KeepAlive VALUE(TRUE),
+ /* Support the Keep-Alive option? */
+ KeepAliveTimeout VALUE(DEFAULT_KEEPALIVE),
+ /* Timeout between requests */
+ ImplicitClasses VALUE(TRUE);
+ /* Are classes implicitly created? */
+VAR FILE *AccessFile VALUE(NULL),
+ /* Access log file */
+ *ErrorFile VALUE(NULL),
+ /* Error log file */
+ *PageFile VALUE(NULL);
+ /* Page log file */
+VAR mime_t *MimeDatabase VALUE(NULL);
+ /* MIME type database */
+
+
+/*
+ * Prototypes...
+ */
+
+extern int ReadConfiguration(void);
+extern int LogRequest(client_t *con, http_status_t code);
+extern int LogMessage(int level, char *message, ...);
+extern int LogPage(job_t *job, char *page);
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/scheduler/cupsd.h b/scheduler/cupsd.h
new file mode 100644
index 000000000..8d7a23b65
--- /dev/null
+++ b/scheduler/cupsd.h
@@ -0,0 +1,161 @@
+/*
+ * "$Id$"
+ *
+ * Main header file for the Common UNIX Printing System (CUPS) scheduler.
+ *
+ * Copyright 1997-1999 by Easy Software Products, all rights reserved.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ */
+
+/*
+ * Include necessary headers.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <limits.h>
+#include <errno.h>
+#include <time.h>
+#include <signal.h>
+#include <fcntl.h>
+/*#include <bstring.h>*/
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#if defined(WIN32) || defined(__EMX__)
+# include <direct.h>
+#else
+# include <unistd.h>
+#endif /* WIN32 || __EMX__ */
+
+#include <cups/cups.h>
+#include <cups/string.h>
+#include <cups/mime.h>
+#include <cups/http.h>
+#include <cups/ipp.h>
+#include <cups/language.h>
+#include <cups/debug.h>
+
+
+/*
+ * Common constants.
+ */
+
+#ifndef FALSE
+# define FALSE 0
+# define TRUE (!FALSE)
+#endif /* !FALSE */
+
+
+/*
+ * Implementation limits...
+ */
+
+#define MAX_BROWSERS 10 /* Maximum number of browse addresses */
+#define MAX_LISTENERS 10 /* Maximum number of listener sockets */
+#define MAX_USERPASS 16 /* Maximum size of username/password */
+#define MAX_FILTERS 20 /* Maximum number of filters */
+
+/*
+ * Defaults...
+ */
+
+#define DEFAULT_TIMEOUT 300 /* Timeout during requests/updates */
+#define DEFAULT_KEEPALIVE 60 /* Timeout between requests */
+#define DEFAULT_INTERVAL 30 /* Interval between browse updates */
+#define DEFAULT_LANGUAGE setlocale(LC_ALL,NULL)
+ /* Default language encoding */
+#define DEFAULT_CHARSET "iso-8859-1"
+ /* Default charset */
+
+#ifdef __sgi
+# define DEFAULT_UID 9 /* Default user ID */
+# define DEFAULT_GID 0 /* Default group ID */
+# define DEFAULT_GROUP "sys" /* Default system group */
+#elif defined(__hpux)
+# define DEFAULT_UID 9 /* Default user ID */
+# define DEFAULT_GID 0 /* Default group ID */
+# define DEFAULT_GROUP "sys" /* Default system group */
+#elif defined(__sun)
+# define DEFAULT_UID 71 /* Default user ID */
+# define DEFAULT_GID 0 /* Default group ID */
+# define DEFAULT_GROUP "sys" /* Default system group */
+#elif defined(__linux)
+# define DEFAULT_UID 4 /* Default user ID */
+# define DEFAULT_GID 0 /* Default group ID */
+# define DEFAULT_GROUP "sys" /* Default system group */
+#elif defined(__osf__)
+# define DEFAULT_UID 8 /* Default user ID */
+# define DEFAULT_GID 0 /* Default group ID */
+# define DEFAULT_GROUP "system"/* Default system group */
+#else
+# define DEFAULT_UID 9 /* Default user ID */
+# define DEFAULT_GID 0 /* Default group ID */
+# define DEFAULT_GROUP "sys" /* Default system group */
+#endif /* __sgi */
+
+
+/*
+ * Global variable macros...
+ */
+
+#ifdef _MAIN_C_
+# define VAR
+# define VALUE(x) =x
+#else
+# define VAR extern
+# define VALUE(x)
+#endif /* _MAIN_C */
+
+
+/*
+ * Other stuff for the scheduler...
+ */
+
+#include "client.h"
+#include "auth.h"
+#include "dirsvc.h"
+#include "printers.h"
+#include "classes.h"
+#include "job.h"
+#include "conf.h"
+
+
+/*
+ * Globals...
+ */
+
+VAR fd_set InputSet, /* Input files for select() */
+ OutputSet; /* Output files for select() */
+
+VAR time_t StartTime; /* Time server was started */
+VAR int NeedReload VALUE(TRUE);
+ /* Need to load configuration? */
+
+
+/*
+ * Prototypes...
+ */
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/scheduler/dirsvc.c b/scheduler/dirsvc.c
new file mode 100644
index 000000000..4c0e28247
--- /dev/null
+++ b/scheduler/dirsvc.c
@@ -0,0 +1,544 @@
+/*
+ * "$Id$"
+ *
+ * Directory services routines for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-1999 by Easy Software Products, all rights reserved.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * Contents:
+ *
+ * StartBrowsing() - Start sending and receiving broadcast information.
+ * StopBrowsing() - Stop sending and receiving broadcast information.
+ * UpdateBrowseList() - Update the browse lists for any new browse data.
+ * SendBrowseList() - Send new browsing information.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "cupsd.h"
+
+
+/*
+ * 'StartBrowsing()' - Start sending and receiving broadcast information.
+ */
+
+void
+StartBrowsing(void)
+{
+ int val; /* Socket option value */
+ struct sockaddr_in addr; /* Broadcast address */
+
+
+ if (!Browsing)
+ return;
+
+ /*
+ * Create the broadcast socket...
+ */
+
+ if ((BrowseSocket = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
+ {
+ LogMessage(LOG_ERROR, "StartBrowsing: Unable to create broadcast socket - %s.",
+ strerror(errno));
+ return;
+ }
+
+ /*
+ * Set the "broadcast" flag...
+ */
+
+ val = 1;
+ if (setsockopt(BrowseSocket, SOL_SOCKET, SO_BROADCAST, &val, sizeof(val)))
+ {
+ LogMessage(LOG_ERROR, "StartBrowsing: Unable to set broadcast mode - %s.",
+ strerror(errno));
+
+#if defined(WIN32) || defined(__EMX__)
+ closesocket(BrowseSocket);
+#else
+ close(BrowseSocket);
+#endif /* WIN32 || __EMX__ */
+
+ BrowseSocket = -1;
+ return;
+ }
+
+ /*
+ * Bind the socket to browse port...
+ */
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(BrowsePort);
+
+ if (bind(BrowseSocket, (struct sockaddr *)&addr, sizeof(addr)))
+ {
+ LogMessage(LOG_ERROR, "StartBrowsing: Unable to bind broadcast socket - %s.",
+ strerror(errno));
+
+#if defined(WIN32) || defined(__EMX__)
+ closesocket(BrowseSocket);
+#else
+ close(BrowseSocket);
+#endif /* WIN32 || __EMX__ */
+
+ BrowseSocket = -1;
+ return;
+ }
+
+ /*
+ * Finally, add the socket to the input selection set...
+ */
+
+ FD_SET(BrowseSocket, &InputSet);
+}
+
+
+/*
+ * 'StopBrowsing()' - Stop sending and receiving broadcast information.
+ */
+
+void
+StopBrowsing(void)
+{
+ if (!Browsing)
+ return;
+
+ /*
+ * Close the socket and remove it from the input selection set.
+ */
+
+ if (BrowseSocket >= 0)
+ {
+#if defined(WIN32) || defined(__EMX__)
+ closesocket(BrowseSocket);
+#else
+ close(BrowseSocket);
+#endif /* WIN32 || __EMX__ */
+
+ FD_CLR(BrowseSocket, &InputSet);
+ }
+}
+
+
+/*
+ * 'UpdateBrowseList()' - Update the browse lists for any new browse data.
+ */
+
+void
+UpdateBrowseList(void)
+{
+ int i; /* Looping var */
+ int len, /* Length of name string */
+ offset; /* Offset in name string */
+ int bytes; /* Number of bytes left */
+ char packet[1540]; /* Broadcast packet */
+ cups_ptype_t type; /* Printer type */
+ ipp_pstate_t state; /* Printer state */
+ char uri[HTTP_MAX_URI], /* Printer URI */
+ method[HTTP_MAX_URI], /* Method portion of URI */
+ username[HTTP_MAX_URI], /* Username portion of URI */
+ host[HTTP_MAX_URI], /* Host portion of URI */
+ resource[HTTP_MAX_URI]; /* Resource portion of URI */
+ int port; /* Port portion of URI */
+ char name[IPP_MAX_NAME], /* Name of printer */
+ *hptr, /* Pointer into hostname */
+ *sptr; /* Pointer into ServerName */
+ printer_t *p, /* Printer information */
+ *pclass, /* Printer class */
+ *first; /* First printer in class */
+
+
+ /*
+ * Read a packet from the browse socket...
+ */
+
+#ifdef DEBUG
+ struct sockaddr_in addr;
+
+
+ len = sizeof(addr);
+ if ((bytes = recvfrom(BrowseSocket, packet, sizeof(packet), 0,
+ (struct sockaddr *)&addr, &len)) <= 0)
+ {
+ LogMessage(LOG_ERROR, "UpdateBrowseList: recv failed - %s.",
+ strerror(errno));
+ return;
+ }
+
+ packet[bytes] = '\0';
+ printf("UpdateBrowseList: (%d bytes from %08x) %s", bytes,
+ ntohl(addr.sin_addr.s_addr), packet);
+#else
+ if ((bytes = recv(BrowseSocket, packet, sizeof(packet), 0)) <= 0)
+ {
+ LogMessage(LOG_ERROR, "UpdateBrowseList: recv failed - %s.",
+ strerror(errno));
+ return;
+ }
+
+ packet[bytes] = '\0';
+#endif /* DEBUG */
+
+ if (sscanf(packet, "%x%x%s", &type, &state, uri) != 3)
+ {
+ LogMessage(LOG_WARN, "UpdateBrowseList: Garbled browse packet - %s",
+ packet);
+ return;
+ }
+
+ DEBUG_printf(("type=%x, state=%x, uri=\"%s\"\n", type, state, uri));
+
+ /*
+ * Pull the URI apart to see if this is a local or remote printer...
+ */
+
+ httpSeparate(uri, method, username, host, &port, resource);
+
+ DEBUG_printf(("host=\"%s\", ServerName=\"%s\"\n", host, ServerName));
+
+ if (strcasecmp(host, ServerName) == 0)
+ return;
+
+ /*
+ * OK, this isn't a local printer; see if we already have it listed in
+ * the Printers list, and add it if not...
+ */
+
+ hptr = strchr(host, '.');
+ sptr = strchr(ServerName, '.');
+
+ if (hptr != NULL && sptr != NULL &&
+ strcasecmp(hptr, sptr) == 0)
+ *hptr = '\0';
+
+ if (type & CUPS_PRINTER_CLASS)
+ {
+ /*
+ * Remote destination is a class...
+ */
+
+ if (strncmp(resource, "/classes/", 9) == 0)
+ sprintf(name, "%s@%s", resource + 9, host);
+ else
+ return;
+
+ if ((p = FindClass(name)) == NULL)
+ if ((p = FindClass(resource + 9)) != NULL)
+ {
+ if (strcasecmp(p->hostname, host) != 0)
+ {
+ /*
+ * Nope, this isn't the same host; if the hostname isn't the local host,
+ * add it to the other class and then find a class using the full host
+ * name...
+ */
+
+ if (p->type & CUPS_PRINTER_REMOTE)
+ {
+ strcat(p->name, "@");
+ strcat(p->name, p->hostname);
+ SetPrinterAttrs(p);
+ SortPrinters();
+ }
+
+ p = NULL;
+ }
+ }
+ else
+ strcpy(name, resource + 9);
+
+ if (p == NULL)
+ {
+ /*
+ * Class doesn't exist; add it...
+ */
+
+ p = AddClass(name);
+
+ /*
+ * Force the URI to point to the real server...
+ */
+
+ p->type = type;
+ strcpy(p->uri, uri);
+ strcpy(p->device_uri, uri);
+ strcpy(p->hostname, host);
+ SetPrinterAttrs(p);
+ }
+ }
+ else
+ {
+ /*
+ * Remote destination is a printer...
+ */
+
+ if (strncmp(resource, "/printers/", 10) == 0)
+ sprintf(name, "%s@%s", resource + 10, host);
+ else
+ return;
+
+ if ((p = FindPrinter(name)) == NULL)
+ if ((p = FindPrinter(resource + 10)) != NULL)
+ {
+ if (strcasecmp(p->hostname, host) != 0)
+ {
+ /*
+ * Nope, this isn't the same host; if the hostname isn't the local host,
+ * add it to the other printer and then find a printer using the full host
+ * name...
+ */
+
+ if (p->type & CUPS_PRINTER_REMOTE)
+ {
+ strcat(p->name, "@");
+ strcat(p->name, p->hostname);
+ SetPrinterAttrs(p);
+ SortPrinters();
+ }
+
+ p = NULL;
+ }
+ }
+ else
+ strcpy(name, resource + 10);
+
+ if (p == NULL)
+ {
+ /*
+ * Printer doesn't exist; add it...
+ */
+
+ p = AddPrinter(name);
+
+ /*
+ * Force the URI to point to the real server...
+ */
+
+ p->type = type;
+ strcpy(p->uri, uri);
+ strcpy(p->device_uri, uri);
+ strcpy(p->hostname, host);
+ SetPrinterAttrs(p);
+ }
+ }
+
+ /*
+ * Update the state...
+ */
+
+ p->type = type;
+ p->state = state;
+ p->accepting = state != IPP_PRINTER_STOPPED;
+ p->browse_time = time(NULL);
+
+ /*
+ * See if we have a default printer... If not, make the first printer the
+ * default.
+ */
+
+ if (DefaultPrinter == NULL && Printers != NULL)
+ DefaultPrinter = Printers;
+
+ /*
+ * Do auto-classing if needed...
+ */
+
+ if (ImplicitClasses)
+ {
+ /*
+ * Loop through all available printers and create classes as needed...
+ */
+
+ for (p = Printers, len = 0, offset = 0; p != NULL; p = p->next)
+ {
+ /*
+ * Skip classes...
+ */
+
+ if (p->type & CUPS_PRINTER_CLASS)
+ {
+ len = 0;
+ continue;
+ }
+
+ /*
+ * If len == 0, get the length of this printer name up to the "@"
+ * sign (if any).
+ */
+
+ if (len > 0 &&
+ strncasecmp(p->name, name + offset, len) == 0 &&
+ (p->name[len] == '\0' || p->name[len] == '@'))
+ {
+ /*
+ * We have more than one printer with the same name; see if
+ * we have a class, and if this printer is a member...
+ */
+
+ if ((pclass = FindClass(name)) == NULL)
+ {
+ /*
+ * Need to add the class...
+ */
+
+ pclass = AddClass(name);
+ pclass->type |= CUPS_PRINTER_IMPLICIT;
+ pclass->accepting = 1;
+ pclass->state = IPP_PRINTER_IDLE;
+
+ SetPrinterAttrs(pclass);
+
+ DEBUG_printf(("Added new class \"%s\", type = %x\n", name,
+ pclass->type));
+ }
+
+ if (first != NULL)
+ {
+ for (i = 0; i < pclass->num_printers; i ++)
+ if (pclass->printers[i] == first)
+ break;
+
+ if (i >= pclass->num_printers)
+ AddPrinterToClass(pclass, first);
+
+ first = NULL;
+ }
+
+ for (i = 0; i < pclass->num_printers; i ++)
+ if (pclass->printers[i] == p)
+ break;
+
+ if (i >= pclass->num_printers)
+ AddPrinterToClass(pclass, p);
+ }
+ else
+ {
+ /*
+ * First time around; just get name length and mark it as first
+ * in the list...
+ */
+
+ if ((hptr = strchr(p->name, '@')) != NULL)
+ len = hptr - p->name;
+ else
+ len = strlen(p->name);
+
+ strncpy(name, p->name, len);
+ name[len] = '\0';
+ offset = 0;
+
+ if (FindPrinter(name) != NULL)
+ {
+ /*
+ * Can't use same name as printer; add "Any" to the front of the
+ * name...
+ */
+
+ strcpy(name, "Any");
+ strncpy(name + 3, p->name, len);
+ name[len + 3] = '\0';
+ offset = 3;
+ }
+
+ first = p;
+ }
+ }
+ }
+}
+
+
+/*
+ * 'SendBrowseList()' - Send new browsing information.
+ */
+
+void
+SendBrowseList(void)
+{
+ int i; /* Looping var */
+ printer_t *p, /* Current printer */
+ *np; /* Next printer */
+ time_t ut, /* Minimum update time */
+ to; /* Timeout time */
+ int bytes; /* Length of packet */
+ char packet[1540];
+ /* Browse data packet */
+
+
+ /*
+ * Compute the update time...
+ */
+
+ ut = time(NULL) - BrowseInterval;
+ to = time(NULL) - BrowseTimeout;
+
+ /*
+ * Loop through all of the printers and send local updates as needed...
+ */
+
+ for (p = Printers; p != NULL; p = np)
+ {
+ np = p->next;
+
+ if (p->type & CUPS_PRINTER_REMOTE)
+ {
+ /*
+ * See if this printer needs to be timed out...
+ */
+
+ if (p->browse_time < to)
+ {
+ DEBUG_printf(("Printer \"%s\" has timed out (%d < %d...)\n",
+ p->name, p->browse_time, to));
+ DeletePrinter(p);
+ }
+ }
+ else if (p->browse_time < ut && !(p->type & CUPS_PRINTER_IMPLICIT))
+ {
+ /*
+ * Need to send an update...
+ */
+
+ p->browse_time = time(NULL);
+
+ sprintf(packet, "%x %x %s\n", p->type | CUPS_PRINTER_REMOTE, p->state,
+ p->uri);
+ bytes = strlen(packet);
+ DEBUG_printf(("SendBrowseList: (%d bytes) %s", bytes, packet));
+
+ /*
+ * Send a packet to each browse address...
+ */
+
+ for (i = 0; i < NumBrowsers; i ++)
+ if (sendto(BrowseSocket, packet, bytes, 0,
+ (struct sockaddr *)Browsers + i, sizeof(Browsers[0])) <= 0)
+ LogMessage(LOG_ERROR, "SendBrowseList: sendto failed for browser %d - %s.",
+ i + 1, strerror(errno));
+ }
+ }
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/scheduler/dirsvc.h b/scheduler/dirsvc.h
new file mode 100644
index 000000000..da17d981a
--- /dev/null
+++ b/scheduler/dirsvc.h
@@ -0,0 +1,58 @@
+/*
+ * "$Id$"
+ *
+ * Directory services definitions for the Common UNIX Printing System
+ * (CUPS) scheduler.
+ *
+ * Copyright 1997-1999 by Easy Software Products, all rights reserved.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ */
+
+/*
+ * Globals...
+ */
+
+VAR int Browsing VALUE(TRUE),
+ /* Whether or not browsing is enabled */
+ BrowseSocket VALUE(-1),
+ /* Socket for browsing */
+ BrowsePort VALUE(IPP_PORT),
+ /* Port number for broadcasts */
+ BrowseInterval VALUE(DEFAULT_INTERVAL),
+ /* Broadcast interval in seconds */
+ BrowseTimeout VALUE(DEFAULT_TIMEOUT),
+ /* Time out for printers in seconds */
+ NumBrowsers VALUE(0);
+ /* Number of broadcast addresses */
+VAR struct sockaddr_in Browsers[MAX_BROWSERS];
+ /* Broadcast addresses */
+
+
+/*
+ * Prototypes...
+ */
+
+extern void StartBrowsing(void);
+extern void StopBrowsing(void);
+extern void UpdateBrowseList(void);
+extern void SendBrowseList(void);
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/scheduler/ipp.c b/scheduler/ipp.c
new file mode 100644
index 000000000..65a854e0e
--- /dev/null
+++ b/scheduler/ipp.c
@@ -0,0 +1,2421 @@
+/*
+ * "$Id$"
+ *
+ * IPP routines for the Common UNIX Printing System (CUPS) scheduler.
+ *
+ * Copyright 1997-1999 by Easy Software Products, all rights reserved.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * Contents:
+ *
+ * ProcessIPPRequest() - Process an incoming IPP request...
+ * accept_jobs() - Accept print jobs to a printer.
+ * add_class() - Add a class to the system.
+ * add_printer() - Add a printer to the system.
+ * cancel_all_jobs() - Cancel all print jobs.
+ * cancel_job() - Cancel a print job.
+ * copy_attrs() - Copy attributes from one request to another.
+ * delete_printer() - Remove a printer or class from the system.
+ * get_default() - Get the default destination.
+ * get_jobs() - Get a list of jobs for the specified printer.
+ * get_job_attrs() - Get job attributes.
+ * get_printers() - Get a list of printers.
+ * get_printer_attrs() - Get printer attributes.
+ * print_job() - Print a file to a printer or class.
+ * reject_jobs() - Reject print jobs to a printer.
+ * send_ipp_error() - Send an error status back to the IPP client.
+ * start_printer() - Start a printer.
+ * stop_printer() - Stop a printer.
+ * validate_dest() - Validate a printer class destination.
+ * validate_job() - Validate printer options and destination.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "cupsd.h"
+
+
+/*
+ * Local functions...
+ */
+
+static void accept_jobs(client_t *con, ipp_attribute_t *uri);
+static void add_class(client_t *con, ipp_attribute_t *uri);
+static void add_printer(client_t *con, ipp_attribute_t *uri);
+static void cancel_all_jobs(client_t *con, ipp_attribute_t *uri);
+static void cancel_job(client_t *con, ipp_attribute_t *uri);
+static void copy_attrs(ipp_t *to, ipp_t *from, ipp_attribute_t *req);
+static void delete_printer(client_t *con, ipp_attribute_t *uri);
+static void get_default(client_t *con);
+static void get_jobs(client_t *con, ipp_attribute_t *uri);
+static void get_job_attrs(client_t *con, ipp_attribute_t *uri);
+static void get_printers(client_t *con, int type);
+static void get_printer_attrs(client_t *con, ipp_attribute_t *uri);
+static void print_job(client_t *con, ipp_attribute_t *uri);
+static void reject_jobs(client_t *con, ipp_attribute_t *uri);
+static void send_ipp_error(client_t *con, ipp_status_t status);
+static void set_default(client_t *con, ipp_attribute_t *uri);
+static void start_printer(client_t *con, ipp_attribute_t *uri);
+static void stop_printer(client_t *con, ipp_attribute_t *uri);
+static char *validate_dest(char *resource, cups_ptype_t *dtype);
+static void validate_job(client_t *con, ipp_attribute_t *uri);
+
+
+/*
+ * 'ProcessIPPRequest()' - Process an incoming IPP request...
+ */
+
+void
+ProcessIPPRequest(client_t *con) /* I - Client connection */
+{
+ ipp_tag_t group; /* Current group tag */
+ ipp_attribute_t *attr; /* Current attribute */
+ ipp_attribute_t *charset; /* Character set attribute */
+ ipp_attribute_t *language; /* Language attribute */
+ ipp_attribute_t *uri; /* Printer URI attribute */
+
+
+ DEBUG_printf(("ProcessIPPRequest(%08x)\n", con));
+ DEBUG_printf(("ProcessIPPRequest: operation_id = %04x\n",
+ con->request->request.op.operation_id));
+
+ /*
+ * First build an empty response message for this request...
+ */
+
+ con->response = ippNew();
+
+ con->response->request.status.version[0] = 1;
+ con->response->request.status.version[1] = 0;
+ con->response->request.status.request_id = con->request->request.op.request_id;
+
+ /*
+ * Then validate the request header and required attributes...
+ */
+
+ if (con->request->request.any.version[0] != 1)
+ {
+ /*
+ * Return an error, since we only support IPP 1.x.
+ */
+
+ send_ipp_error(con, IPP_VERSION_NOT_SUPPORTED);
+ }
+ else
+ {
+ /*
+ * Make sure that the attributes are provided in the correct order and
+ * don't repeat groups...
+ */
+
+ for (attr = con->request->attrs, group = attr->group_tag;
+ attr != NULL;
+ attr = attr->next)
+ if (attr->group_tag < group)
+ {
+ /*
+ * Out of order; return an error...
+ */
+
+ DEBUG_puts("ProcessIPPRequest: attribute groups are out of order!");
+ send_ipp_error(con, IPP_BAD_REQUEST);
+ break;
+ }
+ else
+ group = attr->group_tag;
+
+ if (attr == NULL)
+ {
+ /*
+ * Then make sure that the first three attributes are:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * printer-uri
+ */
+
+ attr = con->request->attrs;
+ if (attr != NULL && strcmp(attr->name, "attributes-charset") == 0 &&
+ attr->value_tag == IPP_TAG_CHARSET)
+ charset = attr;
+ else
+ charset = NULL;
+
+ attr = attr->next;
+ if (attr != NULL && strcmp(attr->name, "attributes-natural-language") == 0 &&
+ attr->value_tag == IPP_TAG_LANGUAGE)
+ language = attr;
+ else
+ language = NULL;
+
+ attr = attr->next;
+ if (attr != NULL && strcmp(attr->name, "printer-uri") == 0 &&
+ attr->value_tag == IPP_TAG_URI)
+ uri = attr;
+ else if (attr != NULL && strcmp(attr->name, "job-uri") == 0 &&
+ attr->value_tag == IPP_TAG_URI)
+ uri = attr;
+ else
+ uri = NULL;
+
+ if (charset == NULL || language == NULL ||
+ (uri == NULL && con->request->request.op.operation_id < IPP_PRIVATE))
+ {
+ /*
+ * Return an error, since attributes-charset,
+ * attributes-natural-language, and printer-uri/job-uri are required
+ * for all operations.
+ */
+
+ DEBUG_printf(("ProcessIPPRequest: missing attributes (%08x, %08x, %08x)!\n",
+ charset, language, uri));
+ send_ipp_error(con, IPP_BAD_REQUEST);
+ }
+ else
+ {
+ attr = ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL,
+ charset->values[0].string.text);
+
+ attr = ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL,
+ language->values[0].string.text);
+
+ /*
+ * OK, all the checks pass so far; try processing the operation...
+ */
+
+ switch (con->request->request.op.operation_id)
+ {
+ case IPP_PRINT_JOB :
+ print_job(con, uri);
+ break;
+
+ case IPP_VALIDATE_JOB :
+ validate_job(con, uri);
+ break;
+
+ case IPP_CANCEL_JOB :
+ cancel_job(con, uri);
+ break;
+
+ case IPP_GET_JOB_ATTRIBUTES :
+ get_job_attrs(con, uri);
+ break;
+
+ case IPP_GET_JOBS :
+ get_jobs(con, uri);
+ break;
+
+ case IPP_GET_PRINTER_ATTRIBUTES :
+ get_printer_attrs(con, uri);
+ break;
+
+ case IPP_PAUSE_PRINTER :
+ stop_printer(con, uri);
+ break;
+
+ case IPP_RESUME_PRINTER :
+ start_printer(con, uri);
+ break;
+
+ case IPP_PURGE_JOBS :
+ cancel_all_jobs(con, uri);
+ break;
+
+ case CUPS_GET_DEFAULT :
+ get_default(con);
+ break;
+
+ case CUPS_GET_PRINTERS :
+ get_printers(con, 0);
+ break;
+
+ case CUPS_GET_CLASSES :
+ get_printers(con, CUPS_PRINTER_CLASS);
+ break;
+
+ case CUPS_ADD_PRINTER :
+ add_printer(con, uri);
+ break;
+
+ case CUPS_DELETE_PRINTER :
+ delete_printer(con, uri);
+ break;
+
+ case CUPS_ADD_CLASS :
+ add_class(con, uri);
+ break;
+
+ case CUPS_DELETE_CLASS :
+ delete_printer(con, uri);
+ break;
+
+ case CUPS_ACCEPT_JOBS :
+ accept_jobs(con, uri);
+ break;
+
+ case CUPS_REJECT_JOBS :
+ reject_jobs(con, uri);
+ break;
+
+ case CUPS_SET_DEFAULT :
+ set_default(con, uri);
+ break;
+
+ default :
+ send_ipp_error(con, IPP_OPERATION_NOT_SUPPORTED);
+ }
+ }
+ }
+ }
+
+ SendHeader(con, HTTP_OK, "application/ipp");
+ httpPrintf(HTTP(con), "Content-Length: %d\r\n\r\n", ippLength(con->response));
+
+ FD_SET(con->http.fd, &OutputSet);
+}
+
+
+/*
+ * 'accept_jobs()' - Accept print jobs to a printer.
+ */
+
+static void
+accept_jobs(client_t *con, /* I - Client connection */
+ ipp_attribute_t *uri) /* I - Printer or class URI */
+{
+ cups_ptype_t dtype; /* Destination type (printer or class) */
+ char method[HTTP_MAX_URI],
+ /* Method portion of URI */
+ username[HTTP_MAX_URI],
+ /* Username portion of URI */
+ host[HTTP_MAX_URI],
+ /* Host portion of URI */
+ resource[HTTP_MAX_URI];
+ /* Resource portion of URI */
+ int port; /* Port portion of URI */
+ char *name; /* Printer name */
+ printer_t *printer; /* Printer data */
+
+
+ DEBUG_printf(("accept_jobs(%08x, %08x)\n", con, uri));
+
+ /*
+ * Was this operation called from the correct URI?
+ */
+
+ if (strncmp(con->uri, "/admin/", 7) != 0)
+ {
+ send_ipp_error(con, IPP_NOT_AUTHORIZED);
+ return;
+ }
+
+ /*
+ * Is the destination valid?
+ */
+
+ httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
+
+ if ((name = validate_dest(resource, &dtype)) == NULL)
+ {
+ /*
+ * Bad URI...
+ */
+
+ DEBUG_printf(("accept_jobs: resource name \'%s\' no good!\n",
+ resource));
+ send_ipp_error(con, IPP_NOT_FOUND);
+ return;
+ }
+
+ /*
+ * Accept jobs sent to the printer...
+ */
+
+ printer = FindPrinter(name);
+ printer->accepting = 1;
+ printer->state_message[0] = '\0';
+
+ LogMessage(LOG_INFO, "Printer \'%s\' now accepting jobs (\'%s\').", name,
+ con->username);
+
+ /*
+ * Everything was ok, so return OK status...
+ */
+
+ con->response->request.status.status_code = IPP_OK;
+}
+
+
+/*
+ * 'add_class()' - Add a class to the system.
+ */
+
+static void
+add_class(client_t *con, /* I - Client connection */
+ ipp_attribute_t *uri) /* I - URI of class */
+{
+ int i; /* Looping var */
+ char method[HTTP_MAX_URI],
+ /* Method portion of URI */
+ username[HTTP_MAX_URI],
+ /* Username portion of URI */
+ host[HTTP_MAX_URI],
+ /* Host portion of URI */
+ resource[HTTP_MAX_URI];
+ /* Resource portion of URI */
+ int port; /* Port portion of URI */
+ printer_t *pclass; /* Class */
+ cups_ptype_t dtype; /* Destination type */
+ char *dest; /* Printer or class name */
+ ipp_attribute_t *attr; /* Printer attribute */
+
+
+ /*
+ * Was this operation called from the correct URI?
+ */
+
+ if (strncmp(con->uri, "/admin/", 7) != 0)
+ {
+ send_ipp_error(con, IPP_NOT_AUTHORIZED);
+ return;
+ }
+
+ DEBUG_printf(("add_class(%08x, %08x)\n", con, uri));
+
+ /*
+ * Do we have a valid URI?
+ */
+
+ httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
+
+ if (strncmp(resource, "/classes/", 9) != 0)
+ {
+ /*
+ * No, return an error...
+ */
+
+ send_ipp_error(con, IPP_BAD_REQUEST);
+ return;
+ }
+
+ /*
+ * See if the class already exists; if not, create a new class...
+ */
+
+ if ((pclass = FindClass(resource + 9)) == NULL)
+ {
+ /*
+ * Class doesn't exist; see if we have a printer of the same name...
+ */
+
+ if (FindPrinter(resource + 9) != NULL)
+ {
+ /*
+ * Yes, return an error...
+ */
+
+ send_ipp_error(con, IPP_NOT_POSSIBLE);
+ return;
+ }
+ else
+ {
+ /*
+ * No, add the pclass...
+ */
+
+ pclass = AddClass(resource + 9);
+ }
+ }
+ else if (pclass->type & CUPS_PRINTER_REMOTE)
+ {
+ /*
+ * We found a remote class; rename it and then add the pclass.
+ */
+
+ strcat(pclass->name, "@");
+ strcat(pclass->name, pclass->hostname);
+ SetPrinterAttrs(pclass);
+ SortPrinters();
+
+ pclass = AddClass(resource + 9);
+ }
+
+ /*
+ * Look for attributes and copy them over as needed...
+ */
+
+ if ((attr = ippFindAttribute(con->request, "printer-location", IPP_TAG_TEXT)) != NULL)
+ strcpy(pclass->location, attr->values[0].string.text);
+ if ((attr = ippFindAttribute(con->request, "printer-info", IPP_TAG_TEXT)) != NULL)
+ strcpy(pclass->info, attr->values[0].string.text);
+ if ((attr = ippFindAttribute(con->request, "printer-more-info", IPP_TAG_URI)) != NULL)
+ strcpy(pclass->more_info, attr->values[0].string.text);
+ if ((attr = ippFindAttribute(con->request, "printer-is-accepting-jobs", IPP_TAG_BOOLEAN)) != NULL)
+ {
+ LogMessage(LOG_INFO, "Setting %s printer-is-accepting-jobs to %d (was %d.)",
+ pclass->name, attr->values[0].boolean, pclass->accepting);
+
+ pclass->accepting = attr->values[0].boolean;
+ }
+ if ((attr = ippFindAttribute(con->request, "printer-state", IPP_TAG_ENUM)) != NULL)
+ {
+ LogMessage(LOG_INFO, "Setting %s printer-state to %d (was %d.)", pclass->name,
+ attr->values[0].integer, pclass->state);
+
+ if (pclass->state == IPP_PRINTER_STOPPED &&
+ attr->values[0].integer != IPP_PRINTER_STOPPED)
+ pclass->state = IPP_PRINTER_IDLE;
+ else if (pclass->state != IPP_PRINTER_STOPPED &&
+ attr->values[0].integer == IPP_PRINTER_STOPPED)
+ {
+ if (pclass->state == IPP_PRINTER_PROCESSING)
+ StopJob(((job_t *)pclass->job)->id);
+
+ pclass->state = IPP_PRINTER_STOPPED;
+ }
+
+ pclass->browse_time = 0;
+ }
+ if ((attr = ippFindAttribute(con->request, "printer-state-message", IPP_TAG_TEXT)) != NULL)
+ {
+ strncpy(pclass->state_message, attr->values[0].string.text,
+ sizeof(pclass->state_message) - 1);
+ pclass->state_message[sizeof(pclass->state_message) - 1] = '\0';
+ }
+
+ if ((attr = ippFindAttribute(con->request, "member-uris", IPP_TAG_URI)) != NULL)
+ {
+ /*
+ * Clear the printer array as needed...
+ */
+
+ if (pclass->num_printers > 0)
+ {
+ free(pclass->printers);
+ pclass->num_printers = 0;
+ }
+
+ /*
+ * Add each printer or class that is listed...
+ */
+
+ for (i = 0; i < attr->num_values; i ++)
+ {
+ /*
+ * Search for the printer or class URI...
+ */
+
+ httpSeparate(attr->values[i].string.text, method, username, host,
+ &port, resource);
+
+ if ((dest = validate_dest(resource, &dtype)) == NULL)
+ {
+ /*
+ * Bad URI...
+ */
+
+ DEBUG_printf(("add_class: resource name \'%s\' no good!\n",
+ resource));
+ send_ipp_error(con, IPP_NOT_FOUND);
+ return;
+ }
+
+ /*
+ * Add it to the class...
+ */
+
+ if (dtype == CUPS_PRINTER_CLASS)
+ AddPrinterToClass(pclass, FindClass(dest));
+ else
+ AddPrinterToClass(pclass, FindPrinter(dest));
+ }
+ }
+
+ /*
+ * Update the printer class attributes and return...
+ */
+
+ SetPrinterAttrs(pclass);
+ SaveAllClasses();
+
+ LogMessage(LOG_INFO, "New class \'%s\' added by \'%s\'.", pclass->name,
+ con->username);
+
+ con->response->request.status.status_code = IPP_OK;
+}
+
+
+/*
+ * 'add_printer()' - Add a printer to the system.
+ */
+
+static void
+add_printer(client_t *con, /* I - Client connection */
+ ipp_attribute_t *uri) /* I - URI of printer */
+{
+ char method[HTTP_MAX_URI],
+ /* Method portion of URI */
+ username[HTTP_MAX_URI],
+ /* Username portion of URI */
+ host[HTTP_MAX_URI],
+ /* Host portion of URI */
+ resource[HTTP_MAX_URI];
+ /* Resource portion of URI */
+ int port; /* Port portion of URI */
+ printer_t *printer; /* Printer/class */
+ ipp_attribute_t *attr; /* Printer attribute */
+ FILE *fp; /* Script/PPD file */
+ char line[1024]; /* Line from file... */
+ char filename[1024]; /* Script/PPD file */
+
+
+ /*
+ * Was this operation called from the correct URI?
+ */
+
+ if (strncmp(con->uri, "/admin/", 7) != 0)
+ {
+ send_ipp_error(con, IPP_NOT_AUTHORIZED);
+ return;
+ }
+
+ DEBUG_printf(("add_printer(%08x, %08x)\n", con, uri));
+
+ /*
+ * Do we have a valid URI?
+ */
+
+ httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
+
+ if (strncmp(resource, "/printers/", 10) != 0)
+ {
+ /*
+ * No, return an error...
+ */
+
+ send_ipp_error(con, IPP_BAD_REQUEST);
+ return;
+ }
+
+ /*
+ * See if the printer already exists; if not, create a new printer...
+ */
+
+ if ((printer = FindPrinter(resource + 10)) == NULL)
+ {
+ /*
+ * Printer doesn't exist; see if we have a class of the same name...
+ */
+
+ if (FindClass(resource + 10) != NULL)
+ {
+ /*
+ * Yes, return an error...
+ */
+
+ send_ipp_error(con, IPP_NOT_POSSIBLE);
+ return;
+ }
+ else
+ {
+ /*
+ * No, add the printer...
+ */
+
+ printer = AddPrinter(resource + 10);
+ }
+ }
+ else if (printer->type & CUPS_PRINTER_REMOTE)
+ {
+ /*
+ * We found a remote printer; rename it and then add the printer.
+ */
+
+ strcat(printer->name, "@");
+ strcat(printer->name, printer->hostname);
+ SetPrinterAttrs(printer);
+ SortPrinters();
+
+ printer = AddPrinter(resource + 10);
+ }
+
+ /*
+ * Look for attributes and copy them over as needed...
+ */
+
+ if ((attr = ippFindAttribute(con->request, "printer-location", IPP_TAG_TEXT)) != NULL)
+ strcpy(printer->location, attr->values[0].string.text);
+ if ((attr = ippFindAttribute(con->request, "printer-info", IPP_TAG_TEXT)) != NULL)
+ strcpy(printer->info, attr->values[0].string.text);
+ if ((attr = ippFindAttribute(con->request, "printer-more-info", IPP_TAG_URI)) != NULL)
+ strcpy(printer->more_info, attr->values[0].string.text);
+ if ((attr = ippFindAttribute(con->request, "device-uri", IPP_TAG_URI)) != NULL)
+ {
+ LogMessage(LOG_INFO, "Setting %s device-uri to \"%s\" (was \"%s\".)",
+ printer->name, attr->values[0].string.text, printer->device_uri);
+
+ strcpy(printer->device_uri, attr->values[0].string.text);
+ }
+ if ((attr = ippFindAttribute(con->request, "printer-is-accepting-jobs", IPP_TAG_BOOLEAN)) != NULL)
+ {
+ LogMessage(LOG_INFO, "Setting %s printer-is-accepting-jobs to %d (was %d.)",
+ printer->name, attr->values[0].boolean, printer->accepting);
+
+ printer->accepting = attr->values[0].boolean;
+ }
+ if ((attr = ippFindAttribute(con->request, "printer-state", IPP_TAG_ENUM)) != NULL)
+ {
+ LogMessage(LOG_INFO, "Setting %s printer-state to %d (was %d.)", printer->name,
+ attr->values[0].integer, printer->state);
+
+ if (printer->state == IPP_PRINTER_STOPPED &&
+ attr->values[0].integer != IPP_PRINTER_STOPPED)
+ printer->state = IPP_PRINTER_IDLE;
+ else if (printer->state != IPP_PRINTER_STOPPED &&
+ attr->values[0].integer == IPP_PRINTER_STOPPED)
+ {
+ if (printer->state == IPP_PRINTER_PROCESSING)
+ StopJob(((job_t *)printer->job)->id);
+
+ printer->state = IPP_PRINTER_STOPPED;
+ }
+
+ printer->browse_time = 0;
+ }
+ if ((attr = ippFindAttribute(con->request, "printer-state-message", IPP_TAG_TEXT)) != NULL)
+ {
+ strncpy(printer->state_message, attr->values[0].string.text,
+ sizeof(printer->state_message) - 1);
+ printer->state_message[sizeof(printer->state_message) - 1] = '\0';
+ }
+
+ /*
+ * See if we have all required attributes...
+ */
+
+ if (printer->device_uri[0] == '\0')
+ strcpy(printer->device_uri, "file:/dev/null");
+
+ /*
+ * See if we have an interface script or PPD file attached to the request...
+ */
+
+ if (con->filename[0] &&
+ (fp = fopen(con->filename, "r")) != NULL)
+ {
+ /*
+ * Yes; get the first line from it...
+ */
+
+ line[0] = '\0';
+ fgets(line, sizeof(line), fp);
+ fclose(fp);
+
+ /*
+ * Then see what kind of file it is...
+ */
+
+ sprintf(filename, "%s/interfaces/%s", ServerRoot, printer->name);
+
+ if (strncmp(line, "*PPD-Adobe", 10) == 0)
+ {
+ /*
+ * The new file is a PPD file, so remove any old interface script
+ * that might be lying around...
+ */
+
+ unlink(filename);
+ }
+ else
+ {
+ /*
+ * This must be an interface script, so move the file over to the
+ * interfaces directory and make it executable...
+ */
+
+ rename(con->filename, filename);
+ chmod(filename, 0755);
+ }
+
+ sprintf(filename, "%s/ppd/%s.ppd", ServerRoot, printer->name);
+
+ if (strncmp(line, "*PPD-Adobe", 10) == 0)
+ {
+ /*
+ * The new file is a PPD file, so move the file over to the
+ * ppd directory and make it readable by all...
+ */
+
+ rename(con->filename, filename);
+ chmod(filename, 0644);
+ }
+ else
+ {
+ /*
+ * This must be an interface script, so remove any old PPD file that
+ * may be lying around...
+ */
+
+ unlink(filename);
+ }
+ }
+
+ /*
+ * Make this printer the default if there is none...
+ */
+
+ if (DefaultPrinter == NULL)
+ DefaultPrinter = printer;
+
+ /*
+ * Update the printer attributes and return...
+ */
+
+ SetPrinterAttrs(printer);
+ SaveAllPrinters();
+
+ LogMessage(LOG_INFO, "New printer \'%s\' added by \'%s\'.", printer->name,
+ con->username);
+
+ con->response->request.status.status_code = IPP_OK;
+}
+
+
+/*
+ * 'cancel_all_jobs()' - Cancel all print jobs.
+ */
+
+static void
+cancel_all_jobs(client_t *con, /* I - Client connection */
+ ipp_attribute_t *uri) /* I - Job or Printer URI */
+{
+ ipp_attribute_t *attr; /* Current attribute */
+ char *dest; /* Destination */
+ cups_ptype_t dtype; /* Destination type */
+ char method[HTTP_MAX_URI],
+ /* Method portion of URI */
+ username[HTTP_MAX_URI],
+ /* Username portion of URI */
+ host[HTTP_MAX_URI],
+ /* Host portion of URI */
+ resource[HTTP_MAX_URI];
+ /* Resource portion of URI */
+ int port; /* Port portion of URI */
+
+
+ DEBUG_printf(("cancel_all_jobs(%08x, %08x)\n", con, uri));
+
+ /*
+ * Was this operation called from the correct URI?
+ */
+
+ if (strncmp(con->uri, "/admin/", 7) != 0)
+ {
+ send_ipp_error(con, IPP_NOT_AUTHORIZED);
+ return;
+ }
+
+ /*
+ * See if we have a printer URI...
+ */
+
+ if (strcmp(uri->name, "printer-uri") != 0)
+ {
+ DEBUG_printf(("cancel_all_jobs: bad %s attribute \'%s\'!\n",
+ uri->name, uri->values[0].string.text));
+ send_ipp_error(con, IPP_BAD_REQUEST);
+ return;
+ }
+
+ /*
+ * And if the destination is valid...
+ */
+
+ httpSeparate(uri->values[0].string.text, method, username, host, &port,
+ resource);
+
+ if ((dest = validate_dest(resource, &dtype)) == NULL)
+ {
+ /*
+ * Bad URI...
+ */
+
+ DEBUG_printf(("cancel_all_jobs: resource name \'%s\' no good!\n",
+ resource));
+ send_ipp_error(con, IPP_NOT_FOUND);
+ return;
+ }
+
+ /*
+ * Cancel all of the jobs and return...
+ */
+
+ CancelJobs(dest);
+ LogMessage(LOG_INFO, "All jobs on \'%s\' were cancelled by \'%s\'.", dest,
+ con->username);
+
+ con->response->request.status.status_code = IPP_OK;
+}
+
+
+/*
+ * 'cancel_job()' - Cancel a print job.
+ */
+
+static void
+cancel_job(client_t *con, /* I - Client connection */
+ ipp_attribute_t *uri) /* I - Job or Printer URI */
+{
+ ipp_attribute_t *attr; /* Current attribute */
+ int jobid; /* Job ID */
+ char method[HTTP_MAX_URI],
+ /* Method portion of URI */
+ username[HTTP_MAX_URI],
+ /* Username portion of URI */
+ host[HTTP_MAX_URI],
+ /* Host portion of URI */
+ resource[HTTP_MAX_URI];
+ /* Resource portion of URI */
+ int port; /* Port portion of URI */
+
+
+ DEBUG_printf(("cancel_job(%08x, %08x)\n", con, uri));
+
+ /*
+ * See if we have a job URI or a printer URI...
+ */
+
+ if (strcmp(uri->name, "printer-uri") == 0)
+ {
+ /*
+ * Got a printer URI; see if we also have a job-id attribute...
+ */
+
+ if ((attr = ippFindAttribute(con->request, "job-id", IPP_TAG_INTEGER)) == NULL)
+ {
+ DEBUG_puts("cancel_job: got a printer-uri attribute but no job-id!");
+ send_ipp_error(con, IPP_BAD_REQUEST);
+ return;
+ }
+
+ jobid = attr->values[0].integer;
+ }
+ else
+ {
+ /*
+ * Got a job URI; parse it to get the job ID...
+ */
+
+ httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
+
+ if (strncmp(resource, "/jobs/", 6) != 0)
+ {
+ /*
+ * Not a valid URI!
+ */
+
+ DEBUG_printf(("cancel_job: bad job-uri attribute \'%s\'!\n",
+ uri->values[0].string.text));
+ send_ipp_error(con, IPP_BAD_REQUEST);
+ return;
+ }
+
+ jobid = atoi(resource + 6);
+ }
+
+ /*
+ * See if the job exists...
+ */
+
+ if (FindJob(jobid) == NULL)
+ {
+ /*
+ * Nope - return a "not found" error...
+ */
+
+ DEBUG_printf(("cancel_job: job #%d doesn't exist!\n", jobid));
+ send_ipp_error(con, IPP_NOT_FOUND);
+ return;
+ }
+
+ /*
+ * Cancel the job and return...
+ */
+
+ CancelJob(jobid);
+ CheckJobs();
+
+ LogMessage(LOG_INFO, "Job %d was cancelled by \'%s\'.", jobid,
+ con->username[0] ? con->username : "unknown");
+
+ con->response->request.status.status_code = IPP_OK;
+}
+
+
+/*
+ * 'copy_attrs()' - Copy attributes from one request to another.
+ */
+
+static void
+copy_attrs(ipp_t *to, /* I - Destination request */
+ ipp_t *from, /* I - Source request */
+ ipp_attribute_t *req) /* I - Requested attributes */
+{
+ int i; /* Looping var */
+ ipp_attribute_t *toattr, /* Destination attribute */
+ *fromattr; /* Source attribute */
+
+
+ DEBUG_printf(("copy_attrs(%08x, %08x)\n", to, from));
+
+ if (to == NULL || from == NULL)
+ return;
+
+ if (req != NULL && strcmp(req->values[0].string.text, "all") == 0)
+ req = NULL; /* "all" means no filter... */
+
+ for (fromattr = from->attrs; fromattr != NULL; fromattr = fromattr->next)
+ {
+ /*
+ * Filter attributes as needed...
+ */
+
+ if (req != NULL)
+ {
+ for (i = 0; i < req->num_values; i ++)
+ if (strcmp(fromattr->name, req->values[i].string.text) == 0)
+ break;
+
+ if (i == req->num_values)
+ continue;
+ }
+
+ DEBUG_printf(("copy_attrs: copying attribute \'%s\'...\n", fromattr->name));
+
+ switch (fromattr->value_tag)
+ {
+ case IPP_TAG_INTEGER :
+ case IPP_TAG_ENUM :
+ toattr = ippAddIntegers(to, fromattr->group_tag, fromattr->value_tag,
+ fromattr->name, fromattr->num_values, NULL);
+
+ for (i = 0; i < fromattr->num_values; i ++)
+ toattr->values[i].integer = fromattr->values[i].integer;
+ break;
+
+ case IPP_TAG_BOOLEAN :
+ toattr = ippAddBooleans(to, fromattr->group_tag, fromattr->name,
+ fromattr->num_values, NULL);
+
+ for (i = 0; i < fromattr->num_values; i ++)
+ toattr->values[i].boolean = fromattr->values[i].boolean;
+ break;
+
+ case IPP_TAG_STRING :
+ case IPP_TAG_TEXT :
+ case IPP_TAG_NAME :
+ case IPP_TAG_KEYWORD :
+ case IPP_TAG_URI :
+ case IPP_TAG_URISCHEME :
+ case IPP_TAG_CHARSET :
+ case IPP_TAG_LANGUAGE :
+ case IPP_TAG_MIMETYPE :
+ toattr = ippAddStrings(to, fromattr->group_tag, fromattr->value_tag,
+ fromattr->name, fromattr->num_values, NULL,
+ NULL);
+
+ for (i = 0; i < fromattr->num_values; i ++)
+ toattr->values[i].string.text = strdup(fromattr->values[i].string.text);
+ break;
+
+ case IPP_TAG_DATE :
+ toattr = ippAddDate(to, fromattr->group_tag, fromattr->name,
+ fromattr->values[0].date);
+ break;
+
+ case IPP_TAG_RESOLUTION :
+ toattr = ippAddResolutions(to, fromattr->group_tag, fromattr->name,
+ fromattr->num_values, IPP_RES_PER_INCH,
+ NULL, NULL);
+
+ for (i = 0; i < fromattr->num_values; i ++)
+ {
+ toattr->values[i].resolution.xres = fromattr->values[i].resolution.xres;
+ toattr->values[i].resolution.yres = fromattr->values[i].resolution.yres;
+ toattr->values[i].resolution.units = fromattr->values[i].resolution.units;
+ }
+ break;
+
+ case IPP_TAG_RANGE :
+ toattr = ippAddRanges(to, fromattr->group_tag, fromattr->name,
+ fromattr->num_values, NULL, NULL);
+
+ for (i = 0; i < fromattr->num_values; i ++)
+ {
+ toattr->values[i].range.lower = fromattr->values[i].range.lower;
+ toattr->values[i].range.upper = fromattr->values[i].range.upper;
+ }
+ break;
+
+ case IPP_TAG_TEXTLANG :
+ case IPP_TAG_NAMELANG :
+ toattr = ippAddStrings(to, fromattr->group_tag, fromattr->value_tag,
+ fromattr->name, fromattr->num_values, NULL, NULL);
+
+ for (i = 0; i < fromattr->num_values; i ++)
+ {
+ if (i == 0)
+ toattr->values[0].string.charset =
+ strdup(fromattr->values[0].string.charset);
+ else
+ toattr->values[i].string.charset =
+ toattr->values[0].string.charset;
+
+ toattr->values[i].string.text =
+ strdup(fromattr->values[i].string.text);
+ }
+ break;
+ }
+ }
+}
+
+
+/*
+ * 'delete_printer()' - Remove a printer or class from the system.
+ */
+
+static void
+delete_printer(client_t *con, /* I - Client connection */
+ ipp_attribute_t *uri) /* I - URI of printer or class */
+{
+ char *dest; /* Destination */
+ cups_ptype_t dtype; /* Destination type (printer or class) */
+ char method[HTTP_MAX_URI],
+ /* Method portion of URI */
+ username[HTTP_MAX_URI],
+ /* Username portion of URI */
+ host[HTTP_MAX_URI],
+ /* Host portion of URI */
+ resource[HTTP_MAX_URI];
+ /* Resource portion of URI */
+ int port; /* Port portion of URI */
+ printer_t *printer; /* Printer/class */
+ char filename[1024]; /* Script/PPD filename */
+
+
+ /*
+ * Was this operation called from the correct URI?
+ */
+
+ if (strncmp(con->uri, "/admin/", 7) != 0)
+ {
+ send_ipp_error(con, IPP_NOT_AUTHORIZED);
+ return;
+ }
+
+ DEBUG_printf(("delete_printer(%08x, %08x)\n", con, uri));
+
+ /*
+ * Do we have a valid URI?
+ */
+
+ httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
+
+ if ((dest = validate_dest(resource, &dtype)) == NULL)
+ {
+ /*
+ * Bad URI...
+ */
+
+ DEBUG_printf(("get_printer_attrs: resource name \'%s\' no good!\n",
+ resource));
+ send_ipp_error(con, IPP_NOT_FOUND);
+ return;
+ }
+
+ /*
+ * Find the printer or class and delete it...
+ */
+
+ if (dtype == CUPS_PRINTER_CLASS)
+ printer = FindClass(dest);
+ else
+ printer = FindPrinter(dest);
+
+ DeletePrinter(printer);
+
+ /*
+ * Remove any old PPD or script files...
+ */
+
+ sprintf(filename, "%s/interfaces/%s", ServerRoot, dest);
+ unlink(filename);
+
+ sprintf(filename, "%s/ppd/%s.ppd", ServerRoot, dest);
+ unlink(filename);
+
+ SaveAllPrinters();
+
+ if (dtype == CUPS_PRINTER_CLASS)
+ LogMessage(LOG_INFO, "Class \'%s\' deleted by \'%s\'.", dest,
+ con->username);
+ else
+ LogMessage(LOG_INFO, "Printer \'%s\' deleted by \'%s\'.", dest,
+ con->username);
+
+ /*
+ * Return with no errors...
+ */
+
+ con->response->request.status.status_code = IPP_OK;
+}
+
+
+/*
+ * 'get_default()' - Get the default destination.
+ */
+
+static void
+get_default(client_t *con) /* I - Client connection */
+{
+ DEBUG_printf(("get_default(%08x)\n", con));
+
+ if (DefaultPrinter != NULL)
+ {
+ copy_attrs(con->response, DefaultPrinter->attrs,
+ ippFindAttribute(con->request, "requested-attributes",
+ IPP_TAG_KEYWORD));
+
+ con->response->request.status.status_code = IPP_OK;
+ }
+ else
+ con->response->request.status.status_code = IPP_NOT_FOUND;
+}
+
+
+/*
+ * 'get_jobs()' - Get a list of jobs for the specified printer.
+ */
+
+static void
+get_jobs(client_t *con, /* I - Client connection */
+ ipp_attribute_t *uri) /* I - Printer URI */
+{
+ ipp_attribute_t *attr; /* Current attribute */
+ char *dest; /* Destination */
+ cups_ptype_t dtype; /* Destination type (printer or class) */
+ char method[HTTP_MAX_URI],
+ /* Method portion of URI */
+ username[HTTP_MAX_URI],
+ /* Username portion of URI */
+ host[HTTP_MAX_URI],
+ /* Host portion of URI */
+ resource[HTTP_MAX_URI];
+ /* Resource portion of URI */
+ int port; /* Port portion of URI */
+ int limit; /* Maximum number of jobs to return */
+ int count; /* Number of jobs that match */
+ job_t *job; /* Current job pointer */
+ char job_uri[HTTP_MAX_URI],
+ /* Job URI... */
+ printer_uri[HTTP_MAX_URI];
+ /* Printer URI... */
+ struct stat filestats; /* Print file information */
+
+
+ DEBUG_printf(("get_jobs(%08x, %08x)\n", con, uri));
+
+ /*
+ * Is the destination valid?
+ */
+
+ httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
+
+ if ((strncmp(resource, "/jobs", 5) == 0 && strlen(resource) <= 6) ||
+ (strncmp(resource, "/printers", 9) == 0 && strlen(resource) <= 10))
+ {
+ dest = NULL;
+ dtype = (cups_ptype_t)0;
+ }
+ else if (strncmp(resource, "/classes", 8) == 0 && strlen(resource) <= 9)
+ {
+ dest = NULL;
+ dtype = CUPS_PRINTER_CLASS;
+ }
+ else if ((dest = validate_dest(resource, &dtype)) == NULL)
+ {
+ /*
+ * Bad URI...
+ */
+
+ DEBUG_printf(("get_jobs: resource name \'%s\' no good!\n",
+ resource));
+ send_ipp_error(con, IPP_NOT_FOUND);
+ return;
+ }
+
+ /*
+ * See if the "which-jobs" attribute have been specified; if so, return
+ * right away if they specify "completed" - we don't keep old job records...
+ */
+
+ if ((attr = ippFindAttribute(con->request, "which-jobs", IPP_TAG_KEYWORD)) != NULL &&
+ strcmp(attr->values[0].string.text, "completed") == 0)
+ {
+ con->response->request.status.status_code = IPP_OK;
+ return;
+ }
+
+ /*
+ * See if they want to limit the number of jobs reported; if not, limit
+ * the report to 1000 jobs to prevent swamping of the server...
+ */
+
+ if ((attr = ippFindAttribute(con->request, "limit", IPP_TAG_INTEGER)) != NULL)
+ limit = attr->values[0].integer;
+ else
+ limit = 1000;
+
+ /*
+ * See if we only want to see jobs for a specific user...
+ */
+
+ if ((attr = ippFindAttribute(con->request, "my-jobs", IPP_TAG_BOOLEAN)) != NULL &&
+ attr->values[0].boolean)
+ {
+ strcpy(username, con->username);
+
+ if ((attr = ippFindAttribute(con->request, "requesting-user-name", IPP_TAG_NAME)) != NULL)
+ {
+ strncpy(username, attr->values[0].string.text, sizeof(username) - 1);
+ username[sizeof(username) - 1] = '\0';
+ }
+ }
+ else
+ username[0] = '\0';
+
+ /*
+ * OK, build a list of jobs for this printer...
+ */
+
+ for (count = 0, job = Jobs; count < limit && job != NULL; job = job->next)
+ {
+ /*
+ * Filter out jobs that don't match...
+ */
+
+ DEBUG_printf(("get_jobs: job->id = %d\n", job->id));
+
+ if ((dest != NULL && strcmp(job->dest, dest) != 0))
+ continue;
+ if (job->dtype != dtype &&
+ (username[0] == '\0' || strncmp(resource, "/jobs", 5) != 0))
+ continue;
+ if (username[0] != '\0' && strcmp(username, job->username) != 0)
+ continue;
+
+ count ++;
+
+ DEBUG_printf(("get_jobs: count = %d\n", count));
+
+ /*
+ * Send the following attributes for each job:
+ *
+ * job-id
+ * job-k-octets
+ * job-more-info
+ * job-originating-user-name
+ * job-printer-uri
+ * job-priority
+ * job-state
+ * job-uri
+ * job-name
+ *
+ * Note that we are supposed to look at the "requested-attributes"
+ * attribute to determine what we send, however the IPP/1.0 spec also
+ * doesn't state that the server must limit the attributes to those
+ * requested. In other words, the server can either implement the
+ * filtering or not, and if not it needs to send all attributes that
+ * it has...
+ */
+
+ sprintf(job_uri, "http://%s:%d/jobs/%d", ServerName,
+ ntohs(con->http.hostaddr.sin_port), job->id);
+
+ if (job->dtype == CUPS_PRINTER_CLASS)
+ sprintf(printer_uri, "http://%s:%d/classes/%s", ServerName,
+ ntohs(con->http.hostaddr.sin_port), job->dest);
+ else
+ sprintf(printer_uri, "http://%s:%d/printers/%s", ServerName,
+ ntohs(con->http.hostaddr.sin_port), job->dest);
+
+ ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id);
+
+ ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_NAME, "job-name",
+ NULL, job->title);
+
+ stat(job->filename, &filestats);
+ ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER,
+ "job-k-octets", (filestats.st_size + 1023) / 1024);
+
+ ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI,
+ "job-more-info", NULL, job_uri);
+
+ ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_NAME,
+ "job-originating-user-name", NULL, job->username);
+
+ ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI,
+ "job-printer-uri", NULL, printer_uri);
+
+ ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_ENUM,
+ "job-state", job->state);
+
+ ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI,
+ "job-uri", NULL, job_uri);
+
+ ippAddSeparator(con->response);
+ }
+
+ if (ippFindAttribute(con->request, "requested-attributes", IPP_TAG_KEYWORD) != NULL)
+ con->response->request.status.status_code = IPP_OK_SUBST;
+ else
+ con->response->request.status.status_code = IPP_OK;
+}
+
+
+/*
+ * 'get_job_attrs()' - Get job attributes.
+ */
+
+static void
+get_job_attrs(client_t *con, /* I - Client connection */
+ ipp_attribute_t *uri) /* I - Job URI */
+{
+ ipp_attribute_t *attr; /* Current attribute */
+ int jobid; /* Job ID */
+ job_t *job; /* Current job */
+ char method[HTTP_MAX_URI],
+ /* Method portion of URI */
+ username[HTTP_MAX_URI],
+ /* Username portion of URI */
+ host[HTTP_MAX_URI],
+ /* Host portion of URI */
+ resource[HTTP_MAX_URI];
+ /* Resource portion of URI */
+ int port; /* Port portion of URI */
+ char job_uri[HTTP_MAX_URI],
+ /* Job URI... */
+ printer_uri[HTTP_MAX_URI];
+ /* Printer URI... */
+ struct stat filestats; /* Print file information */
+
+
+ DEBUG_printf(("get_job_attrs(%08x, %08x)\n", con, uri));
+
+ /*
+ * See if we have a job URI or a printer URI...
+ */
+
+ if (strcmp(uri->name, "printer-uri") == 0)
+ {
+ /*
+ * Got a printer URI; see if we also have a job-id attribute...
+ */
+
+ if ((attr = ippFindAttribute(con->request, "job-id", IPP_TAG_INTEGER)) == NULL)
+ {
+ DEBUG_puts("get_job_attrs: got a printer-uri attribute but no job-id!");
+ send_ipp_error(con, IPP_BAD_REQUEST);
+ return;
+ }
+
+ jobid = attr->values[0].integer;
+ }
+ else
+ {
+ /*
+ * Got a job URI; parse it to get the job ID...
+ */
+
+ httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
+
+ if (strncmp(resource, "/jobs/", 6) != 0)
+ {
+ /*
+ * Not a valid URI!
+ */
+
+ DEBUG_printf(("get_job_attrs: bad job-uri attribute \'%s\'!\n",
+ uri->values[0].string.text));
+ send_ipp_error(con, IPP_BAD_REQUEST);
+ return;
+ }
+
+ jobid = atoi(resource + 6);
+ }
+
+ /*
+ * See if the job exists...
+ */
+
+ if ((job = FindJob(jobid)) == NULL)
+ {
+ /*
+ * Nope - return a "not found" error...
+ */
+
+ DEBUG_printf(("get_job_attrs: job #%d doesn't exist!\n", jobid));
+ send_ipp_error(con, IPP_NOT_FOUND);
+ return;
+ }
+
+ /*
+ * Put out the standard attributes...
+ */
+
+ sprintf(job_uri, "http://%s:%d/jobs/%d", ServerName,
+ ntohs(con->http.hostaddr.sin_port), job->id);
+
+ if (job->dtype == CUPS_PRINTER_CLASS)
+ sprintf(printer_uri, "http://%s:%d/classes/%s", ServerName,
+ ntohs(con->http.hostaddr.sin_port), job->dest);
+ else
+ sprintf(printer_uri, "http://%s:%d/printers/%s", ServerName,
+ ntohs(con->http.hostaddr.sin_port), job->dest);
+
+ ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id);
+
+ ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_NAME, "job-name",
+ NULL, job->title);
+
+ stat(job->filename, &filestats);
+ ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER,
+ "job-k-octets", (filestats.st_size + 1023) / 1024);
+
+ ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI,
+ "job-more-info", NULL, job_uri);
+
+ ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_NAME,
+ "job-originating-user-name", NULL, job->username);
+
+ ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI,
+ "job-printer-uri", NULL, printer_uri);
+
+ ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_ENUM,
+ "job-state", job->state);
+
+ ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI,
+ "job-uri", NULL, job_uri);
+
+ /*
+ * Copy the job attributes to the response using the requested-attributes
+ * attribute that may be provided by the client.
+ */
+
+ copy_attrs(con->response, job->attrs,
+ ippFindAttribute(con->request, "requested-attributes",
+ IPP_TAG_KEYWORD));
+
+ con->response->request.status.status_code = IPP_OK;
+}
+
+
+/*
+ * 'get_printers()' - Get a list of printers.
+ */
+
+static void
+get_printers(client_t *con, /* I - Client connection */
+ int type) /* I - 0 or CUPS_PRINTER_CLASS */
+{
+ ipp_attribute_t *attr; /* Current attribute */
+ int limit; /* Maximum number of printers to return */
+ int count; /* Number of printers that match */
+ printer_t *printer; /* Current printer pointer */
+ time_t curtime; /* Current time */
+
+
+ DEBUG_printf(("get_printers(%08x)\n", con));
+
+ /*
+ * See if they want to limit the number of printers reported; if not, limit
+ * the report to 1000 printers to prevent swamping of the server...
+ */
+
+ if ((attr = ippFindAttribute(con->request, "limit", IPP_TAG_INTEGER)) != NULL)
+ limit = attr->values[0].integer;
+ else
+ limit = 1000;
+
+ /*
+ * OK, build a list of printers for this printer...
+ */
+
+ curtime = time(NULL);
+
+ for (count = 0, printer = Printers;
+ count < limit && printer != NULL;
+ printer = printer->next)
+ if ((printer->type & CUPS_PRINTER_CLASS) == type)
+ {
+ /*
+ * Send the following attributes for each printer:
+ *
+ * printer-state
+ * printer-state-message
+ * printer-is-accepting-jobs
+ * + all printer attributes
+ */
+
+ ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_ENUM,
+ "printer-state", printer->state);
+
+ if (printer->state_message[0])
+ ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_TEXT,
+ "printer-state-message", NULL, printer->state_message);
+
+ ippAddBoolean(con->response, IPP_TAG_PRINTER, "printer-is-accepting-jobs",
+ printer->accepting);
+
+ ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
+ "printer-up-time", curtime - StartTime);
+ ippAddDate(con->response, IPP_TAG_PRINTER, "printer-current-time",
+ ippTimeToDate(curtime));
+
+ copy_attrs(con->response, printer->attrs,
+ ippFindAttribute(con->request, "requested-attributes",
+ IPP_TAG_KEYWORD));
+
+ ippAddSeparator(con->response);
+ }
+
+ con->response->request.status.status_code = IPP_OK;
+}
+
+
+/*
+ * 'get_printer_attrs()' - Get printer attributes.
+ */
+
+static void
+get_printer_attrs(client_t *con, /* I - Client connection */
+ ipp_attribute_t *uri) /* I - Printer URI */
+{
+ char *dest; /* Destination */
+ cups_ptype_t dtype; /* Destination type (printer or class) */
+ char method[HTTP_MAX_URI],
+ /* Method portion of URI */
+ username[HTTP_MAX_URI],
+ /* Username portion of URI */
+ host[HTTP_MAX_URI],
+ /* Host portion of URI */
+ resource[HTTP_MAX_URI];
+ /* Resource portion of URI */
+ int port; /* Port portion of URI */
+ printer_t *printer; /* Printer/class */
+ time_t curtime; /* Current time */
+
+
+ DEBUG_printf(("get_printer_attrs(%08x, %08x)\n", con, uri));
+
+ /*
+ * Is the destination valid?
+ */
+
+ httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
+
+ if ((dest = validate_dest(resource, &dtype)) == NULL)
+ {
+ /*
+ * Bad URI...
+ */
+
+ DEBUG_printf(("get_printer_attrs: resource name \'%s\' no good!\n",
+ resource));
+ send_ipp_error(con, IPP_NOT_FOUND);
+ return;
+ }
+
+ if (dtype == CUPS_PRINTER_CLASS)
+ printer = FindClass(dest);
+ else
+ printer = FindPrinter(dest);
+
+ curtime = time(NULL);
+
+ /*
+ * Copy the printer attributes to the response using requested-attributes
+ * and document-format attributes that may be provided by the client.
+ */
+
+ copy_attrs(con->response, printer->attrs,
+ ippFindAttribute(con->request, "requested-attributes",
+ IPP_TAG_KEYWORD));
+
+ ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_ENUM, "printer-state",
+ printer->state);
+
+ if (printer->state_message[0])
+ ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_TEXT,
+ "printer-state-message", NULL, printer->state_message);
+
+ ippAddBoolean(con->response, IPP_TAG_PRINTER, "printer-is-accepting-jobs",
+ printer->accepting);
+
+ ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
+ "printer-up-time", curtime - StartTime);
+ ippAddDate(con->response, IPP_TAG_PRINTER, "printer-current-time",
+ ippTimeToDate(curtime));
+
+ con->response->request.status.status_code = IPP_OK;
+}
+
+
+/*
+ * 'print_job()' - Print a file to a printer or class.
+ */
+
+static void
+print_job(client_t *con, /* I - Client connection */
+ ipp_attribute_t *uri) /* I - Printer URI */
+{
+ ipp_attribute_t *attr; /* Current attribute */
+ ipp_attribute_t *format; /* Document-format attribute */
+ char *dest; /* Destination */
+ cups_ptype_t dtype; /* Destination type (printer or class) */
+ int priority; /* Job priority */
+ char *title; /* Job name/title */
+ job_t *job; /* Current job */
+ char job_uri[HTTP_MAX_URI],
+ /* Job URI */
+ method[HTTP_MAX_URI],
+ /* Method portion of URI */
+ username[HTTP_MAX_URI],
+ /* Username portion of URI */
+ host[HTTP_MAX_URI],
+ /* Host portion of URI */
+ resource[HTTP_MAX_URI];
+ /* Resource portion of URI */
+ int port; /* Port portion of URI */
+ mime_type_t *filetype; /* Type of file */
+ char super[MIME_MAX_SUPER],
+ /* Supertype of file */
+ type[MIME_MAX_TYPE],
+ /* Subtype of file */
+ mimetype[MIME_MAX_SUPER + MIME_MAX_TYPE + 2];
+ /* Textual name of mime type */
+ printer_t *printer; /* Printer data */
+
+
+ DEBUG_printf(("print_job(%08x, %08x)\n", con, uri));
+
+ /*
+ * OK, see if the client is sending the document compressed - CUPS
+ * doesn't support compression yet...
+ */
+
+ if ((attr = ippFindAttribute(con->request, "compression", IPP_TAG_KEYWORD)) != NULL)
+ {
+ DEBUG_puts("print_job: Unsupported compression attribute!");
+ send_ipp_error(con, IPP_ATTRIBUTES);
+ attr = ippAddString(con->response, IPP_TAG_UNSUPPORTED, IPP_TAG_KEYWORD,
+ "compression", NULL, attr->values[0].string.text);
+ return;
+ }
+
+ /*
+ * Do we have a file to print?
+ */
+
+ if (con->filename[0] == '\0')
+ {
+ DEBUG_puts("print_job: No filename!?!");
+ send_ipp_error(con, IPP_BAD_REQUEST);
+ return;
+ }
+
+ /*
+ * Is it a format we support?
+ */
+
+ if ((format = ippFindAttribute(con->request, "document-format", IPP_TAG_MIMETYPE)) == NULL)
+ {
+ DEBUG_puts("print_job: missing document-format attribute!");
+ send_ipp_error(con, IPP_BAD_REQUEST);
+ return;
+ }
+
+ if (sscanf(format->values[0].string.text, "%15[^/]/%31[^;]", super, type) != 2)
+ {
+ DEBUG_printf(("print_job: could not scan type \'%s\'!\n",
+ format->values[0].string.text));
+ send_ipp_error(con, IPP_BAD_REQUEST);
+ return;
+ }
+
+ if (strcmp(super, "application") == 0 &&
+ strcmp(type, "octet-stream") == 0)
+ {
+ /*
+ * Auto-type the file...
+ */
+
+ DEBUG_puts("print_job: auto-typing request using magic rules.");
+ filetype = mimeFileType(MimeDatabase, con->filename);
+
+ if (filetype != NULL)
+ {
+ /*
+ * Replace the document-format attribute value with the auto-typed one.
+ */
+
+ free(format->values[0].string.text);
+ sprintf(mimetype, "%s/%s", filetype->super, filetype->type);
+ format->values[0].string.text = strdup(mimetype);
+ }
+ }
+ else
+ filetype = mimeType(MimeDatabase, super, type);
+
+ if (filetype == NULL)
+ {
+ DEBUG_printf(("print_job: Unsupported format \'%s\'!\n",
+ format->values[0].string.text));
+ send_ipp_error(con, IPP_DOCUMENT_FORMAT);
+ attr = ippAddString(con->response, IPP_TAG_UNSUPPORTED, IPP_TAG_MIMETYPE,
+ "document-format", NULL, format->values[0].string.text);
+ return;
+ }
+
+ DEBUG_printf(("print_job: request file type is %s/%s.\n",
+ filetype->super, filetype->type));
+
+ /*
+ * Is the destination valid?
+ */
+
+ httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
+
+ if ((dest = validate_dest(resource, &dtype)) == NULL)
+ {
+ /*
+ * Bad URI...
+ */
+
+ DEBUG_printf(("print_job: resource name \'%s\' no good!\n",
+ resource));
+ send_ipp_error(con, IPP_NOT_FOUND);
+ return;
+ }
+
+ /*
+ * See if the printer is accepting jobs...
+ */
+
+ if (dtype == CUPS_PRINTER_CLASS)
+ printer = FindClass(dest);
+ else
+ printer = FindPrinter(dest);
+
+ if (!printer->accepting)
+ {
+ send_ipp_error(con, IPP_NOT_ACCEPTING);
+ return;
+ }
+
+ /*
+ * Create the job and set things up...
+ */
+
+ if ((attr = ippFindAttribute(con->request, "job-priority", IPP_TAG_INTEGER)) != NULL)
+ priority = attr->values[0].integer;
+ else
+ priority = 50;
+
+ if ((attr = ippFindAttribute(con->request, "job-name", IPP_TAG_NAME)) != NULL)
+ title = attr->values[0].string.text;
+ else
+ title = "Untitled";
+
+ if ((job = AddJob(priority, printer->name)) == NULL)
+ {
+ send_ipp_error(con, IPP_INTERNAL_ERROR);
+ return;
+ }
+
+ job->dtype = dtype;
+ job->state = IPP_JOB_PENDING;
+ job->filetype = filetype;
+ job->attrs = con->request;
+ con->request = NULL;
+
+ strcpy(job->filename, con->filename);
+ strcpy(job->title, title);
+ con->filename[0] = '\0';
+
+ strcpy(job->username, con->username);
+ if ((attr = ippFindAttribute(job->attrs, "requesting-user-name", IPP_TAG_NAME)) != NULL)
+ {
+ DEBUG_printf(("print_job: requesting-user-name = \'%s\'\n",
+ attr->values[0].string.text));
+
+ strncpy(job->username, attr->values[0].string.text, sizeof(job->username) - 1);
+ job->username[sizeof(job->username) - 1] = '\0';
+ }
+
+ if (job->username[0] == '\0')
+ strcpy(job->username, "guest");
+
+ DEBUG_printf(("print_job: job->username = \'%s\', attr = %08x\n",
+ job->username, attr));
+
+ /*
+ * Start the job if possible...
+ */
+
+ CheckJobs();
+
+ LogMessage(LOG_INFO, "Job %d queued on \'%s\' by \'%s\'.", job->id,
+ job->dest, job->username);
+
+ /*
+ * Fill in the response info...
+ */
+
+ sprintf(job_uri, "http://%s:%d/jobs/%d", ServerName,
+ ntohs(con->http.hostaddr.sin_port), job->id);
+ attr = ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI, "job-uri",
+ NULL, job_uri);
+
+ attr = ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER,
+ "job-id", job->id);
+
+ attr = ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_ENUM,
+ "job-state", job->state);
+
+ con->response->request.status.status_code = IPP_OK;
+}
+
+
+/*
+ * 'reject_jobs()' - Reject print jobs to a printer.
+ */
+
+static void
+reject_jobs(client_t *con, /* I - Client connection */
+ ipp_attribute_t *uri) /* I - Printer or class URI */
+{
+ cups_ptype_t dtype; /* Destination type (printer or class) */
+ char method[HTTP_MAX_URI],
+ /* Method portion of URI */
+ username[HTTP_MAX_URI],
+ /* Username portion of URI */
+ host[HTTP_MAX_URI],
+ /* Host portion of URI */
+ resource[HTTP_MAX_URI];
+ /* Resource portion of URI */
+ int port; /* Port portion of URI */
+ char *name; /* Printer name */
+ printer_t *printer; /* Printer data */
+ ipp_attribute_t *attr; /* printer-state-message text */
+
+
+ DEBUG_printf(("reject_jobs(%08x, %08x)\n", con, uri));
+
+ /*
+ * Was this operation called from the correct URI?
+ */
+
+ if (strncmp(con->uri, "/admin/", 7) != 0)
+ {
+ send_ipp_error(con, IPP_NOT_AUTHORIZED);
+ return;
+ }
+
+ /*
+ * Is the destination valid?
+ */
+
+ httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
+
+ if ((name = validate_dest(resource, &dtype)) == NULL)
+ {
+ /*
+ * Bad URI...
+ */
+
+ DEBUG_printf(("reject_jobs: resource name \'%s\' no good!\n",
+ resource));
+ send_ipp_error(con, IPP_NOT_FOUND);
+ return;
+ }
+
+ /*
+ * Reject jobs sent to the printer...
+ */
+
+ if (dtype == CUPS_PRINTER_CLASS)
+ printer = FindClass(name);
+ else
+ printer = FindPrinter(name);
+
+ printer->accepting = 0;
+
+ if ((attr = ippFindAttribute(con->request, "printer-state-message",
+ IPP_TAG_TEXT)) == NULL)
+ strcpy(printer->state_message, "Rejecting Jobs");
+ else
+ strcpy(printer->state_message, attr->values[0].string.text);
+
+ if (dtype == CUPS_PRINTER_CLASS)
+ LogMessage(LOG_INFO, "Class \'%s\' rejecting jobs (\'%s\').", name,
+ con->username);
+ else
+ LogMessage(LOG_INFO, "Printer \'%s\' rejecting jobs (\'%s\').", name,
+ con->username);
+
+ /*
+ * Everything was ok, so return OK status...
+ */
+
+ con->response->request.status.status_code = IPP_OK;
+}
+
+
+/*
+ * 'send_ipp_error()' - Send an error status back to the IPP client.
+ */
+
+static void
+send_ipp_error(client_t *con, /* I - Client connection */
+ ipp_status_t status) /* I - IPP status code */
+{
+ DEBUG_printf(("send_ipp_error(%08x, %04x)\n", con, status));
+
+ if (con->filename[0])
+ unlink(con->filename);
+
+ con->response->request.status.status_code = status;
+
+ ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, DefaultCharset);
+
+ ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, DefaultLanguage);
+}
+
+
+/*
+ * 'set_default()' - Set the default destination...
+ */
+
+static void
+set_default(client_t *con, /* I - Client connection */
+ ipp_attribute_t *uri) /* I - Printer URI */
+{
+ cups_ptype_t dtype; /* Destination type (printer or class) */
+ char method[HTTP_MAX_URI],
+ /* Method portion of URI */
+ username[HTTP_MAX_URI],
+ /* Username portion of URI */
+ host[HTTP_MAX_URI],
+ /* Host portion of URI */
+ resource[HTTP_MAX_URI];
+ /* Resource portion of URI */
+ int port; /* Port portion of URI */
+ char *name; /* Printer name */
+
+
+ DEBUG_printf(("set_default(%08x, %08x)\n", con, uri));
+
+ /*
+ * Was this operation called from the correct URI?
+ */
+
+ if (strncmp(con->uri, "/admin/", 7) != 0)
+ {
+ send_ipp_error(con, IPP_NOT_AUTHORIZED);
+ return;
+ }
+
+ /*
+ * Is the destination valid?
+ */
+
+ httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
+
+ if ((name = validate_dest(resource, &dtype)) == NULL)
+ {
+ /*
+ * Bad URI...
+ */
+
+ DEBUG_printf(("set_default: resource name \'%s\' no good!\n",
+ resource));
+ send_ipp_error(con, IPP_NOT_FOUND);
+ return;
+ }
+
+ /*
+ * Set it as the default...
+ */
+
+ if (dtype == CUPS_PRINTER_CLASS)
+ DefaultPrinter = FindClass(name);
+ else
+ DefaultPrinter = FindPrinter(name);
+
+ SaveAllPrinters();
+ SaveAllClasses();
+
+ LogMessage(LOG_INFO, "Default destination set to \'%s\' by \'%s\'.", name,
+ con->username);
+
+ /*
+ * Everything was ok, so return OK status...
+ */
+
+ con->response->request.status.status_code = IPP_OK;
+}
+
+
+/*
+ * 'start_printer()' - Start a printer.
+ */
+
+static void
+start_printer(client_t *con, /* I - Client connection */
+ ipp_attribute_t *uri) /* I - Printer URI */
+{
+ cups_ptype_t dtype; /* Destination type (printer or class) */
+ char method[HTTP_MAX_URI],
+ /* Method portion of URI */
+ username[HTTP_MAX_URI],
+ /* Username portion of URI */
+ host[HTTP_MAX_URI],
+ /* Host portion of URI */
+ resource[HTTP_MAX_URI];
+ /* Resource portion of URI */
+ int port; /* Port portion of URI */
+ char *name; /* Printer name */
+ printer_t *printer; /* Printer data */
+
+
+ DEBUG_printf(("start_printer(%08x, %08x)\n", con, uri));
+
+ /*
+ * Was this operation called from the correct URI?
+ */
+
+ if (strncmp(con->uri, "/admin/", 7) != 0)
+ {
+ send_ipp_error(con, IPP_NOT_AUTHORIZED);
+ return;
+ }
+
+ /*
+ * Is the destination valid?
+ */
+
+ httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
+
+ if ((name = validate_dest(resource, &dtype)) == NULL)
+ {
+ /*
+ * Bad URI...
+ */
+
+ DEBUG_printf(("start_printer: resource name \'%s\' no good!\n",
+ resource));
+ send_ipp_error(con, IPP_NOT_FOUND);
+ return;
+ }
+
+ /*
+ * Start the printer...
+ */
+
+ if (dtype == CUPS_PRINTER_CLASS)
+ printer = FindClass(name);
+ else
+ printer = FindPrinter(name);
+
+ StartPrinter(printer);
+
+ if (dtype == CUPS_PRINTER_CLASS)
+ SaveAllClasses();
+ else
+ SaveAllPrinters();
+
+ if (dtype == CUPS_PRINTER_CLASS)
+ LogMessage(LOG_INFO, "Class \'%s\' started by \'%s\'.", name,
+ con->username);
+ else
+ LogMessage(LOG_INFO, "Printer \'%s\' started by \'%s\'.", name,
+ con->username);
+
+ printer->state_message[0] = '\0';
+
+ /*
+ * Everything was ok, so return OK status...
+ */
+
+ con->response->request.status.status_code = IPP_OK;
+}
+
+
+/*
+ * 'stop_printer()' - Stop a printer.
+ */
+
+static void
+stop_printer(client_t *con, /* I - Client connection */
+ ipp_attribute_t *uri) /* I - Printer URI */
+{
+ cups_ptype_t dtype; /* Destination type (printer or class) */
+ char method[HTTP_MAX_URI],
+ /* Method portion of URI */
+ username[HTTP_MAX_URI],
+ /* Username portion of URI */
+ host[HTTP_MAX_URI],
+ /* Host portion of URI */
+ resource[HTTP_MAX_URI];
+ /* Resource portion of URI */
+ int port; /* Port portion of URI */
+ char *name; /* Printer name */
+ printer_t *printer; /* Printer data */
+ ipp_attribute_t *attr; /* printer-state-message attribute */
+
+
+ DEBUG_printf(("stop_printer(%08x, %08x)\n", con, uri));
+
+ /*
+ * Was this operation called from the correct URI?
+ */
+
+ if (strncmp(con->uri, "/admin/", 7) != 0)
+ {
+ send_ipp_error(con, IPP_NOT_AUTHORIZED);
+ return;
+ }
+
+ /*
+ * Is the destination valid?
+ */
+
+ httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
+
+ if ((name = validate_dest(resource, &dtype)) == NULL)
+ {
+ /*
+ * Bad URI...
+ */
+
+ DEBUG_printf(("stop_printer: resource name \'%s\' no good!\n",
+ resource));
+ send_ipp_error(con, IPP_NOT_FOUND);
+ return;
+ }
+
+ /*
+ * Stop the printer...
+ */
+
+ if (dtype == CUPS_PRINTER_CLASS)
+ printer = FindClass(name);
+ else
+ printer = FindPrinter(name);
+
+ StopPrinter(printer);
+
+ if (dtype == CUPS_PRINTER_CLASS)
+ SaveAllClasses();
+ else
+ SaveAllPrinters();
+
+ if ((attr = ippFindAttribute(con->request, "printer-state-message",
+ IPP_TAG_TEXT)) == NULL)
+ strcpy(printer->state_message, "Paused");
+ else
+ strcpy(printer->state_message, attr->values[0].string.text);
+
+ if (dtype == CUPS_PRINTER_CLASS)
+ LogMessage(LOG_INFO, "Class \'%s\' stopped by \'%s\'.", name,
+ con->username);
+ else
+ LogMessage(LOG_INFO, "Printer \'%s\' stopped by \'%s\'.", name,
+ con->username);
+
+ /*
+ * Everything was ok, so return OK status...
+ */
+
+ con->response->request.status.status_code = IPP_OK;
+}
+
+
+/*
+ * 'validate_dest()' - Validate a printer class destination.
+ */
+
+static char * /* O - Printer or class name */
+validate_dest(char *resource, /* I - Resource name */
+ cups_ptype_t *dtype) /* O - Type (printer or class) */
+{
+ if (strncmp(resource, "/classes/", 9) == 0)
+ {
+ /*
+ * Print to a class...
+ */
+
+ *dtype = CUPS_PRINTER_CLASS;
+
+ if (FindClass(resource + 9) == NULL)
+ return (NULL);
+ else
+ return (resource + 9);
+ }
+ else if (strncmp(resource, "/printers/", 10) == 0)
+ {
+ /*
+ * Print to a specific printer...
+ */
+
+ *dtype = (cups_ptype_t)0;
+
+ if (FindPrinter(resource + 10) == NULL)
+ {
+ *dtype = CUPS_PRINTER_CLASS;
+
+ if (FindClass(resource + 10) == NULL)
+ return (NULL);
+ }
+
+ return (resource + 10);
+ }
+ else
+ return (NULL);
+}
+
+
+/*
+ * 'validate_job()' - Validate printer options and destination.
+ */
+
+static void
+validate_job(client_t *con, /* I - Client connection */
+ ipp_attribute_t *uri) /* I - Printer URI */
+{
+ ipp_attribute_t *attr; /* Current attribute */
+ ipp_attribute_t *format; /* Document-format attribute */
+ cups_ptype_t dtype; /* Destination type (printer or class) */
+ char method[HTTP_MAX_URI],
+ /* Method portion of URI */
+ username[HTTP_MAX_URI],
+ /* Username portion of URI */
+ host[HTTP_MAX_URI],
+ /* Host portion of URI */
+ resource[HTTP_MAX_URI];
+ /* Resource portion of URI */
+ int port; /* Port portion of URI */
+ char super[MIME_MAX_SUPER],
+ /* Supertype of file */
+ type[MIME_MAX_TYPE];
+ /* Subtype of file */
+
+
+ DEBUG_printf(("validate_job(%08x, %08x)\n", con, uri));
+
+ /*
+ * OK, see if the client is sending the document compressed - CUPS
+ * doesn't support compression yet...
+ */
+
+ if ((attr = ippFindAttribute(con->request, "compression", IPP_TAG_KEYWORD)) != NULL)
+ {
+ DEBUG_puts("validate_job: Unsupported compression attribute!");
+ send_ipp_error(con, IPP_ATTRIBUTES);
+ attr = ippAddString(con->response, IPP_TAG_UNSUPPORTED, IPP_TAG_KEYWORD,
+ "compression", NULL, attr->values[0].string.text);
+ return;
+ }
+
+ /*
+ * Is it a format we support?
+ */
+
+ if ((format = ippFindAttribute(con->request, "document-format", IPP_TAG_MIMETYPE)) == NULL)
+ {
+ DEBUG_puts("validate_job: missing document-format attribute!");
+ send_ipp_error(con, IPP_BAD_REQUEST);
+ return;
+ }
+
+ if (sscanf(format->values[0].string.text, "%15[^/]/%31[^;]", super, type) != 2)
+ {
+ DEBUG_printf(("validate_job: could not scan type \'%s\'!\n",
+ format->values[0].string.text));
+ send_ipp_error(con, IPP_BAD_REQUEST);
+ return;
+ }
+
+ if ((strcmp(super, "application") != 0 ||
+ strcmp(type, "octet-stream") != 0) &&
+ mimeType(MimeDatabase, super, type) == NULL)
+ {
+ DEBUG_printf(("validate_job: Unsupported format \'%s\'!\n",
+ format->values[0].string.text));
+ send_ipp_error(con, IPP_DOCUMENT_FORMAT);
+ attr = ippAddString(con->response, IPP_TAG_UNSUPPORTED, IPP_TAG_MIMETYPE,
+ "document-format", NULL, format->values[0].string.text);
+ return;
+ }
+
+ /*
+ * Is the destination valid?
+ */
+
+ httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
+
+ if (validate_dest(resource, &dtype) == NULL)
+ {
+ /*
+ * Bad URI...
+ */
+
+ DEBUG_printf(("validate_job: resource name \'%s\' no good!\n",
+ resource));
+ send_ipp_error(con, IPP_NOT_FOUND);
+ return;
+ }
+
+ /*
+ * Everything was ok, so return OK status...
+ */
+
+ con->response->request.status.status_code = IPP_OK;
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/scheduler/job.c b/scheduler/job.c
new file mode 100644
index 000000000..f3e7a3cae
--- /dev/null
+++ b/scheduler/job.c
@@ -0,0 +1,971 @@
+/*
+ * "$Id$"
+ *
+ * Job management routines for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-1999 by Easy Software Products, all rights reserved.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * Contents:
+ *
+ * AddJob() - Add a new job to the job queue...
+ * CancelJob() - Cancel the specified print job.
+ * CancelJobs() - Cancel all jobs on the given printer or class.
+ * CheckJobs() - Check the pending jobs and start any if the destination
+ * is available.
+ * FindJob() - Find the specified job.
+ * MoveJob() - Move the specified job to a different destination.
+ * StartJob() - Start a print job.
+ * StopJob() - Stop a print job.
+ * UpdateJob() - Read a status update from a job's filters.
+ * start_process() - Start a background process.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "cupsd.h"
+
+
+/*
+ * Local functions...
+ */
+
+static int start_process(char *command, char *argv[], char *envp[],
+ int in, int out, int err);
+
+
+/*
+ * 'AddJob()' - Add a new job to the job queue...
+ */
+
+job_t * /* O - New job record */
+AddJob(int priority, /* I - Job priority */
+ char *dest) /* I - Job destination */
+{
+ job_t *job, /* New job record */
+ *current, /* Current job in queue */
+ *prev; /* Previous job in queue */
+
+
+ job = calloc(sizeof(job_t), 1);
+
+ job->id = NextJobId ++;
+ job->priority = priority;
+ strncpy(job->dest, dest, sizeof(job->dest) - 1);
+ job->state = IPP_JOB_HELD;
+
+ NumJobs ++;
+
+ for (current = Jobs, prev = NULL; current != NULL; prev = current, current = current->next)
+ if (job->priority > current->priority)
+ break;
+
+ job->next = current;
+ if (prev != NULL)
+ prev->next = job;
+ else
+ Jobs = job;
+
+ return (job);
+}
+
+
+/*
+ * 'CancelJob()' - Cancel the specified print job.
+ */
+
+void
+CancelJob(int id) /* I - Job to cancel */
+{
+ job_t *current, /* Current job */
+ *prev; /* Previous job in list */
+
+
+ DEBUG_printf(("CancelJob(%d)\n", id));
+
+ for (current = Jobs, prev = NULL; current != NULL; prev = current, current = current->next)
+ if (current->id == id)
+ {
+ /*
+ * Stop any processes that are working on the current...
+ */
+
+ DEBUG_puts("CancelJob: found job in list.");
+
+ if (current->state == IPP_JOB_PROCESSING)
+ StopJob(current->id);
+
+ /*
+ * Update pointers...
+ */
+
+ if (prev == NULL)
+ Jobs = current->next;
+ else
+ prev->next = current->next;
+
+ /*
+ * Free all memory used...
+ */
+
+ if (current->attrs != NULL)
+ ippDelete(current->attrs);
+
+ /*
+ * Remove the print file for good...
+ */
+
+ unlink(current->filename);
+ free(current);
+ return;
+ }
+}
+
+
+/*
+ * 'CancelJobs()' - Cancel all jobs on the given printer or class.
+ */
+
+void
+CancelJobs(char *dest) /* I - Destination to cancel */
+{
+ job_t *current, /* Current job */
+ *prev; /* Previous job in list */
+
+
+ for (current = Jobs, prev = NULL; current != NULL; prev = current)
+ if (strcmp(current->dest, dest) == 0)
+ {
+ /*
+ * Cancel all jobs matching this destination...
+ */
+
+ CancelJob(current->id);
+
+ if (prev == NULL)
+ current = Jobs;
+ else
+ current = prev->next;
+ }
+ else
+ current = current->next;
+
+ CheckJobs();
+}
+
+
+/*
+ * 'CheckJobs()' - Check the pending jobs and start any if the destination
+ * is available.
+ */
+
+void
+CheckJobs(void)
+{
+ job_t *current,
+ *prev;
+ printer_t *printer;
+
+
+ DEBUG_puts("CheckJobs()");
+
+ for (current = Jobs, prev = NULL; current != NULL; prev = current)
+ {
+ DEBUG_printf(("CheckJobs: current->state = %d\n", current->state));
+
+ if (current->state != IPP_JOB_PROCESSING)
+ {
+ DEBUG_printf(("CheckJobs: current->dest = \'%s\'\n", current->dest));
+
+ if (FindClass(current->dest) != NULL)
+ printer = FindAvailablePrinter(current->dest);
+ else
+ printer = FindPrinter(current->dest);
+
+ if (printer == NULL && FindClass(current->dest) == NULL)
+ {
+ /*
+ * Whoa, the printer and/or class for this destination went away;
+ * cancel the job...
+ */
+
+ LogMessage(LOG_WARN, "Printer/class %s has gone away; cancelling job %d!",
+ current->dest, current->id);
+ CancelJob(current->id);
+
+ if (prev == NULL)
+ current = Jobs;
+ else
+ current = prev->next;
+ }
+ else if (printer != NULL)
+ {
+ /*
+ * See if the printer is available; if so, start the job...
+ */
+
+ DEBUG_printf(("CheckJobs: printer->state = %d\n", printer->state));
+
+ if (printer->state == IPP_PRINTER_IDLE)
+ StartJob(current->id, printer);
+
+ current = current->next;
+ }
+ else
+ current = current->next;
+ }
+ else
+ current = current->next;
+ }
+}
+
+
+/*
+ * 'FindJob()' - Find the specified job.
+ */
+
+job_t * /* O - Job data */
+FindJob(int id) /* I - Job ID */
+{
+ job_t *current; /* Current job */
+
+
+ for (current = Jobs; current != NULL; current = current->next)
+ if (current->id == id)
+ break;
+
+ return (current);
+}
+
+
+/*
+ * 'MoveJob()' - Move the specified job to a different destination.
+ */
+
+void
+MoveJob(int id, char *dest)
+{
+ job_t *current; /* Current job */
+
+
+ for (current = Jobs; current != NULL; current = current->next)
+ if (current->id == id)
+ {
+ if (current->state == IPP_JOB_PENDING)
+ strcpy(current->dest, dest);
+
+ return;
+ }
+}
+
+
+/*
+ * 'StartJob()' - Start a print job.
+ */
+
+void
+StartJob(int id, /* I - Job ID */
+ printer_t *printer) /* I - Printer to print job */
+{
+ job_t *current; /* Current job */
+ int i; /* Looping var */
+ int num_filters; /* Number of filters for job */
+ mime_filter_t *filters; /* Filters for job */
+ char method[255], /* Method for output */
+ *optptr; /* Pointer to options */
+ ipp_attribute_t *attr; /* Current attribute */
+ int pid; /* Process ID of new filter process */
+ int statusfds[2], /* Pipes used between the filters and scheduler */
+ filterfds[2][2];/* Pipes used between the filters */
+ char *argv[8], /* Filter command-line arguments */
+ command[1024], /* Full path to filter/backend command */
+ jobid[255], /* Job ID string */
+ title[IPP_MAX_NAME],
+ /* Job title string */
+ copies[255], /* # copies string */
+ options[16384], /* Full list of options */
+ *envp[13], /* Environment variables */
+ language[255], /* LANG environment variable */
+ charset[255], /* CHARSET environment variable */
+ content_type[255],/* CONTENT_TYPE environment variable */
+ ppd[1024], /* PPD environment variable */
+ root[1024], /* SERVER_ROOT environment variable */
+ cache[255], /* RIP_MAX_CACHE environment variable */
+ tmpdir[1024]; /* TMPDIR environment variable */
+
+
+ DEBUG_printf(("StartJob(%d, %08x)\n", id, printer));
+
+
+ for (current = Jobs; current != NULL; current = current->next)
+ if (current->id == id)
+ break;
+
+ if (current == NULL)
+ return;
+
+ /*
+ * Update the printer and job state to "processing"...
+ */
+
+ DEBUG_puts("StartJob: found job in list.");
+
+ current->state = IPP_JOB_PROCESSING;
+ current->status = 0;
+ current->printer = printer;
+ printer->job = current;
+ SetPrinterState(printer, IPP_PRINTER_PROCESSING);
+
+ /*
+ * Figure out what filters are required to convert from
+ * the source to the destination type...
+ */
+
+ num_filters = 0;
+
+#ifdef DEBUG
+ printf("Filtering from %s/%s to %s/%s...\n",
+ current->filetype->super, current->filetype->type,
+ printer->filetype->super, printer->filetype->type);
+ printf("num_filters = %d\n", MimeDatabase->num_filters);
+ for (i = 0; i < MimeDatabase->num_filters; i ++)
+ printf("filters[%d] = %s/%s to %s/%s using \"%s\" (cost %d)\n",
+ i, MimeDatabase->filters[i].src->super,
+ MimeDatabase->filters[i].src->type,
+ MimeDatabase->filters[i].dst->super,
+ MimeDatabase->filters[i].dst->type,
+ MimeDatabase->filters[i].filter,
+ MimeDatabase->filters[i].cost);
+#endif /* DEBUG */
+
+ if (printer->type & CUPS_PRINTER_REMOTE)
+ {
+ /*
+ * Remote jobs go directly to the remote job...
+ */
+
+ filters = NULL;
+ }
+ else
+ {
+ /*
+ * Local jobs get filtered...
+ */
+
+ filters = mimeFilter(MimeDatabase, current->filetype,
+ printer->filetype, &num_filters);
+
+ if (num_filters == 0)
+ {
+ LogMessage(LOG_ERROR, "Unable to convert file to printable format for job %s-%d!",
+ printer->name, current->id);
+ CancelJob(current->id);
+ return;
+ }
+ }
+
+ /*
+ * Building the options string is harder than it needs to be, but
+ * for the moment we need to pass strings for command-line args and
+ * not IPP attribute pointers... :)
+ */
+
+ optptr = options;
+ *optptr = '\0';
+
+ sprintf(title, "%s-%d", printer->name, current->id);
+ strcpy(copies, "1");
+
+ for (attr = current->attrs->attrs; attr != NULL; attr = attr->next)
+ {
+ if (strcmp(attr->name, "copies") == 0 &&
+ attr->value_tag == IPP_TAG_INTEGER)
+ sprintf(copies, "%d", attr->values[0].integer);
+ else if (strcmp(attr->name, "job-name") == 0 &&
+ (attr->value_tag == IPP_TAG_NAME ||
+ attr->value_tag == IPP_TAG_NAMELANG))
+ strcpy(title, attr->values[0].string.text);
+ else if ((attr->group_tag == IPP_TAG_JOB ||
+ attr->group_tag == IPP_TAG_EXTENSION) &&
+ (optptr - options) < (sizeof(options) - 128))
+ {
+ if (attr->value_tag == IPP_TAG_MIMETYPE ||
+ attr->value_tag == IPP_TAG_NAMELANG ||
+ attr->value_tag == IPP_TAG_TEXTLANG ||
+ attr->value_tag == IPP_TAG_URI ||
+ attr->value_tag == IPP_TAG_URISCHEME)
+ continue;
+
+ if (optptr > options)
+ strcat(optptr, " ");
+
+ if (attr->value_tag != IPP_TAG_BOOLEAN)
+ {
+ strcat(optptr, attr->name);
+ strcat(optptr, "=");
+ }
+
+ for (i = 0; i < attr->num_values; i ++)
+ {
+ if (i)
+ strcat(optptr, ",");
+
+ optptr += strlen(optptr);
+
+ switch (attr->value_tag)
+ {
+ case IPP_TAG_INTEGER :
+ case IPP_TAG_ENUM :
+ sprintf(optptr, "%d", attr->values[i].integer);
+ break;
+
+ case IPP_TAG_BOOLEAN :
+ if (!attr->values[i].boolean)
+ strcat(optptr, "no");
+
+ case IPP_TAG_NOVALUE :
+ strcat(optptr, attr->name);
+ break;
+
+ case IPP_TAG_RANGE :
+ sprintf(optptr, "%d-%d", attr->values[i].range.lower,
+ attr->values[i].range.upper);
+ break;
+
+ case IPP_TAG_RESOLUTION :
+ sprintf(optptr, "%dx%d%s", attr->values[i].resolution.xres,
+ attr->values[i].resolution.yres,
+ attr->values[i].resolution.units == IPP_RES_PER_INCH ?
+ "dpi" : "dpc");
+ break;
+
+ case IPP_TAG_STRING :
+ case IPP_TAG_TEXT :
+ case IPP_TAG_NAME :
+ case IPP_TAG_KEYWORD :
+ case IPP_TAG_CHARSET :
+ case IPP_TAG_LANGUAGE :
+ if (strchr(attr->values[i].string.text, ' ') != NULL ||
+ strchr(attr->values[i].string.text, '\t') != NULL ||
+ strchr(attr->values[i].string.text, '\n') != NULL)
+ {
+ strcat(optptr, "\'");
+ strcat(optptr, attr->values[i].string.text);
+ strcat(optptr, "\'");
+ }
+ else
+ strcat(optptr, attr->values[i].string.text);
+ break;
+ }
+ }
+
+ optptr += strlen(optptr);
+ }
+ }
+
+ /*
+ * Build the command-line arguments for the filters. Each filter
+ * has 6 or 7 arguments:
+ *
+ * argv[0] = printer
+ * argv[1] = job ID
+ * argv[2] = username
+ * argv[3] = title
+ * argv[4] = # copies
+ * argv[5] = options
+ * argv[6] = filename (optional; normally stdin)
+ *
+ * This allows legacy printer drivers that use the old System V
+ * printing interface to be used by CUPS.
+ */
+
+ sprintf(jobid, "%d", current->id);
+
+ argv[0] = printer->name;
+ argv[1] = jobid;
+ argv[2] = current->username;
+ argv[3] = title;
+ argv[4] = copies;
+ argv[5] = options;
+ argv[6] = current->filename;
+ argv[7] = NULL;
+
+ DEBUG_printf(("StartJob: args = \'%s\',\'%s\',\'%s\',\'%s\',\'%s\',\'%s\',\'%s\'\n",
+ argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6]));
+
+ /*
+ * Create environment variable strings for the filters...
+ */
+
+ attr = ippFindAttribute(current->attrs, "attributes-natural-language",
+ IPP_TAG_LANGUAGE);
+ sprintf(language, "LANG=%s", attr->values[0].string.text);
+
+ attr = ippFindAttribute(current->attrs, "document-format",
+ IPP_TAG_MIMETYPE);
+ if ((optptr = strstr(attr->values[0].string.text, "charset=")) != NULL)
+ sprintf(charset, "CHARSET=%s", optptr + 8);
+ else
+ {
+ attr = ippFindAttribute(current->attrs, "attributes-charset",
+ IPP_TAG_CHARSET);
+ sprintf(charset, "CHARSET=%s", attr->values[0].string.text);
+ }
+
+ sprintf(ppd, "PPD=%s/ppd/%s.ppd", ServerRoot, printer->name);
+ sprintf(root, "SERVER_ROOT=%s", ServerRoot);
+ sprintf(cache, "RIP_MAX_CACHE=%s", RIPCache);
+ sprintf(tmpdir, "TMPDIR=%s", TempDir);
+ sprintf(content_type, "CONTENT_TYPE=%s/%s", current->filetype->super,
+ current->filetype->type);
+
+ envp[0] = "PATH=/bin:/usr/bin";
+ envp[1] = "SOFTWARE=CUPS/1.0";
+ envp[2] = "TZ=GMT";
+ envp[3] = "USER=root";
+ envp[4] = charset;
+ envp[5] = language;
+ envp[6] = "TZ=GMT";
+ envp[7] = ppd;
+ envp[8] = root;
+ envp[9] = cache;
+ envp[10] = tmpdir;
+ envp[11] = content_type;
+ envp[12] = NULL;
+
+ DEBUG_puts(envp[0]);
+ DEBUG_puts(envp[1]);
+ DEBUG_puts(envp[2]);
+ DEBUG_puts(envp[3]);
+ DEBUG_puts(envp[4]);
+ DEBUG_puts(envp[5]);
+ DEBUG_puts(envp[6]);
+ DEBUG_puts(envp[7]);
+ DEBUG_puts(envp[8]);
+ DEBUG_puts(envp[9]);
+ DEBUG_puts(envp[10]);
+ DEBUG_puts(envp[11]);
+
+ /*
+ * Now create processes for all of the filters...
+ */
+
+ if (pipe(statusfds))
+ {
+ LogMessage(LOG_ERROR, "StartJob: unable to create status pipes - %s.",
+ strerror(errno));
+ StopPrinter(printer);
+ sprintf(printer->state_message, "Unable to create status pipes - %s.",
+ strerror(errno));
+ return;
+ }
+
+ DEBUG_printf(("statusfds = %d, %d\n", statusfds[0], statusfds[1]));
+
+ current->pipe = statusfds[0];
+ current->status = 0;
+ memset(current->procs, 0, sizeof(current->procs));
+
+ if (num_filters > 0 && strcmp(filters[num_filters - 1].filter, "-") == 0)
+ num_filters --;
+
+ filterfds[1][0] = open("/dev/null", O_RDONLY);
+ filterfds[1][1] = -1;
+ DEBUG_printf(("filterfds[%d] = %d, %d\n", 1, filterfds[1][0],
+ filterfds[1][1]));
+
+ for (i = 0; i < num_filters; i ++)
+ {
+ if (i == 1)
+ argv[6] = NULL;
+
+ if (filters[i].filter[0] != '/')
+ sprintf(command, "%s/filter/%s", ServerRoot, filters[i].filter);
+ else
+ strcpy(command, filters[i].filter);
+
+ DEBUG_printf(("%s: %s %s %s %s %s %s %s\n", command, argv[0],
+ argv[1], argv[2], argv[3], argv[4], argv[5], argv[6]));
+
+ if (i < (num_filters - 1) ||
+ strncmp(printer->device_uri, "file:", 5) != 0)
+ pipe(filterfds[i & 1]);
+ else
+ {
+ filterfds[i & 1][0] = -1;
+ if (strncmp(printer->device_uri, "file:/dev/", 10) == 0)
+ filterfds[i & 1][1] = open(printer->device_uri + 5,
+ O_WRONLY | O_EXCL);
+ else
+ filterfds[i & 1][1] = open(printer->device_uri + 5,
+ O_WRONLY | O_CREAT | O_TRUNC, 0666);
+ }
+
+ DEBUG_printf(("filterfds[%d] = %d, %d\n", i & 1, filterfds[i & 1][0],
+ filterfds[i & 1][1]));
+
+ pid = start_process(command, argv, envp, filterfds[!(i & 1)][0],
+ filterfds[i & 1][1], statusfds[1]);
+
+ close(filterfds[!(i & 1)][0]);
+ close(filterfds[!(i & 1)][1]);
+
+ if (pid == 0)
+ {
+ LogMessage(LOG_ERROR, "Unable to start filter \"%s\" - %s.",
+ filters[i].filter, strerror(errno));
+ StopPrinter(current->printer);
+ sprintf(printer->state_message, "Unable to start filter \"%s\" - %s.",
+ filters[i].filter, strerror(errno));
+ return;
+ }
+ else
+ {
+ current->procs[i] = pid;
+
+ DEBUG_printf(("StartJob: started %s - pid = %d.\n", command, pid));
+ }
+ }
+
+ if (filters != NULL)
+ free(filters);
+
+ /*
+ * Finally, pipe the final output into a backend process if needed...
+ */
+
+ if (strncmp(printer->device_uri, "file:", 5) != 0)
+ {
+ sscanf(printer->device_uri, "%[^:]", method);
+ sprintf(command, "%s/backend/%s", ServerRoot, method);
+
+ argv[0] = printer->device_uri;
+ if (num_filters)
+ argv[6] = NULL;
+
+ DEBUG_printf(("%s: %s %s %s %s %s %s %s\n", command, argv[0],
+ argv[1], argv[2], argv[3], argv[4], argv[5], argv[6]));
+
+ filterfds[i & 1][0] = -1;
+ filterfds[i & 1][1] = open("/dev/null", O_WRONLY);
+
+ DEBUG_printf(("filterfds[%d] = %d, %d\n", i & 1, filterfds[i & 1][0],
+ filterfds[i & 1][1]));
+
+ pid = start_process(command, argv, envp, filterfds[!(i & 1)][0],
+ filterfds[i & 1][1], statusfds[1]);
+
+ close(filterfds[!(i & 1)][0]);
+ close(filterfds[!(i & 1)][1]);
+
+ if (pid == 0)
+ {
+ LogMessage(LOG_ERROR, "Unable to start backend \"%s\" - %s.",
+ method, strerror(errno));
+ StopPrinter(current->printer);
+ sprintf(printer->state_message, "Unable to start backend \"%s\" - %s.",
+ method, strerror(errno));
+ return;
+ }
+ else
+ {
+ current->procs[i] = pid;
+
+ DEBUG_printf(("StartJob: started %s - pid = %d.\n", command, pid));
+ }
+ }
+ else
+ {
+ filterfds[i & 1][0] = -1;
+ filterfds[i & 1][1] = -1;
+
+ close(filterfds[!(i & 1)][0]);
+ close(filterfds[!(i & 1)][1]);
+ }
+
+ close(filterfds[i & 1][0]);
+ close(filterfds[i & 1][1]);
+
+ close(statusfds[1]);
+
+ FD_SET(current->pipe, &InputSet);
+}
+
+
+/*
+ * 'StopJob()' - Stop a print job.
+ */
+
+void
+StopJob(int id)
+{
+ int i; /* Looping var */
+ job_t *current; /* Current job */
+
+
+ DEBUG_printf(("StopJob(%d)\n", id));
+
+ for (current = Jobs; current != NULL; current = current->next)
+ if (current->id == id)
+ {
+ DEBUG_puts("StopJob: found job in list.");
+
+ if (current->state == IPP_JOB_PROCESSING)
+ {
+ DEBUG_puts("StopJob: job state is \'processing\'.");
+
+ if (current->status)
+ SetPrinterState(current->printer, IPP_PRINTER_STOPPED);
+ else
+ SetPrinterState(current->printer, IPP_PRINTER_IDLE);
+
+ current->state = IPP_JOB_STOPPED;
+ current->printer->job = NULL;
+ current->printer = NULL;
+
+ for (i = 0; current->procs[i]; i ++)
+ if (current->procs[i] > 0)
+ kill(current->procs[i], SIGTERM);
+ current->procs[0] = 0;
+
+ if (current->pipe)
+ {
+ close(current->pipe);
+ FD_CLR(current->pipe, &InputSet);
+ current->pipe = 0;
+ }
+ }
+ return;
+ }
+}
+
+
+/*
+ * 'UpdateJob()' - Read a status update from a job's filters.
+ */
+
+void
+UpdateJob(job_t *job) /* I - Job to check */
+{
+ int bytes; /* Number of bytes read */
+ char *lineptr, /* Pointer to end of line in buffer */
+ *message; /* Pointer to message text */
+ int loglevel; /* Log level for message */
+ static int bufused = 0; /* Amount of buffer used */
+ static char buffer[8192]; /* Data buffer */
+
+
+ if ((bytes = read(job->pipe, buffer + bufused, sizeof(buffer) - bufused - 1)) > 0)
+ {
+ bufused += bytes;
+ buffer[bufused] = '\0';
+
+ while ((lineptr = strchr(buffer, '\n')) != NULL)
+ {
+ /*
+ * Terminate each line and process it...
+ */
+
+ *lineptr++ = '\0';
+
+ /*
+ * Figure out the logging level...
+ */
+
+ if (strncmp(buffer, "ERROR:", 6) == 0)
+ {
+ loglevel = LOG_ERROR;
+ message = buffer + 6;
+ }
+ else if (strncmp(buffer, "WARNING:", 8) == 0)
+ {
+ loglevel = LOG_WARN;
+ message = buffer + 8;
+ }
+ if (strncmp(buffer, "INFO:", 5) == 0)
+ {
+ loglevel = LOG_INFO;
+ message = buffer + 5;
+ }
+ else if (strncmp(buffer, "DEBUG:", 6) == 0)
+ {
+ loglevel = LOG_DEBUG;
+ message = buffer + 6;
+ }
+ else if (strncmp(buffer, "PAGE:", 5) == 0)
+ {
+ loglevel = LOG_PAGE;
+ message = buffer + 5;
+ }
+ else
+ {
+ loglevel = LOG_DEBUG;
+ message = buffer;
+ }
+
+ /*
+ * Skip leading whitespace in the message...
+ */
+
+ while (isspace(*message))
+ message ++;
+
+ /*
+ * Send it to the log file and printer state message as needed...
+ */
+
+ if (loglevel == LOG_PAGE)
+ {
+ /*
+ * Page message; send the message to the page_log file...
+ */
+
+ LogPage(job, message);
+ }
+ else
+ {
+ /*
+ * Other status message; send it to the error_log file...
+ */
+
+ if (loglevel != LOG_INFO)
+ LogMessage(loglevel, "%s", message);
+
+ if ((loglevel >= LOG_INFO && !job->state) ||
+ loglevel == LOG_ERROR)
+ strncpy(job->printer->state_message, message,
+ sizeof(job->printer->state_message) - 1);
+ }
+
+ /*
+ * Update the input buffer...
+ */
+
+ strcpy(buffer, lineptr);
+ bufused -= lineptr - buffer;
+ }
+ }
+ else
+ {
+ DEBUG_printf(("UpdateJob: job %d is complete.\n", job->id));
+
+ if (job->status)
+ {
+ /*
+ * Job had errors; stop it...
+ */
+
+ StopJob(job->id);
+ }
+ else
+ {
+ /*
+ * Job printed successfully; cancel it...
+ */
+
+ job->printer->state_message[0] = '\0';
+
+ CancelJob(job->id);
+ }
+ }
+}
+
+
+/*
+ * 'start_process()' - Start a background process.
+ */
+
+static int /* O - Process ID or 0 */
+start_process(char *command, /* I - Full path to command */
+ char *argv[], /* I - Command-line arguments */
+ char *envp[], /* I - Environment */
+ int infd, /* I - Standard input file descriptor */
+ int outfd, /* I - Standard output file descriptor */
+ int errfd) /* I - Standard error file descriptor */
+{
+ int fd; /* Looping var */
+ int pid; /* Process ID */
+
+
+ DEBUG_printf(("start_process(\"%s\", %08x, %08x, %d, %d, %d)\n",
+ command, argv, envp, infd, outfd, errfd));
+
+ if ((pid = fork()) == 0)
+ {
+ /*
+ * Child process goes here...
+ *
+ * Update stdin/stdout/stderr as needed...
+ */
+
+ close(0);
+ dup(infd);
+ close(1);
+ dup(outfd);
+ if (errfd > 2)
+ {
+ close(2);
+ dup(errfd);
+ }
+
+ /*
+ * Close extra file descriptors...
+ */
+
+ for (fd = 3; fd < 1024; fd ++)
+ close(fd);
+
+ /*
+ * Change user to something "safe"...
+ */
+
+ setuid(User);
+ setgid(Group);
+
+ /*
+ * Execute the command; if for some reason this doesn't work,
+ * return the error code...
+ */
+
+ execve(command, argv, envp);
+
+ perror("cupsd: execve() failed");
+
+ exit(errno);
+ }
+ else if (pid < 0)
+ {
+ /*
+ * Error - couldn't fork a new process!
+ */
+
+ DEBUG_printf(("StartJob: unable to fork() %s - %s.\n", command,
+ strerror(errno)));
+
+ return (0);
+ }
+
+ return (pid);
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/scheduler/job.h b/scheduler/job.h
new file mode 100644
index 000000000..0cd039e5d
--- /dev/null
+++ b/scheduler/job.h
@@ -0,0 +1,75 @@
+/*
+ * "$Id$"
+ *
+ * Print job definitions for the Common UNIX Printing System (CUPS) scheduler.
+ *
+ * Copyright 1997-1999 by Easy Software Products, all rights reserved.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ */
+
+/*
+ * Job request structure...
+ */
+
+typedef struct job_str
+{
+ struct job_str *next; /* Next job in queue */
+ int id, /* Job ID */
+ priority; /* Job priority */
+ ipp_jstate_t state; /* Job state */
+ char username[16]; /* Printing user */
+ char dest[IPP_MAX_NAME]; /* Destination printer or class */
+ char title[IPP_MAX_NAME]; /* Job name/title */
+ cups_ptype_t dtype; /* Destination type (class/remote bits) */
+ char filename[HTTP_MAX_URI]; /* Name of job file */
+ mime_type_t *filetype; /* File type */
+ ipp_t *attrs; /* Job attributes */
+ int pipe; /* Status pipe for this job */
+ int procs[MAX_FILTERS + 2]; /* Process IDs, 0 terminated */
+ int status; /* Status code from filters */
+ printer_t *printer; /* Printer this job is assigned to */
+} job_t;
+
+
+/*
+ * Globals...
+ */
+
+VAR int NumJobs VALUE(0); /* Number of jobs in queue */
+VAR job_t *Jobs VALUE(NULL); /* List of current jobs */
+VAR int NextJobId VALUE(1); /* Next job ID to use */
+
+/*
+ * Prototypes...
+ */
+
+extern job_t *AddJob(int priority, char *dest);
+extern void CancelJob(int id);
+extern void CancelJobs(char *dest);
+extern void CheckJobs(void);
+extern void DeleteJob(int id);
+extern job_t *FindJob(int id);
+extern void LoadJobs(void);
+extern void MoveJob(int id, char *dest);
+extern void StartJob(int id, printer_t *printer);
+extern void StopJob(int id);
+extern void UpdateJob(job_t *job);
+
+/*
+ * End of "$Id$".
+ */
diff --git a/scheduler/listen.c b/scheduler/listen.c
new file mode 100644
index 000000000..175f31b10
--- /dev/null
+++ b/scheduler/listen.c
@@ -0,0 +1,142 @@
+/*
+ * "$Id$"
+ *
+ * Server listening routines for the Common UNIX Printing System (CUPS)
+ * scheduler.
+ *
+ * Copyright 1997-1999 by Easy Software Products, all rights reserved.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * Contents:
+ *
+ * StartListening() - Create all listening sockets...
+ * StopListening() - Close all listening sockets...
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "cupsd.h"
+
+
+/*
+ * 'StartListening()' - Create all listening sockets...
+ */
+
+void
+StartListening(void)
+{
+ int i, /* Looping var */
+ val; /* Parameter value */
+ listener_t *lis; /* Current listening socket */
+
+
+ /*
+ * Setup socket listeners...
+ */
+
+ for (i = NumListeners, lis = Listeners; i > 0; i --, lis ++)
+ {
+ LogMessage(LOG_DEBUG, "StartListening() address=%08x port=%d",
+ ntohl(lis->address.sin_addr.s_addr),
+ ntohs(lis->address.sin_port));
+
+ if ((lis->fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
+ {
+ LogMessage(LOG_ERROR, "StartListening() Unable to open listen socket - %s.",
+ strerror(errno));
+ exit(errno);
+ }
+
+ fcntl(lis->fd, F_SETFD, fcntl(lis->fd, F_GETFD) | FD_CLOEXEC);
+
+ /*
+ * Set things up to reuse the local address for this port.
+ */
+
+ val = 1;
+#ifdef __sun
+ setsockopt(lis->fd, SOL_SOCKET, SO_REUSEADDR, (char *)&val, sizeof(val));
+#else
+ setsockopt(lis->fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
+#endif /* __sun */
+
+ /*
+ * Bind to the port we found...
+ */
+
+ if (bind(lis->fd, (struct sockaddr *)&(lis->address), sizeof(lis->address)) < 0)
+ {
+ LogMessage(LOG_ERROR, "StartListening() Unable to bind socket - %s.", strerror(errno));
+ exit(errno);
+ }
+
+ /*
+ * Listen for new clients.
+ */
+
+ if (listen(lis->fd, SOMAXCONN) < 0)
+ {
+ LogMessage(LOG_ERROR, "StartListening() Unable to listen for clients - %s.",
+ strerror(errno));
+ exit(errno);
+ }
+
+ /*
+ * Setup the select() input mask to contain the listening socket we have.
+ */
+
+ DEBUG_printf(("StartListening: Adding fd %d to InputSet...\n", lis->fd));
+ FD_SET(lis->fd, &InputSet);
+ }
+
+ LogMessage(LOG_DEBUG, "StartListening() NumListeners=%d", NumListeners);
+}
+
+
+/*
+ * 'StopListening()' - Close all listening sockets...
+ */
+
+void
+StopListening(void)
+{
+ int i; /* Looping var */
+ listener_t *lis; /* Current listening socket */
+
+
+ for (i = NumListeners, lis = Listeners; i > 0; i --, lis ++)
+ {
+#if defined(WIN32) || defined(__EMX__)
+ closesocket(lis->fd);
+#else
+ close(lis->fd);
+#endif /* WIN32 || __EMX__ */
+
+ DEBUG_printf(("StopListening: Removing fd %d from InputSet...\n", lis->fd));
+ FD_CLR(lis->fd, &InputSet);
+ }
+
+ LogMessage(LOG_DEBUG, "StopListening()");
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/scheduler/main.c b/scheduler/main.c
new file mode 100644
index 000000000..00a8ad2af
--- /dev/null
+++ b/scheduler/main.c
@@ -0,0 +1,422 @@
+/*
+ * "$Id$"
+ *
+ * Scheduler main loop for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-1999 by Easy Software Products, all rights reserved.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * Contents:
+ *
+ * main() - Main entry for the CUPS scheduler.
+ * sigchld_handler() - Handle 'child' signals from old processes.
+ * sighup_handler() - Handle 'hangup' signals to reconfigure the scheduler.
+ * usage() - Show scheduler usage.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#define _MAIN_C_
+#include "cupsd.h"
+#include <sys/resource.h>
+
+
+/*
+ * Local functions...
+ */
+
+static void sigchld_handler(int sig);
+static void sighup_handler(int sig);
+static void usage(void);
+
+
+/*
+ * 'main()' - Main entry for the CUPS scheduler.
+ */
+
+int /* O - Exit status */
+main(int argc, /* I - Number of command-line arguments */
+ char *argv[]) /* I - Command-line arguments */
+{
+ int i; /* Looping var */
+ char *opt; /* Option character */
+ fd_set input, /* Input set for select() */
+ output; /* Output set for select() */
+ client_t *con; /* Current client */
+ job_t *job, /* Current job */
+ *next; /* Next job */
+ listener_t *lis; /* Current listener */
+ time_t activity; /* Activity timer */
+ struct timeval timeout; /* select() timeout */
+ struct rlimit limit; /* Runtime limit */
+#ifdef HAVE_SIGACTION
+ struct sigaction action; /* Actions for POSIX signals */
+#endif /* HAVE_SIGACTION */
+
+
+ /*
+ * Check for command-line arguments...
+ */
+
+ for (i = 1; i < argc; i ++)
+ if (argv[i][0] == '-')
+ for (opt = argv[i] + 1; *opt != '\0'; opt ++)
+ switch (*opt)
+ {
+ case 'c' : /* Configuration file */
+ i ++;
+ if (i >= argc)
+ usage();
+
+ strncpy(ConfigurationFile, argv[i], sizeof(ConfigurationFile) - 1);
+ ConfigurationFile[sizeof(ConfigurationFile) - 1] = '\0';
+ break;
+
+ default : /* Unknown option */
+ fprintf(stderr, "cupsd: Unknown option \'%c\' - aborting!\n", *opt);
+ usage();
+ break;
+ }
+ else
+ {
+ fprintf(stderr, "cupsd: Unknown argument \'%s\' - aborting!\n", argv[i]);
+ usage();
+ }
+
+ /*
+ * Set the timezone info...
+ */
+
+ tzset();
+
+#ifndef DEBUG
+ /*
+ * Disable core dumps...
+ */
+
+ getrlimit(RLIMIT_CORE, &limit);
+ limit.rlim_cur = 0;
+ setrlimit(RLIMIT_CORE, &limit);
+#endif /* DEBUG */
+
+ /*
+ * Set the maximum number of files...
+ */
+
+ getrlimit(RLIMIT_NOFILE, &limit);
+ limit.rlim_cur = limit.rlim_max;
+ setrlimit(RLIMIT_NOFILE, &limit);
+
+ /*
+ * Catch hangup and child signals and ignore broken pipes...
+ */
+
+#ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
+ sigset(SIGHUP, sighup_handler);
+ sigset(SIGCHLD, sigchld_handler);
+ sigset(SIGPIPE, SIG_IGN);
+#elif defined(HAVE_SIGACTION)
+ memset(&action, 0, sizeof(action));
+
+ sigemptyset(&action.sa_mask);
+ sigaddset(&action.sa_mask, SIGHUP);
+ action.sa_handler = sighup_handler;
+ sigaction(SIGHUP, &action, NULL);
+
+ sigemptyset(&action.sa_mask);
+ sigaddset(&action.sa_mask, SIGCHLD);
+ action.sa_handler = sigchld_handler;
+ sigaction(SIGCHLD, &action, NULL);
+
+ sigemptyset(&action.sa_mask);
+ action.sa_handler = SIG_IGN;
+ sigaction(SIGPIPE, &action, NULL);
+#else
+ signal(SIGHUP, sighup_handler);
+ signal(SIGCLD, sigchld_handler); /* No, SIGCLD isn't a typo... */
+ signal(SIGPIPE, SIG_IGN);
+#endif /* HAVE_SIGSET */
+
+ /*
+ * Loop forever...
+ */
+
+ for (;;)
+ {
+ /*
+ * Check if we need to load the server configuration file...
+ */
+
+ if (NeedReload)
+ {
+ if (NumClients > 0)
+ {
+ for (i = NumClients, con = Clients; i > 0; i --, con ++)
+ if (con->http.state == HTTP_WAITING)
+ {
+ CloseClient(con);
+ con --;
+ }
+ else
+ con->http.keep_alive = HTTP_KEEPALIVE_OFF;
+
+ for (i = 0; i < NumListeners; i ++)
+ FD_CLR(Listeners[i].fd, &InputSet);
+ }
+ else if (!ReadConfiguration())
+ {
+ fprintf(stderr, "cupsd: Unable to read configuration file \'%s\' - exiting!\n",
+ ConfigurationFile);
+ exit(1);
+ }
+ }
+
+ /*
+ * Check for available input or ready output. If select() returns
+ * 0 or -1, something bad happened and we should exit immediately.
+ *
+ * Note that we at least have one listening socket open at all
+ * times.
+ */
+
+ input = InputSet;
+ output = OutputSet;
+
+ timeout.tv_sec = 1;
+ timeout.tv_usec = 0;
+
+ for (i = NumClients, con = Clients; i > 0; i --, con ++)
+ if (con->http.used > 0)
+ {
+ timeout.tv_sec = 0;
+ break;
+ }
+
+ if ((i = select(100, &input, &output, NULL, &timeout)) < 0)
+ {
+ /*
+ * Got an error from select!
+ */
+
+ if (errno == EINTR) /* Just interrupted by a signal */
+ continue;
+
+ /*
+ * Display all sorts of logging info to help track down the problem.
+ */
+
+ perror("cupsd: select() failed");
+
+ fprintf(stderr, "cupsd: InputSet =");
+ for (i = 0; i < 100; i ++)
+ if (FD_ISSET(i, &input))
+ fprintf(stderr, " %d", i);
+ fputs("\n", stderr);
+
+ fprintf(stderr, "cupsd: OutputSet =");
+ for (i = 0; i < 100; i ++)
+ if (FD_ISSET(i, &output))
+ fprintf(stderr, " %d", i);
+ fputs("\n", stderr);
+
+ for (i = 0, con = Clients; i < NumClients; i ++, con ++)
+ fprintf(stderr, "cupsd: Clients[%d] = %d, file = %d, state = %d\n",
+ i, con->http.fd, con->file, con->http.state);
+
+ for (i = 0, lis = Listeners; i < NumListeners; i ++, lis ++)
+ fprintf(stderr, "cupsd: Listeners[%d] = %d\n", i, lis->fd);
+
+ fprintf(stderr, "cupsd: BrowseSocket = %d\n", BrowseSocket);
+
+ for (job = Jobs; job != NULL; job = job->next)
+ fprintf(stderr, "cupsd: Jobs[%d] = %d\n", job->id, job->pipe);
+
+ break;
+ }
+
+ for (i = NumListeners, lis = Listeners; i > 0; i --, lis ++)
+ if (FD_ISSET(lis->fd, &input))
+ AcceptClient(lis);
+
+ for (i = NumClients, con = Clients; i > 0; i --, con ++)
+ {
+ /*
+ * Process the input buffer...
+ */
+
+ if (FD_ISSET(con->http.fd, &input) || con->http.used)
+ if (!ReadClient(con))
+ {
+ con --;
+ continue;
+ }
+
+ /*
+ * Write data as needed...
+ */
+
+ if (FD_ISSET(con->http.fd, &output) &&
+ (!con->pipe_pid || FD_ISSET(con->file, &input)))
+ if (!WriteClient(con))
+ {
+ con --;
+ continue;
+ }
+
+ /*
+ * Check the activity and close old clients...
+ */
+
+ activity = time(NULL) - 30;
+ if (con->http.activity < activity)
+ {
+ CloseClient(con);
+ con --;
+ continue;
+ }
+ }
+
+ /*
+ * Check for status info from job filters...
+ */
+
+ for (job = Jobs; job != NULL; job = next)
+ {
+ next = job->next;
+
+ if (job->pipe && FD_ISSET(job->pipe, &input))
+ UpdateJob(job);
+ }
+
+ /*
+ * Update the browse list as needed...
+ */
+
+ if (BrowseSocket >= 0)
+ {
+ if (FD_ISSET(BrowseSocket, &input))
+ UpdateBrowseList();
+
+ SendBrowseList();
+ }
+ }
+
+ /*
+ * If we get here something very bad happened and we need to exit
+ * immediately.
+ */
+
+ CloseAllClients();
+ StopListening();
+
+ return (1);
+}
+
+
+/*
+ * 'sigchld_handler()' - Handle 'child' signals from old processes.
+ */
+
+static void
+sigchld_handler(int sig) /* I - Signal number */
+{
+ int status; /* Exit status of child */
+ int pid; /* Process ID of child */
+ job_t *job; /* Current job */
+ int i; /* Looping var */
+
+
+ (void)sig;
+
+#ifdef HAVE_WAITPID
+ while ((pid = waitpid(-1, &status, WNOHANG)) > 0)
+#elif defined(HAVE_WAIT3)
+ while ((pid = wait3(&status, WNOHANG, NULL)) > 0)
+#else
+ if ((pid = wait(&status)) > 0)
+#endif /* HAVE_WAITPID */
+ {
+ DEBUG_printf(("sigcld_handler: pid = %d, status = %d\n", pid, status));
+
+ for (job = Jobs; job != NULL; job = job->next)
+ if (job->state == IPP_JOB_PROCESSING)
+ {
+ for (i = 0; job->procs[i]; i ++)
+ if (job->procs[i] == pid)
+ break;
+
+ if (job->procs[i])
+ {
+ /*
+ * OK, this process has gone away; what's left?
+ */
+
+ job->procs[i] = -pid;
+
+ if (status)
+ {
+ /*
+ * A fatal error occurred; save the exit status so we know to stop
+ * the printer when all of the filters finish...
+ */
+
+ job->status = status;
+ }
+ break;
+ }
+ }
+ }
+
+#ifdef HAVE_SIGSET
+ sigset(SIGCHLD, sigchld_handler);
+#elif !defined(HAVE_SIGACTION)
+ signal(SIGCLD, sigchld_handler);
+#endif /* HAVE_SIGSET */
+}
+
+
+/*
+ * 'sighup_handler()' - Handle 'hangup' signals to reconfigure the scheduler.
+ */
+
+static void
+sighup_handler(int sig) /* I - Signal number */
+{
+ (void)sig;
+
+ NeedReload = TRUE;
+}
+
+
+/*
+ * 'usage()' - Show scheduler usage.
+ */
+
+static void
+usage(void)
+{
+ fputs("Usage: cupsd [-c config-file]\n", stderr);
+ exit(1);
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/scheduler/printers.c b/scheduler/printers.c
new file mode 100644
index 000000000..62a9e98a1
--- /dev/null
+++ b/scheduler/printers.c
@@ -0,0 +1,1035 @@
+/*
+ * "$Id$"
+ *
+ * Printer routines for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-1999 by Easy Software Products, all rights reserved.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * Contents:
+ *
+ * AddPrinter() - Add a printer to the system.
+ * DeleteAllPrinters() - Delete all printers from the system.
+ * DeletePrinter() - Delete a printer from the system.
+ * FindPrinter() - Find a printer in the list.
+ * LoadAllPrinters() - Load printers from the printers.conf file.
+ * SaveAllPrinters() - Save all printer definitions to the printers.conf
+ * SetPrinterAttrs() - Set printer attributes based upon the PPD file.
+ * SetPrinterState() - Update the current state of a printer.
+ * SortPrinters() - Sort the printer list when a printer name is
+ * changed.
+ * StopPrinter() - Stop a printer from printing any jobs...
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "cupsd.h"
+
+
+/*
+ * 'AddPrinter()' - Add a printer to the system.
+ */
+
+printer_t * /* O - New printer */
+AddPrinter(char *name) /* I - Name of printer */
+{
+ printer_t *p, /* New printer */
+ *current, /* Current printer in list */
+ *prev; /* Previous printer in list */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (name == NULL)
+ return (NULL);
+
+ /*
+ * Create a new printer entity...
+ */
+
+ if ((p = calloc(sizeof(printer_t), 1)) == NULL)
+ return (NULL);
+
+ strcpy(p->name, name);
+ strcpy(p->hostname, ServerName);
+ sprintf(p->uri, "ipp://%s:%d/printers/%s", ServerName,
+ ntohs(Listeners[0].address.sin_port), name);
+
+ p->state = IPP_PRINTER_STOPPED;
+ p->accepting = 0;
+ p->filetype = mimeAddType(MimeDatabase, "printer", name);
+
+ /*
+ * Setup required filters and IPP attributes...
+ */
+
+ SetPrinterAttrs(p);
+
+ /*
+ * Insert the printer in the printer list alphabetically...
+ */
+
+ for (prev = NULL, current = Printers;
+ current != NULL;
+ prev = current, current = current->next)
+ if (strcasecmp(p->name, current->name) < 0)
+ break;
+
+ /*
+ * Insert this printer before the current one...
+ */
+
+ if (prev == NULL)
+ Printers = p;
+ else
+ prev->next = p;
+
+ p->next = current;
+
+ return (p);
+}
+
+
+/*
+ * 'AddPrinterFilter()' - Add a MIME filter for a printer.
+ */
+
+void
+AddPrinterFilter(printer_t *p, /* I - Printer to add to */
+ char *filter) /* I - Filter to add */
+{
+ int i; /* Looping var */
+ char super[MIME_MAX_SUPER], /* Super-type for filter */
+ type[MIME_MAX_TYPE], /* Type for filter */
+ program[1024]; /* Program/filter name */
+ int cost; /* Cost of filter */
+ mime_type_t **temptype; /* MIME type looping var */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (p == NULL || filter == NULL)
+ return;
+
+ /*
+ * Parse the filter string; it should be in the following format:
+ *
+ * super/type cost program
+ */
+
+ if (sscanf(filter, "%[^/]/%s%d%s", super, type, &cost, program) != 4)
+ {
+ LogMessage(LOG_ERROR, "AddPrinterFilter: Invalid filter string \"%s\"!",
+ filter);
+ return;
+ }
+
+ /*
+ * Add the filter to the MIME database, supporting wildcards as needed...
+ */
+
+ for (temptype = MimeDatabase->types, i = MimeDatabase->num_types;
+ i > 0;
+ i --, temptype ++)
+ if (((super[0] == '*' && strcmp((*temptype)->super, "printer") != 0) ||
+ strcmp((*temptype)->super, super) == 0) &&
+ (type[0] == '*' || strcmp((*temptype)->type, type) == 0))
+ {
+ LogMessage(LOG_DEBUG, "Adding filter %s/%s %s/%s %d %s",
+ (*temptype)->super, (*temptype)->type,
+ p->filetype->super, p->filetype->type,
+ cost, program);
+ mimeAddFilter(MimeDatabase, *temptype, p->filetype, cost, program);
+ }
+}
+
+
+/*
+ * 'DeleteAllPrinters()' - Delete all printers from the system.
+ */
+
+void
+DeleteAllPrinters(void)
+{
+ printer_t *p, /* Pointer to current printer/class */
+ *next; /* Pointer to next printer in list */
+
+
+ for (p = Printers; p != NULL; p = next)
+ {
+ next = p->next;
+
+ if (!(p->type & CUPS_PRINTER_CLASS))
+ DeletePrinter(p);
+ }
+}
+
+
+/*
+ * 'DeletePrinter()' - Delete a printer from the system.
+ */
+
+void
+DeletePrinter(printer_t *p) /* I - Printer to delete */
+{
+ int i; /* Looping var */
+ printer_t *current, /* Current printer in list */
+ *prev; /* Previous printer in list */
+
+
+ DEBUG_printf(("DeletePrinter(%08x): p->name = \"%s\"...\n", p, p->name));
+
+ /*
+ * Range check input...
+ */
+
+ if (p == NULL)
+ return;
+
+ /*
+ * Stop printing on this printer...
+ */
+
+ StopPrinter(p);
+
+ /*
+ * Remove the printer from the list...
+ */
+
+ for (prev = NULL, current = Printers;
+ current != NULL;
+ prev = current, current = current->next)
+ if (p == current)
+ break;
+
+ if (current == NULL)
+ {
+ fputs("cupsd: WARNING - tried to delete a non-existent printer!\n", stderr);
+ return;
+ }
+
+ if (prev == NULL)
+ Printers = p->next;
+ else
+ prev->next = p->next;
+
+ if (p->printers != NULL)
+ free(p->printers);
+
+ ippDelete(p->attrs);
+
+ free(p);
+
+ /*
+ * If p is the default printer, assign the next one...
+ */
+
+ if (p == DefaultPrinter)
+ DefaultPrinter = Printers;
+}
+
+
+/*
+ * 'AddPrinterFilter()' - Add a MIME filter for a printer.
+ */
+
+void
+DeletePrinterFilters(printer_t *p) /* I - Printer to remove from */
+{
+ int i; /* Looping var */
+ mime_filter_t *filter; /* MIME filter looping var */
+
+
+ /*
+ * Range check input...
+ */
+
+ if (p == NULL)
+ return;
+
+ /*
+ * Remove all filters from the MIME database that have a destination
+ * type == printer...
+ */
+
+ for (filter = MimeDatabase->filters, i = MimeDatabase->num_filters;
+ i > 0;
+ i --, filter ++)
+ if (filter->dst == p->filetype)
+ {
+ /*
+ * Delete the current filter...
+ */
+
+ MimeDatabase->num_filters --;
+
+ if (i > 1)
+ memcpy(filter, filter + 1, sizeof(mime_filter_t) * (i - 1));
+
+ filter --;
+ }
+}
+
+
+/*
+ * 'FindPrinter()' - Find a printer in the list.
+ */
+
+printer_t * /* O - Printer in list */
+FindPrinter(char *name) /* I - Name of printer to find */
+{
+ printer_t *p; /* Current printer */
+
+
+ for (p = Printers; p != NULL; p = p->next)
+ switch (strcasecmp(name, p->name))
+ {
+ case 0 : /* name == p->name */
+ if (!(p->type & CUPS_PRINTER_CLASS))
+ return (p);
+ case 1 : /* name > p->name */
+ break;
+ case -1 : /* name < p->name */
+ return (NULL);
+ }
+
+ return (NULL);
+}
+
+
+/*
+ * 'LoadAllPrinters()' - Load printers from the printers.conf file.
+ */
+
+void
+LoadAllPrinters(void)
+{
+ FILE *fp; /* printers.conf file */
+ int i; /* Looping var */
+ int linenum; /* Current line number */
+ int len; /* Length of line */
+ char line[HTTP_MAX_BUFFER], /* Line from file */
+ name[256], /* Parameter name */
+ *nameptr, /* Pointer into name */
+ *value, /* Pointer to value */
+ *lineptr, /* Pointer in line */
+ *temp; /* Temporary pointer */
+ printer_t *p; /* Current printer */
+
+
+ /*
+ * Open the printer.conf file...
+ */
+
+ sprintf(line, "%s/conf/printers.conf", ServerRoot);
+ if ((fp = fopen(line, "r")) == NULL)
+ return;
+
+ /*
+ * Read printer configurations until we hit EOF...
+ */
+
+ linenum = 0;
+ p = NULL;
+
+ while (fgets(line, sizeof(line), fp) != NULL)
+ {
+ linenum ++;
+
+ /*
+ * Skip comment lines...
+ */
+
+ if (line[0] == '#')
+ continue;
+
+ /*
+ * Strip trailing newline, if any...
+ */
+
+ len = strlen(line);
+
+ if (line[len - 1] == '\n')
+ {
+ len --;
+ line[len] = '\0';
+ }
+
+ /*
+ * Extract the name from the beginning of the line...
+ */
+
+ for (value = line; isspace(*value); value ++);
+
+ for (nameptr = name; *value != '\0' && !isspace(*value);)
+ *nameptr++ = *value++;
+ *nameptr = '\0';
+
+ while (isspace(*value))
+ value ++;
+
+ if (name[0] == '\0')
+ continue;
+
+ /*
+ * Decode the directive...
+ */
+
+ if (strcmp(name, "<Printer") == 0 ||
+ strcmp(name, "<DefaultPrinter") == 0)
+ {
+ /*
+ * <Printer name> or <DefaultPrinter name>
+ */
+
+ if (line[len - 1] == '>' && p == NULL)
+ {
+ /*
+ * Add the printer and a base file type...
+ */
+
+ line[len - 1] = '\0';
+
+ p = AddPrinter(value);
+ p->accepting = 1;
+ p->state = IPP_PRINTER_IDLE;
+
+ /*
+ * Set the default printer as needed...
+ */
+
+ if (strcmp(name, "<DefaultPrinter") == 0)
+ DefaultPrinter = p;
+ }
+ else
+ {
+ LogMessage(LOG_ERROR, "Syntax error on line %d of printers.conf.",
+ linenum);
+ return;
+ }
+ }
+ else if (strcmp(name, "</Printer>") == 0)
+ {
+ if (p != NULL)
+ {
+ SetPrinterAttrs(p);
+ p = NULL;
+ }
+ else
+ {
+ LogMessage(LOG_ERROR, "Syntax error on line %d of printers.conf.",
+ linenum);
+ return;
+ }
+ }
+ else if (p == NULL)
+ {
+ LogMessage(LOG_ERROR, "Syntax error on line %d of printers.conf.",
+ linenum);
+ return;
+ }
+
+ else if (strcmp(name, "Info") == 0)
+ strncpy(p->info, value, sizeof(p->info) - 1);
+ else if (strcmp(name, "MoreInfo") == 0)
+ strncpy(p->more_info, value, sizeof(p->more_info) - 1);
+ else if (strcmp(name, "Location") == 0)
+ strncpy(p->location, value, sizeof(p->location) - 1);
+ else if (strcmp(name, "DeviceURI") == 0)
+ strncpy(p->device_uri, value, sizeof(p->device_uri) - 1);
+ else if (strcmp(name, "State") == 0)
+ {
+ /*
+ * Set the initial queue state...
+ */
+
+ if (strcasecmp(value, "idle") == 0)
+ {
+ p->state = IPP_PRINTER_IDLE;
+ p->accepting = 1;
+ }
+ else if (strcasecmp(value, "stopped") == 0)
+ {
+ p->state = IPP_PRINTER_STOPPED;
+ p->accepting = 0;
+ }
+ }
+ else
+ {
+ /*
+ * Something else we don't understand...
+ */
+
+ LogMessage(LOG_ERROR, "Unknown configuration directive %s on line %d of printers.conf.",
+ name, linenum);
+ }
+ }
+
+ fclose(fp);
+}
+
+
+/*
+ * 'SaveAllPrinters()' - Save all printer definitions to the printers.conf
+ * file.
+ */
+
+void
+SaveAllPrinters(void)
+{
+ FILE *fp; /* printers.conf file */
+ char temp[1024]; /* Temporary string */
+ printer_t *printer; /* Current printer class */
+ int i; /* Looping var */
+ time_t curtime; /* Current time */
+ struct tm *curdate; /* Current date */
+
+
+ /*
+ * Create the printers.conf file...
+ */
+
+ sprintf(temp, "%s/conf/printers.conf", ServerRoot);
+ if ((fp = fopen(temp, "w")) == NULL)
+ {
+ LogMessage(LOG_ERROR, "Unable to save printers.conf - %s", strerror(errno));
+ return;
+ }
+ else
+ LogMessage(LOG_INFO, "Saving printers.conf...");
+
+ /*
+ * Write a small header to the file...
+ */
+
+ curtime = time(NULL);
+ curdate = gmtime(&curtime);
+ strftime(temp, sizeof(temp) - 1, "# Written by cupsd on %c\n", curdate);
+
+ fputs("# Printer configuration file for " CUPS_SVERSION "\n", fp);
+ fputs(temp, fp);
+
+ /*
+ * Write each local printer known to the system...
+ */
+
+ for (printer = Printers; printer != NULL; printer = printer->next)
+ {
+ /*
+ * Skip remote destinations and printer classes...
+ */
+
+ if ((printer->type & CUPS_PRINTER_REMOTE) ||
+ (printer->type & CUPS_PRINTER_CLASS))
+ continue;
+
+ /*
+ * Write printers as needed...
+ */
+
+ if (printer == DefaultPrinter)
+ fprintf(fp, "<DefaultPrinter %s>\n", printer->name);
+ else
+ fprintf(fp, "<Printer %s>\n", printer->name);
+
+ if (printer->info[0])
+ fprintf(fp, "Info %s\n", printer->info);
+ if (printer->more_info[0])
+ fprintf(fp, "MoreInfo %s\n", printer->more_info);
+ if (printer->location[0])
+ fprintf(fp, "Location %s\n", printer->location);
+ if (printer->device_uri[0])
+ fprintf(fp, "DeviceURI %s\n", printer->device_uri);
+ if (printer->state == IPP_PRINTER_STOPPED)
+ fputs("State Stopped\n", fp);
+ else
+ fputs("State Idle\n", fp);
+
+ fputs("</Printer>\n", fp);
+ }
+
+ fclose(fp);
+}
+
+
+/*
+ * 'SetPrinterAttrs()' - Set printer attributes based upon the PPD file.
+ */
+
+void
+SetPrinterAttrs(printer_t *p) /* I - Printer to setup */
+{
+ char uri[HTTP_MAX_URI];/* URI for printer */
+ int i; /* Looping var */
+ char filename[1024]; /* Name of PPD file */
+ int num_media; /* Number of media options */
+ ppd_file_t *ppd; /* PPD file data */
+ ppd_option_t *input_slot, /* InputSlot options */
+ *media_type, /* MediaType options */
+ *page_size; /* PageSize options */
+ ipp_attribute_t *attr; /* Attribute data */
+ ipp_value_t *val; /* Attribute value */
+ int nups[3] = /* number-up-supported values */
+ { 1, 2, 4 };
+ ipp_orient_t orients[4] = /* orientation-requested-supported values */
+ {
+ IPP_PORTRAIT,
+ IPP_LANDSCAPE,
+ IPP_REVERSE_LANDSCAPE,
+ IPP_REVERSE_PORTRAIT
+ };
+ const char *sides[3] = /* sides-supported values */
+ {
+ "one",
+ "two-long-edge",
+ "two-short-edge"
+ };
+ ipp_op_t ops[] = /* operations-supported values */
+ {
+ IPP_PRINT_JOB,
+ IPP_VALIDATE_JOB,
+ IPP_CANCEL_JOB,
+ IPP_GET_JOB_ATTRIBUTES,
+ IPP_GET_JOBS,
+ IPP_GET_PRINTER_ATTRIBUTES,
+ IPP_PAUSE_PRINTER,
+ IPP_RESUME_PRINTER,
+ IPP_PURGE_JOBS,
+ CUPS_GET_DEFAULT,
+ CUPS_GET_PRINTERS,
+ CUPS_ADD_PRINTER,
+ CUPS_DELETE_PRINTER,
+ CUPS_GET_CLASSES,
+ CUPS_ADD_CLASS,
+ CUPS_DELETE_CLASS,
+ CUPS_ACCEPT_JOBS,
+ CUPS_REJECT_JOBS
+ };
+ const char *charsets[] = /* charset-supported values */
+ {
+ "us-ascii",
+ "iso-8859-1",
+ "iso-8859-2",
+ "iso-8859-3",
+ "iso-8859-4",
+ "iso-8859-5",
+ "iso-8859-6",
+ "iso-8859-7",
+ "iso-8859-8",
+ "iso-8859-9",
+ "iso-8859-10",
+ "utf-8"
+ };
+ int num_finishings;
+ ipp_finish_t finishings[5];
+
+
+ DEBUG_printf(("SetPrinterAttrs: entering name = %s, type = %x\n", p->name,
+ p->type));
+
+ /*
+ * Clear out old filters and add a filter from application/vnd.cups-raw to
+ * printer/name to handle "raw" printing by users.
+ */
+
+ DeletePrinterFilters(p);
+ AddPrinterFilter(p, "application/vnd.cups-raw 0 -");
+
+ /*
+ * Create the required IPP attributes for a printer...
+ */
+
+ if (p->attrs)
+ ippDelete(p->attrs);
+
+ p->attrs = ippNew();
+
+ ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-uri-supported",
+ NULL, p->uri);
+ ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
+ "uri-security-supported", NULL, "none");
+ ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_NAME, "printer-name", NULL,
+ p->name);
+ ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-location",
+ NULL, p->location);
+ ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-info",
+ NULL, p->info);
+ ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-more-info",
+ NULL, p->more_info);
+ ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
+ "pdl-override-supported", NULL, "not-attempted");
+ ippAddIntegers(p->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "operations-supported",
+ sizeof(ops) / sizeof(ops[0]), (int *)ops);
+ ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_CHARSET, "charset-configured",
+ NULL, DefaultCharset);
+ ippAddStrings(p->attrs, IPP_TAG_PRINTER, IPP_TAG_CHARSET, "charset-supported",
+ sizeof(charsets) / sizeof(charsets[0]), NULL, charsets);
+ ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_LANGUAGE,
+ "natural-language-configured", NULL, DefaultLanguage);
+ ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_LANGUAGE,
+ "generated-natural-language-supported", NULL, DefaultLanguage);
+ ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_MIMETYPE,
+ "document-format-default", NULL, "application/octet-stream");
+ ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_MIMETYPE,
+ "document-format-supported", NULL, "application/octet-stream");
+ ippAddInteger(p->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
+ "job-priority-supported", 100);
+ ippAddInteger(p->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
+ "job-priority-default", 50);
+ ippAddRange(p->attrs, IPP_TAG_PRINTER, "copies-supported", 1, 100);
+ ippAddInteger(p->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
+ "copies-default", 1);
+ ippAddBoolean(p->attrs, IPP_TAG_PRINTER, "page-ranges-supported", 1);
+ ippAddIntegers(p->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
+ "number-up-supported", 3, nups);
+ ippAddInteger(p->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
+ "number-up-default", 1);
+ ippAddIntegers(p->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM,
+ "orientation-requested-supported", 4, (int *)orients);
+ ippAddInteger(p->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM,
+ "orientation-requested-default", IPP_PORTRAIT);
+
+ if (!(p->type & CUPS_PRINTER_REMOTE))
+ {
+ /*
+ * Assign additional attributes depending on whether this is a printer
+ * or class...
+ */
+
+ p->type &= ~CUPS_PRINTER_OPTIONS;
+
+ if (p->type & CUPS_PRINTER_CLASS)
+ {
+ /*
+ * Add class-specific attributes...
+ */
+
+ if (p->num_printers > 0)
+ {
+ /*
+ * Add a list of member URIs and names...
+ */
+
+ attr = ippAddStrings(p->attrs, IPP_TAG_PRINTER, IPP_TAG_URI,
+ "member-uris", p->num_printers, NULL, NULL);
+
+ p->type |= CUPS_PRINTER_OPTIONS;
+
+ for (i = 0; i < p->num_printers; i ++)
+ {
+ attr->values[i].string.text = strdup(p->printers[i]->uri);
+
+ p->type &= ~CUPS_PRINTER_OPTIONS | p->printers[i]->type;
+ }
+
+ attr = ippAddStrings(p->attrs, IPP_TAG_PRINTER, IPP_TAG_NAME,
+ "member-names", p->num_printers, NULL, NULL);
+
+ for (i = 0; i < p->num_printers; i ++)
+ attr->values[i].string.text = strdup(p->printers[i]->name);
+ }
+ }
+ else
+ {
+ /*
+ * Add printer-specific attributes...
+ */
+
+ ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_URI, "device-uri", NULL,
+ p->device_uri);
+
+ /*
+ * Assign additional attributes from the PPD file (if any)...
+ */
+
+ p->type |= CUPS_PRINTER_BW;
+ finishings[0] = IPP_FINISH_NONE;
+ num_finishings = 1;
+
+ sprintf(filename, "%s/ppd/%s.ppd", ServerRoot, p->name);
+ if ((ppd = ppdOpenFile(filename)) != NULL)
+ {
+ /*
+ * Add make/model and other various attributes...
+ */
+
+ if (ppd->color_device)
+ p->type |= CUPS_PRINTER_COLOR;
+ if (ppd->variable_sizes)
+ p->type |= CUPS_PRINTER_VARIABLE;
+ if (!ppd->manual_copies)
+ p->type |= CUPS_PRINTER_COPIES;
+
+ ippAddBoolean(p->attrs, IPP_TAG_PRINTER, "color-supported",
+ ppd->color_device);
+ ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT,
+ "printer-make-and-model", NULL, ppd->nickname);
+
+ /*
+ * Add media options from the PPD file...
+ */
+
+ if ((input_slot = ppdFindOption(ppd, "InputSlot")) != NULL)
+ num_media = input_slot->num_choices;
+ else
+ num_media = 0;
+
+ if ((media_type = ppdFindOption(ppd, "MediaType")) != NULL)
+ num_media += media_type->num_choices;
+
+ if ((page_size = ppdFindOption(ppd, "PageSize")) != NULL)
+ num_media += page_size->num_choices;
+
+ attr = ippAddStrings(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
+ "media-supported", num_media, NULL, NULL);
+ val = attr->values;
+
+ if (input_slot != NULL)
+ for (i = 0; i < input_slot->num_choices; i ++, val ++)
+ val->string.text = strdup(input_slot->choices[i].choice);
+
+ if (media_type != NULL)
+ for (i = 0; i < media_type->num_choices; i ++, val ++)
+ val->string.text = strdup(media_type->choices[i].choice);
+
+ if (page_size != NULL)
+ for (i = 0; i < page_size->num_choices; i ++, val ++)
+ val->string.text = strdup(page_size->choices[i].choice);
+
+ ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-default",
+ NULL, page_size->defchoice);
+
+ if (ppdFindOption(ppd, "Duplex") != NULL)
+ {
+ p->type |= CUPS_PRINTER_DUPLEX;
+
+ ippAddStrings(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "sides-supported",
+ 3, NULL, sides);
+ ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "sides-default",
+ NULL, "one");
+ }
+
+ if (ppdFindOption(ppd, "Collate") != NULL)
+ p->type |= CUPS_PRINTER_COLLATE;
+
+ if (ppdFindOption(ppd, "StapleLocation") != NULL)
+ {
+ p->type |= CUPS_PRINTER_STAPLE;
+ finishings[num_finishings++] = IPP_FINISH_STAPLE;
+ }
+
+ if (ppdFindOption(ppd, "BindEdge") != NULL)
+ {
+ p->type |= CUPS_PRINTER_BIND;
+ finishings[num_finishings++] = IPP_FINISH_BIND;
+ }
+
+ for (i = 0; i < ppd->num_sizes; i ++)
+ if (ppd->sizes[i].length > 1728)
+ p->type |= CUPS_PRINTER_LARGE;
+ else if (ppd->sizes[i].length > 1008)
+ p->type |= CUPS_PRINTER_MEDIUM;
+ else
+ p->type |= CUPS_PRINTER_SMALL;
+
+ /*
+ * Add any filters in the PPD file...
+ */
+
+ DEBUG_printf(("ppd->num_filters = %d\n", ppd->num_filters));
+ for (i = 0; i < ppd->num_filters; i ++)
+ {
+ DEBUG_printf(("ppd->filters[%d] = \"%s\"\n", i, ppd->filters[i]));
+ AddPrinterFilter(p, ppd->filters[i]);
+ }
+
+ if (ppd->num_filters == 0)
+ AddPrinterFilter(p, "application/vnd.cups-postscript 0 -");
+
+ ppdClose(ppd);
+ }
+ else if (access(filename, 0) == 0)
+ {
+ LogMessage(LOG_ERROR, "PPD file for %s cannot be loaded!", p->name);
+
+ AddPrinterFilter(p, "application/vnd.cups-postscript 0 -");
+ }
+ else
+ {
+ /*
+ * If we have an interface script, add a filter entry for it...
+ */
+
+ sprintf(filename, "%s/interfaces/%s", ServerRoot, p->name);
+ if (access(filename, X_OK) == 0)
+ {
+ /*
+ * Yes, we have a System V style interface script; use it!
+ */
+
+ sprintf(filename, "*/* 0 %s/interfaces/%s", ServerRoot, p->name);
+ AddPrinterFilter(p, filename);
+ }
+ else
+ {
+ /*
+ * Otherwise we have neither - treat this as a "generic" PostScript
+ * printer with no PPD file...
+ */
+
+ AddPrinterFilter(p, "application/vnd.cups-postscript 0 -");
+ }
+ }
+
+ ippAddIntegers(p->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM,
+ "finishings-supported", num_finishings, (int *)finishings);
+ ippAddInteger(p->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM,
+ "finishings-default", IPP_FINISH_NONE);
+ }
+ }
+
+ /*
+ * Add the CUPS-specific printer-type attribute...
+ */
+
+ ippAddInteger(p->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "printer-type", p->type);
+
+ DEBUG_printf(("SetPrinterAttrs: leaving name = %s, type = %x\n", p->name,
+ p->type));
+}
+
+
+/*
+ * 'SetPrinterState()' - Update the current state of a printer.
+ */
+
+void
+SetPrinterState(printer_t *p, /* I - Printer to change */
+ ipp_pstate_t s) /* I - New state */
+{
+ ipp_pstate_t old_state; /* Old printer state */
+
+
+ /*
+ * Can't set status of remote printers...
+ */
+
+ if (p->type & CUPS_PRINTER_REMOTE)
+ return;
+
+ /*
+ * Set the new state...
+ */
+
+ old_state = p->state;
+ p->state = s;
+ p->state_time = time(NULL);
+
+ if (old_state != s)
+ p->browse_time = 0;
+
+ /*
+ * Save the printer configuration if a printer goes from idle or processing
+ * to stopped (or visa-versa)...
+ */
+
+ if ((old_state == IPP_PRINTER_STOPPED) != (s == IPP_PRINTER_STOPPED))
+ SaveAllPrinters();
+
+ /*
+ * Check to see if any pending jobs can now be printed...
+ */
+
+ CheckJobs();
+}
+
+
+/*
+ * 'SortPrinters()' - Sort the printer list when a printer name is changed.
+ */
+
+void
+SortPrinters(void)
+{
+ printer_t *current, /* Current printer */
+ *start, /* Starting printer */
+ *prev, /* Previous printer */
+ *next; /* Next printer */
+ int did_swap; /* Non-zero if we did a swap */
+
+
+ do
+ {
+ for (did_swap = 0, current = Printers, prev = NULL; current != NULL;)
+ if (current->next == NULL)
+ break;
+ else if (strcasecmp(current->name, current->next->name) > 0)
+ {
+ DEBUG_printf(("Swapping %s and %s...\n", current->name,
+ current->next->name));
+
+ /*
+ * Need to swap these two printers...
+ */
+
+ did_swap = 1;
+
+ if (prev == NULL)
+ Printers = current->next;
+ else
+ prev->next = current->next;
+
+ /*
+ * Yes, we can all get a headache from the next bunch of pointer
+ * swapping...
+ */
+
+ next = current->next;
+ current->next = next->next;
+ next->next = current;
+ }
+ else
+ current = current->next;
+ }
+ while (did_swap);
+}
+
+
+/*
+ * 'StopPrinter()' - Stop a printer from printing any jobs...
+ */
+
+void
+StopPrinter(printer_t *p) /* I - Printer to stop */
+{
+ if (p->job)
+ StopJob(((job_t *)p->job)->id);
+
+ p->state = IPP_PRINTER_STOPPED;
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/scheduler/printers.h b/scheduler/printers.h
new file mode 100644
index 000000000..296567555
--- /dev/null
+++ b/scheduler/printers.h
@@ -0,0 +1,81 @@
+/*
+ * "$Id$"
+ *
+ * Printer definitions for the Common UNIX Printing System (CUPS) scheduler.
+ *
+ * Copyright 1997-1999 by Easy Software Products, all rights reserved.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ */
+
+/*
+ * Printer/class information structure...
+ */
+
+typedef struct printer_str
+{
+ struct printer_str *next; /* Next printer in list */
+ char uri[HTTP_MAX_URI], /* Printer URI */
+ hostname[HTTP_MAX_HOST],/* Host printer resides on */
+ name[IPP_MAX_NAME], /* Printer name */
+ location[IPP_MAX_NAME], /* Location code */
+ info[IPP_MAX_NAME], /* Description */
+ more_info[HTTP_MAX_URI];/* URL for site-specific info */
+ int accepting; /* Accepting jobs? */
+ ipp_pstate_t state; /* Printer state */
+ char state_message[1024]; /* Printer state message */
+ time_t state_time; /* Time at this state */
+ cups_ptype_t type; /* Printer type (color, small, etc.) */
+ time_t browse_time; /* Last time update was sent/received */
+ char device_uri[HTTP_MAX_URI],/* Device URI */
+ backend[1024]; /* Backend to use */
+ mime_type_t *filetype; /* Pseudo-filetype for printer */
+ void *job; /* Current job in queue */
+ ipp_t *attrs; /* Attributes supported by this printer */
+ int num_printers; /* Number of printers in class */
+ struct printer_str **printers; /* Printers in class */
+} printer_t;
+
+
+/*
+ * Globals...
+ */
+
+VAR printer_t *Printers VALUE(NULL); /* Printer list */
+VAR printer_t *DefaultPrinter VALUE(NULL);
+ /* Default printer */
+
+/*
+ * Prototypes...
+ */
+
+extern printer_t *AddPrinter(char *name);
+extern void DeleteAllPrinters(void);
+extern void DeletePrinter(printer_t *p);
+extern printer_t *FindPrinter(char *name);
+extern void LoadAllPrinters(void);
+extern void SaveAllPrinters(void);
+extern void SetPrinterAttrs(printer_t *p);
+extern void SetPrinterState(printer_t *p, ipp_pstate_t s);
+extern void SortPrinters(void);
+#define StartPrinter(p) SetPrinterState((p), IPP_PRINTER_IDLE)
+extern void StopPrinter(printer_t *p);
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/scheduler/testspeed.c b/scheduler/testspeed.c
new file mode 100644
index 000000000..060568c0c
--- /dev/null
+++ b/scheduler/testspeed.c
@@ -0,0 +1,126 @@
+/*
+ * "$Id$"
+ *
+ * Scheduler speed test for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-1999 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * Contents:
+ *
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <sys/time.h>
+#include <cups/cups.h>
+#include <cups/language.h>
+#include <cups/debug.h>
+
+
+/*
+ * 'main()' - Send multiple IPP requests and report on the average response
+ * time.
+ */
+
+int
+main(int argc, /* I - Number of command-line arguments */
+ char *argv[]) /* I - Command-line arguments */
+{
+ int i; /* Looping var */
+ http_t *http; /* Connection to server */
+ ipp_t *request, /* IPP Request */
+ *response; /* IPP Response */
+ cups_lang_t *language; /* Default language */
+ struct timeval start, /* Start time */
+ end; /* End time */
+ double elapsed; /* Elapsed time */
+
+
+ if (argc > 1)
+ http = httpConnect(argv[1], ippPort());
+ else
+ http = httpConnect("localhost", ippPort());
+
+ if (http == NULL)
+ {
+ perror("testspeed: unable to connect to server");
+ return (1);
+ }
+
+ language = cupsLangDefault();
+
+ /*
+ * Do requests 100 times...
+ */
+
+ printf("Testing: ");
+
+ for (elapsed = 0.0, i = 0; i < 100; i ++)
+ {
+ putchar('>');
+ fflush(stdout);
+
+ /*
+ * Build a CUPS_GET_PRINTERS request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = CUPS_GET_PRINTERS;
+ request->request.op.request_id = 1;
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, cupsLangEncoding(language));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ gettimeofday(&start, NULL);
+ response = cupsDoRequest(http, request, "/printers/");
+ gettimeofday(&end, NULL);
+
+ putchar('<');
+
+ if (response != NULL)
+ ippDelete(response);
+
+ elapsed += (end.tv_sec - start.tv_sec) +
+ 0.000001 * (end.tv_usec - start.tv_usec);
+ }
+
+ puts("");
+ printf("Total elapsed time for %d requests was %.1fs (%.3fs/r)\n",
+ i, elapsed, elapsed / i);
+
+ return (0);
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/systemv/Makefile b/systemv/Makefile
new file mode 100644
index 000000000..b48518c81
--- /dev/null
+++ b/systemv/Makefile
@@ -0,0 +1,114 @@
+#
+# "$Id$"
+#
+# System V commands makefile for the Common UNIX Printing System (CUPS).
+#
+# Copyright 1997-1999 by Easy Software Products, all rights reserved.
+#
+# These coded instructions, statements, and computer programs are the
+# property of Easy Software Products and are protected by Federal
+# copyright law. Distribution and use rights are outlined in the file
+# "LICENSE.txt" which should have been included with this file. If this
+# file is missing or damaged please contact Easy Software Products
+# at:
+#
+# Attn: CUPS Licensing Information
+# Easy Software Products
+# 44141 Airport View Drive, Suite 204
+# Hollywood, Maryland 20636-3111 USA
+#
+# Voice: (301) 373-9603
+# EMail: cups-info@cups.org
+# WWW: http://www.cups.org
+#
+
+include ../Makedefs
+
+TARGETS = accept cancel lp lpadmin lpstat
+OBJS = accept.o cancel.o lp.o lpadmin.o lpstat.o
+
+#
+# Make all targets...
+#
+
+all: $(TARGETS)
+
+#
+# Clean all object files...
+#
+
+clean:
+ rm -f $(OBJS) $(TARGETS)
+
+#
+# Install all targets...
+#
+
+install:
+ -$(MKDIR) $(BINDIR)
+ -$(MKDIR) $(LIBDIR)
+ $(CP) accept lpadmin $(SBINDIR)
+ -$(LN) accept $(SBINDIR)/reject
+ -$(LN) $(SBINDIR)/lpadmin $(LIBDIR)
+ -$(LN) $(SBINDIR)/accept $(LIBDIR)
+ -$(LN) $(SBINDIR)/accept $(LIBDIR)/reject
+ -$(LN) $(SBINDIR)/accept $(BINDIR)/disable
+ -$(LN) $(SBINDIR)/accept $(BINDIR)/enable
+ $(CP) cancel lp lpstat $(BINDIR)
+
+#
+# accept
+#
+
+accept: accept.o ../cups/$(LIBCUPS)
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o accept accept.o $(LIBS)
+ -$(LN) accept disable
+ -$(LN) accept enable
+ -$(LN) accept reject
+
+accept.o: ../cups/cups.h ../Makedefs
+
+#
+# cancel
+#
+
+cancel: cancel.o ../cups/$(LIBCUPS)
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o cancel cancel.o $(LIBS)
+
+cancel.o: ../cups/cups.h ../Makedefs
+
+#
+# lp
+#
+
+lp: lp.o ../cups/$(LIBCUPS)
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o lp lp.o $(LIBS)
+
+lp.o: ../cups/cups.h ../Makedefs
+
+#
+# lpadmin
+#
+
+lpadmin: lpadmin.o ../cups/$(LIBCUPS)
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o lpadmin lpadmin.o $(LIBZ) $(LIBS)
+
+lpadmin.o: ../cups/cups.h ../Makedefs
+
+#
+# lpstat
+#
+
+lpstat: lpstat.o ../cups/$(LIBCUPS)
+ echo Linking $@...
+ $(CC) $(LDFLAGS) -o lpstat lpstat.o $(LIBS)
+
+lpstat.o: ../cups/cups.h ../Makedefs
+
+#
+# End of "$Id$".
+#
diff --git a/systemv/accept.c b/systemv/accept.c
new file mode 100644
index 000000000..6741f2727
--- /dev/null
+++ b/systemv/accept.c
@@ -0,0 +1,218 @@
+/*
+ * "$Id$"
+ *
+ * "accept", "disable", "enable", and "reject" commands for the Common
+ * UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-1999 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * Contents:
+ *
+ * main() - Parse options and accept/reject jobs or disable/enable printers.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#include <cups/cups.h>
+#include <cups/language.h>
+
+
+/*
+ * 'main()' - Parse options and accept/reject jobs or disable/enable printers.
+ */
+
+int /* O - Exit status */
+main(int argc, /* I - Number of command-line arguments */
+ char *argv[]) /* I - Command-line arguments */
+{
+ http_t *http; /* HTTP connection to server */
+ int i; /* Looping var */
+ char *command, /* Command to do */
+ hostname[HTTP_MAX_URI],
+ /* Name of host */
+ printer[HTTP_MAX_URI],
+ /* Name of printer or class */
+ uri[1024], /* Printer URI */
+ *reason; /* Reason for reject/disable */
+ ipp_t *request; /* IPP request */
+ ipp_t *response; /* IPP response */
+ ipp_op_t op; /* Operation */
+ cups_lang_t *language; /* Language */
+
+
+ /*
+ * See what operation we're supposed to do...
+ */
+
+ if ((command = strrchr(argv[0], '/')) != NULL)
+ command ++;
+ else
+ command = argv[0];
+
+ if (strcmp(command, "accept") == 0)
+ op = CUPS_ACCEPT_JOBS;
+ else if (strcmp(command, "reject") == 0)
+ op = CUPS_REJECT_JOBS;
+ else if (strcmp(command, "disable") == 0)
+ op = IPP_PAUSE_PRINTER;
+ else if (strcmp(command, "enable") == 0)
+ op = IPP_RESUME_PRINTER;
+ else
+ {
+ fprintf(stderr, "%s: Don't know what to do!\n", command);
+ return (1);
+ }
+
+ http = NULL;
+ reason = NULL;
+
+ /*
+ * Process command-line arguments...
+ */
+
+ for (i = 1; i < argc; i ++)
+ if (argv[i][0] == '-')
+ switch (argv[i][1])
+ {
+ case 'h' : /* Connect to host */
+ if (http != NULL)
+ httpClose(http);
+
+ if (argv[i][2] != '\0')
+ http = httpConnect(argv[i] + 2, ippPort());
+ else
+ {
+ i ++;
+ http = httpConnect(argv[i], ippPort());
+ }
+
+ if (http == NULL)
+ {
+ fputs(argv[0], stderr);
+ perror(": Unable to connect to server");
+ return (1);
+ }
+ break;
+
+ case 'r' : /* Reason for cancellation */
+ if (argv[i][2] != '\0')
+ reason = argv[i] + 2;
+ else
+ {
+ i ++;
+ if (i >= argc)
+ {
+ fprintf(stderr, "%s: Expected reason text after -r!\n", command);
+ return (1);
+ }
+
+ reason = argv[i];
+ }
+ break;
+
+ default :
+ fprintf(stderr, "%s: Unknown option \'%c\'!\n", command,
+ argv[i][1]);
+ return (1);
+ }
+ else
+ {
+ /*
+ * Accept/disable/enable/reject a destination...
+ */
+
+ if (sscanf(argv[i], "%[^@]@%s", printer, hostname) == 1)
+ strcpy(hostname, "localhost");
+
+ if (http != NULL && strcasecmp(http->hostname, hostname) != 0)
+ {
+ httpClose(http);
+ http = NULL;
+ }
+
+ if (http == NULL)
+ http = httpConnect(hostname, ippPort());
+
+ if (http == NULL)
+ {
+ fprintf(stderr, "%s: Unable to contact server at %s!\n", command,
+ hostname);
+ return (1);
+ }
+
+ /*
+ * Build an IPP request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * printer-uri
+ * printer-state-message [optional]
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = op;
+ request->request.op.request_id = 1;
+
+ language = cupsLangDefault();
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, cupsLangEncoding(language));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ sprintf(uri, "ipp://%s:%d/printers/%s", hostname, ippPort(), printer);
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
+ "printer-uri", NULL, uri);
+
+ if (reason != NULL)
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_TEXT,
+ "printer-state-message", NULL, reason);
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ if ((response = cupsDoRequest(http, request, "/admin/")) != NULL)
+ ippDelete(response);
+ else
+ {
+ fprintf(stderr, "%s: Operation failed!\n", command);
+ return (1);
+ }
+ }
+
+ if (http != NULL)
+ httpClose(http);
+
+ return (0);
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/systemv/cancel.c b/systemv/cancel.c
new file mode 100644
index 000000000..cf37f1c63
--- /dev/null
+++ b/systemv/cancel.c
@@ -0,0 +1,220 @@
+/*
+ * "$Id$"
+ *
+ * "cancel" command for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-1999 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * Contents:
+ *
+ * main() - Parse options and cancel jobs.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#include <cups/cups.h>
+#include <cups/language.h>
+
+
+/*
+ * 'main()' - Parse options and cancel jobs.
+ */
+
+int /* O - Exit status */
+main(int argc, /* I - Number of command-line arguments */
+ char *argv[]) /* I - Command-line arguments */
+{
+ http_t *http; /* HTTP connection to server */
+ int i; /* Looping var */
+ int job_id; /* Job ID */
+ char *dest, /* Destination printer */
+ *host; /* Host name */
+ char name[255]; /* Printer name */
+ char uri[1024]; /* Printer or job URI */
+ ipp_t *request; /* IPP request */
+ ipp_t *response; /* IPP response */
+ ipp_op_t op; /* Operation */
+ cups_lang_t *language; /* Language */
+
+
+ /*
+ * Setup to cancel individual print jobs...
+ */
+
+ op = IPP_CANCEL_JOB;
+ job_id = 0;
+ dest = NULL;
+
+ /*
+ * Open a connection to the server...
+ */
+
+ if ((http = httpConnect(cupsServer(), ippPort())) == NULL)
+ {
+ fputs("cancel: Unable to contact server!\n", stderr);
+ return (1);
+ }
+
+ /*
+ * Process command-line arguments...
+ */
+
+ for (i = 1; i < argc; i ++)
+ if (argv[i][0] == '-')
+ switch (argv[i][1])
+ {
+ case 'a' : /* Cancel all jobs */
+ op = IPP_PURGE_JOBS;
+ break;
+
+ case 'h' : /* Connect to host */
+ httpClose(http);
+
+ if (argv[i][2] != '\0')
+ http = httpConnect(argv[i] + 2, ippPort());
+ else
+ {
+ i ++;
+ http = httpConnect(argv[i], ippPort());
+ }
+
+ if (http == NULL)
+ {
+ perror("cancel: Unable to connect to server");
+ return (1);
+ }
+ break;
+
+ default :
+ fprintf(stderr, "cancel: Unknown option \'%c\'!\n", argv[i][1]);
+ return (1);
+ }
+ else
+ {
+ /*
+ * Cancel a job or printer...
+ */
+
+ if (isdigit(argv[i][0]))
+ {
+ dest = NULL;
+ op = IPP_CANCEL_JOB;
+ job_id = atoi(argv[i]);
+ }
+ else
+ {
+ dest = name;
+ job_id = 0;
+ sscanf(argv[i], "%[^-]-%d", name, &job_id);
+ if (job_id)
+ op = IPP_CANCEL_JOB;
+
+ if ((host = strchr(name, '@')) != NULL)
+ {
+ /*
+ * Reconnect to the named host...
+ */
+
+ httpClose(http);
+
+ *host++ = '\0';
+
+ if ((http = httpConnect(host, ippPort())) == NULL)
+ {
+ perror("cancel: Unable to connect to server");
+ return (1);
+ }
+ }
+ }
+
+ /*
+ * Build an IPP request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * printer-uri + job-id *or* job-uri
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = op;
+ request->request.op.request_id = 1;
+
+ language = cupsLangDefault();
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, cupsLangEncoding(language));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ if (dest)
+ {
+ sprintf(uri, "ipp://localhost/printers/%s", dest);
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
+ "printer-uri", NULL, uri);
+ ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id",
+ job_id);
+ }
+ else
+ {
+ sprintf(uri, "ipp://localhost/jobs/%d", job_id);
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", NULL,
+ uri);
+ }
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ if (op == IPP_PURGE_JOBS)
+ response = cupsDoRequest(http, request, "/admin/");
+ else
+ response = cupsDoRequest(http, request, "/jobs/");
+
+ if (response != NULL)
+ {
+ if (response->request.status.status_code == IPP_NOT_FOUND)
+ fputs("cancel: Job or printer not found!\n", stderr);
+ else if (response->request.status.status_code > IPP_OK_CONFLICT)
+ fputs("cancel: Unable to cancel job(s)!\n", stderr);
+
+ ippDelete(response);
+ }
+ else
+ {
+ fputs("cancel: Unable to cancel job(s)!\n", stderr);
+ return (1);
+ }
+ }
+
+ return (0);
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/systemv/lp.c b/systemv/lp.c
new file mode 100644
index 000000000..4b4829676
--- /dev/null
+++ b/systemv/lp.c
@@ -0,0 +1,246 @@
+/*
+ * "$Id$"
+ *
+ * "lp" command for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-1999 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * Contents:
+ *
+ * main() - Parse options and send files for printing.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <cups/cups.h>
+
+
+/*
+ * 'main()' - Parse options and send files for printing.
+ */
+
+int
+main(int argc, /* I - Number of command-line arguments */
+ char *argv[]) /* I - Command-line arguments */
+{
+ int i; /* Looping var */
+ int job_id; /* Job ID */
+ const char *dest; /* Destination printer */
+ char *title; /* Job title */
+ int priority; /* Job priority (1-100) */
+ int num_copies; /* Number of copies per file */
+ int num_files; /* Number of files printed */
+ int num_options; /* Number of options */
+ cups_option_t *options; /* Options */
+ int silent; /* Silent or verbose output? */
+ char tempfile[1024]; /* Temporary file for printing from stdin */
+ char buffer[8192]; /* Copy buffer */
+ FILE *temp; /* Temporary file pointer */
+
+
+ silent = 0;
+ dest = cupsGetDefault();
+ num_options = 0;
+ options = NULL;
+ num_files = 0;
+ title = NULL;
+
+ for (i = 1; i < argc; i ++)
+ if (argv[i][0] == '-')
+ switch (argv[i][1])
+ {
+ case 'c' : /* Copy to spool dir (always enabled) */
+ break;
+
+ case 'd' : /* Destination printer or class */
+ if (argv[i][2] != '\0')
+ dest = argv[i] + 2;
+ else
+ {
+ i ++;
+ dest = argv[i];
+ }
+ break;
+
+ case 'm' : /* Send email when job is done */
+ case 'w' : /* Write to console or email */
+ break;
+
+ case 'n' : /* Number of copies */
+ if (argv[i][2] != '\0')
+ num_copies = atoi(argv[i] + 2);
+ else
+ {
+ i ++;
+ num_copies = atoi(argv[i]);
+ }
+
+ if (num_copies < 1 || num_copies > 100)
+ {
+ fputs("lp: Number copies must be between 1 and 100.\n", stderr);
+ return (1);
+ }
+
+ sprintf(buffer, "%d", num_copies);
+ num_options = cupsAddOption("copies", buffer, num_options, &options);
+ break;
+
+ case 'o' : /* Option */
+ if (argv[i][2] != '\0')
+ num_options = cupsParseOptions(argv[i] + 2, num_options, &options);
+ else
+ {
+ i ++;
+ num_options = cupsParseOptions(argv[i], num_options, &options);
+ }
+ break;
+
+ case 'p' : /* Queue priority */
+ case 'q' : /* Queue priority */
+ if (argv[i][2] != '\0')
+ priority = atoi(argv[i] + 2);
+ else
+ {
+ i ++;
+ priority = atoi(argv[i]);
+ }
+
+ if (priority < 1 || priority > 100)
+ {
+ fputs("lp: Priority must be between 1 and 100.\n", stderr);
+ return (1);
+ }
+
+ sprintf(buffer, "%d", priority);
+ num_options = cupsAddOption("job-priority", buffer, num_options, &options);
+ break;
+
+ case 's' : /* Silent */
+ silent = 1;
+ break;
+
+ case 't' : /* Title */
+ if (argv[i][2] != '\0')
+ title = argv[i] + 2;
+ else
+ {
+ i ++;
+ title = argv[i];
+ }
+ break;
+
+ default :
+ fprintf(stderr, "lp: Unknown option \'%c\'!\n", argv[i][1]);
+ return (1);
+ }
+ else
+ {
+ /*
+ * Print a file...
+ */
+
+ if (dest == NULL)
+ {
+ fputs("lp: error - no default destination available.\n", stderr);
+ return (1);
+ }
+
+ num_files ++;
+ if (title)
+ job_id = cupsPrintFile(dest, argv[i], title, num_options, options);
+ else
+ {
+ char *filename;
+
+ if ((filename = strrchr(argv[i], '/')) != NULL)
+ filename ++;
+ else
+ filename = argv[i];
+
+ job_id = cupsPrintFile(dest, argv[i], filename, num_options, options);
+ }
+
+ if (job_id < 1)
+ {
+ fprintf(stderr, "lp: unable to print file \'%s\'.\n", argv[i]);
+ return (1);
+ }
+ else if (!silent)
+ fprintf(stderr, "request id is %s-%d (1 file(s))\n", dest, job_id);
+ }
+
+ /*
+ * See if we printed anything; if not, print from stdin...
+ */
+
+ if (num_files == 0)
+ {
+ if (dest == NULL)
+ {
+ fputs("lp: error - no default destination available.\n", stderr);
+ return (1);
+ }
+
+ temp = fopen(cupsTempFile(tempfile, sizeof(tempfile)), "w");
+
+ if (temp == NULL)
+ {
+ fputs("lp: unable to create temporary file.\n", stderr);
+ return (1);
+ }
+
+ while ((i = fread(buffer, 1, sizeof(buffer), stdin)) > 0)
+ fwrite(buffer, 1, i, temp);
+
+ i = ftell(temp);
+ fclose(temp);
+
+ if (i == 0)
+ {
+ fputs("lp: stdin is empty, so no job has been sent.\n", stderr);
+ return (1);
+ }
+
+ if (title)
+ job_id = cupsPrintFile(dest, tempfile, title, num_options, options);
+ else
+ job_id = cupsPrintFile(dest, tempfile, "(stdin)", num_options, options);
+
+ unlink(tempfile);
+
+ if (job_id < 1)
+ {
+ fputs("lp: unable to print stdin.\n", stderr);
+ return (1);
+ }
+ else if (!silent)
+ fprintf(stderr, "request id is %s-%d (1 file(s))\n", dest, job_id);
+ }
+
+ return (0);
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/systemv/lpadmin.c b/systemv/lpadmin.c
new file mode 100644
index 000000000..0617c8731
--- /dev/null
+++ b/systemv/lpadmin.c
@@ -0,0 +1,1147 @@
+/*
+ * "$Id$"
+ *
+ * "lpadmin" command for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-1999 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * Contents:
+ *
+ * main() - Parse options and configure the scheduler.
+ * add_printer_to_class() - Add a printer to a class.
+ * default_printer() - Set the default printing destination.
+ * delete_printer() - Delete a printer from the system...
+ * delete_printer_from_class() - Delete a printer from a class.
+ * enable_printer() - Enable a printer...
+ * set_printer_device() - Set the device-uri attribute.
+ * set_printer_file() - Set the interface script or PPD file.
+ * set_printer_info() - Set the printer description string.
+ * set_printer_location() - Set the printer location string.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <cups/cups.h>
+#include <cups/language.h>
+#include <cups/debug.h>
+#include <config.h>
+#ifdef HAVE_LIBZ
+# include <zlib.h>
+#endif /* HAVE_LIBZ */
+
+
+/*
+ * Local functions...
+ */
+
+static void add_printer_to_class(http_t *, char *, char *);
+static void default_printer(http_t *, char *);
+static void delete_printer(http_t *, char *);
+static void delete_printer_from_class(http_t *, char *, char *);
+static void enable_printer(http_t *, char *);
+static void set_printer_device(http_t *, char *, char *);
+static void set_printer_file(http_t *, char *, char *);
+static void set_printer_info(http_t *, char *, char *);
+static void set_printer_location(http_t *, char *, char *);
+
+
+/*
+ * 'main()' - Parse options and configure the scheduler.
+ */
+
+int
+main(int argc, /* I - Number of command-line arguments */
+ char *argv[]) /* I - Command-line arguments */
+{
+ int i; /* Looping var */
+ http_t *http; /* Connection to server */
+ char *printer, /* Destination printer */
+ *host, /* Pointer to hostname */
+ filename[1024]; /* Model filename */
+
+
+ if ((http = httpConnect(cupsServer(), ippPort())) == NULL)
+ {
+ fputs("lpadmin: Unable to contact server!\n", stderr);
+ return (1);
+ }
+
+ printer = NULL;
+
+ for (i = 1; i < argc; i ++)
+ if (argv[i][0] == '-')
+ switch (argv[i][1])
+ {
+ case 'c' : /* Add printer to class */
+ if (printer == NULL)
+ {
+ fputs("lpadmin: Unable to add a printer to the class:\n", stderr);
+ fputs(" You must specify a printer name first!\n", stderr);
+ return (1);
+ }
+
+ if (argv[i][2])
+ add_printer_to_class(http, printer, argv[i] + 2);
+ else
+ {
+ i ++;
+ add_printer_to_class(http, printer, argv[i]);
+ }
+ break;
+
+ case 'd' : /* Set as default destination */
+ if (argv[i][2])
+ printer = argv[i] + 2;
+ else
+ {
+ i ++;
+ printer = argv[i];
+ }
+
+ default_printer(http, printer);
+ i = argc;
+ break;
+
+ case 'h' : /* Connect to host */
+ httpClose(http);
+
+ if (argv[i][2] != '\0')
+ http = httpConnect(argv[i] + 2, ippPort());
+ else
+ {
+ i ++;
+ http = httpConnect(argv[i], ippPort());
+ }
+
+ if (http == NULL)
+ {
+ perror("lpadmin: Unable to connect to server");
+ return (1);
+ }
+ break;
+
+ case 'i' : /* Use the specified interface script */
+ if (printer == NULL)
+ {
+ fputs("lpadmin: Unable to set the interface script:\n", stderr);
+ fputs(" You must specify a printer name first!\n", stderr);
+ return (1);
+ }
+
+ if (argv[i][2])
+ set_printer_file(http, printer, argv[i] + 2);
+ else
+ {
+ i ++;
+ set_printer_file(http, printer, argv[i]);
+ }
+ break;
+
+ case 'E' : /* Enable the printer */
+ if (printer == NULL)
+ {
+ fputs("lpadmin: Unable to enable the printer:\n", stderr);
+ fputs(" You must specify a printer name first!\n", stderr);
+ return (1);
+ }
+
+ enable_printer(http, printer);
+ break;
+
+ case 'm' : /* Use the specified standard script/PPD file */
+ if (printer == NULL)
+ {
+ fputs("lpadmin: Unable to set the interface script or PPD file:\n", stderr);
+ fputs(" You must specify a printer name first!\n", stderr);
+ return (1);
+ }
+
+ if (argv[i][2])
+ sprintf(filename, CUPS_DATADIR "/model/%s", argv[i] + 2);
+ else
+ {
+ i ++;
+ sprintf(filename, CUPS_DATADIR "/model/%s", argv[i]);
+ }
+
+ set_printer_file(http, printer, filename);
+ break;
+
+ case 'p' : /* Add/modify a printer */
+ if (argv[i][2])
+ printer = argv[i] + 2;
+ else
+ {
+ i ++;
+ printer = argv[i];
+ }
+
+ if ((host = strchr(printer, '@')) != NULL)
+ {
+ /*
+ * printer@host - reconnect to the named host...
+ */
+
+ httpClose(http);
+
+ *host++ = '\0';
+ if ((http = httpConnect(host, ippPort())) == NULL)
+ {
+ perror("lpadmin: Unable to connect to server");
+ return (1);
+ }
+ }
+ break;
+
+ case 'r' : /* Remove printer from class */
+ if (printer == NULL)
+ {
+ fputs("lpadmin: Unable to remove a printer from the class:\n", stderr);
+ fputs(" You must specify a printer name first!\n", stderr);
+ return (1);
+ }
+
+ if (argv[i][2])
+ delete_printer_from_class(http, printer, argv[i] + 2);
+ else
+ {
+ i ++;
+ delete_printer_from_class(http, printer, argv[i]);
+ }
+ break;
+
+ case 'v' : /* Set the device-uri attribute */
+ if (printer == NULL)
+ {
+ fputs("lpadmin: Unable to set the device URI:\n", stderr);
+ fputs(" You must specify a printer name first!\n", stderr);
+ return (1);
+ }
+
+ if (argv[i][2])
+ set_printer_device(http, printer, argv[i] + 2);
+ else
+ {
+ i ++;
+ set_printer_device(http, printer, argv[i]);
+ }
+ break;
+
+ case 'x' : /* Delete a printer */
+ if (argv[i][2])
+ printer = argv[i] + 2;
+ else
+ {
+ i ++;
+ printer = argv[i];
+ }
+
+ if ((host = strchr(printer, '@')) != NULL)
+ {
+ /*
+ * printer@host - reconnect to the named host...
+ */
+
+ httpClose(http);
+
+ *host++ = '\0';
+ if ((http = httpConnect(host, ippPort())) == NULL)
+ {
+ perror("lpadmin: Unable to connect to server");
+ return (1);
+ }
+ }
+
+ delete_printer(http, printer);
+ i = argc;
+ break;
+
+ case 'D' : /* Set the printer-info attribute */
+ if (printer == NULL)
+ {
+ fputs("lpadmin: Unable to set the printer description:\n", stderr);
+ fputs(" You must specify a printer name first!\n", stderr);
+ return (1);
+ }
+
+ if (argv[i][2])
+ set_printer_info(http, printer, argv[i] + 2);
+ else
+ {
+ i ++;
+ set_printer_info(http, printer, argv[i]);
+ }
+ break;
+
+ case 'L' : /* Set the printer-location attribute */
+ if (printer == NULL)
+ {
+ fputs("lpadmin: Unable to set the printer location:\n", stderr);
+ fputs(" You must specify a printer name first!\n", stderr);
+ return (1);
+ }
+
+ if (argv[i][2])
+ set_printer_location(http, printer, argv[i] + 2);
+ else
+ {
+ i ++;
+ set_printer_location(http, printer, argv[i]);
+ }
+ break;
+
+ case 'P' : /* Use the specified PPD file */
+ if (printer == NULL)
+ {
+ fputs("lpadmin: Unable to set the PPD file:\n", stderr);
+ fputs(" You must specify a printer name first!\n", stderr);
+ return (1);
+ }
+
+ if (argv[i][2])
+ set_printer_file(http, printer, argv[i] + 2);
+ else
+ {
+ i ++;
+ set_printer_file(http, printer, argv[i]);
+ }
+ break;
+
+ default :
+ fprintf(stderr, "lpadmin: Unknown option \'%c\'!\n", argv[i][1]);
+ return (1);
+ }
+ else
+ {
+ fprintf(stderr, "lpadmin: Unknown argument \'%s\'!\n", argv[i]);
+ return (1);
+ }
+
+ if (printer == NULL)
+ {
+ puts("Usage:");
+ puts("");
+ puts(" lpadmin [-h server] -d destination");
+ puts(" lpadmin [-h server] -x destination");
+ puts(" lpadmin [-h server] -p printer [-c add-class] [-i interface] [-m model]");
+ puts(" [-r remove-class] [-v device] [-D description]");
+ puts(" [-P ppd-file]");
+ puts("");
+ }
+
+ httpClose(http);
+
+ return (0);
+}
+
+
+/*
+ * 'add_printer_to_class()' - Add a printer to a class.
+ */
+
+static void
+add_printer_to_class(http_t *http, /* I - Server connection */
+ char *printer, /* I - Printer to add */
+ char *pclass) /* I - Class to add to */
+{
+ int i; /* Looping var */
+ ipp_t *request, /* IPP Request */
+ *response; /* IPP Response */
+ ipp_attribute_t *attr, /* Current attribute */
+ *members; /* Members in class */
+ cups_lang_t *language; /* Default language */
+ char uri[HTTP_MAX_URI]; /* URI for printer/class */
+
+
+ DEBUG_printf(("add_printer_to_class(%08x, \"%s\", \"%s\")\n", http,
+ printer, pclass));
+
+ /*
+ * Build an IPP_GET_PRINTER_ATTRIBUTES request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * printer-uri
+ */
+
+ sprintf(uri, "ipp://localhost/classes/%s", pclass);
+
+ request = ippNew();
+
+ request->request.op.operation_id = IPP_GET_PRINTER_ATTRIBUTES;
+ request->request.op.request_id = 1;
+
+ language = cupsLangDefault();
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, cupsLangEncoding(language));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
+ "printer-uri", NULL, uri);
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ response = cupsDoRequest(http, request, "/classes/");
+
+ /*
+ * Build a CUPS_ADD_CLASS request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * printer-uri
+ * member-uris
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = CUPS_ADD_CLASS;
+ request->request.op.request_id = 1;
+
+ language = cupsLangDefault();
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, cupsLangEncoding(language));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
+ "printer-uri", NULL, uri);
+
+ /*
+ * See if the printer is already in the class...
+ */
+
+ if (response != NULL &&
+ (members = ippFindAttribute(response, "member-names", IPP_TAG_NAME)) != NULL)
+ for (i = 0; i < members->num_values; i ++)
+ if (strcasecmp(printer, members->values[i].string.text) == 0)
+ {
+ fprintf(stderr, "lpadmin: Printer %s is already a member of class %s.\n",
+ printer, pclass);
+ ippDelete(request);
+ ippDelete(response);
+ return;
+ }
+
+ /*
+ * OK, the printer isn't part of the class, so add it...
+ */
+
+ sprintf(uri, "ipp://localhost/printers/%s", printer);
+
+ if (response != NULL &&
+ (members = ippFindAttribute(response, "member-uris", IPP_TAG_URI)) != NULL)
+ {
+ /*
+ * Add the printer to the existing list...
+ */
+
+ attr = ippAddStrings(request, IPP_TAG_PRINTER, IPP_TAG_URI,
+ "member-uris", members->num_values + 1, NULL, NULL);
+ for (i = 0; i < members->num_values; i ++)
+ attr->values[i].string.text = strdup(members->values[i].string.text);
+
+ attr->values[i].string.text = strdup(uri);
+ }
+ else
+ ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_URI, "member-uris", NULL, uri);
+
+ /*
+ * Then send the request...
+ */
+
+ ippDelete(response);
+
+ if ((response = cupsDoRequest(http, request, "/admin/")) == NULL)
+ fputs("lpadmin: Unable to add printer to class!\n", stderr);
+ else
+ ippDelete(response);
+}
+
+
+/*
+ * 'default_printer()' - Set the default printing destination.
+ */
+
+static void
+default_printer(http_t *http, /* I - Server connection */
+ char *printer) /* I - Printer name */
+{
+ ipp_t *request, /* IPP Request */
+ *response; /* IPP Response */
+ cups_lang_t *language; /* Default language */
+ char uri[HTTP_MAX_URI]; /* URI for printer/class */
+
+
+ DEBUG_printf(("default_printer(%08x, \"%s\")\n", http, printer));
+
+ /*
+ * Build a CUPS_SET_DEFAULT request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * printer-uri
+ */
+
+ sprintf(uri, "ipp://localhost/printers/%s", printer);
+
+ request = ippNew();
+
+ request->request.op.operation_id = CUPS_SET_DEFAULT;
+ request->request.op.request_id = 1;
+
+ language = cupsLangDefault();
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, cupsLangEncoding(language));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
+ "printer-uri", NULL, uri);
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ if ((response = cupsDoRequest(http, request, "/admin/")) == NULL)
+ fputs("lpadmin: Unable to set default destination!\n", stderr);
+ else
+ {
+ if (response->request.status.status_code == IPP_NOT_FOUND)
+ fprintf(stderr, "lpadmin: Destination %s does not exist!\n", printer);
+
+ ippDelete(response);
+ }
+}
+
+
+/*
+ * 'delete_printer()' - Delete a printer from the system...
+ */
+
+static void
+delete_printer(http_t *http, /* I - Server connection */
+ char *printer) /* I - Printer to delete */
+{
+ ipp_t *request, /* IPP Request */
+ *response; /* IPP Response */
+ cups_lang_t *language; /* Default language */
+ char uri[HTTP_MAX_URI]; /* URI for printer/class */
+
+
+ DEBUG_printf(("delete_printer(%08x, \"%s\")\n", http, printer));
+
+ /*
+ * Build a CUPS_DELETE_PRINTER request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * printer-uri
+ */
+
+ sprintf(uri, "ipp://localhost/printers/%s", printer);
+
+ request = ippNew();
+
+ request->request.op.operation_id = CUPS_DELETE_PRINTER;
+ request->request.op.request_id = 1;
+
+ language = cupsLangDefault();
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, cupsLangEncoding(language));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
+ "printer-uri", NULL, uri);
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ if ((response = cupsDoRequest(http, request, "/admin/")) == NULL)
+ fputs("lpadmin: Unable to delete printer!\n", stderr);
+ else
+ {
+ if (response->request.status.status_code == IPP_NOT_FOUND)
+ fprintf(stderr, "lpadmin: Destination %s does not exist!\n", printer);
+
+ ippDelete(response);
+ }
+}
+
+
+/*
+ * 'delete_printer_from_class()' - Delete a printer from a class.
+ */
+
+static void
+delete_printer_from_class(http_t *http, /* I - Server connection */
+ char *printer, /* I - Printer to remove */
+ char *pclass) /* I - Class to remove from */
+{
+ int i, j, k; /* Looping vars */
+ ipp_t *request, /* IPP Request */
+ *response; /* IPP Response */
+ ipp_attribute_t *attr, /* Current attribute */
+ *members; /* Members in class */
+ cups_lang_t *language; /* Default language */
+ char uri[HTTP_MAX_URI]; /* URI for printer/class */
+
+
+ DEBUG_printf(("delete_printer_from_class(%08x, \"%s\", \"%s\")\n", http,
+ printer, pclass));
+
+ /*
+ * Build an IPP_GET_PRINTER_ATTRIBUTES request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * printer-uri
+ */
+
+ sprintf(uri, "ipp://localhost/classes/%s", pclass);
+
+ request = ippNew();
+
+ request->request.op.operation_id = IPP_GET_PRINTER_ATTRIBUTES;
+ request->request.op.request_id = 1;
+
+ language = cupsLangDefault();
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, cupsLangEncoding(language));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
+ "printer-uri", NULL, uri);
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ if ((response = cupsDoRequest(http, request, "/classes/")) == NULL ||
+ response->request.status.status_code == IPP_NOT_FOUND)
+ {
+ ippDelete(response);
+ fprintf(stderr, "lpadmin: Class %s does not exist!\n", pclass);
+ return;
+ }
+
+ /*
+ * See if the printer is already in the class...
+ */
+
+ if ((members = ippFindAttribute(response, "member-names", IPP_TAG_NAME)) == NULL)
+ {
+ ippDelete(response);
+ fputs("lpadmin: No member names were seen!\n", stderr);
+ return;
+ }
+
+ for (i = 0; i < members->num_values; i ++)
+ if (strcasecmp(printer, members->values[i].string.text) == 0)
+ break;
+
+ if (i >= members->num_values)
+ {
+ fprintf(stderr, "lpadmin: Printer %s is not a member of class %s.\n",
+ printer, pclass);
+ ippDelete(response);
+ return;
+ }
+
+ if (members->num_values == 1)
+ {
+ /*
+ * Build a CUPS_DELETE_CLASS request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * printer-uri
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = CUPS_DELETE_CLASS;
+ request->request.op.request_id = 1;
+
+ language = cupsLangDefault();
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, cupsLangEncoding(language));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
+ "printer-uri", NULL, uri);
+ }
+ else
+ {
+ /*
+ * Build a CUPS_ADD_CLASS request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * printer-uri
+ * member-uris
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = CUPS_ADD_CLASS;
+ request->request.op.request_id = 1;
+
+ language = cupsLangDefault();
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, cupsLangEncoding(language));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
+ "printer-uri", NULL, uri);
+
+ /*
+ * Delete the printer from the class...
+ */
+
+ members = ippFindAttribute(response, "member-uris", IPP_TAG_URI);
+ attr = ippAddStrings(request, IPP_TAG_PRINTER, IPP_TAG_URI,
+ "member-uris", members->num_values - 1, NULL, NULL);
+
+ for (j = 0, k = 0; j < members->num_values; j ++)
+ if (j != i)
+ attr->values[k ++].string.text = strdup(members->values[j].string.text);
+ }
+
+ /*
+ * Then send the request...
+ */
+
+ ippDelete(response);
+
+ if ((response = cupsDoRequest(http, request, "/admin/")) == NULL)
+ fputs("lpadmin: Unable to remove printer from class!\n", stderr);
+ else
+ ippDelete(response);
+}
+
+
+/*
+ * 'enable_printer()' - Enable a printer...
+ */
+
+static void
+enable_printer(http_t *http, /* I - Server connection */
+ char *printer) /* I - Printer to enable */
+{
+ ipp_t *request, /* IPP Request */
+ *response; /* IPP Response */
+ cups_lang_t *language; /* Default language */
+ char uri[HTTP_MAX_URI]; /* URI for printer/class */
+
+
+ DEBUG_printf(("enable_printer(%08x, \"%s\")\n", http, printer));
+
+ /*
+ * Build a CUPS_ADD_PRINTER request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * printer-uri
+ * printer-state
+ * printer-is-accepting-jobs
+ */
+
+ sprintf(uri, "ipp://localhost/printers/%s", printer);
+
+ request = ippNew();
+
+ request->request.op.operation_id = CUPS_ADD_PRINTER;
+ request->request.op.request_id = 1;
+
+ language = cupsLangDefault();
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, cupsLangEncoding(language));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
+ "printer-uri", NULL, uri);
+
+ ippAddInteger(request, IPP_TAG_PRINTER, IPP_TAG_ENUM, "printer-state",
+ IPP_PRINTER_IDLE);
+
+ ippAddBoolean(request, IPP_TAG_PRINTER, "printer-is-accepting-jobs", 1);
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ if ((response = cupsDoRequest(http, request, "/admin/")) == NULL)
+ fputs("lpadmin: Unable to delete printer!\n", stderr);
+ else
+ {
+ if (response->request.status.status_code == IPP_NOT_FOUND)
+ fprintf(stderr, "lpadmin: Destination %s does not exist!\n", printer);
+
+ ippDelete(response);
+ }
+}
+
+
+/*
+ * 'set_printer_device()' - Set the device-uri attribute.
+ */
+
+static void
+set_printer_device(http_t *http, /* I - Server connection */
+ char *printer, /* I - Printer */
+ char *device) /* I - New device URI */
+{
+ ipp_t *request, /* IPP Request */
+ *response; /* IPP Response */
+ cups_lang_t *language; /* Default language */
+ char uri[HTTP_MAX_URI]; /* URI for printer/class */
+
+
+ DEBUG_printf(("set_printer_device(%08x, \"%s\", \"%s\")\n", http, printer,
+ device));
+
+ /*
+ * Build a CUPS_ADD_PRINTER request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * printer-uri
+ */
+
+ sprintf(uri, "ipp://localhost/printers/%s", printer);
+
+ request = ippNew();
+
+ request->request.op.operation_id = CUPS_ADD_PRINTER;
+ request->request.op.request_id = 1;
+
+ language = cupsLangDefault();
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, cupsLangEncoding(language));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
+ "printer-uri", NULL, uri);
+
+ /*
+ * Add the device URI...
+ */
+
+ if (device[0] == '/')
+ {
+ /*
+ * Convert filename to URI...
+ */
+
+ sprintf(uri, "file:%s", device);
+ ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_URI, "device-uri", NULL,
+ uri);
+ }
+ else
+ ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_URI, "device-uri", NULL,
+ device);
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ if ((response = cupsDoRequest(http, request, "/admin/")) == NULL)
+ fputs("lpadmin: Unable to set device-uri attribute!\n", stderr);
+ else
+ ippDelete(response);
+}
+
+
+/*
+ * 'set_printer_file()' - Set the interface script or PPD file.
+ */
+
+static void
+set_printer_file(http_t *http, /* I - Server connection */
+ char *printer, /* I - Printer */
+ char *file) /* I - PPD file or interface script */
+{
+ ipp_t *request, /* IPP Request */
+ *response; /* IPP Response */
+ cups_lang_t *language; /* Default language */
+ char uri[HTTP_MAX_URI]; /* URI for printer/class */
+#ifdef HAVE_LIBZ
+ char tempfile[1024]; /* Temporary filename */
+ FILE *fp; /* Temporary file */
+ gzFile *gz; /* GZIP'd file */
+ char buffer[8192]; /* Copy buffer */
+ int bytes; /* Bytes in buffer */
+
+
+ DEBUG_printf(("set_printer_file(%08x, \"%s\", \"%s\")\n", http, printer,
+ file));
+
+ /*
+ * See if the file is gzip'd; if so, unzip it to a temporary file and
+ * send the uncompressed file.
+ */
+
+ if (strcmp(file + strlen(file) - 3, ".gz") == 0)
+ {
+ /*
+ * Yes, the file is compressed; uncompress to a temp file...
+ */
+
+ if ((fp = fopen(cupsTempFile(tempfile, sizeof(tempfile)), "wb")) == NULL)
+ {
+ perror("lpadmin: Unable to create temporary file");
+ return;
+ }
+
+ if ((gz = gzopen(file, "rb")) == NULL)
+ {
+ perror("lpadmin: Unable to open file");
+ fclose(fp);
+ unlink(tempfile);
+ return;
+ }
+
+ while ((bytes = gzread(gz, buffer, sizeof(buffer))) > 0)
+ fwrite(buffer, bytes, 1, fp);
+
+ fclose(fp);
+ gzclose(gz);
+
+ file = tempfile;
+ }
+#endif /* HAVE_LIBZ */
+
+ /*
+ * Build a CUPS_ADD_PRINTER request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * printer-uri
+ */
+
+ sprintf(uri, "ipp://localhost/printers/%s", printer);
+
+ request = ippNew();
+
+ request->request.op.operation_id = CUPS_ADD_PRINTER;
+ request->request.op.request_id = 1;
+
+ language = cupsLangDefault();
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, cupsLangEncoding(language));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
+ "printer-uri", NULL, uri);
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ if ((response = cupsDoFileRequest(http, request, "/admin/", file)) == NULL)
+ fputs("lpadmin: Unable to set interface script or PPD file!\n", stderr);
+ else
+ ippDelete(response);
+
+#ifdef HAVE_LIBZ
+ /*
+ * Remove the temporary file as needed...
+ */
+
+ if (file == tempfile)
+ unlink(tempfile);
+#endif /* HAVE_LIBZ */
+}
+
+
+/*
+ * 'set_printer_info()' - Set the printer description string.
+ */
+
+static void
+set_printer_info(http_t *http, /* I - Server connection */
+ char *printer, /* I - Printer */
+ char *info) /* I - New description string */
+{
+ ipp_t *request, /* IPP Request */
+ *response; /* IPP Response */
+ cups_lang_t *language; /* Default language */
+ char uri[HTTP_MAX_URI]; /* URI for printer/class */
+
+
+ DEBUG_printf(("set_printer_info(%08x, \"%s\", \"%s\")\n", http, printer,
+ info));
+
+ /*
+ * Build a CUPS_ADD_PRINTER request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * printer-uri
+ */
+
+ sprintf(uri, "ipp://localhost/printers/%s", printer);
+
+ request = ippNew();
+
+ request->request.op.operation_id = CUPS_ADD_PRINTER;
+ request->request.op.request_id = 1;
+
+ language = cupsLangDefault();
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, cupsLangEncoding(language));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
+ "printer-uri", NULL, uri);
+
+ /*
+ * Add the info string...
+ */
+
+ ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-info", NULL,
+ info);
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ if ((response = cupsDoRequest(http, request, "/admin/")) == NULL)
+ fputs("lpadmin: Unable to set printer-info attribute!\n", stderr);
+ else
+ ippDelete(response);
+}
+
+
+/*
+ * 'set_printer_location()' - Set the printer location string.
+ */
+
+static void
+set_printer_location(http_t *http, /* I - Server connection */
+ char *printer, /* I - Printer */
+ char *location) /* I - New location string */
+{
+ ipp_t *request, /* IPP Request */
+ *response; /* IPP Response */
+ cups_lang_t *language; /* Default language */
+ char uri[HTTP_MAX_URI]; /* URI for printer/class */
+
+
+ DEBUG_printf(("set_printer_location(%08x, \"%s\", \"%s\")\n", http, printer,
+ location));
+
+ /*
+ * Build a CUPS_ADD_PRINTER request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * printer-uri
+ */
+
+ sprintf(uri, "ipp://localhost/printers/%s", printer);
+
+ request = ippNew();
+
+ request->request.op.operation_id = CUPS_ADD_PRINTER;
+ request->request.op.request_id = 1;
+
+ language = cupsLangDefault();
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, cupsLangEncoding(language));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
+ "printer-uri", NULL, uri);
+
+ /*
+ * Add the location string...
+ */
+
+ ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-location", NULL,
+ location);
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ if ((response = cupsDoRequest(http, request, "/admin/")) == NULL)
+ fputs("lpadmin: Unable to set printer-location attribute!\n", stderr);
+ else
+ ippDelete(response);
+}
+
+
+/*
+ * End of "$Id$".
+ */
diff --git a/systemv/lpstat.c b/systemv/lpstat.c
new file mode 100644
index 000000000..56b3822e1
--- /dev/null
+++ b/systemv/lpstat.c
@@ -0,0 +1,1268 @@
+/*
+ * "$Id$"
+ *
+ * "lpstat" command for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 1997-1999 by Easy Software Products.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE.txt" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636-3111 USA
+ *
+ * Voice: (301) 373-9603
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * Contents:
+ *
+ * main() - Parse options and show status information.
+ * show_accepting() - Show acceptance status.
+ * show_classes() - Show printer classes.
+ * show_default() - Show default destination.
+ * show_devices() - Show printer devices.
+ * show_jobs() - Show active print jobs.
+ * show_printers() - Show printers.
+ * show_scheduler() - Show scheduler status.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <cups/cups.h>
+#include <cups/language.h>
+#include <cups/debug.h>
+
+
+/*
+ * Local functions...
+ */
+
+static void show_accepting(http_t *, const char *);
+static void show_classes(http_t *, const char *);
+static void show_default(http_t *);
+static void show_devices(http_t *, const char *);
+static void show_jobs(http_t *, const char *, const char *);
+static void show_printers(http_t *, const char *);
+static void show_scheduler(http_t *);
+
+
+/*
+ * 'main()' - Parse options and show status information.
+ */
+
+int
+main(int argc, /* I - Number of command-line arguments */
+ char *argv[]) /* I - Command-line arguments */
+{
+ int i; /* Looping var */
+ http_t *http; /* Connection to server */
+
+
+ http = httpConnect(cupsServer(), ippPort());
+
+ for (i = 1; i < argc; i ++)
+ if (argv[i][0] == '-')
+ switch (argv[i][1])
+ {
+ case 'a' : /* Show acceptance status */
+ if (argv[i][2] != '\0')
+ show_accepting(http, argv[i] + 2);
+ else if ((i + 1) < argc && argv[i + 1][0] != '-')
+ {
+ i ++;
+ show_accepting(http, argv[i]);
+ }
+ else
+ show_accepting(http, NULL);
+ break;
+
+ case 'c' : /* Show classes and members */
+ if (argv[i][2] != '\0')
+ show_classes(http, argv[i] + 2);
+ else if ((i + 1) < argc && argv[i + 1][0] != '-')
+ {
+ i ++;
+ show_classes(http, argv[i]);
+ }
+ else
+ show_classes(http, NULL);
+ break;
+
+ case 'd' : /* Show default destination */
+ show_default(http);
+ break;
+
+ case 'h' : /* Connect to host */
+ httpClose(http);
+
+ if (argv[i][2] != '\0')
+ http = httpConnect(argv[i] + 2, ippPort());
+ else
+ {
+ i ++;
+ http = httpConnect(argv[i], ippPort());
+ }
+
+ if (http == NULL)
+ {
+ perror("lpstat: Unable to connect to server");
+ return (1);
+ }
+ break;
+
+ case 'o' : /* Show jobs by destination */
+ if (argv[i][2] != '\0')
+ show_jobs(http, argv[i] + 2, NULL);
+ else if ((i + 1) < argc && argv[i + 1][0] != '-')
+ {
+ i ++;
+ show_jobs(http, argv[i], NULL);
+ }
+ else
+ show_jobs(http, NULL, NULL);
+ break;
+
+ case 'p' : /* Show printers */
+ if (argv[i][2] != '\0')
+ show_printers(http, argv[i] + 2);
+ else if ((i + 1) < argc && argv[i + 1][0] != '-')
+ {
+ i ++;
+ show_printers(http, argv[i]);
+ }
+ else
+ show_printers(http, NULL);
+ break;
+
+ case 'r' : /* Show scheduler status */
+ show_scheduler(http);
+ break;
+
+ case 's' : /* Show summary */
+ show_default(http);
+ show_classes(http, NULL);
+ show_devices(http, NULL);
+ break;
+
+ case 't' : /* Show all info */
+ show_scheduler(http);
+ show_default(http);
+ show_classes(http, NULL);
+ show_devices(http, NULL);
+ show_accepting(http, NULL);
+ show_printers(http, NULL);
+ show_jobs(http, NULL, NULL);
+ break;
+
+ case 'u' : /* Show jobs by user */
+ if (argv[i][2] != '\0')
+ show_jobs(http, NULL, argv[i] + 2);
+ else if ((i + 1) < argc && argv[i + 1][0] != '-')
+ {
+ i ++;
+ show_jobs(http, NULL, argv[i]);
+ }
+ else
+ show_jobs(http, NULL, NULL);
+ break;
+
+ case 'v' : /* Show printer devices */
+ if (argv[i][2] != '\0')
+ show_devices(http, argv[i] + 2);
+ else if ((i + 1) < argc && argv[i + 1][0] != '-')
+ {
+ i ++;
+ show_devices(http, argv[i]);
+ }
+ else
+ show_devices(http, NULL);
+ break;
+
+
+ default :
+ fprintf(stderr, "lpstat: Unknown option \'%c\'!\n", argv[i][1]);
+ return (1);
+ }
+ else
+ {
+ fprintf(stderr, "lpstat: Unknown argument \'%s\'!\n", argv[i]);
+ return (1);
+ }
+
+ if (argc == 1)
+ show_jobs(http, NULL, cupsUser());
+
+ return (0);
+}
+
+
+/*
+ * 'show_accepting()' - Show acceptance status.
+ */
+
+static void
+show_accepting(http_t *http, /* I - HTTP connection to server */
+ const char *dests) /* I - Destinations */
+{
+ ipp_t *request, /* IPP Request */
+ *response; /* IPP Response */
+ ipp_attribute_t *attr; /* Current attribute */
+ cups_lang_t *language; /* Default language */
+ const char *printer, /* Printer name */
+ *message; /* Printer device URI */
+ int accepting; /* Accepting requests? */
+ const char *dptr, /* Pointer into destination list */
+ *ptr; /* Pointer into printer name */
+ int match; /* Non-zero if this job matches */
+
+
+ DEBUG_printf(("show_accepting(%08x, %08x)\n", http, dests));
+
+ if (http == NULL)
+ return;
+
+ /*
+ * Build a CUPS_GET_PRINTERS request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = CUPS_GET_PRINTERS;
+ request->request.op.request_id = 1;
+
+ language = cupsLangDefault();
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, cupsLangEncoding(language));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ if ((response = cupsDoRequest(http, request, "/printers/")) != NULL)
+ {
+ DEBUG_puts("show_accepting: request succeeded...");
+
+ /*
+ * Loop through the printers returned in the list and display
+ * their devices...
+ */
+
+ for (attr = response->attrs; attr != NULL; attr = attr->next)
+ {
+ /*
+ * Skip leading attributes until we hit a job...
+ */
+
+ while (attr != NULL && attr->group_tag != IPP_TAG_PRINTER)
+ attr = attr->next;
+
+ if (attr == NULL)
+ break;
+
+ /*
+ * Pull the needed attributes from this job...
+ */
+
+ printer = NULL;
+ message = NULL;
+ accepting = 1;
+
+ while (attr != NULL && attr->group_tag == IPP_TAG_PRINTER)
+ {
+ if (strcmp(attr->name, "printer-name") == 0 &&
+ attr->value_tag == IPP_TAG_NAME)
+ printer = attr->values[0].string.text;
+
+ if (strcmp(attr->name, "printer-state-message") == 0 &&
+ attr->value_tag == IPP_TAG_TEXT)
+ message = attr->values[0].string.text;
+
+ if (strcmp(attr->name, "printer-is-accepting-jobs") == 0 &&
+ attr->value_tag == IPP_TAG_BOOLEAN)
+ accepting = attr->values[0].boolean;
+
+ attr = attr->next;
+ }
+
+ /*
+ * See if we have everything needed...
+ */
+
+ if (printer == NULL)
+ {
+ if (attr == NULL)
+ break;
+ else
+ continue;
+ }
+
+ /*
+ * See if this is a printer we're interested in...
+ */
+
+ match = dests == NULL;
+
+ if (dests != NULL)
+ {
+ for (dptr = dests; *dptr != '\0';)
+ {
+ /*
+ * Skip leading whitespace and commas...
+ */
+
+ while (isspace(*dptr) || *dptr == ',')
+ dptr ++;
+
+ if (*dptr == '\0')
+ break;
+
+ /*
+ * Compare names...
+ */
+
+ for (ptr = printer;
+ *ptr != '\0' && *dptr != '\0' && *ptr == *dptr;
+ ptr ++, dptr ++);
+
+ if (*ptr == '\0' && (*dptr == '\0' || *dptr == ',' || isspace(*dptr)))
+ {
+ match = 1;
+ break;
+ }
+
+ /*
+ * Skip trailing junk...
+ */
+
+ while (!isspace(*dptr) && *dptr != '\0')
+ dptr ++;
+ while (isspace(*dptr) || *dptr == ',')
+ dptr ++;
+
+ if (*dptr == '\0')
+ break;
+ }
+ }
+
+ /*
+ * Display the printer entry if needed...
+ */
+
+ if (match)
+ {
+ if (accepting)
+ printf("%s accepting requests\n", printer);
+ else
+ printf("%s not accepting requests -\n\t%s\n", printer,
+ message == NULL ? "reason unknown" : message);
+ }
+
+ if (attr == NULL)
+ break;
+ }
+
+ ippDelete(response);
+ }
+}
+
+
+/*
+ * 'show_classes()' - Show printer classes.
+ */
+
+static void
+show_classes(http_t *http, /* I - HTTP connection to server */
+ const char *dests) /* I - Destinations */
+{
+ int i; /* Looping var */
+ ipp_t *request, /* IPP Request */
+ *response; /* IPP Response */
+ ipp_attribute_t *attr; /* Current attribute */
+ cups_lang_t *language; /* Default language */
+ const char *printer; /* Printer class name */
+ ipp_attribute_t *members; /* Printer members */
+ const char *dptr, /* Pointer into destination list */
+ *ptr; /* Pointer into printer name */
+ int match; /* Non-zero if this job matches */
+
+
+ DEBUG_printf(("show_classes(%08x, %08x)\n", http, dests));
+
+ if (http == NULL)
+ return;
+
+ /*
+ * Build a CUPS_GET_CLASSES request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = CUPS_GET_CLASSES;
+ request->request.op.request_id = 1;
+
+ language = cupsLangDefault();
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, cupsLangEncoding(language));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ if ((response = cupsDoRequest(http, request, "/classes/")) != NULL)
+ {
+ DEBUG_puts("show_devices: request succeeded...");
+
+ /*
+ * Loop through the printers returned in the list and display
+ * their devices...
+ */
+
+ for (attr = response->attrs; attr != NULL; attr = attr->next)
+ {
+ /*
+ * Skip leading attributes until we hit a job...
+ */
+
+ while (attr != NULL && attr->group_tag != IPP_TAG_PRINTER)
+ attr = attr->next;
+
+ if (attr == NULL)
+ break;
+
+ /*
+ * Pull the needed attributes from this job...
+ */
+
+ printer = NULL;
+ members = NULL;
+
+ while (attr != NULL && attr->group_tag == IPP_TAG_PRINTER)
+ {
+ if (strcmp(attr->name, "printer-name") == 0 &&
+ attr->value_tag == IPP_TAG_NAME)
+ printer = attr->values[0].string.text;
+
+ if (strcmp(attr->name, "member-names") == 0 &&
+ attr->value_tag == IPP_TAG_NAME)
+ members = attr;
+
+ attr = attr->next;
+ }
+
+ /*
+ * See if we have everything needed...
+ */
+
+ if (printer == NULL || members == NULL)
+ {
+ if (attr == NULL)
+ break;
+ else
+ continue;
+ }
+
+ /*
+ * See if this is a printer we're interested in...
+ */
+
+ match = dests == NULL;
+
+ if (dests != NULL)
+ {
+ for (dptr = dests; *dptr != '\0';)
+ {
+ /*
+ * Skip leading whitespace and commas...
+ */
+
+ while (isspace(*dptr) || *dptr == ',')
+ dptr ++;
+
+ if (*dptr == '\0')
+ break;
+
+ /*
+ * Compare names...
+ */
+
+ for (ptr = printer;
+ *ptr != '\0' && *dptr != '\0' && *ptr == *dptr;
+ ptr ++, dptr ++);
+
+ if (*ptr == '\0' && (*dptr == '\0' || *dptr == ',' || isspace(*dptr)))
+ {
+ match = 1;
+ break;
+ }
+
+ /*
+ * Skip trailing junk...
+ */
+
+ while (!isspace(*dptr) && *dptr != '\0')
+ dptr ++;
+ while (isspace(*dptr) || *dptr == ',')
+ dptr ++;
+
+ if (*dptr == '\0')
+ break;
+ }
+ }
+
+ /*
+ * Display the printer entry if needed...
+ */
+
+ if (match)
+ {
+ printf("members of class %s:\n", printer);
+ for (i = 0; i < members->num_values; i ++)
+ printf("\t%s\n", members->values[i].string.text);
+ }
+
+ if (attr == NULL)
+ break;
+ }
+
+ ippDelete(response);
+ }
+}
+
+
+/*
+ * 'show_default()' - Show default destination.
+ */
+
+static void
+show_default(http_t *http) /* I - HTTP connection to server */
+{
+ ipp_t *request, /* IPP Request */
+ *response; /* IPP Response */
+ ipp_attribute_t *attr; /* Current attribute */
+ cups_lang_t *language; /* Default language */
+
+
+ DEBUG_printf(("show_default(%08x)\n", http));
+
+ if (http == NULL)
+ return;
+
+ /*
+ * Build a CUPS_GET_DEFAULT request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = CUPS_GET_DEFAULT;
+ request->request.op.request_id = 1;
+
+ language = cupsLangDefault();
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, cupsLangEncoding(language));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ if ((response = cupsDoRequest(http, request, "/printers/")) != NULL)
+ {
+ if ((attr = ippFindAttribute(response, "printer-name", IPP_TAG_NAME)) != NULL)
+ printf("system default destination: %s\n", attr->values[0].string.text);
+ else
+ puts("no system default destination");
+
+ ippDelete(response);
+ }
+ else
+ puts("no system default destination");
+}
+
+
+/*
+ * 'show_devices()' - Show printer devices.
+ */
+
+static void
+show_devices(http_t *http, /* I - HTTP connection to server */
+ const char *dests) /* I - Destinations */
+{
+ ipp_t *request, /* IPP Request */
+ *response; /* IPP Response */
+ ipp_attribute_t *attr; /* Current attribute */
+ cups_lang_t *language; /* Default language */
+ const char *printer, /* Printer name */
+ *device, /* Printer device URI */
+ *dptr, /* Pointer into destination list */
+ *ptr; /* Pointer into printer name */
+ int match; /* Non-zero if this job matches */
+
+
+ DEBUG_printf(("show_devices(%08x, %08x)\n", http, dests));
+
+ if (http == NULL)
+ return;
+
+ /*
+ * Build a CUPS_GET_PRINTERS request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = CUPS_GET_PRINTERS;
+ request->request.op.request_id = 1;
+
+ language = cupsLangDefault();
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, cupsLangEncoding(language));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ if ((response = cupsDoRequest(http, request, "/printers/")) != NULL)
+ {
+ DEBUG_puts("show_devices: request succeeded...");
+
+ /*
+ * Loop through the printers returned in the list and display
+ * their devices...
+ */
+
+ for (attr = response->attrs; attr != NULL; attr = attr->next)
+ {
+ /*
+ * Skip leading attributes until we hit a job...
+ */
+
+ while (attr != NULL && attr->group_tag != IPP_TAG_PRINTER)
+ attr = attr->next;
+
+ if (attr == NULL)
+ break;
+
+ /*
+ * Pull the needed attributes from this job...
+ */
+
+ printer = NULL;
+ device = NULL;
+
+ while (attr != NULL && attr->group_tag == IPP_TAG_PRINTER)
+ {
+ if (strcmp(attr->name, "printer-name") == 0 &&
+ attr->value_tag == IPP_TAG_NAME)
+ printer = attr->values[0].string.text;
+
+ if (strcmp(attr->name, "device-uri") == 0 &&
+ attr->value_tag == IPP_TAG_URI)
+ device = attr->values[0].string.text;
+
+ attr = attr->next;
+ }
+
+ /*
+ * See if we have everything needed...
+ */
+
+ if (printer == NULL || device == NULL)
+ {
+ if (attr == NULL)
+ break;
+ else
+ continue;
+ }
+
+ /*
+ * See if this is a printer we're interested in...
+ */
+
+ match = dests == NULL;
+
+ if (dests != NULL)
+ {
+ for (dptr = dests; *dptr != '\0';)
+ {
+ /*
+ * Skip leading whitespace and commas...
+ */
+
+ while (isspace(*dptr) || *dptr == ',')
+ dptr ++;
+
+ if (*dptr == '\0')
+ break;
+
+ /*
+ * Compare names...
+ */
+
+ for (ptr = printer;
+ *ptr != '\0' && *dptr != '\0' && *ptr == *dptr;
+ ptr ++, dptr ++);
+
+ if (*ptr == '\0' && (*dptr == '\0' || *dptr == ',' || isspace(*dptr)))
+ {
+ match = 1;
+ break;
+ }
+
+ /*
+ * Skip trailing junk...
+ */
+
+ while (!isspace(*dptr) && *dptr != '\0')
+ dptr ++;
+ while (isspace(*dptr) || *dptr == ',')
+ dptr ++;
+
+ if (*dptr == '\0')
+ break;
+ }
+ }
+
+ /*
+ * Display the printer entry if needed...
+ */
+
+ if (match)
+ {
+ if (strncmp(device, "file:", 5) == 0)
+ printf("device for %s: %s\n", printer, device + 5);
+ else
+ printf("device for %s: %s\n", printer, device);
+ }
+
+ if (attr == NULL)
+ break;
+ }
+
+ ippDelete(response);
+ }
+}
+
+
+/*
+ * 'show_jobs()' - Show active print jobs.
+ */
+
+static void
+show_jobs(http_t *http, /* I - HTTP connection to server */
+ const char *dests, /* I - Destinations */
+ const char *users) /* I - Users */
+{
+ ipp_t *request, /* IPP Request */
+ *response; /* IPP Response */
+ ipp_attribute_t *attr; /* Current attribute */
+ cups_lang_t *language; /* Default language */
+ const char *dest, /* Pointer into job-printer-uri */
+ *username; /* Pointer to job-originating-user-name */
+ int jobid, /* job-id */
+ size; /* job-k-octets */
+ const char *dptr, /* Pointer into destination list */
+ *ptr; /* Pointer into printer name */
+ int match; /* Non-zero if this job matches */
+
+
+ DEBUG_printf(("show_jobs(%08x, %08x, %08x)\n", http, dests, users));
+
+ if (http == NULL)
+ return;
+
+ /*
+ * Build a IPP_GET_JOBS request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * job-uri
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = IPP_GET_JOBS;
+ request->request.op.request_id = 1;
+
+ language = cupsLangDefault();
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, cupsLangEncoding(language));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri",
+ NULL, "ipp://localhost/jobs/");
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ if ((response = cupsDoRequest(http, request, "/jobs/")) != NULL)
+ {
+ /*
+ * Loop through the job list and display them...
+ */
+
+ for (attr = response->attrs; attr != NULL; attr = attr->next)
+ {
+ /*
+ * Skip leading attributes until we hit a job...
+ */
+
+ while (attr != NULL && attr->group_tag != IPP_TAG_JOB)
+ attr = attr->next;
+
+ if (attr == NULL)
+ break;
+
+ /*
+ * Pull the needed attributes from this job...
+ */
+
+ jobid = 0;
+ size = 0;
+ username = NULL;
+ dest = NULL;
+
+ while (attr != NULL && attr->group_tag == IPP_TAG_JOB)
+ {
+ if (strcmp(attr->name, "job-id") == 0 &&
+ attr->value_tag == IPP_TAG_INTEGER)
+ jobid = attr->values[0].integer;
+
+ if (strcmp(attr->name, "job-k-octets") == 0 &&
+ attr->value_tag == IPP_TAG_INTEGER)
+ size = attr->values[0].integer * 1024;
+
+ if (strcmp(attr->name, "job-printer-uri") == 0 &&
+ attr->value_tag == IPP_TAG_URI)
+ if ((dest = strrchr(attr->values[0].string.text, '/')) != NULL)
+ dest ++;
+
+ if (strcmp(attr->name, "job-originating-user-name") == 0 &&
+ attr->value_tag == IPP_TAG_NAME)
+ username = attr->values[0].string.text;
+
+ attr = attr->next;
+ }
+
+ /*
+ * See if we have everything needed...
+ */
+
+ if (dest == NULL || jobid == 0)
+ {
+ if (attr == NULL)
+ break;
+ else
+ continue;
+ }
+
+ /*
+ * See if this is a job we're interested in...
+ */
+
+ match = dests == NULL && users == NULL;
+
+ if (dests != NULL)
+ {
+ for (dptr = dests; *dptr != '\0';)
+ {
+ /*
+ * Skip leading whitespace and commas...
+ */
+
+ while (isspace(*dptr) || *dptr == ',')
+ dptr ++;
+
+ if (*dptr == '\0')
+ break;
+
+ /*
+ * Compare names...
+ */
+
+ for (ptr = dest;
+ *ptr != '\0' && *dptr != '\0' && *ptr == *dptr;
+ ptr ++, dptr ++);
+
+ if (*ptr == '\0' && (*dptr == '\0' || *dptr == ',' || isspace(*dptr)))
+ {
+ match = 1;
+ break;
+ }
+
+ /*
+ * Skip trailing junk...
+ */
+
+ while (!isspace(*dptr) && *dptr != '\0')
+ dptr ++;
+ while (isspace(*dptr) || *dptr == ',')
+ dptr ++;
+
+ if (*dptr == '\0')
+ break;
+ }
+ }
+
+ if (users != NULL && username != NULL)
+ {
+ for (dptr = users; *dptr != '\0';)
+ {
+ /*
+ * Skip leading whitespace and commas...
+ */
+
+ while (isspace(*dptr) || *dptr == ',')
+ dptr ++;
+
+ if (*dptr == '\0')
+ break;
+
+ /*
+ * Compare names...
+ */
+
+ for (ptr = username;
+ *ptr != '\0' && *dptr != '\0' && *ptr == *dptr;
+ ptr ++, dptr ++);
+
+ if (*ptr == '\0' && (*dptr == '\0' || *dptr == ',' || isspace(*dptr)))
+ {
+ match = 1;
+ break;
+ }
+
+ /*
+ * Skip trailing junk...
+ */
+
+ while (!isspace(*dptr) && *dptr != '\0')
+ dptr ++;
+ while (isspace(*dptr) || *dptr == ',')
+ dptr ++;
+
+ if (*dptr == '\0')
+ break;
+ }
+ }
+
+ /*
+ * Display the job...
+ */
+
+ if (match)
+ printf("%s-%d %s %d\n", dest, jobid, username ? username : "unknown",
+ size);
+
+ if (attr == NULL)
+ break;
+ }
+
+ ippDelete(response);
+ }
+}
+
+
+/*
+ * 'show_printers()' - Show printers.
+ */
+
+static void
+show_printers(http_t *http, /* I - HTTP connection to server */
+ const char *dests)/* I - Destinations */
+{
+ ipp_t *request, /* IPP Request */
+ *response, /* IPP Response */
+ *jobs; /* IPP Get Jobs response */
+ ipp_attribute_t *attr; /* Current attribute */
+ cups_lang_t *language; /* Default language */
+ const char *printer, /* Printer name */
+ *message; /* Printer state message */
+ ipp_pstate_t pstate; /* Printer state */
+ int jobid; /* Job ID of current job */
+ const char *dptr, /* Pointer into destination list */
+ *ptr; /* Pointer into printer name */
+ int match; /* Non-zero if this job matches */
+ char printer_uri[HTTP_MAX_URI];
+ /* Printer URI */
+
+
+ DEBUG_printf(("show_printers(%08x, %08x)\n", http, dests));
+
+ if (http == NULL)
+ return;
+
+ /*
+ * Build a CUPS_GET_PRINTERS request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = CUPS_GET_PRINTERS;
+ request->request.op.request_id = 1;
+
+ language = cupsLangDefault();
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, cupsLangEncoding(language));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ if ((response = cupsDoRequest(http, request, "/printers/")) != NULL)
+ {
+ DEBUG_puts("show_printers: request succeeded...");
+
+ /*
+ * Loop through the printers returned in the list and display
+ * their status...
+ */
+
+ for (attr = response->attrs; attr != NULL; attr = attr->next)
+ {
+ /*
+ * Skip leading attributes until we hit a job...
+ */
+
+ while (attr != NULL && attr->group_tag != IPP_TAG_PRINTER)
+ attr = attr->next;
+
+ if (attr == NULL)
+ break;
+
+ /*
+ * Pull the needed attributes from this job...
+ */
+
+ printer = NULL;
+ pstate = IPP_PRINTER_IDLE;
+ message = NULL;
+ jobid = 0;
+
+ while (attr != NULL && attr->group_tag == IPP_TAG_PRINTER)
+ {
+ if (strcmp(attr->name, "printer-name") == 0 &&
+ attr->value_tag == IPP_TAG_NAME)
+ printer = attr->values[0].string.text;
+
+ if (strcmp(attr->name, "printer-state") == 0 &&
+ attr->value_tag == IPP_TAG_ENUM)
+ pstate = (ipp_pstate_t)attr->values[0].integer;
+
+ if (strcmp(attr->name, "printer-state-message") == 0 &&
+ attr->value_tag == IPP_TAG_TEXT)
+ message = attr->values[0].string.text;
+
+ attr = attr->next;
+ }
+
+ /*
+ * See if we have everything needed...
+ */
+
+ if (printer == NULL)
+ {
+ if (attr == NULL)
+ break;
+ else
+ continue;
+ }
+
+ /*
+ * See if this is a printer we're interested in...
+ */
+
+ match = dests == NULL;
+
+ if (dests != NULL)
+ {
+ for (dptr = dests; *dptr != '\0';)
+ {
+ /*
+ * Skip leading whitespace and commas...
+ */
+
+ while (isspace(*dptr) || *dptr == ',')
+ dptr ++;
+
+ if (*dptr == '\0')
+ break;
+
+ /*
+ * Compare names...
+ */
+
+ for (ptr = printer;
+ *ptr != '\0' && *dptr != '\0' && *ptr == *dptr;
+ ptr ++, dptr ++);
+
+ if (*ptr == '\0' && (*dptr == '\0' || *dptr == ',' || isspace(*dptr)))
+ {
+ match = 1;
+ break;
+ }
+
+ /*
+ * Skip trailing junk...
+ */
+
+ while (!isspace(*dptr) && *dptr != '\0')
+ dptr ++;
+ while (isspace(*dptr) || *dptr == ',')
+ dptr ++;
+
+ if (*dptr == '\0')
+ break;
+ }
+ }
+
+ /*
+ * Display the printer entry if needed...
+ */
+
+ if (match)
+ {
+ /*
+ * If the printer state is "IPP_PRINTER_PROCESSING", then grab the
+ * current job for the printer.
+ */
+
+ if (pstate == IPP_PRINTER_PROCESSING)
+ {
+ /*
+ * Build an IPP_GET_JOBS request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * printer-uri
+ * limit
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = IPP_GET_JOBS;
+ request->request.op.request_id = 1;
+
+ language = cupsLangDefault();
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL,
+ cupsLangEncoding(language));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL,
+ language->language);
+
+ sprintf(printer_uri, "ipp://%s/printers/%s", http->hostname, printer);
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
+ "printer-uri", NULL, printer_uri);
+
+ ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
+ "limit", 1);
+
+ if ((jobs = cupsDoRequest(http, request, "/jobs/")) != NULL)
+ {
+ if ((attr = ippFindAttribute(jobs, "job-id", IPP_TAG_INTEGER)) != NULL)
+ jobid = attr->values[0].integer;
+
+ ippDelete(jobs);
+ }
+ }
+
+ /*
+ * Display it...
+ */
+
+ switch (pstate)
+ {
+ case IPP_PRINTER_IDLE :
+ printf("printer %s is idle.\n", printer);
+ break;
+ case IPP_PRINTER_PROCESSING :
+ printf("printer %s now printing %s-%d.\n", printer, printer, jobid);
+ break;
+ case IPP_PRINTER_STOPPED :
+ printf("printer %s disabled -\n\t%s\n", printer,
+ message == NULL ? "reason unknown" : message);
+ break;
+ }
+ }
+
+ if (attr == NULL)
+ break;
+ }
+
+ ippDelete(response);
+ }
+}
+
+
+/*
+ * 'show_scheduler()' - Show scheduler status.
+ */
+
+static void
+show_scheduler(http_t *http) /* I - HTTP connection to server */
+{
+ printf("scheduler is %srunning\n", http == NULL ? "not " : "");
+}
+
+
+/*
+ * End of "$Id$".
+ */